├── .branched_from ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── blocked-from-upgrading.md │ ├── bug_report.md │ └── failed-upgrade.md └── workflows │ ├── close_prs.yml │ ├── openstack-almalinux-8.yml │ ├── openstack-centos-7.yml │ ├── openstack-cloudlinux-7.yml │ ├── openstack-ubuntu-20.yml │ ├── openstack │ ├── cloud-config.yaml │ ├── cloud-init.yml │ ├── main.tf │ ├── outputs.tf │ ├── prune_openstack │ ├── reboot_watch │ ├── ssh_retry │ ├── status_marker │ └── variables.tf │ ├── pruner.yml │ ├── testsuite.yml │ └── website.yml ├── .gitignore ├── .perlcriticrc ├── .perltidyrc ├── Changelog.md ├── LICENSE.md ├── Makefile ├── README.md ├── Release.md ├── docs-website-src ├── .hugo_build.lock ├── .nojekyll ├── archetypes │ └── default.md ├── config.toml ├── content │ ├── Changelog.md │ ├── _index.md │ ├── blockers.md │ ├── elevate-faq.md │ ├── getting-started.md │ └── whitepaper.md ├── public ├── static │ ├── images │ │ ├── elevate-logo-social.png │ │ ├── elevate-logo.png │ │ ├── elevate-logo.svg │ │ └── favicon.png │ ├── js │ │ └── jquery-3.4.1.min.js │ └── sass │ │ └── main.min.css └── themes │ └── cptheme │ ├── LICENSE │ ├── archetypes │ └── default.md │ ├── layouts │ ├── 404.html │ ├── _default │ │ ├── baseof.html │ │ ├── list.html │ │ └── single.html │ ├── index.html │ ├── page │ │ └── single.html │ └── partials │ │ ├── breadcrumb.html │ │ ├── footer.html │ │ ├── head.html │ │ ├── header.html │ │ ├── left_menu.html │ │ ├── metadata.html │ │ └── script.html │ ├── static │ ├── css │ │ ├── bootstrap.min.css │ │ └── style.css │ └── js │ │ └── copy-code.js │ └── theme.toml ├── docs ├── .nojekyll ├── Elevating cPanel & WHM_2022.pdf ├── cPanel-CLA.pdf └── elevate-video.png ├── elevate-cpanel ├── lib └── Elevate │ ├── Components.pm │ ├── Components │ ├── AbsoluteSymlinks.pm │ ├── Acronis.pm │ ├── AutoSSL.pm │ ├── Base.pm │ ├── BootKernel.pm │ ├── CCS.pm │ ├── CloudLinux.pm │ ├── CryptoPolicies.pm │ ├── DNS.pm │ ├── DatabaseUpgrade.pm │ ├── DiskSpace.pm │ ├── Distros.pm │ ├── EA4.pm │ ├── ELS.pm │ ├── ElevateScript.pm │ ├── Grub2ChecksWorkarounds.pm │ ├── Grub2ControlTest.pm │ ├── Imunify.pm │ ├── InfluxDB.pm │ ├── IsContainer.pm │ ├── JetBackup.pm │ ├── Kernel.pm │ ├── KernelCare.pm │ ├── Leapp.pm │ ├── Lists.pm │ ├── LiteSpeed.pm │ ├── MountPoints.pm │ ├── MySQL.pm │ ├── NICs.pm │ ├── NetworkManager.pm │ ├── NixStats.pm │ ├── OVH.pm │ ├── PECL.pm │ ├── PackageDupes.pm │ ├── PackageRestore.pm │ ├── Panopta.pm │ ├── PerlXS.pm │ ├── PostgreSQL.pm │ ├── R1Soft.pm │ ├── Repositories.pm │ ├── RmMod.pm │ ├── RpmDB.pm │ ├── SSH.pm │ ├── Softaculous.pm │ ├── Ufw.pm │ ├── UnconvertedModules.pm │ ├── UpdateReleaseUpgrades.pm │ ├── UpdateSystem.pm │ ├── WHM.pm │ ├── WPToolkit.pm │ ├── cPanelPlugins.pm │ └── cPanelPrep.pm │ ├── Constants.pm │ ├── Database.pm │ ├── EA4.pm │ ├── Fetch.pm │ ├── Leapp.pm │ ├── Logger.pm │ ├── Marker.pm │ ├── Motd.pm │ ├── NICs.pm │ ├── Notify.pm │ ├── OS.pm │ ├── OS │ ├── AlmaLinux8.pm │ ├── CentOS7.pm │ ├── CloudLinux7.pm │ ├── RHEL.pm │ ├── Ubuntu.pm │ └── Ubuntu20.pm │ ├── PkgMgr.pm │ ├── PkgMgr │ ├── APT.pm │ ├── Base.pm │ └── YUM.pm │ ├── Roles │ └── Run.pm │ ├── Script.pm │ ├── Service.pm │ ├── StageFile.pm │ ├── Stages.pm │ ├── SystemctlService.pm │ └── Usage.pm ├── maint ├── generate_changelog ├── marker └── perlpkg.static ├── script └── elevate-cpanel.PL ├── t ├── 00_load.t ├── 01_components.t ├── 02_fatpack-packages.t ├── 03_fatpack-script.t ├── Elevate-Database.t ├── Elevate-OS.pm ├── Elevate-OS_detect_changes.t ├── Elevate-PkgMgr.t ├── blocker-AutoSSL.t ├── check-notify.t ├── check_and_fix_grub.t ├── components-AbsoluteSymlink.t ├── components-Acronis.t ├── components-AutoSSL.t ├── components-BootKernel.t ├── components-CCS.t ├── components-CloudLinux.t ├── components-CryptoPolicies.t ├── components-DatabaseUpgrade.t ├── components-DiskSpace.t ├── components-Distros.t ├── components-ElevateScript.t ├── components-Grub2ChecksWorkarounds.t ├── components-Grub2ChecksWorkarounds_parse_shell_variable.t ├── components-Grub2ControlTest.t ├── components-Imunify.t ├── components-IsContainer.t ├── components-JetBackup.t ├── components-KernelCare.pm ├── components-Leapp.t ├── components-Lists.t ├── components-MountPoints.t ├── components-MySQL.t ├── components-NICs.t ├── components-NetworkManager.t ├── components-OVH.t ├── components-PackageDupes.t ├── components-PackageRestore.t ├── components-PostgreSQL.t ├── components-R1Soft.t ├── components-Repositories.t ├── components-SSH.t ├── components-Softaculous.t ├── components-Ufw.t ├── components-UnconvertedModules.t ├── components-UpdateReleaseUpgrades.t ├── components-UpdateSystem.t ├── components-archive_elevate_files.t ├── components-cPanelPlugins.t ├── components-cPanelPrep.t ├── components-dns.t ├── components-ea4.t ├── components-whm.t ├── cpanel-setup ├── cpanfile ├── critic.t ├── elevate-marker.t ├── elevate-nics.t ├── grub2_workaround.t ├── integration │ ├── almalinux8-to-almalinux9 │ │ └── complete.t │ ├── centos7-to-almalinux8 │ │ └── complete.t │ ├── cloudlinux7-to-cloudlinux8 │ │ └── complete.t │ ├── complete.t │ ├── setup │ └── ubuntu20-to-ubuntu22 │ │ └── complete.t ├── is_experimental.t ├── leapp_beta.t ├── leapp_upgrade.t ├── lib │ ├── Perl │ │ └── Critic │ │ │ ├── Cpanel.pm │ │ │ └── Policy │ │ │ ├── Cpanel │ │ │ ├── CpanelExceptions.pm │ │ │ ├── CpanelOS.pm │ │ │ ├── MultiDimensionalArrayEmulation.pm │ │ │ ├── NoExitsFromSubroutines.pm │ │ │ ├── ProhibitQxAndBackticks.pm │ │ │ ├── TransliterationUsage.pm │ │ │ └── TryTinyUsage.pm │ │ │ └── TryTiny │ │ │ └── ProhibitExitingSubroutine.pm │ ├── Test │ │ ├── Elevate.pm │ │ └── Elevate │ │ │ └── OS.pm │ └── cPstrict.pm ├── notify.t ├── os.dump │ └── elevate-os-dump.md ├── pecl.t ├── pkg_list.t ├── pod.t ├── setup │ └── tailwatchd.fake.service ├── ssystem.t ├── stage_file.t ├── tidy.t └── usage.t └── version /.branched_from: -------------------------------------------------------------------------------- 1 | main 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @webpros-cpanel/retengineers-maintain 2 | .* @webpros-cpanel/retengineers-maintain 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/blocked-from-upgrading.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Blocked from upgrading 3 | about: Blocked from upgrading 4 | title: "[BLOCK]" 5 | labels: Blocked 6 | assignees: toddr 7 | 8 | --- 9 | 10 | STOP: We cannot provide support on individual server issues. cPanel Technical Support is ready to help! Please contact [cPanel Technical Support](https://docs.cpanel.net/knowledge-base/technical-support-services/how-to-open-a-technical-support-ticket/) if you encounter problems. 11 | 12 | Be sure to review the [documentation on what you should do before upgrading](https://cpanel.github.io/elevate/#before-updating). 13 | 14 | If you are getting a message that you cannot upgrade from 7 to 8 due to installed software, this is a limitation of `elevate-cpanel`. We are working hard to automate support for many of these problems but we may not yet have gotten to your particular blocker. Information may also exist on how you can overcome this blocker in our [blocker documentation](https://cpanel.github.io/elevate/blockers/). 15 | 16 | If the above issues are your situation, please do not open a ticket here. We cannot help you! 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: Needs Triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/failed-upgrade.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Failed upgrade 3 | about: Failed Upgrade 4 | title: "[UPG FAIL] " 5 | labels: Needs Triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | If you were able to upgrade but something has gone wrong, the quickest way to get help is to open a ticket with [cPanel Technical Support](https://docs.cpanel.net/knowledge-base/technical-support-services/how-to-open-a-technical-support-ticket/). 11 | 12 | NOTE: 13 | * During stage 3, a reboot will happen and the server will be inaccessible from the network for an extended period of time while distro packages are upgraded. [We recommend you have console access](https://cpanel.github.io/elevate/#before-updating) prior to start so you can know if it is hung or simply running slow. 14 | * If an error occurs during the elevation process, once you have fixed it, you can resume 15 | the update process by running: `/scripts/elevate-cpanel --continue` 16 | 17 | *We cannot provide support on individual server issues. We have to refer you to technical support.* 18 | -------------------------------------------------------------------------------- /.github/workflows/close_prs.yml: -------------------------------------------------------------------------------- 1 | name: Close Pull Requests from cpanel/elevate 2 | on: 3 | pull_request: 4 | types: [opened] 5 | 6 | jobs: 7 | close_pr: 8 | if: github.event.pull_request.head.repo.full_name == 'cpanel/elevate' 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Close the PR 12 | uses: peter-evans/close-pull@v2 13 | with: 14 | comment: "This repository does not accept pull requests from cpanel/elevate." 15 | -------------------------------------------------------------------------------- /.github/workflows/openstack/cloud-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | users: 3 | - name: root 4 | lock_passwd: false 5 | disable_root: false -------------------------------------------------------------------------------- /.github/workflows/openstack/cloud-init.yml: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | disable_root: false 3 | ssh_pwauth: true 4 | hostname: host 5 | fqdn: host.domain.tbd 6 | prefer_fqdn_over_hostname: true 7 | -------------------------------------------------------------------------------- /.github/workflows/openstack/main.tf: -------------------------------------------------------------------------------- 1 | # Define required providers 2 | terraform { 3 | required_version = ">= 0.14.0" 4 | required_providers { 5 | openstack = { 6 | source = "terraform-provider-openstack/openstack" 7 | version = "~> 1.54.1" 8 | } 9 | } 10 | } 11 | 12 | # Configure the OpenStack Provider 13 | provider "openstack" { 14 | user_name = var.user 15 | application_credential_id = var.application_credential_id 16 | application_credential_secret = var.application_credential_secret 17 | auth_url = var.os_auth_url 18 | region = var.os_region_name 19 | } 20 | 21 | data "openstack_images_image_ids_v2" "images" { 22 | name_regex = var.image_name 23 | sort = "updated_at" 24 | } 25 | 26 | data "template_cloudinit_config" "config" { 27 | gzip = true 28 | base64_encode = true 29 | 30 | part { 31 | content_type = "text/cloud-config" 32 | content = "cloud-config.yaml" 33 | } 34 | } 35 | 36 | resource "tls_private_key" "ssh" { 37 | algorithm = "ECDSA" 38 | ecdsa_curve = "P384" 39 | } 40 | 41 | resource "random_string" "keyname" { 42 | length = 22 43 | special = false 44 | } 45 | 46 | resource "openstack_compute_keypair_v2" "tf_remote_key" { 47 | name = "${random_string.keyname.result}-deletethis" 48 | public_key = tls_private_key.ssh.public_key_openssh 49 | } 50 | 51 | resource "openstack_compute_instance_v2" "elevatevm" { 52 | name = "${var.github_run_id}.${var.github_repository}.github.cpanel.net" 53 | image_id = data.openstack_images_image_ids_v2.images.ids[0] 54 | flavor_name = var.flavor_name 55 | key_pair = openstack_compute_keypair_v2.tf_remote_key.name 56 | user_data = data.template_cloudinit_config.config.rendered 57 | network { 58 | name = "hou-prod-external" 59 | } 60 | 61 | provisioner "remote-exec" { 62 | inline = [<> /root/.ssh/id_ed25519 67 | echo "${var.ssh_public_key}" >> /root/.ssh/authorized_keys 68 | echo 'waiting on cloud-init...' 69 | cloud-init status --wait > /dev/null || true 70 | EOF 71 | ] 72 | connection { 73 | type = "ssh" 74 | agent = "false" 75 | host = self.access_ip_v4 76 | user = "root" 77 | script_path = "/root/elevate_bootstrap" 78 | private_key = tls_private_key.ssh.private_key_pem 79 | } 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /.github/workflows/openstack/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = openstack_compute_instance_v2.elevatevm.access_ip_v4 3 | } 4 | 5 | output "id" { 6 | value = openstack_compute_instance_v2.elevatevm.id 7 | } -------------------------------------------------------------------------------- /.github/workflows/openstack/prune_openstack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use JSON::PP (); 4 | use POSIX (); 5 | use strict; 6 | use warnings; 7 | 8 | # Static constants 9 | use constant { 10 | KEY_NAME => "deletethis", 11 | VM_NAME => "app-elevate-cpanel.github.cpanel.net", 12 | }; 13 | 14 | my $openstack_path = "/usr/bin/openstack" if -x "/usr/bin/openstack"; 15 | die "No Openstack CLI binary installed." unless $openstack_path; 16 | 17 | # Define "two hours ago" timestamp 18 | my $time_offset = 7200; 19 | my $hammer_time = POSIX::strftime( "%Y-%m-%dT%H:%M:%SZ", gmtime( time - $time_offset ) ); 20 | 21 | # Run removal functions 22 | remove_stale_entities( 'keys', KEY_NAME, 'keypair' ); 23 | remove_stale_entities( 'instances', VM_NAME, 'server' ); 24 | 25 | # Fetch and format entities 26 | sub get_entities { 27 | my ($entity_type) = @_; 28 | 29 | my $entities = {}; 30 | 31 | my ( $list_command, $show_command ); 32 | if ( $entity_type eq 'keys' ) { 33 | $list_command = "$openstack_path keypair list -f json"; 34 | $show_command = "$openstack_path keypair show -f json"; 35 | } 36 | else { 37 | $list_command = "$openstack_path server list -f json --no-name-lookup"; 38 | $show_command = "$openstack_path server show -f json"; 39 | } 40 | 41 | my $out = _run_cmd( $list_command, 0 ); 42 | my $json = JSON::PP->new->decode($out); 43 | 44 | foreach my $entry (@$json) { 45 | my $entity_name = length $entry->{ID} ? $entry->{ID} : $entry->{Name}; 46 | my $show_out = _run_cmd( qq{$show_command "$entity_name"}, 0 ); 47 | my $entity_data = JSON::PP->new->decode($show_out); 48 | 49 | $entities->{$entity_name}->{name} = $entity_data->{name}; 50 | $entities->{$entity_name}->{created_on} = length $entity_data->{created_at} ? $entity_data->{created_at} : $entity_data->{created}; 51 | if ( length $entity_data->{id} ) { 52 | $entities->{$entity_name}->{id} = $entity_data->{id}; 53 | } 54 | } 55 | return $entities; 56 | } 57 | 58 | # Remove stale entities 59 | sub remove_stale_entities { 60 | my ( $entity_type, $name_pattern, $delete_command ) = @_; 61 | 62 | my $entities = get_entities($entity_type); 63 | 64 | foreach my $entity ( values %$entities ) { 65 | unless ( length $entity->{id} && length $entity->{name} && length $entity->{created_on} ) { 66 | print "## [WARN]: Skipping entity due to missing keys\n"; 67 | next; 68 | } 69 | 70 | next unless $entity->{'name'} =~ /${\$name_pattern}/; 71 | 72 | if ( $entity->{created_on} lt $hammer_time ) { 73 | print "## [INFO]: Deleting: ID: $entity->{id}, Name: $entity->{name}, Created On: $entity->{created_on}\n"; 74 | _run_cmd( "$openstack_path $delete_command delete $entity->{id}", 1 ); 75 | } 76 | } 77 | return 0; 78 | } 79 | 80 | # Run command subroutine with basic error handling 81 | sub _run_cmd { 82 | my ( $cmd, $logger ) = @_; 83 | 84 | print "## [INFO]: Running command: $cmd\n" if $logger; 85 | my $output = `$cmd`; 86 | die "Command failed with error: $?" if $?; 87 | 88 | return $output; 89 | } 90 | -------------------------------------------------------------------------------- /.github/workflows/openstack/ssh_retry: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use POSIX; 4 | 5 | my $HOST = $ARGV[0]; 6 | my $PORT = $ARGV[1] // 22; 7 | my $RETVAL = 1; 8 | my $RETRIES = 0; 9 | my $RETRY = $ARGV[2] // 1200; # Bumping this to 20 minutes per cloudlinux Stage 4 averaging right at 15 mins 10 | 11 | return unless defined $HOST; 12 | 13 | while ( $RETVAL != 0 ) { 14 | my $cmd = qq{ /usr/bin/nc -z -w 1 $HOST $PORT }; 15 | my $output = `$cmd`; 16 | my $time = POSIX::strftime( "%Y-%m-%d %H:%M:%S", localtime ); 17 | 18 | $RETVAL = $?; 19 | 20 | if ( $RETVAL == 0 ) { 21 | print "## [$time] [INFO] SUCCESS: Connected to SSH on $HOST ##\n"; 22 | exit 0; 23 | } 24 | 25 | $RETRIES++; 26 | 27 | if ( $RETVAL != 0 ) { 28 | print "## [$time] [INFO]: Retrying SSH Connect: Attempt ${RETRIES} ...\n"; 29 | } 30 | 31 | if ( $RETRIES >= $RETRY ) { 32 | print "## [$time] [ERROR]: ssh_retry.pl: MAX_RETRIES has been reached.\n"; 33 | exit 1; 34 | } 35 | sleep 1; 36 | } 37 | -------------------------------------------------------------------------------- /.github/workflows/openstack/status_marker: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | my $STAGE = $ARGV[0]; 4 | my $RELEASE_INFO = `awk -F'"' '/PRETTY_NAME/ {print \$2}' /etc/os-release`; 5 | my $CPANEL_VERSION = `cat /usr/local/cpanel/version`; 6 | 7 | use strict; 8 | 9 | print "###################################\n"; 10 | 11 | sub main { 12 | my @arr = ( 13 | [ 'Stage:', $STAGE ], [ 'OS Release:', $RELEASE_INFO ] 14 | , [ 'cP Version:', $CPANEL_VERSION ] 15 | ); 16 | 17 | for my $row (@arr) { 18 | format STDOUT = 19 | @<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<< 20 | @$row 21 | . 22 | write; 23 | } 24 | 25 | } 26 | 27 | main(); 28 | 29 | print "###################################\n"; 30 | -------------------------------------------------------------------------------- /.github/workflows/openstack/variables.tf: -------------------------------------------------------------------------------- 1 | variable "user" { 2 | type = string 3 | default = "resu" 4 | } 5 | 6 | variable "application_credential_id" { 7 | type = string 8 | } 9 | 10 | variable "application_credential_secret" { 11 | type = string 12 | } 13 | 14 | variable "github_repository" { 15 | type = string 16 | } 17 | 18 | variable "github_run_id" { 19 | type = string 20 | } 21 | 22 | variable "os_region_name" { 23 | type = string 24 | default = "RegionOne" 25 | } 26 | 27 | variable "os_auth_url" { 28 | type = string 29 | } 30 | 31 | variable "ssh_private_key" { 32 | type = string 33 | description = "SSH private key matching the public key added to the VMs /root/.ssh/authorized_keys file to allow user access." 34 | sensitive = true 35 | } 36 | 37 | variable "ssh_public_key" { 38 | type = string 39 | description = "SSH public key matching the public key added to the VMs /root/.ssh/authorized_keys file to allow user access." 40 | sensitive = true 41 | } 42 | 43 | variable "image_name" { 44 | type = string 45 | } 46 | 47 | variable "cpanel_release_version" { 48 | type = string 49 | } 50 | 51 | variable "flavor_name" { 52 | type = string 53 | default = "c2.d20.r2048" 54 | } 55 | -------------------------------------------------------------------------------- /.github/workflows/pruner.yml: -------------------------------------------------------------------------------- 1 | name: Openstack Pruner 2 | run-name: Openstack Pruner 3 | on: 4 | schedule: 5 | - cron: "5 * * * *" 6 | workflow_dispatch: 7 | jobs: 8 | run-pruner: 9 | if: github.repository == 'webpros-cpanel/app-elevate-cpanel' 10 | runs-on: [arc-runners-elevate] 11 | env: 12 | ACTIONS_STEP_DEBUG: true 13 | OS_APPLICATION_CREDENTIAL_ID: ${{ secrets.OS_APPLICATION_CREDENTIAL_ID }} 14 | OS_APPLICATION_CREDENTIAL_SECRET: ${{ secrets.OS_APPLICATION_CREDENTIAL_SECRET }} 15 | OS_AUTH_TYPE: "v3applicationcredential" 16 | OS_AUTH_URL: "https://keystone.hou-01.cloud.prod.cpanel.net:5000/v3" 17 | OS_IDENTITY_API_VERSION: "3" 18 | OS_INTERFACE: "public" 19 | OS_REGION_NAME: "RegionOne" 20 | container: 21 | image: ghcr.io/webpros-cpanel/misc-ci-containers:ci-elevate-cpanel 22 | steps: 23 | - uses: actions/checkout@v4 24 | - name: Run Openstack Pruner 25 | run: ./.github/workflows/openstack/prune_openstack 26 | -------------------------------------------------------------------------------- /.github/workflows/website.yml: -------------------------------------------------------------------------------- 1 | name: website 2 | 3 | ## https://github.com/peaceiris/actions-hugo 4 | ## https://github.com/github-actions-x/hugo 5 | ## https://github.com/github-actions-x/commit 6 | 7 | on: 8 | push: 9 | branches: 10 | - main 11 | workflow_dispatch: 12 | 13 | jobs: 14 | build: 15 | if: github.repository == 'cpanel/elevate' 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: checkout 19 | uses: actions/checkout@v3 20 | with: 21 | submodules: true # Fetch Hugo themes (true OR recursive) 22 | fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod 23 | persist-credentials: false 24 | 25 | - name: Setup Hugo 26 | uses: peaceiris/actions-hugo@v2 27 | with: 28 | hugo-version: "0.91.2" 29 | 30 | - name: Build 31 | working-directory: ./docs-website-src 32 | run: | 33 | hugo -D 34 | 35 | - name: check delta 36 | run: | 37 | git status 38 | git diff 39 | 40 | - name: push 41 | uses: github-actions-x/commit@v2.9 42 | with: 43 | github-token: ${{ secrets.DOCS_DEPLOY_TOKEN }} 44 | push-branch: "docs" 45 | commit-message: "Regenerate Documentation Website" 46 | force-add: "true" 47 | force-push: "true" 48 | files: docs/ 49 | name: GitHub Action 50 | email: github.bot@cpanel.net 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.DS_Store 2 | *.orig 3 | *.rej 4 | *.ERR 5 | *.bak 6 | /cover_db 7 | .DS_Store 8 | .tdy 9 | /tags 10 | /.vscode 11 | *.code-workspace 12 | -------------------------------------------------------------------------------- /.perltidyrc: -------------------------------------------------------------------------------- 1 | -l=400 2 | -i=4 3 | -dt=4 4 | -it=4 5 | -bar 6 | -nsfs 7 | -nolq 8 | --break-at-old-comma-breakpoints 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2024 WebPros International, LLC 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all test cover tags clean release build prep-integration sanity cl 2 | 3 | GIT ?= /usr/local/cpanel/3rdparty/bin/git 4 | RELEASE_TAG ?= release 5 | PERL_BIN=/usr/local/cpanel/3rdparty/perl/536/bin 6 | PERL_LIB=/usr/local/cpanel/3rdparty/perl/536/lib 7 | VERSION=`cat version` 8 | 9 | all: 10 | $(MAKE) build 11 | $(MAKE) test 12 | $(MAKE) cover 13 | 14 | sanity: 15 | @for file in $$(find lib -type f -name "*.pm" | sort); do \ 16 | $(PERL_BIN)/perl -cw -Ilib $$file || exit 1; \ 17 | done 18 | 19 | test: sanity 20 | -$(MAKE) elevate-cpanel 21 | $(PERL_BIN)/prove t/00_load.t 22 | $(PERL_BIN)/yath test -j8 t/*.t --exclude-pattern t/03_fatpack-script.t 23 | 24 | build: 25 | rm -f elevate-cpanel 26 | $(MAKE) elevate-cpanel 27 | 28 | prep-integration: 29 | curl -fsSL https://raw.githubusercontent.com/skaji/cpm/main/cpm > ./cpm 30 | chmod -v +x ./cpm 31 | /scripts/update_local_rpm_versions --edit target_settings.perl-enhanced installed 32 | ./cpm install Test::PerlTidy 33 | cp -v local/lib/perl5/Test/PerlTidy.pm $(PERL_LIB)/Test/ && rm -Rfv local/ 34 | /scripts/check_cpanel_pkgs --fix --long-list --no-digest 35 | /bin/bash t/integration/setup 36 | 37 | cover: 38 | /usr/bin/rm -rf cover_db 39 | HARNESS_PERL_SWITCHES="-MDevel::Cover=-loose_perms,on,-coverage,statement,branch,condition,subroutine,-ignore,.,-select,elevate-cpanel" $(PERL_BIN)/prove t/*.t ||: 40 | $(PERL_BIN)/cover -silent 41 | find cover_db -type f -exec chmod 644 {} \; 42 | find cover_db -type d -exec chmod 755 {} \; 43 | 44 | tags: 45 | /usr/bin/ctags -R --languages=perl --extra=+q script lib t 46 | 47 | elevate-cpanel: $(wildcard lib/**/*) script/elevate-cpanel.PL 48 | USE_CPANEL_PERL_FOR_PERLSTATIC=1 maint/perlpkg.static \ 49 | --dir=lib \ 50 | --no-cpstrict \ 51 | --no-try-tiny \ 52 | --no-http-tiny \ 53 | --no-file-path-tiny \ 54 | --leave-broken \ 55 | script/$@.PL 56 | mv script/$@.PL.static $@ 57 | MARKER="`cat maint/marker`" $(PERL_BIN)/perl -pi -e 's|^(#!/usr/local/cpanel/3rdparty/bin/perl)|$$1\n\n$$ENV{MARKER}\n|' $@ 58 | VERSION=${VERSION} $(PERL_BIN)/perl -pi -e 's/(^use constant VERSION =>) 1;/$$1 $$ENV{VERSION};/' $@ 59 | $(PERL_BIN)/perltidy -b -bext="/" $@ 60 | chmod +x $@ 61 | $(PERL_BIN)/perl -cw elevate-cpanel 62 | 63 | clean: 64 | rm -f tags 65 | rm -f ./cpm 66 | 67 | release: build 68 | $(GIT) tag -f $(RELEASE_TAG) 69 | $(GIT) tag -f v${VERSION} 70 | $(GIT) push pub main 71 | $(GIT) push --force pub $(RELEASE_TAG) 72 | $(GIT) push --force pub v${VERSION} 73 | $(GIT) push --force ent $(RELEASE_TAG) 74 | $(GIT) push --force ent v${VERSION} 75 | $(MAKE) bump_version 76 | $(GIT) push ent main 77 | 78 | bump_version: version := $(shell dc -f version -e '1 + p') 79 | bump_version: 80 | echo -n $(version) > version 81 | $(MAKE) build; 82 | $(GIT) add version elevate-cpanel 83 | $(GIT) commit -m "Bump version to $(version) after release" 84 | cl: 85 | maint/generate_changelog 86 | 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | docs-website-src/content/_index.md -------------------------------------------------------------------------------- /Release.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "cPanel ELevate Release (QA) Preparation" 3 | summary: "Outline of prerequisite criteria for quality assurance testing considerations in preparation to release new versions of cPanel ELevate." 4 | draft: false 5 | date: 2025-04-01T16:20:00Z 6 | --- 7 | 8 | ## cPanel ELevate Release (QA) Preparation 9 | 10 | ### Automated CI Tests 11 | 12 | First and foremost we should have a consistent and passing CI automated test run for supported OSs. 13 | 14 | 1. Unit/component tests 15 | 2. Integration/QA tests 16 | 17 | ### New Feature & Regression Tests 18 | 19 | Changes specific to the new version to be released should pass all pertinent testing. 20 | 21 | 1. Automated testing is preferred 22 | 2. Manual testing where applicable 23 | 24 | ### Exploratory or Optional Tests 25 | 26 | Carefully consider and discuss with your team & peers if any additional testing may be needed and relevant based on the changes committed. 27 | 28 | * As an example, if changes primarily affect or target a particular Operating System consider scenarios or use cases that may be most applicable to that OS platform based. 29 | * CloudLinux OS would be more likely to involve scenarios using a licensed OS-specific feature like MySQL Governor or a plug-in like Imunify360. 30 | * Other OSs, e.g., AlmaLinux, might be more likely to involve scenarios using free plug-ins like ConfigServer Firewall (CSF) and/or ImunifyAV. 31 | * Compatibility with various other cPane&WHM plug-ins may also benefit from consideration when evaluating applicable scenarios to test. 32 | * e.g., Acronis, CCS, InfluxDB, JetBackup, ImunifyAV+, Installatron, KernelCare, LiteSpeed, NixStats, Panopta, R1Soft, Softaculous, WP Toolkit 33 | 34 | ### Test Failures 35 | 36 | If testing fails, results should be carefully evaluated and, as needed, discussed with your team & peers for consensus. Keep in mind any available data for use cases affected by the issue(s) discovered during testing. 37 | 38 | 1. The result should not be considered worse than what is already in-use by the public so as to avoid releasing a version with avoidable regressions. 39 | * e.g., If a new feature is added that is not yet in widespread use, like support for a new Operating System, it may be acceptable provided the changeset is not adversely affecting existing support for other OSs already in widespread use. 40 | 2. If the result may be considered worse than what is already public, discuss with your team/peers how to proceed, and consider options to proceed. 41 | * e.g., Suggest reverting the specific change(s) determined to be the root cause to also allow more time to properly address while not delaying other changes, the issue(s) and/or suggest holding back the new version until the issue(s) can be adequately addressed. 42 | -------------------------------------------------------------------------------- /docs-website-src/.hugo_build.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpanel/elevate/602e9bb542469779f93456b72ec014c8d40aa8cf/docs-website-src/.hugo_build.lock -------------------------------------------------------------------------------- /docs-website-src/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpanel/elevate/602e9bb542469779f93456b72ec014c8d40aa8cf/docs-website-src/.nojekyll -------------------------------------------------------------------------------- /docs-website-src/archetypes/default.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "{{ replace .Name "-" " " | title }}" 3 | date: {{ .Date }} 4 | draft: true 5 | --- 6 | 7 | -------------------------------------------------------------------------------- /docs-website-src/config.toml: -------------------------------------------------------------------------------- 1 | # References: 2 | # 3 | # https://github.com/marketplace/actions/hugo-static-site-generator 4 | # https://retrolog.io/blog/creating-a-hugo-theme-from-scratch/ 5 | 6 | baseURL = 'https://cpanel.github.io/elevate' 7 | languageCode = 'en-us' 8 | title = 'cPanel ELevate Documentation' 9 | theme = "cptheme" 10 | 11 | sectionPagesMenu = "main" 12 | 13 | [menu] 14 | [[menu.main]] 15 | name = "Report an Issue" 16 | url = "https://docs.cpanel.net/knowledge-base/technical-support-services/how-to-open-a-technical-support-ticket/" 17 | weight = 1 18 | 19 | [[menu.main]] 20 | name = "Contribute" 21 | url = "https://github.com/cpanel/elevate" 22 | weight = 2 23 | 24 | ## 25 | ## left menu 26 | ## 27 | [[menu.left]] 28 | name = "Introduction" 29 | url = "/#the-cpanel-elevate-project" 30 | weight = 1 31 | 32 | [[menu.left]] 33 | name = "The Upgrade Process" 34 | url = "/#the-elevate-process" 35 | weight = 2 36 | 37 | [[menu.left]] 38 | name = "Requirements" 39 | url = "/getting-started/#requirements" 40 | weight = 3 41 | 42 | [[menu.left]] 43 | name = "Risks" 44 | url = "/getting-started/#risks" 45 | weight = 4 46 | 47 | [[menu.left]] 48 | name = "Upgrade Your Server" 49 | url = "/getting-started/" 50 | weight = 5 51 | 52 | [[menu.left]] 53 | name = "Copyright" 54 | url = "/#copyright" 55 | weight = 6 56 | -------------------------------------------------------------------------------- /docs-website-src/content/Changelog.md: -------------------------------------------------------------------------------- 1 | ../../Changelog.md -------------------------------------------------------------------------------- /docs-website-src/public: -------------------------------------------------------------------------------- 1 | ../docs -------------------------------------------------------------------------------- /docs-website-src/static/images/elevate-logo-social.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpanel/elevate/602e9bb542469779f93456b72ec014c8d40aa8cf/docs-website-src/static/images/elevate-logo-social.png -------------------------------------------------------------------------------- /docs-website-src/static/images/elevate-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpanel/elevate/602e9bb542469779f93456b72ec014c8d40aa8cf/docs-website-src/static/images/elevate-logo.png -------------------------------------------------------------------------------- /docs-website-src/static/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpanel/elevate/602e9bb542469779f93456b72ec014c8d40aa8cf/docs-website-src/static/images/favicon.png -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 WebPros International, LLC 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/archetypes/default.md: -------------------------------------------------------------------------------- 1 | +++ 2 | +++ 3 | -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/layouts/404.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpanel/elevate/602e9bb542469779f93456b72ec014c8d40aa8cf/docs-website-src/themes/cptheme/layouts/404.html -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/layouts/_default/baseof.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{- partial "head.html" . -}} 4 | 5 | {{- partial "header.html" . -}} 6 | {{- partial "breadcrumb.html" . -}} 7 |
8 | {{- block "main" . }}{{- end }} 9 |
10 | {{- partial "footer.html" . -}} 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/layouts/_default/list.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |

{{ .Title }}

3 | {{ range .Pages.ByPublishDate.Reverse }} 4 |

5 |

{{ .Title }}

6 | {{ partial "metadata.html" . }} 7 | 8 |

{{ .Summary }}

9 |
10 |

11 | {{ end }} 12 | {{ end }} -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/layouts/_default/single.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |

{{ .Title }}

3 | {{ partial "metadata.html" . }} 4 |

5 | {{ .Content }} 6 | {{ end }} -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/layouts/index.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |
3 |
4 | 5 |
6 | 7 | {{- partial "left_menu.html" . -}} 8 | 9 |
10 | 11 |
13 | {{ .Content }} 14 |
15 | 16 |
17 |
18 |
19 |
20 | {{ end }} -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/layouts/page/single.html: -------------------------------------------------------------------------------- 1 | {{ define "header" }}{{ partial "page-header.html" . }}{{ end }} 2 | {{ define "main" }} 3 |
4 |
5 | 6 |
7 | 8 | {{- partial "left_menu.html" . -}} 9 | 10 |
11 | 12 |
14 | {{ .Content }} 15 |
16 | 17 |
18 |
19 |
20 |
21 | {{ end }} -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/layouts/partials/breadcrumb.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 12 |
13 |
14 |
-------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/layouts/partials/footer.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/layouts/partials/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ $title := print .Site.Title " | " .Title }} 5 | {{ if .IsHome }}{{ $title = .Site.Title }}{{ end }} 6 | {{ $title }} 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/layouts/partials/header.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/layouts/partials/left_menu.html: -------------------------------------------------------------------------------- 1 |
2 | 27 |
28 | -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/layouts/partials/metadata.html: -------------------------------------------------------------------------------- 1 | {{ $dateTime := .PublishDate.Format "2006-01-02" }} 2 | {{ $dateFormat := .Site.Params.dateFormat | default "Jan 2, 2006" }} 3 | 4 | 5 | {{ with .Params.tags }} 6 | 7 | {{ range . }} 8 | {{ $href := print (absURL "tags/") (urlize .) }} 9 | {{ . }} 10 | {{ end }} 11 | {{ end }} -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/layouts/partials/script.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpanel/elevate/602e9bb542469779f93456b72ec014c8d40aa8cf/docs-website-src/themes/cptheme/layouts/partials/script.html -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/static/css/style.css: -------------------------------------------------------------------------------- 1 | .bg-light { 2 | background-color: #293A4A !important; 3 | } 4 | 5 | footer { 6 | background-color: #293A4A; 7 | } 8 | 9 | .navbar-light .navbar-nav .nav-link { 10 | color: rgba(255, 255, 255, .8); 11 | } 12 | 13 | /* https://styleguide.cpanel.net/#/color-swatches */ 14 | 15 | body { 16 | background-color: #D7EDF9; 17 | } 18 | 19 | .side-nav { 20 | background-color: #EAEAEA; 21 | } 22 | 23 | .navbar .navbar-brand img { 24 | height: 6.75rem 25 | } 26 | 27 | /* copy button */ 28 | pre { 29 | position:relative; 30 | overflow: auto; 31 | margin:5px 0; 32 | padding:1.75rem 0 1.75rem 1rem; 33 | border-radius:10px; 34 | } 35 | 36 | button{ 37 | position:absolute; 38 | top:5px; 39 | right:5px; 40 | 41 | font-size:.9rem; 42 | padding:.15rem; 43 | background-color:#828282; 44 | color:1e1e1e; 45 | border:ridge 1px #7b7b7c; 46 | border-radius:5px; 47 | text-shadow:#c4c4c4 0 0 2px; 48 | } 49 | 50 | button:hover{ 51 | cursor:pointer; 52 | background-color:#bcbabb; 53 | } 54 | -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/static/js/copy-code.js: -------------------------------------------------------------------------------- 1 | const copyButtonLabel = "Copy Code"; 2 | 3 | // You can use a class selector instead if available. 4 | let blocks = document.querySelectorAll("pre"); 5 | 6 | blocks.forEach((block) => { 7 | // only add button if browser supports Clipboard API 8 | if (navigator.clipboard) { 9 | let button = document.createElement("button"); 10 | button.innerText = copyButtonLabel; 11 | button.addEventListener("click", copyCode); 12 | block.appendChild(button); 13 | } 14 | }); 15 | 16 | async function copyCode(event) { 17 | const button = event.srcElement; 18 | const pre = button.parentElement; 19 | let code = pre.querySelector("code"); 20 | let text = code.innerText; 21 | await navigator.clipboard.writeText(text); 22 | } -------------------------------------------------------------------------------- /docs-website-src/themes/cptheme/theme.toml: -------------------------------------------------------------------------------- 1 | # theme.toml template for a Hugo theme 2 | # See https://github.com/gohugoio/hugoThemes#themetoml for an example 3 | 4 | name = "Cptheme" 5 | license = "BSD" 6 | licenselink = "https://github.com/cpanel/elevate/blob/main/LICENSE.md" 7 | description = "" 8 | homepage = "http://example.com/" 9 | tags = [] 10 | features = [] 11 | min_version = "0.41.0" 12 | 13 | [author] 14 | name = "cPanel LLC" 15 | homepage = "https://github.com/cpanel" 16 | 17 | # If porting an existing theme 18 | [original] 19 | name = "" 20 | homepage = "" 21 | repo = "" 22 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpanel/elevate/602e9bb542469779f93456b72ec014c8d40aa8cf/docs/.nojekyll -------------------------------------------------------------------------------- /docs/Elevating cPanel & WHM_2022.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpanel/elevate/602e9bb542469779f93456b72ec014c8d40aa8cf/docs/Elevating cPanel & WHM_2022.pdf -------------------------------------------------------------------------------- /docs/cPanel-CLA.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpanel/elevate/602e9bb542469779f93456b72ec014c8d40aa8cf/docs/cPanel-CLA.pdf -------------------------------------------------------------------------------- /docs/elevate-video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpanel/elevate/602e9bb542469779f93456b72ec014c8d40aa8cf/docs/elevate-video.png -------------------------------------------------------------------------------- /lib/Elevate/Components/AbsoluteSymlinks.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::AbsoluteSymlinks; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::AbsoluteSymlinks 8 | 9 | =head2 check 10 | 11 | Verify that there are not any absolute symlinks present in / 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | Update absolute symlinks in / to be relative links 16 | 17 | =head2 post_distro_upgrade 18 | 19 | noop 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use Cpanel::Chdir (); 26 | use Cpanel::UUID (); 27 | use File::Copy (); 28 | 29 | use Log::Log4perl qw(:easy); 30 | 31 | use parent qw{Elevate::Components::Base}; 32 | 33 | sub get_abs_symlinks ($self) { 34 | my %links; 35 | foreach my $entry ( glob "/*" ) { 36 | my $path = readlink($entry); # don't bother with stat, this is fast 37 | next unless $path && substr( $path, 0, 1 ) eq '/'; 38 | $links{$entry} = $path; 39 | } 40 | return %links; 41 | } 42 | 43 | sub pre_distro_upgrade ($self) { 44 | 45 | $self->ssystem(qw{/usr/bin/ln -snf usr/local/cpanel/scripts /scripts}); 46 | $self->_absolute_symlinks; 47 | 48 | return; 49 | } 50 | 51 | sub _absolute_symlinks ($self) { 52 | 53 | my %links = $self->get_abs_symlinks(); 54 | return unless %links; 55 | my $chdir = Cpanel::Chdir->new("/"); 56 | foreach my $link ( keys(%links) ) { 57 | my $updated = substr( $links{$link}, 1 ); 58 | 59 | # Now, this has probably .01% of collision chance, but let's get even 60 | # more paranoid by checking existence and rerolling. 61 | # Presumably if we can't find something by 10k tries, it just isn't 62 | # happening no matter how hard we want it. 63 | my $rand_uid = Cpanel::UUID::random_uuid(); 64 | my $tries = 0; 65 | while ( -e "$link-$rand_uid" && $tries++ < 10000 ) { 66 | $rand_uid = Cpanel::UUID::random_uuid(); 67 | } 68 | symlink( $updated, "$link-$rand_uid" ) or die "Can't create symlink $link-$rand_uid to $updated: $!"; 69 | File::Copy::mv( "$link-$rand_uid", $link ) or die "Can't overwite $link: $!"; 70 | } 71 | return; 72 | } 73 | 74 | sub check ($self) { 75 | my %links = $self->get_abs_symlinks(); 76 | return unless %links; 77 | 78 | my $ln_string = join ", ", sort keys %links; 79 | WARN(<<~"EOS"); 80 | Symlinks with absolute paths were found in /: 81 | $ln_string 82 | This can cause problems during the upgrade, and 83 | the script will correct them to relative symlinks before elevation. 84 | EOS 85 | 86 | return; 87 | } 88 | 89 | 1; 90 | -------------------------------------------------------------------------------- /lib/Elevate/Components/Acronis.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::Acronis; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::Acronis 8 | 9 | =head2 check 10 | 11 | noop 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | Find out if Acronis is installed. 16 | If it is, uninstall it & make a note in the stage file. 17 | (We'll need to reinstall it after the OS upgrade.) 18 | 19 | =head2 post_distro_upgrade 20 | 21 | If the agent had been installed: 22 | Re-install the agent. 23 | 24 | =cut 25 | 26 | use cPstrict; 27 | 28 | use Elevate::Constants (); 29 | use Elevate::PkgMgr (); 30 | use Elevate::StageFile (); 31 | 32 | use Cpanel::Pkgr (); 33 | 34 | use parent qw{Elevate::Components::Base}; 35 | 36 | sub pre_distro_upgrade ($self) { 37 | 38 | return unless Cpanel::Pkgr::is_installed(Elevate::Constants::ACRONIS_BACKUP_PACKAGE); 39 | 40 | Elevate::PkgMgr::remove( 41 | Elevate::Constants::ACRONIS_BACKUP_PACKAGE, 42 | Elevate::Constants::ACRONIS_OTHER_PACKAGES 43 | ); 44 | 45 | Elevate::StageFile::update_stage_file( { 'reinstall' => { 'acronis' => 1 } } ); 46 | 47 | return; 48 | } 49 | 50 | sub post_distro_upgrade ($self) { 51 | 52 | return unless Elevate::StageFile::read_stage_file('reinstall')->{'acronis'}; 53 | 54 | Elevate::PkgMgr::install(Elevate::Constants::ACRONIS_BACKUP_PACKAGE); 55 | 56 | return; 57 | } 58 | 59 | 1; 60 | -------------------------------------------------------------------------------- /lib/Elevate/Components/AutoSSL.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::AutoSSL; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::AutoSSL 8 | 9 | =head2 check 10 | 11 | Warn if Sectigo is the provider for AutoSSL 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | Update the AutoSSL provider to LE 16 | 17 | =head2 post_distro_upgrade 18 | 19 | noop 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use Cpanel::SSL::Auto (); 26 | 27 | use parent qw{Elevate::Components::Base}; 28 | 29 | use Log::Log4perl qw(:easy); 30 | 31 | sub check ($self) { 32 | 33 | return $self->_check_autossl_provider(); 34 | } 35 | 36 | sub _check_autossl_provider ($self) { 37 | 38 | if ( $self->is_using_sectigo() ) { 39 | WARN(<<~"EOS"); 40 | Elevating with Sectigo as the provider for AutoSSL is not supported. 41 | If you proceed with this upgrade, we will switch your system 42 | to use the Let's Encrypt™ provider. 43 | 44 | EOS 45 | } 46 | 47 | return 0; 48 | } 49 | 50 | sub pre_distro_upgrade ($self) { 51 | 52 | if ( $self->is_using_sectigo() ) { 53 | $self->ssystem_and_die(qw{/usr/local/cpanel/scripts/autorepair set_autossl_to_lets_encrypt}); 54 | } 55 | 56 | return; 57 | } 58 | 59 | =head2 is_using_sectigo() 60 | 61 | Determines whether AutoSSL is using Sectigo as a provider 62 | 63 | =head3 ARGUMENTS 64 | 65 | None 66 | 67 | =head3 RETURNS 68 | 69 | true/false if AutoSSL is/isn't using Sectigo 70 | 71 | =cut 72 | 73 | sub is_using_sectigo ($self) { 74 | 75 | my @providers = Cpanel::SSL::Auto::get_all_provider_info(); 76 | 77 | foreach my $provider (@providers) { 78 | next unless ( ref $provider eq 'HASH' && $provider->{enabled} ); 79 | 80 | if ( defined $provider->{display_name} 81 | && $provider->{display_name} =~ /sectigo/i ) { 82 | return 1; 83 | } 84 | } 85 | 86 | return 0; 87 | } 88 | 89 | 1; 90 | -------------------------------------------------------------------------------- /lib/Elevate/Components/BootKernel.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::BootKernel; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::BootKernel 8 | 9 | =head2 check 10 | 11 | Determine if the running kernel matches the configured boot kernel 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | noop 16 | 17 | =head2 post_distro_upgrade 18 | 19 | noop 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use parent qw{Elevate::Components::Base}; 26 | 27 | use Elevate::Constants (); 28 | 29 | use Cpanel::Kernel::Status (); 30 | use Cpanel::Exception (); 31 | use Cpanel::YAML (); 32 | use Cpanel::JSON (); 33 | 34 | use Try::Tiny; 35 | 36 | sub check ($self) { 37 | 38 | return 1 if $self->upgrade_distro_manually; # skip when --upgrade-distro-manually is provided 39 | 40 | my $ok = 0; 41 | try { 42 | my ( $running_version, $boot_version ) = Cpanel::Kernel::Status::reboot_status()->@{ 'running_version', 'boot_version' }; 43 | $ok = $running_version eq $boot_version; 44 | 45 | $self->has_blocker(<<~EOS) if !$ok; 46 | The running kernel version ($running_version) does not match that of 47 | the default boot entry ($boot_version). This could be due to the kernel 48 | being changed by an update, meaning that a reboot should resolve this. 49 | However, this also could indicate that the system does not have control 50 | over which kernel and early boot environment (initrd) is used upon 51 | reboot, which is required to upgrade the operating system with this 52 | script. 53 | 54 | If this message remains after a reboot, your server may have been 55 | configured to boot into a particular kernel directly rather than to an 56 | instance of the GRUB2 boot loader. This often happens to virtualized 57 | servers, but physical servers also can have this problem under certain 58 | configurations. Your provider may have a solution to allow booting into 59 | GRUB2; contact them for further information. 60 | EOS 61 | } 62 | catch { 63 | my $ex = $_; 64 | $self->has_blocker( 65 | "Unable to determine running and boot kernels due to the following error:\n" # 66 | . _to_str($ex) 67 | ); 68 | }; 69 | 70 | return $ok ? 1 : 0; 71 | } 72 | 73 | sub _to_str ($e) { 74 | $e //= ''; 75 | 76 | my $str = Cpanel::Exception::get_string($e); 77 | 78 | if ( length $str ) { 79 | 80 | # can return a YAML or JSON object... handle both 81 | my $hash = eval { Cpanel::YAML::Load($str) } # parse yaml 82 | // eval { Cpanel::JSON::Load($str) } # or json output... we cannot predict 83 | // {}; 84 | if ( ref $hash eq 'HASH' && $hash->{msg} ) { 85 | $str = $hash->{msg}; 86 | } 87 | } 88 | 89 | return $str; 90 | } 91 | 92 | 1; 93 | -------------------------------------------------------------------------------- /lib/Elevate/Components/CloudLinux.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::CloudLinux; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::CloudLinux 8 | 9 | =head2 check 10 | 11 | Ensure CL license is valid 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | noop 16 | 17 | =head2 post_distro_upgrade 18 | 19 | noop 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use parent qw{Elevate::Components::Base}; 26 | 27 | use Elevate::OS (); 28 | 29 | use Log::Log4perl qw(:easy); 30 | 31 | use constant CLDETECT => '/usr/bin/cldetect'; 32 | use constant RHN_CHECK => '/usr/sbin/rhn_check'; 33 | 34 | sub check ($self) { 35 | return $self->_check_cloudlinux_license(); 36 | } 37 | 38 | sub _check_cloudlinux_license ($self) { 39 | return 0 unless Elevate::OS::should_check_cloudlinux_license(); 40 | 41 | my $out = $self->ssystem_capture_output( CLDETECT, '--check-license' ); 42 | 43 | if ( $self->ssystem(RHN_CHECK) != 0 || $out->{status} != 0 || grep { $_ !~ m/^ok/i } @{ $out->{stdout} } ) { 44 | 45 | $self->components->abort_on_first_blocker(1); 46 | 47 | return $self->has_blocker(<<~'EOS'); 48 | The CloudLinux license is reporting that it is not currently valid. A 49 | valid CloudLinux license is required to ELevate from CloudLinux 7 to 50 | CloudLinux 8. 51 | EOS 52 | } 53 | 54 | return 0; 55 | } 56 | 57 | 1; 58 | -------------------------------------------------------------------------------- /lib/Elevate/Components/DNS.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::DNS; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::DNS 8 | 9 | =head2 check 10 | 11 | Verify that the DNS server is supported 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | noop 16 | 17 | =head2 post_distro_upgrade 18 | 19 | noop 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use Elevate::Constants (); 26 | use Elevate::OS (); 27 | 28 | use parent qw{Elevate::Components::Base}; 29 | 30 | use Cpanel::Config::LoadCpConf (); 31 | 32 | use Cwd (); 33 | use Log::Log4perl qw(:easy); 34 | 35 | sub check ($self) { 36 | 37 | return $self->_blocker_nameserver_not_supported( _get_nameserver_type() ); 38 | } 39 | 40 | sub _get_nameserver_type () { 41 | 42 | my $cpconf = Cpanel::Config::LoadCpConf::loadcpconf(); 43 | return $cpconf->{'local_nameserver_type'} // ''; 44 | } 45 | 46 | sub _blocker_nameserver_not_supported ( $self, $nameserver = '' ) { 47 | 48 | # Nameserver is not set so it is likely disabled which is ok 49 | return 0 unless length $nameserver; 50 | 51 | my @supported_nameserver_types = Elevate::OS::supported_cpanel_nameserver_types(); 52 | return 0 if grep { $_ eq $nameserver } @supported_nameserver_types; 53 | 54 | my $pretty_distro_name = Elevate::OS::upgrade_to_pretty_name(); 55 | my $supported_nameservers = join( "\n", @supported_nameserver_types ); 56 | return $self->has_blocker(<<~"EOS"); 57 | $pretty_distro_name only supports the following nameservers: 58 | 59 | $supported_nameservers 60 | 61 | We suggest you switch to powerdns. 62 | Before upgrading, we suggest you run: 63 | 64 | /usr/local/cpanel/scripts/setupnameserver powerdns 65 | 66 | EOS 67 | } 68 | 69 | 1; 70 | -------------------------------------------------------------------------------- /lib/Elevate/Components/Distros.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::Distros; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::Distros 8 | 9 | =head2 check 10 | 11 | Verify that the OS is eligible for elevate 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | noop 16 | 17 | =head2 post_distro_upgrade 18 | 19 | noop 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use Cpanel::OS (); 26 | 27 | use constant MINIMUM_CENTOS_7_SUPPORTED => 9; 28 | 29 | use parent qw{Elevate::Components::Base}; 30 | 31 | use Elevate::OS (); 32 | 33 | use Log::Log4perl qw(:easy); 34 | 35 | sub check ($self) { 36 | 37 | my @checks = qw{ 38 | _blocker_os_is_not_supported 39 | _blocker_is_old_centos7 40 | _blocker_is_experimental_os 41 | }; 42 | 43 | foreach my $name (@checks) { 44 | my $blocker = $self->can($name)->($self); 45 | return $blocker if $blocker; 46 | } 47 | 48 | return 0; 49 | } 50 | 51 | sub _blocker_os_is_not_supported ($self) { 52 | Elevate::OS::is_supported(); # dies 53 | return 0; 54 | } 55 | 56 | sub _blocker_is_old_centos7 ($self) { 57 | 58 | return if Elevate::OS::skip_minor_version_check(); 59 | 60 | if ( Cpanel::OS::minor() < MINIMUM_CENTOS_7_SUPPORTED ) { ## no critic(Cpanel::CpanelOS) 61 | my $pretty_distro_name = Elevate::OS::upgrade_to_pretty_name(); 62 | return $self->has_blocker( 63 | sprintf( 64 | 'You need to run CentOS 7.%s and later to upgrade %s. You are currently using %s', # 65 | MINIMUM_CENTOS_7_SUPPORTED, $pretty_distro_name, Cpanel::OS::display_name() # 66 | ) 67 | ); 68 | } 69 | 70 | return 0; 71 | } 72 | 73 | sub _blocker_is_experimental_os ($self) { 74 | if ( -e '/var/cpanel/caches/Cpanel-OS.custom' ) { 75 | return $self->has_blocker('Experimental operating system detected. This script does not support experimental OS upgrades.'); 76 | } 77 | 78 | return 0; 79 | } 80 | 81 | =head2 bail_out_on_inappropriate_distro 82 | 83 | This sub is the primary gateway to determine if a server is eligible for a OS 84 | upgrade via this script. Never allow a cache to be used here. 85 | 86 | =cut 87 | 88 | sub bail_out_on_inappropriate_distro () { 89 | Elevate::OS::clear_cache(); 90 | Elevate::OS::is_supported(); # dies 91 | return; 92 | } 93 | 94 | 1; 95 | -------------------------------------------------------------------------------- /lib/Elevate/Components/ELS.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::ELS; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::ELS 8 | 9 | =head2 check 10 | 11 | noop 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | Remove ELS repo files and ELS specific package 16 | 17 | =head2 post_distro_upgrade 18 | 19 | noop 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use Elevate::OS (); 26 | use Elevate::PkgMgr (); 27 | 28 | use Cpanel::Pkgr (); 29 | use Log::Log4perl qw(:easy); 30 | 31 | use parent qw{Elevate::Components::Base}; 32 | 33 | use constant ELS_PACKAGE => 'els-define'; 34 | 35 | sub pre_distro_upgrade ($self) { 36 | 37 | return unless Elevate::OS::remove_els(); 38 | 39 | my @files_to_remove = qw{ 40 | /etc/yum.repos.d/centos7-els.repo 41 | /etc/yum.repos.d/centos7-els-rollout.repo 42 | }; 43 | 44 | foreach my $file (@files_to_remove) { 45 | if ( -e $file ) { 46 | unlink $file or WARN("Could not remove file $file: $!"); 47 | } 48 | } 49 | 50 | Elevate::PkgMgr::remove(ELS_PACKAGE) if Cpanel::Pkgr::is_installed(ELS_PACKAGE); 51 | 52 | return; 53 | } 54 | 55 | 1; 56 | -------------------------------------------------------------------------------- /lib/Elevate/Components/ElevateScript.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::ElevateScript; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::ElevateScript 8 | 9 | =head2 check 10 | 11 | Ensure script is in correct location and up to date 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | noop 16 | 17 | =head2 post_distro_upgrade 18 | 19 | noop 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use Elevate::Constants (); 26 | 27 | use parent qw{Elevate::Components::Base}; 28 | 29 | use Cwd (); 30 | use Log::Log4perl qw(:easy); 31 | 32 | sub check ($self) { 33 | 34 | $self->_blocker_wrong_location; 35 | $self->_is_up_to_date; 36 | 37 | return; 38 | } 39 | 40 | sub _blocker_wrong_location ($self) { 41 | 42 | # ensure the script is installed at the correct location 43 | my $running_from = Cwd::abs_path($0) // ''; 44 | 45 | # right location 46 | return 0 47 | if $running_from eq '/scripts/elevate-cpanel' 48 | || $running_from eq '/usr/local/cpanel/scripts/elevate-cpanel'; 49 | 50 | return $self->has_blocker(<<~'EOS'); 51 | The script is not installed to the correct directory. 52 | Please install it to /scripts/elevate-cpanel and run it again. 53 | EOS 54 | 55 | } 56 | 57 | sub _is_up_to_date ($self) { # $self is a cpev object here 58 | 59 | return if $self->getopt('skip-elevate-version-check'); 60 | 61 | my ( $should_block, $message ) = $self->cpev->script->is_out_of_date(); 62 | $message //= ''; 63 | 64 | if ( !$should_block ) { 65 | WARN($message) if length $message; 66 | return; 67 | } 68 | 69 | return $self->has_blocker(<<~"EOS"); 70 | $message 71 | 72 | Pass the --skip-elevate-version-check flag to skip this check. 73 | EOS 74 | } 75 | 76 | 1; 77 | -------------------------------------------------------------------------------- /lib/Elevate/Components/InfluxDB.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::InfluxDB; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::InfluxDB 8 | 9 | =head2 check 10 | 11 | noop 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | Capture that influxdb is installed 16 | 17 | =head2 post_distro_upgrade 18 | 19 | Reinstall influxdb if it was installed 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use Elevate::Constants (); 26 | use Elevate::PkgMgr (); 27 | use Elevate::StageFile (); 28 | 29 | use Cpanel::Pkgr (); 30 | use Cwd (); 31 | use Log::Log4perl qw(:easy); 32 | 33 | use parent qw{Elevate::Components::Base}; 34 | 35 | sub pre_distro_upgrade ($self) { 36 | 37 | Elevate::StageFile::remove_from_stage_file('reinstall.influxdb'); 38 | 39 | return unless Cpanel::Pkgr::is_installed('telegraf'); 40 | 41 | INFO("Not removing influxdb. Will re-install it after elevate."); 42 | Elevate::StageFile::update_stage_file( { 'reinstall' => { 'influxdb' => 1 } } ); 43 | 44 | return; 45 | } 46 | 47 | sub post_distro_upgrade ($self) { 48 | 49 | return unless Elevate::StageFile::read_stage_file('reinstall')->{'influxdb'}; 50 | 51 | INFO("Re-installing telegraf for influxdb"); 52 | Elevate::PkgMgr::reinstall('telegraf'); 53 | 54 | return; 55 | } 56 | 57 | 1; 58 | -------------------------------------------------------------------------------- /lib/Elevate/Components/IsContainer.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::IsContainer; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::IsContainer 8 | 9 | =head2 check 10 | 11 | Prevent elevation if the server is hosted in a container like environment 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | noop 16 | 17 | =head2 post_distro_upgrade 18 | 19 | noop 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use parent qw{Elevate::Components::Base}; 26 | use Log::Log4perl qw(:easy); 27 | 28 | sub check ($self) { # $self is a cpev object here 29 | 30 | return 0 if $self->upgrade_distro_manually; 31 | 32 | if ( _is_container_envtype() ) { 33 | return $self->has_blocker(<<~'EOS'); 34 | cPanel thinks that this is a container-like environment. 35 | This script cannot upgrade container environments. 36 | Consider contacting your hypervisor provider for alternative solutions. 37 | EOS 38 | } 39 | 40 | return 0; 41 | } 42 | 43 | sub _is_container_envtype () { 44 | require Cpanel::OSSys::Env; 45 | my $envtype = Cpanel::OSSys::Env::get_envtype(); 46 | 47 | return scalar grep { $envtype eq $_ } qw( 48 | virtuozzo 49 | vzcontainer 50 | lxc 51 | virtualiron 52 | vserver 53 | ); 54 | } 55 | 56 | 1; 57 | -------------------------------------------------------------------------------- /lib/Elevate/Components/Kernel.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::Kernel; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::Kernel 8 | 9 | =head2 check 10 | 11 | noop 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | noop 16 | 17 | =head2 post_distro_upgrade 18 | 19 | Check and notify of installed el7 kernel packages 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use Elevate::Constants (); 26 | use Elevate::OS (); 27 | use Elevate::PkgMgr (); 28 | 29 | use Cwd (); 30 | use Log::Log4perl qw(:easy); 31 | 32 | use parent qw{Elevate::Components::Base}; 33 | 34 | sub post_distro_upgrade ($self) { 35 | 36 | $self->run_once('_kernel_check'); 37 | 38 | return; 39 | } 40 | 41 | sub _kernel_check ($self) { 42 | 43 | my $kernel_pkgs = Elevate::PkgMgr::get_installed_pkgs('kernel-*'); 44 | 45 | my @el7_kernels; 46 | foreach my $kernel ( sort keys %$kernel_pkgs ) { 47 | if ( $kernel_pkgs->{$kernel} =~ m/\.el7\./ ) { 48 | push @el7_kernels, "$kernel-$kernel_pkgs->{$kernel}"; 49 | } 50 | } 51 | 52 | return unless @el7_kernels; 53 | 54 | my $pretty_distro_name = Elevate::OS::upgrade_to_pretty_name(); 55 | 56 | my $msg = "The following kernels should probably be removed as they will not function on $pretty_distro_name:\n\n"; 57 | foreach my $kernel (@el7_kernels) { 58 | $msg .= " $kernel\n"; 59 | } 60 | 61 | $msg .= "\nYou can remove these by running: /usr/bin/rpm -e " . join( " ", @el7_kernels ) . "\n"; 62 | 63 | Elevate::Notify::add_final_notification($msg); 64 | 65 | return; 66 | } 67 | 68 | 1; 69 | -------------------------------------------------------------------------------- /lib/Elevate/Components/KernelCare.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::KernelCare; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::KernelCare 8 | 9 | =head2 check 10 | 11 | Check if KernelCare is supported for the upgrade if it is installed 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | Remove KernelCare 16 | 17 | =head2 post_distro_upgrade 18 | 19 | Reinstall KernelCare 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use Elevate::Constants (); 26 | use Elevate::OS (); 27 | use Elevate::PkgMgr (); 28 | use Elevate::StageFile (); 29 | 30 | use Cwd (); 31 | use File::Copy (); 32 | use Log::Log4perl qw(:easy); 33 | use Elevate::Fetch (); 34 | 35 | use parent qw{Elevate::Components::Base}; 36 | 37 | sub pre_distro_upgrade ($self) { 38 | 39 | return unless Elevate::OS::supports_kernelcare(); 40 | return if Elevate::OS::leapp_can_handle_kernelcare(); 41 | 42 | $self->run_once("_remove_kernelcare_if_needed"); 43 | 44 | return; 45 | } 46 | 47 | sub post_distro_upgrade ($self) { 48 | 49 | return unless Elevate::OS::supports_kernelcare(); 50 | return if Elevate::OS::leapp_can_handle_kernelcare(); 51 | 52 | $self->run_once('_restore_kernelcare'); 53 | 54 | return; 55 | } 56 | 57 | sub _remove_kernelcare_if_needed ($self) { 58 | 59 | return unless -x q[/usr/bin/kcarectl]; 60 | 61 | # This environment variable signals to the KernelCare RPM scriptlets not to deregister on package removal. 62 | local $ENV{KCARE_KEEP_REGISTRATION} = '1'; 63 | Elevate::PkgMgr::remove_pkgs_from_repos('kernelcare'); 64 | 65 | INFO("Work around issue with occasional missing package signing keys."); 66 | $self->ssystem_and_die(qw{ /usr/bin/rpm --import https://repo.cloudlinux.com/kernelcare/RPM-GPG-KEY-KernelCare }); 67 | 68 | Elevate::StageFile::update_stage_file( { 'reinstall' => { 'kernelcare' => 1 } } ); 69 | 70 | return 1; 71 | } 72 | 73 | sub _restore_kernelcare ($self) { 74 | return unless Elevate::StageFile::read_stage_file('reinstall')->{'kernelcare'}; 75 | 76 | INFO("Restoring kernelcare"); 77 | 78 | INFO("Retrieving kernelcare installer"); 79 | my $installer_script = Elevate::Fetch::script( 'https://kernelcare.com/installer', 'kernelcare_installer' ); 80 | 81 | my $conf_file = q[/etc/sysconfig/kcare/kcare.conf]; 82 | if ( -e $conf_file . q[.rpmsave] ) { 83 | INFO("Restoring Configuration file: $conf_file"); 84 | 85 | # restore configuration file before installing it 86 | File::Copy::cp( $conf_file . q[.rpmsave], $conf_file ); 87 | } 88 | 89 | INFO("Running kernelcare installer"); 90 | $self->ssystem_and_die( '/usr/bin/bash' => $installer_script ); 91 | 92 | unlink $installer_script; 93 | 94 | INFO("Updating kernelcare"); 95 | $self->ssystem(qw{ /usr/bin/kcarectl --update }); 96 | 97 | return; 98 | } 99 | 100 | sub check ($self) { 101 | return unless -x q[/usr/bin/kcarectl]; 102 | return if Elevate::OS::supports_kernelcare(); 103 | 104 | my $name = Elevate::OS::default_upgrade_to(); 105 | return $self->has_blocker(<<~"EOS"); 106 | ELevate does not currently support KernelCare for upgrades of $name. 107 | Support for KernelCare on $name will be added in a future version of ELevate. 108 | EOS 109 | } 110 | 111 | 1; 112 | -------------------------------------------------------------------------------- /lib/Elevate/Components/LiteSpeed.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::LiteSpeed; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::LiteSpeed 8 | 9 | =head2 check 10 | 11 | noop 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | Record if litespeed is installed and has a valid license 16 | 17 | =head2 post_distro_upgrade 18 | 19 | Report if litespeed license is no longer valid and restart service 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use Elevate::Constants (); 26 | use Elevate::StageFile (); 27 | 28 | use Cwd (); 29 | use Log::Log4perl qw(:easy); 30 | 31 | use parent qw{Elevate::Components::Base}; 32 | 33 | sub pre_distro_upgrade ($self) { 34 | 35 | Elevate::StageFile::remove_from_stage_file('reinstall.litespeed'); 36 | 37 | my $ls_cfg_dir = q[/usr/local/lsws/conf]; 38 | return unless -d $ls_cfg_dir; 39 | 40 | INFO("LiteSpeed is installed"); 41 | 42 | # check if the license is valid before updating 43 | $self->ssystem(qw{/usr/local/lsws/bin/lshttpd -V}); 44 | my $has_valid_license = $? == 0 ? 1 : 0; 45 | 46 | my $data = { 47 | has_valid_license => $has_valid_license, 48 | }; 49 | 50 | Elevate::StageFile::update_stage_file( { 'reinstall' => { 'litespeed' => $data } } ); 51 | 52 | return; 53 | } 54 | 55 | sub post_distro_upgrade ($self) { 56 | 57 | my $data = Elevate::StageFile::read_stage_file('reinstall')->{'litespeed'}; 58 | return unless ref $data; 59 | 60 | INFO("Checking LiteSpeed"); 61 | 62 | # check the current license 63 | if ( $data->{has_valid_license} ) { 64 | $self->ssystem(qw{/usr/local/lsws/bin/lshttpd -V}); 65 | ERROR("LiteSpeed license is not valid. Check /usr/local/lsws/conf/serial.no") if $? != 0; 66 | } 67 | 68 | $self->ssystem(qw{/usr/bin/systemctl restart lsws}); 69 | 70 | return; 71 | } 72 | 73 | 1; 74 | -------------------------------------------------------------------------------- /lib/Elevate/Components/MountPoints.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::MountPoints; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::MountPoints 8 | 9 | =head2 check 10 | 11 | Verify that `mount -a` succeeds in order to confirm file system integrity 12 | between reboots 13 | 14 | =head2 pre_distro_upgrade 15 | 16 | noop 17 | 18 | =head2 post_distro_upgrade 19 | 20 | noop 21 | 22 | =cut 23 | 24 | use cPstrict; 25 | 26 | use parent qw{Elevate::Components::Base}; 27 | 28 | use constant FINDMNT_BIN => '/usr/bin/findmnt'; 29 | use constant MOUNT_BIN => '/usr/bin/mount'; 30 | 31 | use Log::Log4perl qw(:easy); 32 | 33 | sub check ($self) { 34 | $self->_ensure_mount_dash_a_succeeds(); 35 | return; 36 | } 37 | 38 | sub _ensure_mount_dash_a_succeeds ($self) { 39 | 40 | # Only do this in start mode because it can change the file system mounts 41 | return if $self->is_check_mode(); 42 | 43 | my $ret = $self->ssystem_capture_output( MOUNT_BIN, '-a' ); 44 | my $stderr = join "\n", @{ $ret->{stderr} }; 45 | if ( $ret->{status} != 0 ) { 46 | 47 | # No use in letting leapp preupgrade execute if this fails 48 | $self->components->abort_on_first_blocker(1); 49 | 50 | my $bin = MOUNT_BIN(); 51 | return $self->has_blocker(<<~"EOS"); 52 | The following command failed to execute successfully on your server: 53 | 54 | $bin -a 55 | 56 | The following message was given as the reason for the failure: 57 | 58 | $stderr 59 | 60 | Since this script will need to reboot your server, we need to ensure a 61 | consistent file system in between in each reboot. Please review the 62 | entries in '/etc/fstab' and ensure that each entry is valid and that 63 | '$bin -a' returns exit code 0 before continuing. 64 | 65 | If your '/etc/fstab' file has not been customized, you may want to 66 | consider reaching out to cPanel Support for assistance: 67 | https://docs.cpanel.net/knowledge-base/technical-support-services/how-to-open-a-technical-support-ticket/ 68 | EOS 69 | } 70 | 71 | return; 72 | } 73 | 74 | 1; 75 | -------------------------------------------------------------------------------- /lib/Elevate/Components/NetworkManager.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::NetworkManager; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::NetworkManager 8 | 9 | =head2 check 10 | 11 | noop 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | Enable the NetworkManager service so that it will start on the next reboot 16 | 17 | =head2 post_distro_upgrade 18 | 19 | noop 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use Elevate::OS (); 26 | use Elevate::SystemctlService (); 27 | 28 | use Log::Log4perl qw(:easy); 29 | 30 | use parent qw{Elevate::Components::Base}; 31 | 32 | sub pre_distro_upgrade ($self) { 33 | return if $self->upgrade_distro_manually(); # skip when --upgrade-distro-manually is provided 34 | return unless Elevate::OS::needs_network_manager(); 35 | 36 | my $service_name = 'NetworkManager'; 37 | my $service = Elevate::SystemctlService->new( name => $service_name ); 38 | 39 | return if $service->is_enabled(); 40 | $service->enable(); 41 | return; 42 | } 43 | 44 | 1; 45 | -------------------------------------------------------------------------------- /lib/Elevate/Components/PackageRestore.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::PackageRestore; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::PackageRestore 8 | 9 | =head2 check 10 | 11 | noop 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | Detect which packages in our list are installed and store our findings 16 | 17 | =head2 post_distro_upgrade 18 | 19 | Reinstall any packages detected pre distro upgrade 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use Elevate::PkgMgr (); 26 | use Elevate::StageFile (); 27 | 28 | use Cpanel::Pkgr (); 29 | 30 | use parent qw{Elevate::Components::Base}; 31 | 32 | # 33 | # Set as a function for unit testing 34 | # 35 | # Returns a hash of package names and whether or not they should be removed 36 | # from the system during the pre_distro_upgrade phase 37 | # For some packages the uninstall/reinstall is handled elsewhere and 38 | # we would like to only backup and restore the config files 39 | # 40 | sub _get_packages_to_check () { 41 | return ( 42 | 'net-snmp' => 1, 43 | 'sys-snap' => 1, 44 | 'cpanel-exim' => 0, 45 | ); 46 | } 47 | 48 | sub pre_distro_upgrade ($self) { 49 | 50 | my %package_list = _get_packages_to_check(); 51 | my @installed_packages; 52 | 53 | foreach my $package ( keys %package_list ) { 54 | if ( Cpanel::Pkgr::is_installed($package) ) { 55 | push @installed_packages, $package; 56 | } 57 | } 58 | 59 | # only remove the packages that are installed and flagged for removal 60 | my @packages_to_remove = grep { $package_list{$_} } @installed_packages; 61 | 62 | Elevate::PkgMgr::remove(@packages_to_remove); 63 | 64 | my $config_files = Elevate::PkgMgr::get_config_files( \@installed_packages ); 65 | 66 | Elevate::StageFile::update_stage_file( 67 | { 68 | 'packages_to_restore' => $config_files, 69 | } 70 | ); 71 | 72 | return; 73 | } 74 | 75 | sub post_distro_upgrade ($self) { 76 | 77 | my $package_info = Elevate::StageFile::read_stage_file('packages_to_restore'); 78 | return unless defined $package_info and ref $package_info eq 'HASH'; 79 | 80 | foreach my $package ( keys %$package_info ) { 81 | 82 | Elevate::PkgMgr::install($package) unless Cpanel::Pkgr::is_installed($package); 83 | 84 | Elevate::PkgMgr::restore_config_files( @{ $package_info->{$package} } ); 85 | } 86 | 87 | return; 88 | } 89 | 90 | 1; 91 | -------------------------------------------------------------------------------- /lib/Elevate/Components/Panopta.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::Panopta; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::Panopta 8 | 9 | =head2 check 10 | 11 | noop 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | Uninstall the Panopta agent since it is deprecated and not compatible with 16 | Elevate 17 | 18 | =head2 post_distro_upgrade 19 | 20 | noop 21 | 22 | =cut 23 | 24 | use cPstrict; 25 | 26 | use Cpanel::Pkgr (); 27 | 28 | use Elevate::PkgMgr (); 29 | 30 | use parent qw{Elevate::Components::Base}; 31 | 32 | sub pre_distro_upgrade ($self) { 33 | 34 | if ( Cpanel::Pkgr::is_installed('panopta-agent') ) { 35 | 36 | Elevate::PkgMgr::remove('panopta-agent'); 37 | } 38 | 39 | return; 40 | } 41 | 42 | 1; 43 | -------------------------------------------------------------------------------- /lib/Elevate/Components/RmMod.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::RmMod; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::RmMod 8 | 9 | =head2 check 10 | 11 | noop 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | Remove unsupported/unwanted kernel modules 16 | 17 | =head2 post_distro_upgrade 18 | 19 | noop 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use Elevate::Constants (); 26 | 27 | use Cwd (); 28 | use Log::Log4perl qw(:easy); 29 | 30 | use parent qw{Elevate::Components::Base}; 31 | 32 | sub pre_distro_upgrade ($self) { 33 | 34 | $self->run_once("_rmod_ln"); 35 | 36 | return; 37 | } 38 | 39 | sub _rmod_ln ($self) { 40 | 41 | $self->ssystem( '/usr/sbin/rmmod', $_ ) foreach qw/floppy pata_acpi/; 42 | 43 | return; 44 | } 45 | 46 | 1; 47 | -------------------------------------------------------------------------------- /lib/Elevate/Components/SSH.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::SSH; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::SSH 8 | 9 | =head2 check 10 | 11 | Check that PermitRootLogin setting is explicitely set in sshd_config since the 12 | default for this config option changes on upgraded systems 13 | 14 | =head2 pre_distro_upgrade 15 | 16 | Explicitely set PermitRootLogin to yes if it was not explicitely set in the 17 | check 18 | 19 | =head2 post_distro_upgrade 20 | 21 | noop 22 | 23 | =cut 24 | 25 | use cPstrict; 26 | 27 | use Elevate::Constants (); 28 | 29 | use Cwd (); 30 | use File::Slurper (); 31 | use Log::Log4perl qw(:easy); 32 | 33 | use parent qw{Elevate::Components::Base}; 34 | 35 | sub pre_distro_upgrade ($self) { 36 | 37 | my $sshd_config = q[/etc/ssh/sshd_config]; 38 | 39 | my $setup = File::Slurper::read_binary($sshd_config) // ''; 40 | 41 | # PermitRootLogin is explicitly set, no need for changes 42 | return if ( $setup =~ m{^\s*PermitRootLogin\b}m ); 43 | 44 | # Add ending newline if file does not end with newline 45 | if ( $setup !~ m{\n$} && length $setup ) { 46 | $setup .= "\n"; 47 | } 48 | 49 | $setup .= "PermitRootLogin yes\n"; 50 | 51 | File::Slurper::write_binary( $sshd_config, $setup ); 52 | 53 | return; 54 | } 55 | 56 | sub check ($self) { 57 | 58 | return $self->_check_ssh_config(); 59 | } 60 | 61 | sub _check_ssh_config ($self) { 62 | my $sshd_config = q[/etc/ssh/sshd_config]; 63 | 64 | my $setup = eval { File::Slurper::read_binary($sshd_config) } // ''; 65 | if ( my $exception = $@ ) { 66 | ERROR("The system could not read the sshd config file ($sshd_config): $exception"); 67 | return $self->has_blocker(qq[Unable to read the sshd config file: $sshd_config]); 68 | } 69 | 70 | if ( $setup !~ m{^\s*PermitRootLogin\b}m ) { 71 | WARN(<<~"EOS"); 72 | The OpenSSH configuration file does not explicitly state the PermitRootLogin 73 | option in the sshd_config file. This option may default to "prohibit-password" 74 | after the upgrade is complete. We will set the 'PermitRootLogin' value in 75 | $sshd_config to 'yes' before upgrading. 76 | 77 | EOS 78 | 79 | return 0; 80 | } 81 | 82 | return 1; 83 | } 84 | 85 | 1; 86 | -------------------------------------------------------------------------------- /lib/Elevate/Components/Softaculous.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::Softaculous; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::Softaculous 8 | 9 | =head2 pre_distro_upgrade 10 | 11 | If we can query the version of Softaculous through their CLI tool, it is installed. 12 | 13 | =head2 post_distro_upgrade 14 | 15 | If Softaculous is installed, re-install it after the upgrade. 16 | 17 | NOTE: This needs to happen after cPanel is updated to work with the new OS, since Softaculous relies on cPanel PHP. 18 | 19 | =cut 20 | 21 | use cPstrict; 22 | 23 | use Elevate::Fetch (); 24 | use Elevate::StageFile (); 25 | 26 | use Cpanel::Binaries (); 27 | use Cpanel::SafeRun::Object (); 28 | 29 | use Log::Log4perl qw(:easy); 30 | 31 | use parent qw{Elevate::Components::Base}; 32 | 33 | use Simple::Accessor qw(cli_path); 34 | 35 | sub _build_cli_path { return '/usr/local/cpanel/whostmgr/docroot/cgi/softaculous/cli.php' } 36 | 37 | sub pre_distro_upgrade ($self) { 38 | 39 | return unless -r $self->cli_path; 40 | 41 | my $sr = _run_script( $self->cli_path ); 42 | 43 | return if $sr->exec_failed() || $sr->to_exception(); 44 | 45 | my $version = $sr->stdout() // ''; 46 | chomp $version; 47 | 48 | if ( length $version ) { 49 | INFO('Softaculous has been detected. The system will re-install that software after the distro upgrade.'); 50 | Elevate::StageFile::update_stage_file( { softaculous => $version } ); 51 | } 52 | 53 | return; 54 | } 55 | 56 | # split out for mocking purposes 57 | sub _run_script ($path) { 58 | return Cpanel::SafeRun::Object->new( 59 | program => Cpanel::Binaries::path('php'), 60 | args => [ $path, '--version' ], 61 | ); 62 | } 63 | 64 | sub post_distro_upgrade ($self) { 65 | 66 | my $version = Elevate::StageFile::read_stage_file( 'softaculous', '' ); 67 | return unless length $version; 68 | 69 | my $path = Elevate::Fetch::script( 'https://files.softaculous.com/install.sh', 'softaculous_install' ); 70 | 71 | if ($path) { 72 | INFO('Re-installing Softaculous:'); 73 | if ( $self->ssystem( Cpanel::Binaries::path('bash'), $path, '--reinstall' ) ) { 74 | ERROR('Re-installation of Softaculous failed.'); 75 | } 76 | } 77 | else { 78 | ERROR('Failed to download Softaculous installer.'); 79 | } 80 | 81 | return; 82 | } 83 | 84 | 1; 85 | -------------------------------------------------------------------------------- /lib/Elevate/Components/Ufw.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::Ufw; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::Ufw 8 | 9 | =head2 check 10 | 11 | noop 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | Open port 1022 for upgrades using do-release-upgrade 16 | 17 | =head2 post_distro_upgrade 18 | 19 | Close port 1022 for upgrades using do-release-upgrade 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use Elevate::OS (); 26 | use Elevate::StageFile (); 27 | 28 | use Log::Log4perl qw(:easy); 29 | 30 | use parent qw{Elevate::Components::Base}; 31 | 32 | use constant UFW => '/usr/sbin/ufw'; 33 | 34 | sub pre_distro_upgrade ($self) { 35 | return if $self->upgrade_distro_manually(); # skip when --upgrade-distro-manually is provided 36 | return unless Elevate::OS::needs_do_release_upgrade(); 37 | 38 | if ( !-x UFW ) { 39 | my $ufw = UFW; 40 | WARN(<<~"EOS"); 41 | '$ufw' is either missing or not executable on this server. Unable to 42 | ensure that port 1022 is open as a secondary ssh option for 43 | do-release-upgrade. 44 | EOS 45 | 46 | return; 47 | } 48 | 49 | my $current_status = $self->ssystem_capture_output( UFW, 'status' ); 50 | my $is_active = grep { $_ =~ m/^Status:\sactive$/ } @{ $current_status->{stdout} }; 51 | my $is_open = grep { $_ =~ m{^1022/tcp.*ALLOW.*Anywhere} } @{ $current_status->{stdout} }; 52 | 53 | my $data = { 54 | is_active => $is_active, 55 | is_open => $is_open, 56 | }; 57 | 58 | Elevate::StageFile::update_stage_file( { ufw => $data } ); 59 | 60 | return if $is_active && $is_open; 61 | 62 | $self->ssystem_and_die( UFW, 'allow', '1022/tcp' ); 63 | 64 | $is_active ? $self->ssystem_and_die( UFW, 'reload' ) : $self->ssystem_and_die( UFW, '--force', 'enable' ); 65 | 66 | return; 67 | } 68 | 69 | sub post_distro_upgrade ($self) { 70 | my $ufw_data = Elevate::StageFile::read_stage_file( 'ufw', '' ); 71 | 72 | return unless ref $ufw_data && ref $ufw_data eq 'HASH'; 73 | 74 | return if $ufw_data->{is_active} && $ufw_data->{is_open}; 75 | 76 | $self->ssystem_and_die( UFW, 'delete', 'allow', '1022/tcp' ); 77 | 78 | return if $ufw_data->{is_active}; 79 | 80 | $self->ssystem_and_die( UFW, 'disable' ); 81 | 82 | return; 83 | } 84 | 85 | 1; 86 | -------------------------------------------------------------------------------- /lib/Elevate/Components/UnconvertedModules.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::UnconvertedModules; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::UnconvertedModules 8 | 9 | =head2 check 10 | 11 | noop 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | noop 16 | 17 | =head2 post_distro_upgrade 18 | 19 | 1. Remove the leapp packages since they do not convert during the leapp upgrade 20 | and are no longer necessary 21 | 2. Warn about other 'el7' modules that are still installed on the system as they 22 | likely will not work properly after the upgrade 23 | 24 | =cut 25 | 26 | use cPstrict; 27 | 28 | use Elevate::OS (); 29 | use Elevate::PkgMgr (); 30 | 31 | use Log::Log4perl qw(:easy); 32 | 33 | use parent qw{Elevate::Components::Base}; 34 | 35 | use constant EXEMPTED_PACKAGES => ( 36 | qr/^kernel-/, 37 | qr/^acronis/, 38 | ); 39 | 40 | sub post_distro_upgrade ($self) { 41 | return unless Elevate::OS::needs_leapp(); 42 | 43 | $self->run_once('_remove_leapp_packages'); 44 | $self->run_once('_warn_about_other_modules_that_did_not_convert'); 45 | return; 46 | } 47 | 48 | sub _remove_leapp_packages ($self) { 49 | my @leapp_packages = qw{ 50 | elevate-release 51 | leapp 52 | leapp-data-almalinux 53 | leapp-data-cloudlinux 54 | leapp-deps 55 | leapp-repository-deps 56 | leapp-upgrade-el7toel8 57 | leapp-upgrade-el7toel8-deps 58 | leapp-upgrade-el8toel9 59 | leapp-upgrade-el8toel9-deps 60 | python2-leapp 61 | python3-leapp 62 | snactor 63 | }; 64 | 65 | INFO('Removing packages provided by leapp'); 66 | my @to_remove = grep { Cpanel::Pkgr::is_installed($_) } @leapp_packages; 67 | Elevate::PkgMgr::remove(@to_remove); 68 | 69 | return; 70 | } 71 | 72 | sub _warn_about_other_modules_that_did_not_convert ($self) { 73 | my $installed_packages = Elevate::PkgMgr::get_installed_pkgs(); 74 | 75 | my @el_installed_packages; 76 | foreach my $pkg ( sort keys %$installed_packages ) { 77 | my $el_package_regex = Elevate::OS::el_package_regex(); 78 | if ( $installed_packages->{$pkg} =~ m/\Q$el_package_regex\E/ ) { 79 | push @el_installed_packages, "$pkg-$installed_packages->{$pkg}"; 80 | } 81 | } 82 | 83 | my @el_packages_minus_exemptions; 84 | foreach my $pkg (@el_installed_packages) { 85 | next if grep { $pkg =~ m/$_/ } EXEMPTED_PACKAGES(); 86 | push @el_packages_minus_exemptions, $pkg; 87 | } 88 | 89 | return unless @el_packages_minus_exemptions; 90 | 91 | my $pretty_distro_name = Elevate::OS::upgrade_to_pretty_name(); 92 | 93 | my $msg = "The following packages should probably be removed as they will not function on $pretty_distro_name\n\n"; 94 | foreach my $pkg (@el_packages_minus_exemptions) { 95 | $msg .= " $pkg\n"; 96 | } 97 | 98 | $msg .= "\nYou can remove these by running: yum -y remove " . join( ' ', @el_packages_minus_exemptions ) . "\n"; 99 | 100 | Elevate::Notify::add_final_notification($msg); 101 | 102 | return; 103 | } 104 | 105 | 1; 106 | -------------------------------------------------------------------------------- /lib/Elevate/Components/UpdateReleaseUpgrades.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::UpdateReleaseUpgrades; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::UpdateReleaseUpgrades 8 | 9 | =head2 check 10 | 11 | noop 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | 1. Remove the install script that puts the block in place for Ubuntu upgrades 16 | 2. Update the release-upgrades file in order to allow Ubuntu upgrades 17 | 18 | =head2 post_distro_upgrade 19 | 20 | noop 21 | 22 | =cut 23 | 24 | use cPstrict; 25 | 26 | use Elevate::OS (); 27 | 28 | use File::Copy (); 29 | use File::Slurper (); 30 | 31 | use Log::Log4perl qw(:easy); 32 | 33 | use constant BLOCK_UBUNTU_UPGRADES => q[/usr/local/cpanel/install/BlockUbuntuUpgrades.pm]; 34 | use constant RELEASE_UPGRADES_FILE => q[/etc/update-manager/release-upgrades]; 35 | 36 | use parent qw{Elevate::Components::Base}; 37 | 38 | sub pre_distro_upgrade ($self) { 39 | 40 | return unless Elevate::OS::needs_do_release_upgrade(); 41 | 42 | my $pretty_distro_name = Elevate::OS::upgrade_to_pretty_name(); 43 | 44 | INFO("Removing install script that blocks upgrades to $pretty_distro_name"); 45 | unlink(BLOCK_UBUNTU_UPGRADES); 46 | 47 | INFO("Updating config file to allow upgrades to $pretty_distro_name"); 48 | my $content = File::Slurper::read_binary(RELEASE_UPGRADES_FILE) // ''; 49 | my @lines = split "\n", $content; 50 | 51 | my $in_default = 0; 52 | my $was_updated = 0; 53 | foreach my $line (@lines) { 54 | 55 | if ( $line =~ qr{^\s*\[(\w+)\]}a ) { 56 | if ( $1 eq 'DEFAULT' ) { 57 | $in_default = 1; 58 | } 59 | next; 60 | } 61 | next unless $in_default; 62 | 63 | return if $line =~ m{^\s*Prompt\s*=\s*lts}a; 64 | 65 | if ( $line =~ s{^\s*Prompt\s*=\s*(?:normal|never)}{Prompt=lts} ) { 66 | $was_updated = 1; 67 | last; 68 | } 69 | } 70 | 71 | if ($was_updated) { 72 | $content = join "\n", @lines; 73 | $content .= "\n"; 74 | File::Slurper::write_binary( RELEASE_UPGRADES_FILE, $content ); 75 | } 76 | 77 | # Did the file have invalid or bad data to reach this? 78 | else { 79 | my $upgrade_file = RELEASE_UPGRADES_FILE; 80 | my $backup_file = $upgrade_file . '.pre_elevate'; 81 | 82 | INFO(<<~"EOS"); 83 | Expected line was not found in the config file. Backing up the config 84 | file to $backup_file and replacing the contents with the necessary 85 | config to ensure that the elevate script can upgrade the server. 86 | EOS 87 | 88 | File::Copy::cp( $upgrade_file, $backup_file ); 89 | 90 | my $new_content = <<~'EOS'; 91 | [DEFAULT] 92 | Prompt=lts 93 | EOS 94 | 95 | File::Slurper::write_binary( RELEASE_UPGRADES_FILE, $new_content ); 96 | } 97 | 98 | return; 99 | } 100 | 101 | 1; 102 | -------------------------------------------------------------------------------- /lib/Elevate/Components/UpdateSystem.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::UpdateSystem; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::UpdateSystem 8 | 9 | =head2 check 10 | 11 | Ensure that the Package System is in an operable state. 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | Ensure that all system packages are up to date 16 | 17 | =head2 post_distro_upgrade 18 | 19 | noop 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use Elevate::OS (); 26 | 27 | use Log::Log4perl qw(:easy); 28 | 29 | use parent qw{Elevate::Components::Base}; 30 | 31 | sub check ($self) { 32 | $self->_check_cpanel_pkgs(); 33 | return; 34 | } 35 | 36 | sub _check_cpanel_pkgs ($self) { 37 | my $out = $self->ssystem_capture_output( '/usr/local/cpanel/scripts/check_cpanel_pkgs', '--list-only' ); 38 | 39 | my $altered = join "\n", @{ $out->{stdout} }; 40 | 41 | if ( $altered =~ /Problems were detected with cPanel-provided files which are controlled by packages/ ) { 42 | WARN(<<~"EOS"); 43 | /usr/local/cpanel/scripts/check_cpanel_pkgs reported that your system 44 | has altered packages. 45 | 46 | $altered 47 | 48 | Running check_cpanel_pkgs with the fix arguent should correct the issue. 49 | 50 | Example: /usr/local/cpanel/scripts/check_cpanel_pkgs --fix 51 | 52 | EOS 53 | 54 | return 0; 55 | } 56 | return 1; 57 | } 58 | 59 | sub pre_distro_upgrade ($self) { 60 | Elevate::PkgMgr::clean_all(); 61 | 62 | my $ok = $self->_check_cpanel_pkgs(); 63 | $self->_fix_cpanel_pkgs() if !$ok; 64 | 65 | $self->ssystem_and_die(qw{/usr/local/cpanel/scripts/update-packages}); 66 | 67 | # Remove this file so that nothing gets held back here since we need 68 | # to make sure that everything can update before we attempt to upgrade 69 | # the server 70 | # NOTE: This has to happen after update-packages or update-packages 71 | # will put it back in place 72 | if ( Elevate::OS::is_apt_based() ) { 73 | INFO('Removing /etc/apt/preferences.d/99-cpanel-exclude-packages'); 74 | unlink('/etc/apt/preferences.d/99-cpanel-exclude-packages'); 75 | } 76 | 77 | Elevate::PkgMgr::update(); 78 | 79 | return; 80 | } 81 | 82 | sub _fix_cpanel_pkgs ($self) { 83 | $self->ssystem(qw{/usr/local/cpanel/scripts/check_cpanel_pkgs --fix}); 84 | 85 | my $out = $self->ssystem_capture_output( 86 | '/usr/local/cpanel/scripts/check_cpanel_pkgs', 87 | '--list-only' 88 | ); 89 | 90 | my $altered = join "\n", @{ $out->{stdout} }; 91 | 92 | if ( $altered =~ /Problems were detected with cPanel-provided files which are controlled by packages/ ) { 93 | LOGDIE(<<~"EOS"); 94 | /usr/local/cpanel/scripts/check_cpanel_pkgs was unable to repair the packages on this system: 95 | 96 | $altered 97 | 98 | You may be able to resolve this by executing 99 | 100 | /usr/local/cpanel/scripts/check_cpanel_pkgs --fix 101 | 102 | Once the issue has been resolved, you may continue this process with 103 | 104 | $0 --continue 105 | EOS 106 | 107 | return 0; 108 | } 109 | 110 | return 1; 111 | } 112 | 113 | 1; 114 | -------------------------------------------------------------------------------- /lib/Elevate/Components/WPToolkit.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::WPToolkit; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::WPToolkit 8 | 9 | =head2 check 10 | 11 | noop 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | Remove wp-toolkit 16 | 17 | =head2 post_distro_upgrade 18 | 19 | Reinstall wp-toolkit 20 | 21 | =cut 22 | 23 | use cPstrict; 24 | 25 | use Elevate::Constants (); 26 | use Elevate::Fetch (); 27 | use Elevate::PkgMgr (); 28 | use Elevate::StageFile (); 29 | 30 | use Cwd (); 31 | use File::Copy (); 32 | use Log::Log4perl qw(:easy); 33 | 34 | use parent qw{Elevate::Components::Base}; 35 | 36 | sub pre_distro_upgrade ($self) { 37 | 38 | $self->run_once("_remove_wordpress_toolkit"); 39 | 40 | return; 41 | } 42 | 43 | sub post_distro_upgrade ($self) { 44 | 45 | $self->run_once('_reinstall_wordpress_toolkit'); 46 | 47 | return; 48 | } 49 | 50 | sub _remove_wordpress_toolkit ($self) { 51 | return unless Cpanel::Pkgr::is_installed('wp-toolkit-cpanel'); 52 | 53 | INFO("Removing Wordpress Toolkit"); 54 | 55 | INFO("Removing the rpm wp-toolkit-cpanel"); 56 | backup_3rdparty_file('/usr/local/cpanel/3rdparty/wp-toolkit/var/wp-toolkit.sqlite3'); 57 | backup_3rdparty_file('/usr/local/cpanel/3rdparty/wp-toolkit/var/etc/.shadow'); 58 | 59 | Elevate::PkgMgr::remove('wp-toolkit-cpanel'); 60 | 61 | Elevate::PkgMgr::remove_pkgs_from_repos(qw/wp-toolkit-cpanel wp-toolkit-thirdparties/); 62 | 63 | Elevate::StageFile::update_stage_file( { 'reinstall' => { 'wordpress_toolkit' => 1 } } ); 64 | 65 | return; 66 | } 67 | 68 | sub _reinstall_wordpress_toolkit ($self) { 69 | return unless Elevate::StageFile::read_stage_file('reinstall')->{'wordpress_toolkit'}; 70 | 71 | INFO("Restoring Wordpress Toolkit"); 72 | my $installer_script = Elevate::Fetch::script( 'https://wp-toolkit.plesk.com/cPanel/installer.sh', 'wptk_installer' ); 73 | 74 | $self->ssystem( '/usr/bin/bash', $installer_script ); 75 | unlink $installer_script; 76 | 77 | return; 78 | } 79 | 80 | sub backup_3rdparty_file ($file) { 81 | my $target = "$file.elevate_backup"; 82 | return File::Copy::cp( $file, $target ); 83 | } 84 | 85 | 1; 86 | -------------------------------------------------------------------------------- /lib/Elevate/Components/cPanelPlugins.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Components::cPanelPlugins; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Components::cPanelPlugins 8 | 9 | =head2 check 10 | 11 | noop 12 | 13 | =head2 pre_distro_upgrade 14 | 15 | Gather list of installed cPanel plugins to reinstall in post 16 | 17 | =head2 post_distro_upgrade 18 | 19 | Reinstall the list of installed cPanel plugins from pre 20 | 21 | =head2 NOTE 22 | 23 | This is skipped an apt based systems. It is not necessary there as things 24 | should continue to work after the upgrade and it would require a larger 25 | refactor away from using pkg_info() to determine what cPanel plugin 26 | packages are installed. Likely, we would need to hard code the list of 27 | packages. 28 | 29 | =cut 30 | 31 | use cPstrict; 32 | 33 | use Elevate::Constants (); 34 | use Elevate::OS (); 35 | use Elevate::PkgMgr (); 36 | use Elevate::StageFile (); 37 | 38 | use Cwd (); 39 | use Log::Log4perl qw(:easy); 40 | 41 | use parent qw{Elevate::Components::Base}; 42 | 43 | sub pre_distro_upgrade ($self) { 44 | return if Elevate::OS::is_apt_based(); 45 | 46 | # Backup arch rpms which we're going to remove and are provided by yum. 47 | my @installed_arch_cpanel_plugins; 48 | 49 | my $installed = Elevate::PkgMgr::pkg_list(); 50 | my @cpanel_repos = grep { m/^cpanel-/ } keys %$installed; 51 | foreach my $repo (@cpanel_repos) { 52 | push @installed_arch_cpanel_plugins, map { $_->{'package'} } $installed->{$repo}->@*; 53 | } 54 | 55 | return unless @installed_arch_cpanel_plugins; 56 | 57 | Elevate::StageFile::update_stage_file( { restore => { yum => \@installed_arch_cpanel_plugins } } ); 58 | 59 | return; 60 | } 61 | 62 | sub post_distro_upgrade ($self) { 63 | return if Elevate::OS::is_apt_based(); 64 | 65 | # Restore YUM arch plugins. 66 | 67 | my $stash = Elevate::StageFile::read_stage_file(); 68 | my $yum_arch_plugins = $stash->{'restore'}->{'yum'} // []; 69 | return unless scalar @$yum_arch_plugins; 70 | 71 | INFO('Restoring cPanel yum-based-plugins'); 72 | Elevate::PkgMgr::reinstall(@$yum_arch_plugins); 73 | 74 | return; 75 | } 76 | 77 | 1; 78 | -------------------------------------------------------------------------------- /lib/Elevate/Constants.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Constants; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Constants 8 | 9 | Define some shared constants for the elevate process. 10 | 11 | Note: not all constants need to be defined here, it could makes more 12 | sense to isolate some constant in their own component or blocker file, 13 | if their usage is self contained. 14 | 15 | =cut 16 | 17 | use cPstrict; 18 | 19 | # In place of Unix::Sysexits: 20 | use constant EX_UNAVAILABLE => 69; 21 | 22 | use constant SERVICE_DIR => '/etc/systemd/system/'; 23 | use constant SERVICE_NAME => 'elevate-cpanel.service'; 24 | 25 | use constant LOG_FILE => q[/var/log/elevate-cpanel.log]; 26 | use constant PID_FILE => q[/var/run/elevate-cpanel.pid]; 27 | 28 | use constant DEFAULT_GRUB_FILE => '/etc/default/grub'; 29 | 30 | use constant YUM_REPOS_D => q[/etc/yum.repos.d]; 31 | 32 | use constant ELEVATE_BACKUP_DIR => "/root/.elevate.backup"; 33 | 34 | use constant RPMDB_DIR => q[/var/lib/rpm]; 35 | use constant RPMDB_BACKUP_DIR => q[/var/lib/rpm-elevate-backup]; 36 | 37 | use constant IMUNIFY_AGENT => '/usr/bin/imunify360-agent'; 38 | 39 | use constant CHKSRVD_SUSPEND_FILE => q[/var/run/chkservd.suspend]; 40 | 41 | use constant IGNORE_OUTDATED_SERVICES_FILE => q[/etc/cpanel/local/ignore_outdated_services]; 42 | 43 | use constant SBIN_IP => q[/sbin/ip]; 44 | 45 | use constant ETH_FILE_PREFIX => q[/etc/sysconfig/network-scripts/ifcfg-]; 46 | 47 | use constant R1SOFT_REPO => 'r1soft'; 48 | use constant R1SOFT_REPO_FILE => '/etc/yum.repos.d/r1soft.repo'; 49 | use constant R1SOFT_MAIN_AGENT_PACKAGE => 'serverbackup-agent'; 50 | use constant R1SOFT_AGENT_PACKAGES => qw{ 51 | r1soft-getmodule 52 | serverbackup-agent 53 | serverbackup-async-agent-2-6 54 | serverbackup-enterprise-agent 55 | serverbackup-setup 56 | }; 57 | 58 | use constant ACRONIS_BACKUP_PACKAGE => 'acronis-backup-cpanel'; 59 | use constant ACRONIS_OTHER_PACKAGES => qw{ 60 | BackupAndRecoveryAgent 61 | BackupAndRecoveryBootableComponents 62 | dkms 63 | file_protector 64 | snapapi26_modules 65 | }; 66 | 67 | use constant POSTGRESQL_SYSTEM_DATADIR => '/var/lib/pgsql/data'; 68 | 69 | use constant OVH_MONITORING_TOUCH_FILE => '/var/cpanel/acknowledge_ovh_monitoring_for_elevate'; 70 | 71 | 1; 72 | -------------------------------------------------------------------------------- /lib/Elevate/Fetch.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Fetch; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Fetch 8 | 9 | Helper to fetch a script to a temporary location. 10 | 11 | =cut 12 | 13 | use cPstrict; 14 | 15 | use Elevate::Constants (); 16 | use Cpanel::HTTP::Client (); 17 | use File::Temp (); 18 | 19 | use Log::Log4perl qw(:easy); 20 | 21 | sub script ( $url, $template, $suffix = '.sh' ) { 22 | my $response = eval { 23 | my $http = Cpanel::HTTP::Client->new()->die_on_http_error(); 24 | $http->get($url); 25 | }; 26 | 27 | if ( my $exception = $@ ) { 28 | ERROR("The system could not fetch the script for $template: $exception"); 29 | return; 30 | } 31 | 32 | my $fh = File::Temp->new( 33 | TEMPLATE => "${template}_XXXX", 34 | SUFFIX => $suffix, 35 | UNLINK => 0, 36 | PERMS => 0600, 37 | TMPDIR => 1 38 | ) 39 | or do { 40 | ERROR(qq[Cannot create a temporary file]); 41 | return; 42 | }; 43 | print {$fh} $response->{'content'}; 44 | close $fh; 45 | 46 | return "$fh"; 47 | } 48 | 49 | 1; 50 | -------------------------------------------------------------------------------- /lib/Elevate/Logger.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Logger; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Logger 8 | 9 | Wrapper around Log4perl to initialize the elevate custom logger. 10 | 11 | =cut 12 | 13 | use cPstrict; 14 | 15 | use Elevate::Constants (); 16 | 17 | use Term::ANSIColor (); 18 | 19 | use Log::Log4perl qw(:easy); 20 | 21 | my %colors = ( 22 | TRACE => 'cyan', 23 | DEBUG => 'bold white', 24 | INFO => 'green', 25 | WARN => 'yellow', 26 | ERROR => 'red', 27 | FATAL => 'bold red', 28 | ); 29 | 30 | sub init ( $self, $debug_level = 'DEBUG' ) { 31 | my $log_file = Elevate::Constants::LOG_FILE; 32 | 33 | my $config = <<~"EOF"; 34 | log4perl.appender.File=Log::Log4perl::Appender::File 35 | log4perl.appender.File.filename=$log_file 36 | log4perl.appender.File.syswrite=1 37 | log4perl.appender.File.utf8=1 38 | log4perl.appender.File.layout=Log::Log4perl::Layout::PatternLayout 39 | log4perl.appender.File.layout.ConversionPattern=* %d{yyyy-MM-dd HH:mm:ss} (%L) [%s%p%u] %m%n 40 | EOF 41 | 42 | if ( $self->getopt('service') ) { 43 | $config .= <<~"EOF"; 44 | log4perl.rootLogger = $debug_level, File 45 | EOF 46 | } 47 | else { 48 | $config .= <<~"EOF"; 49 | log4perl.appender.Screen=Log::Log4perl::Appender::Screen 50 | log4perl.appender.Screen.stderr=0 51 | log4perl.appender.screen.utf8=1 52 | log4perl.appender.Screen.layout=Log::Log4perl::Layout::PatternLayout 53 | log4perl.appender.Screen.layout.ConversionPattern=* %d{yyyy-MM-dd HH:mm:ss} [%s%p%u] %m{indent=2,chomp}%n 54 | log4perl.rootLogger = $debug_level, Screen, File 55 | EOF 56 | } 57 | 58 | Log::Log4perl::Layout::PatternLayout::add_global_cspec( 's' => sub { Term::ANSIColor::color( $colors{ $_[3] } ) } ); 59 | Log::Log4perl::Layout::PatternLayout::add_global_cspec( 'u' => sub { Term::ANSIColor::color('reset') } ); 60 | 61 | Log::Log4perl->init( \$config ); 62 | 63 | return; 64 | } 65 | 66 | sub INFO_nolog ($msg) { 67 | 68 | return _nolog( $msg, 'INFO' ); 69 | } 70 | 71 | sub ERROR_nolog ($msg) { 72 | 73 | return _nolog( $msg, 'ERROR' ); 74 | } 75 | 76 | sub WARN_nolog ($msg) { 77 | 78 | return _nolog( $msg, 'WARN' ); 79 | } 80 | 81 | sub _nolog ( $msg, $type = 'DEBUG' ) { 82 | 83 | return unless length $msg; 84 | 85 | print '# -------------------------> ['; 86 | print Term::ANSIColor::color( $colors{$type} ); 87 | print $type; 88 | print Term::ANSIColor::color('reset'); 89 | print '] '; 90 | print Term::ANSIColor::color('bold white'); 91 | say $msg; 92 | print Term::ANSIColor::color('reset'); 93 | 94 | return; 95 | } 96 | 97 | 1; 98 | -------------------------------------------------------------------------------- /lib/Elevate/Marker.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Marker; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Marker 8 | 9 | This library provides logic to add information about the elevate process to the 10 | stage file 11 | 12 | =cut 13 | 14 | use cPstrict; 15 | 16 | use POSIX (); 17 | 18 | use Cpanel::LoadFile (); 19 | use Cpanel::MD5 (); 20 | use Cpanel::Version::Tiny (); 21 | 22 | use Elevate::StageFile (); 23 | 24 | use Log::Log4perl qw(:easy); 25 | 26 | sub startup ( $sum = undef ) { 27 | 28 | $sum //= Cpanel::MD5::getmd5sum($0); 29 | chomp($sum); 30 | _write_debug_line($sum); 31 | 32 | Elevate::StageFile::update_stage_file( 33 | { 34 | '_elevate_process' => { 35 | script_md5 => $sum, 36 | cpanel_build => $Cpanel::Version::Tiny::VERSION_BUILD, 37 | started_at => _bq_now(), 38 | redhat_release_pre => _read_redhat_release(), 39 | elevate_version_start => cpev::VERSION(), 40 | } 41 | } 42 | ); 43 | 44 | return; 45 | } 46 | 47 | sub success () { 48 | 49 | Elevate::StageFile::update_stage_file( 50 | { 51 | '_elevate_process' => { 52 | finished_at => _bq_now(), 53 | redhat_release_post => _read_redhat_release(), 54 | elevate_version_finish => cpev::VERSION(), 55 | } 56 | } 57 | ); 58 | 59 | return; 60 | } 61 | 62 | sub _bq_now () { 63 | return POSIX::strftime( '%FT%T', gmtime ); 64 | } 65 | 66 | sub _read_redhat_release() { 67 | my ($first_line) = split( "\n", Cpanel::LoadFile::loadfile('/etc/redhat-release') // '' ); 68 | 69 | return $first_line; 70 | } 71 | 72 | sub _write_debug_line ($sum) { 73 | DEBUG( sprintf( "Running $0 (%s/%s)", -s $0, $sum ) ); 74 | return; 75 | } 76 | 77 | 1; 78 | -------------------------------------------------------------------------------- /lib/Elevate/Motd.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Motd; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Motd 8 | 9 | Logic to setup then remove motd for elevate. 10 | 11 | =cut 12 | 13 | use cPstrict; 14 | 15 | sub setup { 16 | 17 | my $f = _motd_file(); 18 | 19 | my $notice = _motd_notice_message(); 20 | 21 | local $/; 22 | my $fh; 23 | my $content = ''; 24 | 25 | if ( open( $fh, '+<', $f ) ) { 26 | $content = <$fh> // ''; 27 | } 28 | elsif ( open( $fh, '>', $f ) ) { 29 | 1; 30 | } 31 | 32 | return 0 if $content =~ qr{elevate in progress}mi; 33 | 34 | print {$fh} "\n" if length($content) && $content !~ qr{\n\z}; 35 | 36 | print {$fh} $notice; 37 | 38 | return 1; 39 | } 40 | 41 | sub cleanup { 42 | 43 | my $f = _motd_file(); 44 | 45 | my $content; 46 | open( my $fh, '+<', $f ) or return; 47 | { 48 | local $/; 49 | $content = <$fh>; 50 | } 51 | 52 | return 0 unless $content && $content =~ qr{elevate in progress}mi; 53 | 54 | my $notice = _motd_notice_message(); 55 | 56 | if ( $content =~ s{\Q$notice\E}{} ) { 57 | seek( $fh, 0, 0 ); 58 | print {$fh} $content; 59 | truncate( $fh, tell($fh) ); 60 | close($fh); 61 | 62 | return 1; 63 | } 64 | 65 | return; 66 | } 67 | 68 | # Looks ugly, but fatpacker will just strip out lines =~ m/^\s*#/ 69 | sub _motd_notice_message { 70 | return 71 | "# -----------------------------------------------------------------------------\n#\n" 72 | . "# /!\\ ELEVATE IN PROGRESS /!\\ \n#\n" 73 | . "# Do not make any changes until it's complete\n" 74 | . "# you can check the current process status by running:\n#\n" 75 | . "#\t\t/scripts/elevate-cpanel --status\n#\n" 76 | . "# Or monitor the progress by running:\n#\n" 77 | . "#\t\t/scripts/elevate-cpanel --log\n#\n" 78 | . "# -----------------------------------------------------------------------------\n"; 79 | } 80 | 81 | sub _motd_file { # allow us to mock it, we cannot use Test::MockFile GH #77 - https://github.com/cpanel/Test-MockFile/issues/77 82 | return q[/etc/motd]; 83 | } 84 | 85 | 1; 86 | -------------------------------------------------------------------------------- /lib/Elevate/NICs.pm: -------------------------------------------------------------------------------- 1 | package Elevate::NICs; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::NICs 8 | 9 | Helper/Utility logic for NIC related tasks. 10 | 11 | =cut 12 | 13 | use cPstrict; 14 | 15 | use Elevate::Constants (); 16 | 17 | use Cpanel::SafeRun::Errors (); 18 | 19 | sub get_nics () { 20 | my $sbin_ip = Elevate::Constants::SBIN_IP(); 21 | my $ip_info = Cpanel::SafeRun::Errors::saferunnoerror( $sbin_ip, 'addr' ) // ''; 22 | 23 | my @eths; 24 | foreach my $line ( split /\n/xms, $ip_info ) { 25 | $line =~ /^[0-9]+: \s (eth[0-9]):/xms 26 | or next; 27 | 28 | my $eth = $1; 29 | my $value = readlink "/sys/class/net/$eth" 30 | or next; 31 | 32 | $value =~ m{/virtual/}xms 33 | and next; 34 | 35 | push @eths, $eth; 36 | } 37 | 38 | return @eths; 39 | } 40 | 41 | 1; 42 | -------------------------------------------------------------------------------- /lib/Elevate/Notify.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Notify; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Notify 8 | 9 | Helpers to display or send some notifications to the customer during the elevation process. 10 | 11 | =cut 12 | 13 | use cPstrict; 14 | 15 | use Elevate::StageFile (); 16 | 17 | use Log::Log4perl qw(:easy); 18 | 19 | # separate sub, so that it can be silenced during tests: 20 | sub warn_skip_version_check { 21 | WARN("The --skip-cpanel-version-check option was specified! This option is provided for testing purposes only! cPanel may not be able to support the resulting conversion. Please consider whether this is what you want."); 22 | return; 23 | } 24 | 25 | sub add_final_notification ( $msg, $warn_now = 0 ) { 26 | my $stage_info = Elevate::StageFile::read_stage_file(); 27 | 28 | return unless defined $msg && length $msg; 29 | 30 | Elevate::StageFile::update_stage_file( { final_notifications => [$msg] } ); # stacked to the previously stored 31 | 32 | if ($warn_now) { 33 | foreach my $l ( split( "\n", $msg ) ) { 34 | next unless length $l; 35 | WARN($l); 36 | } 37 | } 38 | 39 | return 1; 40 | } 41 | 42 | sub send_notification ( $subject, $msg, %opts ) { 43 | 44 | eval { 45 | _send_notification( $subject, $msg, %opts ); 46 | 1; 47 | } 48 | or warn "Failed to send notification: $@"; 49 | 50 | return; 51 | } 52 | 53 | # NOTE: change notification capturing in Test::Elevate if sub signature changes 54 | sub _send_notification ( $subject, $msg, %opts ) { 55 | 56 | my $is_success = delete $opts{is_success}; 57 | 58 | # note: no need to use one iContact::Class this is a one shot message 59 | require Cpanel::iContact; 60 | 61 | INFO("Sending notification: $subject"); 62 | 63 | my $log = $is_success ? \&INFO : \&ERROR; 64 | my @lines = split( "\n", $msg ); 65 | foreach my $line (@lines) { 66 | $log->($line); 67 | } 68 | 69 | Cpanel::iContact::icontact( 70 | 'application' => 'elevate', 71 | 'subject' => $subject, 72 | 'message' => $msg, 73 | ); 74 | 75 | return; 76 | } 77 | 1; 78 | -------------------------------------------------------------------------------- /lib/Elevate/OS/AlmaLinux8.pm: -------------------------------------------------------------------------------- 1 | package Elevate::OS::AlmaLinux8; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::OS::AlmaLinux8 - AlmaLinux8 custom values 8 | 9 | =cut 10 | 11 | use cPstrict; 12 | 13 | use parent 'Elevate::OS::RHEL'; 14 | 15 | use constant supported_cpanel_mysql_versions => qw{ 16 | 8.0 17 | 8.4 18 | 10.5 19 | 10.6 20 | 10.11 21 | 11.4 22 | }; 23 | 24 | use constant archive_dir => 'CentOS7-to-AlmaLinux8'; 25 | use constant default_upgrade_to => 'AlmaLinux'; 26 | use constant ea_alias => 'CentOS_9'; 27 | use constant el_package_regex => 'el8'; 28 | use constant elevate_rpm_url => 'https://repo.almalinux.org/elevate/elevate-release-latest-el8.noarch.rpm'; 29 | use constant has_imunify_ea_alias => 1; 30 | use constant imunify_ea_alias => 'CloudLinux_9'; 31 | use constant expected_post_upgrade_major => 9; 32 | use constant jetbackup_repo_rpm_url => 'https://repo.jetlicense.com/centOS/jetapps-repo-4096-latest.rpm'; 33 | use constant leapp_data_pkg => 'leapp-data-almalinux'; 34 | use constant name => 'AlmaLinux8'; 35 | use constant needs_crb => 1; 36 | use constant needs_grub_enable_blscfg => 1; 37 | use constant needs_network_manager => 1; 38 | use constant needs_sha1_enabled => 1; 39 | use constant needs_type_in_ifcfg => 1; 40 | use constant needs_vdo => 1; 41 | use constant original_os_major => 8; 42 | use constant pkgmgr_lib_path => '/var/lib/dnf'; 43 | use constant pretty_name => 'AlmaLinux 8'; 44 | use constant should_archive_elevate_files => 1; 45 | use constant supports_named_tiers => 1; 46 | use constant upgrade_to_pretty_name => 'AlmaLinux 9'; 47 | 48 | sub vetted_yum_repo ($self) { 49 | my @repos = $self->SUPER::vetted_yum_repo(); 50 | push @repos, $self->vetted_mysql_yum_repo_ids(); 51 | push @repos, 'powertools', 'appstream'; 52 | return @repos; 53 | } 54 | 55 | sub vetted_mysql_yum_repo_ids ($self) { 56 | 57 | # No use doing this since we have to call this sub directly via 58 | # Elevate::OS::vetted_yum_repo() anyway 59 | # my @repos = $self->SUPER::vetted_mysql_yum_repo_ids(); 60 | 61 | return ( 62 | qr/^mysql-[0-9]\.[0-9]-lts-community$/, 63 | qr/^mysql-(?:tools|cluster)-[0-9]\.[0-9]-lts-community$/, 64 | ); 65 | } 66 | 67 | 1; 68 | -------------------------------------------------------------------------------- /lib/Elevate/OS/CentOS7.pm: -------------------------------------------------------------------------------- 1 | package Elevate::OS::CentOS7; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::OS::CentOS7 - CentOS7 custom values 8 | 9 | =cut 10 | 11 | use cPstrict; 12 | 13 | use parent 'Elevate::OS::RHEL'; 14 | 15 | use constant supported_cpanel_mysql_versions => qw{ 16 | 8.0 17 | 10.3 18 | 10.4 19 | 10.5 20 | 10.6 21 | 10.11 22 | 11.4 23 | }; 24 | 25 | use constant default_upgrade_to => 'AlmaLinux'; 26 | use constant ea_alias => 'CentOS_8'; 27 | use constant el_package_regex => 'el7'; 28 | use constant elevate_rpm_url => 'https://repo.almalinux.org/elevate/elevate-release-latest-el7.noarch.rpm'; 29 | use constant expected_post_upgrade_major => 8; 30 | use constant has_crypto_policies => 0; 31 | use constant has_imunify_ea_alias => 1; 32 | use constant imunify_ea_alias => 'CloudLinux_8'; 33 | use constant leapp_data_pkg => 'leapp-data-almalinux'; 34 | use constant lts_supported => 110; 35 | use constant name => 'CentOS7'; 36 | use constant needs_powertools => 1; 37 | use constant original_os_major => 7; 38 | use constant pkgmgr_lib_path => '/var/lib/yum'; 39 | use constant pretty_name => 'CentOS 7'; 40 | use constant remove_els => 1; 41 | use constant upgrade_to_pretty_name => 'AlmaLinux 8'; 42 | 43 | sub vetted_yum_repo ($self) { 44 | 45 | my @repos = $self->SUPER::vetted_yum_repo(); 46 | 47 | # A component uninstalls this repo on CentOS 7, no need to block on it 48 | push @repos, qr/centos7[-]*els(-rollout-[0-9]+|)/; 49 | return @repos; 50 | } 51 | 52 | 1; 53 | -------------------------------------------------------------------------------- /lib/Elevate/OS/CloudLinux7.pm: -------------------------------------------------------------------------------- 1 | package Elevate::OS::CloudLinux7; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::OS::CloudLinux7 - CloudLinux7 custom values 8 | 9 | =cut 10 | 11 | use cPstrict; 12 | 13 | use parent 'Elevate::OS::RHEL'; 14 | 15 | use constant supported_cpanel_mysql_versions => qw{ 16 | 8.0 17 | 10.3 18 | 10.4 19 | 10.5 20 | 10.6 21 | 10.11 22 | 11.4 23 | }; 24 | 25 | use constant default_upgrade_to => 'CloudLinux'; 26 | use constant ea_alias => 'CloudLinux_8'; 27 | use constant el_package_regex => 'el7'; 28 | use constant elevate_rpm_url => 'https://repo.cloudlinux.com/elevate/elevate-release-latest-el7.noarch.rpm'; 29 | use constant expected_post_upgrade_major => 8; 30 | use constant has_crypto_policies => 0; 31 | use constant leapp_repo_prod => 'cloudlinux-elevate'; 32 | use constant leapp_repo_beta => 'cloudlinux-elevate-updates-testing'; 33 | use constant leapp_can_handle_imunify => 1; 34 | use constant leapp_can_handle_kernelcare => 1; 35 | use constant leapp_data_pkg => 'leapp-data-cloudlinux'; 36 | use constant leapp_flag => '--nowarn'; 37 | use constant lts_supported => 110; 38 | use constant name => 'CloudLinux7'; 39 | use constant needs_powertools => 1; 40 | use constant original_os_major => 7; 41 | use constant pkgmgr_lib_path => '/var/lib/yum'; 42 | use constant pretty_name => 'CloudLinux 7'; 43 | use constant provides_mysql_governor => 1; 44 | use constant should_check_cloudlinux_license => 1; 45 | use constant upgrade_to_pretty_name => 'CloudLinux 8'; 46 | use constant yum_conf_needs_plugins => 1; 47 | 48 | sub vetted_yum_repo ($self) { 49 | my @vetted_cloudlinux_yum_repo = ( 50 | qr/^cloudlinux(?:-(?:base|updates|extras|compat|imunify360|elevate))?$/, 51 | qr/^cloudlinux-rollout(?:-[0-9]+)?$/, 52 | qr/^cloudlinux-ea4(?:-[0-9]+)?$/, 53 | qr/^cloudlinux-ea4-rollout(?:-[0-9]+)?$/, 54 | 'cl-ea4', 55 | qr/^cl-mysql(?:-meta)?/, 56 | 'mysqclient', 'mysql-debuginfo', 57 | 'cl7h', 58 | ); 59 | 60 | my @repos = $self->SUPER::vetted_yum_repo(); 61 | push @repos, @vetted_cloudlinux_yum_repo; 62 | return @repos; 63 | } 64 | 65 | 1; 66 | -------------------------------------------------------------------------------- /lib/Elevate/OS/Ubuntu.pm: -------------------------------------------------------------------------------- 1 | package Elevate::OS::Ubuntu; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::OS::Ubuntu 8 | 9 | ubuntu base class 10 | 11 | =cut 12 | 13 | use cPstrict; 14 | 15 | use constant supported_cpanel_nameserver_types => qw{ 16 | disabled 17 | powerdns 18 | }; 19 | 20 | use constant archive_dir => undef; 21 | use constant bootloader_config_method => 'grub-mkconfig'; 22 | use constant default_upgrade_to => undef; 23 | use constant disable_mysql_yum_repos => undef; 24 | use constant ea_alias => undef; 25 | use constant el_package_regex => undef; 26 | use constant elevate_rpm_url => undef; 27 | use constant has_crypto_policies => 0; 28 | use constant has_imunify_ea_alias => 0; 29 | use constant imunify_ea_alias => undef; 30 | use constant is_apt_based => 1; 31 | use constant is_experimental => 0; 32 | use constant is_supported => 1; 33 | use constant jetbackup_repo_rpm_url => undef; 34 | use constant leapp_can_handle_imunify => undef; 35 | use constant leapp_can_handle_kernelcare => undef; 36 | use constant leapp_data_pkg => undef; 37 | use constant leapp_flag => undef; 38 | use constant leapp_repo_beta => undef; 39 | use constant leapp_repo_prod => undef; 40 | use constant lts_supported => undef; 41 | use constant name => 'Ubuntu'; 42 | use constant needs_crb => 0; 43 | use constant needs_do_release_upgrade => 1; 44 | use constant needs_epel => 0; 45 | use constant needs_grub_enable_blscfg => 0; 46 | use constant needs_leapp => 0; 47 | use constant needs_network_manager => 0; 48 | use constant needs_powertools => 0; 49 | use constant needs_sha1_enabled => 0; 50 | use constant needs_type_in_ifcfg => 0; 51 | use constant needs_vdo => 0; 52 | use constant package_manager => 'APT'; 53 | use constant pkgmgr_lib_path => undef; 54 | use constant pretty_name => 'Ubuntu'; 55 | use constant provides_mysql_governor => 0; 56 | use constant remove_els => 0; 57 | use constant should_archive_elevate_files => 0; 58 | use constant should_check_cloudlinux_license => 0; 59 | use constant skip_minor_version_check => 1; 60 | use constant supported_cpanel_mysql_versions => undef; 61 | use constant supports_jetbackup => 1; 62 | use constant supports_kernelcare => 0; 63 | use constant supports_named_tiers => 0; 64 | use constant supports_postgresql => 0; 65 | use constant upgrade_to_pretty_name => undef; 66 | use constant vetted_yum_repo => undef; 67 | use constant vetted_mysql_yum_repo_ids => undef; 68 | use constant yum_conf_needs_plugins => 0; 69 | 70 | 1; 71 | -------------------------------------------------------------------------------- /lib/Elevate/OS/Ubuntu20.pm: -------------------------------------------------------------------------------- 1 | package Elevate::OS::Ubuntu20; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::OS::Ubuntu20 - Ubuntu20 custom values 8 | 9 | =cut 10 | 11 | use cPstrict; 12 | 13 | use parent 'Elevate::OS::Ubuntu'; 14 | 15 | # This is intentionally very ridid for the MVP of u20->u22 upgrades 16 | # The key represents the exact name of the file that is supported 17 | # The value represents the contents that the file should contain after the 18 | # upgrade has completed 19 | use constant vetted_apt_lists => { 20 | 'cpanel-plugins.list' => q{deb mirror://httpupdate.cpanel.net/cpanel-plugins-u22-mirrorlist ./}, 21 | 22 | 'droplet-agent.list' => q{deb [signed-by=/usr/share/keyrings/droplet-agent-keyring.gpg] https://repos-droplet.digitalocean.com/apt/droplet-agent main main}, 23 | 24 | 'EA4.list' => q{deb mirror://httpupdate.cpanel.net/ea4-u22-mirrorlist ./}, 25 | 26 | 'imunify-rollout.list' => q{deb [arch=amd64] https://download.imunify360.com/ubuntu/22.04/slot-1/ jammy main 27 | deb [arch=amd64] https://download.imunify360.com/ubuntu/22.04/slot-2/ jammy main 28 | deb [arch=amd64] https://download.imunify360.com/ubuntu/22.04/slot-3/ jammy main 29 | deb [arch=amd64] https://download.imunify360.com/ubuntu/22.04/slot-4/ jammy main 30 | deb [arch=amd64] https://download.imunify360.com/ubuntu/22.04/slot-5/ jammy main 31 | deb [arch=amd64] https://download.imunify360.com/ubuntu/22.04/slot-6/ jammy main 32 | deb [arch=amd64] https://download.imunify360.com/ubuntu/22.04/slot-7/ jammy main 33 | deb [arch=amd64] https://download.imunify360.com/ubuntu/22.04/slot-8/ jammy main}, 34 | 35 | 'imunify360.list' => q{deb [arch=amd64] https://repo.imunify360.cloudlinux.com/imunify360/ubuntu/22.04/ jammy main'}, 36 | 37 | 'mysql.list' => q{# Use command 'dpkg-reconfigure mysql-apt-config' as root for modifications. 38 | deb https://repo.mysql.com/apt/ubuntu/ jammy mysql-apt-config 39 | deb https://repo.mysql.com/apt/ubuntu/ jammy mysql-8.0 40 | deb https://repo.mysql.com/apt/ubuntu/ jammy mysql-tools 41 | #deb https://repo.mysql.com/apt/ubuntu/ jammy mysql-tools-preview 42 | deb-src https://repo.mysql.com/apt/ubuntu/ jammy mysql-8.0}, 43 | 44 | 'wp-toolkit-cpanel.list' => q{# WP Toolkit 45 | deb https://wp-toolkit.plesk.com/cPanel/Ubuntu-22.04-x86_64/latest/wp-toolkit/ ./ 46 | 47 | # WP Toolkit Thirdparties 48 | deb https://wp-toolkit.plesk.com/cPanel/Ubuntu-22.04-x86_64/latest/thirdparty/ ./}, 49 | 50 | # If you don't assign $_ it is a syntax error in this context. Can't cheat with postfix for either. 51 | map { my $thing = $_; "jetapps-$_.list" => "deb [arch=amd64] https://repo.jetlicense.com/ubuntu jammy/$_ main" } qw{base plugins alpha beta edge rc release stable}, 52 | }; 53 | 54 | use constant supported_cpanel_mysql_versions => qw{ 55 | 8.0 56 | 10.6 57 | 10.11 58 | }; 59 | 60 | use constant default_upgrade_to => 'Ubuntu'; 61 | use constant ea_alias => 'Ubuntu_22.04'; 62 | use constant expected_post_upgrade_major => 22; 63 | use constant is_experimental => 0; 64 | use constant lts_supported => 118; 65 | use constant name => 'Ubuntu20'; 66 | use constant original_os_major => 20; 67 | use constant pretty_name => 'Ubuntu 20.04'; 68 | use constant upgrade_to_pretty_name => 'Ubuntu 22.04'; 69 | 70 | 1; 71 | -------------------------------------------------------------------------------- /lib/Elevate/Script.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Script; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Script 8 | 9 | Object to fetch and check the elevate script. 10 | 11 | =cut 12 | 13 | use cPstrict; 14 | 15 | use Elevate::Constants (); 16 | use Elevate::Fetch (); 17 | 18 | use Cpanel::HTTP::Client (); 19 | 20 | use Log::Log4perl qw(:easy); 21 | 22 | use Simple::Accessor qw{ 23 | latest_version 24 | base_url 25 | }; 26 | 27 | use constant DEFAULT_ELEVATE_BASE_URL => 'https://raw.githubusercontent.com/cpanel/elevate/release/'; 28 | 29 | sub _build_base_url ($self) { 30 | return $ENV{'ELEVATE_BASE_URL'} || DEFAULT_ELEVATE_BASE_URL; 31 | } 32 | 33 | sub _build_latest_version ($self) { 34 | my $response = Cpanel::HTTP::Client->new->get( $self->base_url() . 'version' ); 35 | return undef if !$response->success; 36 | my $version = $response->content // ''; 37 | chomp $version if length $version; 38 | return $version; 39 | } 40 | 41 | sub is_out_of_date ($self) { 42 | my ( $should_block, $message ); 43 | 44 | my ( $latest_version, $self_version ) = ( $self->latest_version(), cpev::VERSION() ); 45 | 46 | if ( !defined $latest_version ) { 47 | $should_block = 1; 48 | $message = "The script could not fetch information about the latest version."; 49 | } 50 | elsif ( $self_version > $latest_version ) { 51 | $message = qq[You are using a development version of elevate-cpanel. Latest version available is v$latest_version.]; 52 | } 53 | elsif ( $self_version < $latest_version ) { 54 | $should_block = 1; 55 | $message = <<~EOS; 56 | This script (version $self_version) does not appear to be the newest available release ($latest_version). 57 | Run this script with the --update option: 58 | 59 | /scripts/elevate-cpanel --update 60 | EOS 61 | } 62 | 63 | return ( $should_block, $message ); 64 | } 65 | 66 | sub fetch ($self) { 67 | return Elevate::Fetch::script( $self->base_url . 'elevate-cpanel', 'elevate-cpanel', '' ); 68 | } 69 | 70 | 1; 71 | -------------------------------------------------------------------------------- /lib/Elevate/Service.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Service; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Service 8 | 9 | Class to manage the systemctl service used by the elevate process. 10 | 11 | =cut 12 | 13 | use cPstrict; 14 | 15 | use Cpanel::SafeRun::Simple (); 16 | use Cpanel::RestartSrv::Systemd (); 17 | 18 | use Elevate::Constants (); 19 | use Elevate::OS (); 20 | 21 | use Log::Log4perl qw(:easy); 22 | 23 | use Elevate::Roles::Run (); # for fatpck 24 | use Elevate::Stages (); 25 | 26 | use Simple::Accessor qw{ 27 | name 28 | file 29 | short_name 30 | cpev 31 | }; 32 | 33 | use parent qw{ 34 | Elevate::Roles::Run 35 | Elevate::SystemctlService 36 | }; 37 | 38 | sub _build_name { 39 | return Elevate::Constants::SERVICE_NAME; 40 | } 41 | 42 | sub _build_file ($self) { 43 | return Elevate::Constants::SERVICE_DIR . '/' . $self->name; 44 | } 45 | 46 | sub _build_short_name ($self) { 47 | my $s = $self->name; 48 | $s =~ s{\Q.service\E$}{}; 49 | 50 | return $s; 51 | } 52 | 53 | sub _build_cpev { 54 | die q[Missing cpev]; 55 | } 56 | 57 | sub install ($self) { 58 | 59 | my $upgrade_from = Elevate::OS::pretty_name(); 60 | my $upgrade_to = Elevate::OS::upgrade_to_pretty_name(); 61 | 62 | my $name = $self->name; 63 | 64 | INFO( "Installing service $name which will upgrade the server to " . $upgrade_to ); 65 | open( my $fh, '>', $self->file ) or die; 66 | 67 | # When leapp upgrades from AlmaLinux 8 to AlmaLinux 9, it breaks cPanel Perl 68 | # Due to this, we need to ensure that we have a functioning cPanel Perl 69 | # before calling elevate-cpanel --service 70 | print {$fh} <<~"EOF"; 71 | [Unit] 72 | Description=Upgrade process from $upgrade_from to $upgrade_to. 73 | After=network.target network-online.target 74 | 75 | [Service] 76 | Type=simple 77 | # want to run it once per boot time 78 | RemainAfterExit=yes 79 | ExecStartPre=-/usr/local/cpanel/scripts/fix-cpanel-perl >/dev/null 2>&1 80 | ExecStart=/usr/local/cpanel/scripts/elevate-cpanel --service 81 | Environment="LANG=C" 82 | Environment="LANGUAGE=C" 83 | Environment="LC_ALL=C" 84 | Environment="LC_MESSAGES=C" 85 | Environment="LC_CTYPE=C" 86 | 87 | [Install] 88 | WantedBy=multi-user.target 89 | EOF 90 | 91 | close $fh; 92 | 93 | $self->ssystem_and_die( '/usr/bin/systemctl', 'daemon-reload' ); 94 | $self->ssystem_and_die( '/usr/bin/systemctl', 'enable', $name ); 95 | 96 | Elevate::Stages::bump_stage(); 97 | 98 | my $pid = fork(); 99 | die qq[Failed to fork: $!] unless defined $pid; 100 | if ($pid) { 101 | INFO("Starting service $name"); 102 | return 0; 103 | } 104 | else { 105 | unlink(Elevate::Constants::PID_FILE); # release the pid so the service can use it 106 | $self->ssystem_and_die( '/usr/bin/systemctl', 'start', $name ); 107 | exit(0); 108 | } 109 | } 110 | 111 | sub disable ($self) { 112 | 113 | $self->SUPER::disable( 'now' => 0 ); 114 | unlink $self->file; 115 | 116 | return; 117 | } 118 | 119 | 1; 120 | -------------------------------------------------------------------------------- /lib/Elevate/StageFile.pm: -------------------------------------------------------------------------------- 1 | package Elevate::StageFile; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::StageFile 8 | 9 | Library to get/set values in the stage file 10 | 11 | =cut 12 | 13 | use cPstrict; 14 | 15 | use Cpanel::JSON (); 16 | 17 | use File::Copy (); 18 | use Hash::Merge (); 19 | 20 | use constant ELEVATE_STAGE_FILE => '/var/cpanel/elevate'; 21 | use constant ELEVATE_SUCCESS_FILE => '/var/cpanel/version/elevate'; 22 | 23 | sub create_success_file () { 24 | File::Copy::cp( ELEVATE_STAGE_FILE, ELEVATE_SUCCESS_FILE ); 25 | return; 26 | } 27 | 28 | sub read_stage_file ( $k = undef, $default = {} ) { 29 | my $stage_info = Elevate::StageFile::_read_stage_file() // {}; 30 | 31 | return $stage_info->{$k} // $default if defined $k; 32 | return $stage_info; 33 | } 34 | 35 | sub remove_from_stage_file ($key) { 36 | return unless length $key; 37 | 38 | my $stage = Elevate::StageFile::read_stage_file(); 39 | 40 | my @list = split( qr/\./, $key ); 41 | return unless scalar @list; 42 | 43 | my $to_delete = pop @list; 44 | 45 | my $h = $stage; 46 | while ( my $k = shift @list ) { 47 | $h = $h->{$k}; 48 | last unless ref $h; 49 | } 50 | 51 | return if scalar @list; 52 | return unless exists $h->{$to_delete}; 53 | 54 | delete $h->{$to_delete}; 55 | 56 | return Elevate::StageFile::_save_stage_file($stage); 57 | } 58 | 59 | sub remove_stage_file () { 60 | unlink ELEVATE_STAGE_FILE; 61 | return; 62 | } 63 | 64 | sub update_stage_file ($data) { 65 | 66 | die q[Need a hash] unless ref $data eq 'HASH'; 67 | 68 | my $current = Elevate::StageFile::read_stage_file(); 69 | my $merged = Hash::Merge::merge( $data, $current ); 70 | 71 | return Elevate::StageFile::_save_stage_file($merged); 72 | } 73 | 74 | sub _read_stage_file () { 75 | return eval { Cpanel::JSON::LoadFile(ELEVATE_STAGE_FILE) }; 76 | } 77 | 78 | sub _save_stage_file ($stash) { 79 | open( my $fh, '>', ELEVATE_STAGE_FILE ) or LOGDIE( "Failed to open " . ELEVATE_STAGE_FILE . ": $!" ); 80 | print {$fh} Cpanel::JSON::pretty_canonical_dump($stash); 81 | close $fh; 82 | 83 | return 1; 84 | } 85 | 86 | 1; 87 | -------------------------------------------------------------------------------- /lib/Elevate/Stages.pm: -------------------------------------------------------------------------------- 1 | package Elevate::Stages; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::Stages 8 | 9 | Library to handle the various stages of the elevate process. 10 | 11 | 1. This contains the run stage logic 12 | 2. This contains the logic to determine get/set the stage we are in 13 | 14 | =cut 15 | 16 | use cPstrict; 17 | 18 | sub bump_stage ( $by = 1 ) { 19 | 20 | return Elevate::Stages::update_stage_number( Elevate::Stages::get_stage() + $by ); 21 | } 22 | 23 | sub get_stage { 24 | return Elevate::StageFile::read_stage_file( 'stage_number', 0 ); 25 | } 26 | 27 | sub update_stage_number ($stage_id) { 28 | 29 | if ( $stage_id > 10 ) { # protection for stage 30 | require Carp; 31 | Carp::confess("Invalid stage number $stage_id"); 32 | } 33 | 34 | Elevate::StageFile::update_stage_file( { stage_number => $stage_id } ); 35 | 36 | return $stage_id; 37 | } 38 | 39 | 1; 40 | -------------------------------------------------------------------------------- /lib/Elevate/SystemctlService.pm: -------------------------------------------------------------------------------- 1 | package Elevate::SystemctlService; 2 | 3 | =encoding utf-8 4 | 5 | =head1 NAME 6 | 7 | Elevate::SystemctlService 8 | 9 | Interact with a systemctl service. 10 | 11 | =cut 12 | 13 | use cPstrict; 14 | 15 | use Cpanel::SafeRun::Simple (); 16 | use Cpanel::RestartSrv::Systemd (); 17 | 18 | use Log::Log4perl qw(:easy); 19 | 20 | use Elevate::Roles::Run (); # for fatpck 21 | 22 | use Simple::Accessor qw{ 23 | name 24 | }; 25 | 26 | use parent qw{ 27 | Elevate::Roles::Run 28 | }; 29 | 30 | sub _build_name { 31 | die q[Missing sevice name ] . __PACKAGE__; 32 | } 33 | 34 | sub is_active ($self) { 35 | 36 | # cannot trust: `systemctl is-active` with a one-shot service 37 | my $service = $self->name; 38 | 39 | my $is_active; 40 | Cpanel::SafeRun::Simple::saferunnoerror( qw{/usr/bin/systemctl is-active}, $service ); 41 | $is_active = 1 if $? == 0; 42 | 43 | my $info = Cpanel::RestartSrv::Systemd::get_service_info_via_systemd($service); 44 | $info->{'ActiveState'} //= ''; 45 | $info->{'SubState'} //= ''; 46 | 47 | $is_active = 1 if $info->{'ActiveState'} eq 'activating' && $info->{'SubState'} eq 'start'; 48 | 49 | if ( $is_active && $info->{'SubState'} ne 'exited' ) { 50 | return 1; 51 | } 52 | 53 | return 0; 54 | } 55 | 56 | sub is_enabled ($self) { 57 | 58 | my $service = $self->name; 59 | 60 | my $out = Cpanel::SafeRun::Simple::saferunnoerror( qw{/usr/bin/systemctl is-enabled}, $service ) // ''; 61 | chomp $out; 62 | 63 | return 1 if $out eq 'enabled'; 64 | 65 | return 0; 66 | } 67 | 68 | sub restart ($self) { 69 | return $self->ssystem( qw{/usr/bin/systemctl restart}, $self->name ); 70 | } 71 | 72 | sub remove ($self) { 73 | 74 | my $info = eval { Cpanel::RestartSrv::Systemd::get_service_info_via_systemd( $self->name ) } // {}; 75 | 76 | $self->stop; 77 | $self->disable; 78 | 79 | if ( my $path = $info->{FragmentPath} ) { 80 | unlink $path; 81 | } 82 | 83 | return; 84 | } 85 | 86 | sub start ($self) { 87 | 88 | return if $self->is_active; 89 | 90 | $self->ssystem( '/usr/bin/systemctl', 'start', $self->name ); 91 | 92 | return; 93 | } 94 | 95 | sub stop ($self) { 96 | 97 | return unless $self->is_active; 98 | 99 | $self->ssystem( '/usr/bin/systemctl', 'stop', $self->name ); 100 | 101 | return; 102 | } 103 | 104 | sub disable ( $self, %opts ) { 105 | 106 | return unless $self->is_enabled; 107 | 108 | my $now = $opts{'now'} // 1; # by default disable it now... 109 | 110 | my @args = qw{ disable }; 111 | push @args, '--now' if $now; 112 | 113 | $self->ssystem( '/usr/bin/systemctl', @args, $self->name ); 114 | 115 | return; 116 | } 117 | 118 | sub enable ( $self, %opts ) { 119 | 120 | return if $self->is_enabled; 121 | 122 | my $now = $opts{'now'} // 1; # by default enable it now... 123 | 124 | my @args = qw{ enable }; 125 | push @args, '--now' if $now; 126 | 127 | $self->ssystem( '/usr/bin/systemctl', @args, $self->name ); 128 | 129 | return; 130 | } 131 | 132 | 1; 133 | -------------------------------------------------------------------------------- /maint/generate_changelog: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | use cPstrict; 4 | use File::Slurper qw/read_text write_binary/; 5 | 6 | use DateTime (); 7 | 8 | my $changelog = 'Changelog.md'; 9 | 10 | my $to = read_text('version'); 11 | chomp $to; 12 | my $from = $to - 1; 13 | 14 | print `git tag v$to`; 15 | 16 | my @changelog_entries; 17 | my ( $case, $changelog_entry ); 18 | my $changelog_started = 0; 19 | my $new_commit = 1; 20 | my $git_logs = `git log v$from..v$to`; 21 | my @lines = split "\n", $git_logs; 22 | foreach my $line (@lines) { 23 | $new_commit = $line =~ /^commit / ? 1 : 0; 24 | if ($new_commit) { 25 | 26 | # Capture the data from the previous commit 27 | if ($changelog_entry) { 28 | if ($case) { 29 | $changelog_entry = "* Fixed case $case: $changelog_entry"; 30 | } 31 | else { 32 | $changelog_entry = "* $changelog_entry"; 33 | } 34 | 35 | push @changelog_entries, $changelog_entry; 36 | } 37 | 38 | # Reset for a new commit 39 | $case = undef; 40 | $changelog_entry = undef; 41 | $changelog_started = 0; 42 | } 43 | 44 | # Parse an ongoing commit 45 | else { 46 | if ( $line =~ /^\s*Case ([A-Z]+-[0-9]+):/ ) { 47 | $case = $1; 48 | } 49 | elsif ( $line =~ /^\s*Changelog: (.*)/ ) { 50 | $changelog_entry = $1; 51 | $changelog_entry =~ s/\s+$//; 52 | $changelog_started = 1; 53 | } 54 | elsif ($changelog_started) { 55 | $line =~ s/^\s+//; 56 | $changelog_entry .= ' ' . $line; 57 | $changelog_entry =~ s/\s+$//; 58 | } 59 | } 60 | } 61 | 62 | # Capture the data from the last commit 63 | if ($changelog_entry) { 64 | if ($case) { 65 | $changelog_entry = "* Fixed case $case: $changelog_entry"; 66 | } 67 | else { 68 | $changelog_entry = "* $changelog_entry"; 69 | } 70 | 71 | push @changelog_entries, $changelog_entry; 72 | } 73 | 74 | print `git tag -d v$to`; 75 | 76 | my $date = DateTime->now->ymd; 77 | my $new_changelog_entries = join "\n", @changelog_entries; 78 | my $new_changelog_content = <<"EOS"; 79 | ##### **$date** - version $to 80 | 81 | $new_changelog_entries 82 | EOS 83 | 84 | my $content = $new_changelog_content . "\n" . read_text($changelog); 85 | 86 | write_binary( $changelog, $content ); 87 | 88 | print `git diff Changelog.md`; 89 | 90 | 1; 91 | -------------------------------------------------------------------------------- /maint/marker: -------------------------------------------------------------------------------- 1 | ## ---------------------------------------------------------------------------- 2 | ## 3 | ## DO NOT EDIT THIS FILE 4 | ## 5 | ## This file is automatically generated from script/elevate-cpanel.PL 6 | ## 7 | ## view https://github.com/cpanel/elevate for more details 8 | ## 9 | ## ---------------------------------------------------------------------------- 10 | -------------------------------------------------------------------------------- /t/00_load.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use cPstrict; 9 | use FindBin; 10 | 11 | use version; 12 | use Test::More; 13 | 14 | open( my $fh, '<', "$FindBin::Bin/cpanfile" ) or die("$!"); 15 | while ( my $line = <$fh> ) { 16 | next unless $line =~ m{requires\s+"([^"]+)"(?:\s+=>\s+"([^"]+)")?;}; 17 | my ( $module, $version ) = ( $1, $2 ); 18 | 19 | #next if ($module =~ m/Test2/); # Could lead to unexpected imports. 20 | require_ok($module); 21 | 22 | next unless length $version; 23 | ok( eval "version->parse(\$${module}::VERSION)" >= version->parse($version), "$module is at least version $version" ); 24 | } 25 | 26 | done_testing(); 27 | -------------------------------------------------------------------------------- /t/01_components.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use FindBin; 9 | 10 | use Test2::V0; 11 | use Test2::Tools::Explain; 12 | use Test2::Plugin::NoWarnings; 13 | 14 | use cPstrict; 15 | 16 | use lib $FindBin::Bin . "/lib", $FindBin::Bin . "/../lib"; 17 | 18 | use Elevate::Components (); 19 | 20 | my @components_from_lib = _get_components_from_lib(); 21 | 22 | foreach my $component (@components_from_lib) { 23 | is( $component, in_set(@Elevate::Components::CHECKS), "component '$component' has a check registered in Elevate::Blockers::CHECKS list" ); 24 | 25 | my $name = "Elevate::Components::$component"; 26 | my $pkg = $name->new(); 27 | for my $method ( 'check', 'pre_distro_upgrade', 'post_distro_upgrade' ) { 28 | my $check_method = $pkg->can($method); 29 | is( ref $check_method, 'CODE', "$component provides '$method' method" ); 30 | } 31 | } 32 | 33 | is( 34 | [ sort @Elevate::Components::CHECKS ], 35 | [ sort @components_from_lib ], 36 | q[all components listed in lib are used by @Elevate::Blockers::CHECKS] 37 | ); 38 | 39 | done_testing; 40 | exit; 41 | 42 | sub _get_components_from_lib { 43 | 44 | my @list; 45 | 46 | opendir( my $dh, $FindBin::Bin . "/../lib/Elevate/Components" ) or die; 47 | while ( my $e = readdir($dh) ) { 48 | next unless $e =~ s{\Q.pm\E$}{}; 49 | next if $e eq 'Base'; 50 | push @list, $e; 51 | } 52 | 53 | return sort @list; 54 | } 55 | -------------------------------------------------------------------------------- /t/02_fatpack-packages.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use FindBin; 9 | 10 | use Test2::V0; 11 | use Test2::Tools::Explain; 12 | use Test2::Plugin::NoWarnings; 13 | 14 | use cPstrict; 15 | 16 | use lib $FindBin::Bin . "/lib", $FindBin::Bin . "/../lib"; 17 | 18 | use File::Find (); 19 | use File::Slurper (); 20 | 21 | my @packages = _get_packages_from_lib(); 22 | 23 | my $elevate_script = $FindBin::Bin . "/../elevate-cpanel"; 24 | my $elevate_script_content = File::Slurper::read_binary($elevate_script); 25 | 26 | foreach my $package (@packages) { 27 | ok $elevate_script_content =~ qr{^ \s+ package \s+ $package;}xms, "$package is fatpack'd in elevate-cpanel script"; 28 | } 29 | 30 | done_testing; 31 | exit; 32 | 33 | sub _get_packages_from_lib ( $root = undef ) { 34 | 35 | $root //= $FindBin::Bin . "/../lib"; 36 | 37 | my @list; 38 | 39 | my $process = sub { 40 | 41 | my $name = $File::Find::name; 42 | return unless -f $name; 43 | return unless $name =~ s{\.pm$}{}; 44 | 45 | $name =~ s{^$root/+}{}; 46 | $name =~ s{/}{::}g; 47 | 48 | push @list, $name; 49 | 50 | return 1; 51 | }; 52 | 53 | File::Find::find( { wanted => $process, follow => 1 }, $root ); 54 | 55 | return sort @list; 56 | } 57 | -------------------------------------------------------------------------------- /t/03_fatpack-script.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use FindBin; 9 | 10 | use Test2::V0; 11 | use Test2::Tools::Explain; 12 | use Test2::Plugin::NoWarnings; 13 | 14 | use cPstrict; 15 | 16 | note 'Fatpacking elevate-cpanel'; 17 | system(qw{make --quiet build}) and die "Failed to fatpack elevate-cpanel\n"; 18 | 19 | note 'Checking git status'; 20 | my $status = `git status --short`; 21 | my @lines = split "\n", $status; 22 | @lines = grep { $_ !~ /^\s*$/ } @lines; 23 | 24 | is( scalar @lines, 0, 'git status returned cleanly' ) or do { 25 | diag explain \@lines; 26 | diag "Diff for modified files:"; 27 | diag `git diff`; 28 | }; 29 | 30 | done_testing(); 31 | -------------------------------------------------------------------------------- /t/Elevate-OS.pm: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use FindBin; 9 | 10 | use Test2::V0; 11 | use Test2::Tools::Explain; 12 | use Test2::Plugin::NoWarnings; 13 | use Test2::Tools::Exception; 14 | 15 | use Test::MockFile 0.032 qw; 16 | use Test::MockModule qw/strict/; 17 | 18 | use lib $FindBin::Bin . "/lib"; 19 | use Test::Elevate; 20 | 21 | use cPstrict; 22 | 23 | use Elevate::Usage; 24 | 25 | require $FindBin::Bin . '/../elevate-cpanel'; 26 | 27 | my @skip = qw{ ALL ALWAYS AUTOLOAD BEGIN DESTROY DEBUG ERROR FATAL INFO INIT LOGCARP LOGCLUCK LOGCONFESS LOGCROAK LOGDIE LOGEXIT LOGWARN OFF OS TRACE WARN SUPPORTED_DISTROS get_logger _set_cache clear_cache factory instance supported_methods }; 28 | 29 | my @stash = sort keys %{Elevate::OS::}; 30 | 31 | foreach my $os (qw{ CentOS7 CloudLinux7 Ubuntu20 }) { 32 | note "Test $os"; 33 | set_os_to($os); 34 | 35 | foreach my $sub (@stash) { 36 | next if $sub =~ m{::}; 37 | next if grep { $_ eq $sub } @skip; 38 | 39 | my $pkg = "Elevate::OS::$os"; 40 | ok( $pkg->can($sub), "Elevate::OS::${os}::$sub" ); 41 | } 42 | } 43 | 44 | done_testing(); 45 | exit; 46 | -------------------------------------------------------------------------------- /t/Elevate-PkgMgr.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use FindBin; 9 | 10 | use Test2::V0; 11 | use Test2::Tools::Explain; 12 | use Test2::Plugin::NoWarnings; 13 | use Test2::Tools::Exception; 14 | 15 | use Test::MockFile 0.032 qw; 16 | use Test::MockModule qw/strict/; 17 | 18 | use lib $FindBin::Bin . "/lib"; 19 | use Test::Elevate; 20 | 21 | use cPstrict; 22 | 23 | use Elevate::Usage; 24 | 25 | require $FindBin::Bin . '/../elevate-cpanel'; 26 | 27 | { 28 | set_os_to_centos_7(); 29 | 30 | my $obj = Elevate::PkgMgr::instance(); 31 | isa_ok $obj, 'Elevate::PkgMgr::YUM'; 32 | 33 | $Elevate::PkgMgr::PKGUTILITY = undef; 34 | } 35 | 36 | { 37 | set_os_to_cloudlinux_7(); 38 | 39 | my $obj = Elevate::PkgMgr::instance(); 40 | isa_ok $obj, 'Elevate::PkgMgr::YUM'; 41 | 42 | $Elevate::PkgMgr::PKGUTILITY = undef; 43 | } 44 | 45 | { 46 | set_os_to_ubuntu_20(); 47 | 48 | my $obj = Elevate::PkgMgr::instance(); 49 | isa_ok $obj, 'Elevate::PkgMgr::APT'; 50 | 51 | $Elevate::PkgMgr::PKGUTILITY = undef; 52 | } 53 | 54 | { 55 | set_os_to_almalinux_8(); 56 | 57 | my $obj = Elevate::PkgMgr::instance(); 58 | isa_ok $obj, 'Elevate::PkgMgr::YUM'; 59 | 60 | $Elevate::PkgMgr::PKGUTILITY = undef; 61 | } 62 | 63 | { 64 | note 'Test PkgMgr methods'; 65 | 66 | my @skip = qw{ factory instance BEGIN PKGUTILITY }; 67 | 68 | my @stash = sort keys %{Elevate::PkgMgr::}; 69 | 70 | my $package = q[Elevate::PkgMgr]; 71 | 72 | foreach my $sub (@stash) { 73 | next if $sub =~ m{::}; 74 | next if grep { $_ eq $sub } @skip; 75 | 76 | ok( Elevate::PkgMgr::YUM->can($sub), "Elevate::PkgMgr::YUM::$sub" ); 77 | ok( Elevate::PkgMgr::APT->can($sub), "Elevate::PkgMgr::APT::$sub" ); 78 | } 79 | } 80 | 81 | { 82 | note 'Test PkgMgr::APT::makecache filtering'; 83 | 84 | set_os_to_ubuntu_20(); 85 | 86 | my $mock_pkgmgr = Test::MockModule->new('Elevate::Roles::Run'); 87 | $mock_pkgmgr->redefine( 88 | ssystem_capture_output => { 89 | stderr => [ 90 | 'something', 91 | 'WARNING: apt does not have a stable CLI interface. Use with caution in scripts.', 92 | 'W: https://wp-toolkit.plesk.com/cPanel/Ubuntu-20.04-x86_64/6.6.4/wp-toolkit/./InRelease: Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details.', 93 | 'something else', 94 | ], 95 | } 96 | ); 97 | 98 | my $obj = Elevate::PkgMgr::instance(); 99 | is( $obj->makecache(), "something\nsomething else", "makecache() filters the stderr text which is irrelevant" ); 100 | } 101 | 102 | done_testing(); 103 | exit; 104 | -------------------------------------------------------------------------------- /t/blocker-AutoSSL.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | package test::cpev::blockers; 9 | 10 | use FindBin; 11 | 12 | use Test2::V0; 13 | use Test2::Tools::Explain; 14 | use Test2::Plugin::NoWarnings; 15 | use Test2::Tools::Exception; 16 | 17 | use Test::MockModule qw/strict/; 18 | 19 | use lib $FindBin::Bin . "/lib"; 20 | use Test::Elevate; 21 | 22 | use cPstrict; 23 | 24 | my $ssl_auto_mock = Test::MockModule->new('Cpanel::SSL::Auto'); 25 | 26 | my $cpev = cpev->new; 27 | my $auto_ssl = $cpev->get_blocker('AutoSSL'); 28 | 29 | my @test_data = ( 30 | "asdfasfd", 31 | {}, 32 | { 33 | enabled => undef, 34 | }, 35 | { 36 | enabled => 1, 37 | }, 38 | { 39 | enabled => 0, 40 | display_name => 'Let Us Encrypt', 41 | }, 42 | { 43 | enabled => 0, 44 | display_name => 'QAPortal BogoSSL', 45 | }, 46 | { 47 | enabled => 1, 48 | display_name => 'Sectigo', 49 | }, 50 | ); 51 | 52 | $ssl_auto_mock->redefine( 53 | 'get_all_provider_info' => sub { return @test_data; }, 54 | ); 55 | 56 | { 57 | $auto_ssl->_check_autossl_provider(); 58 | message_seen( 'WARN' => qr/is not supported/ ); 59 | 60 | $test_data[-1]->{enabled} = 0; 61 | $test_data[-2]->{enabled} = 1; 62 | 63 | $auto_ssl->_check_autossl_provider(); 64 | no_messages_seen(); 65 | } 66 | 67 | done_testing(); 68 | -------------------------------------------------------------------------------- /t/check-notify.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | package test::cpev::blockers; 9 | 10 | use FindBin; 11 | 12 | use Test2::V0; 13 | use Test2::Tools::Explain; 14 | use Test2::Plugin::NoWarnings; 15 | use Test2::Tools::Exception; 16 | 17 | use Test::MockFile 0.032; 18 | 19 | use lib $FindBin::Bin . "/lib"; 20 | use Test::Elevate; 21 | 22 | use Test::MockModule qw/strict/; 23 | 24 | use Cpanel::JSON; 25 | 26 | use cPstrict; 27 | 28 | my $log_file = Test::MockFile->file('/var/log/elevate-cpanel.log'); 29 | my $mock_www = Test::MockFile->file('/etc/wwwacct.conf'); 30 | 31 | my $cpev = cpev->new->_init; 32 | 33 | cpev::DEBUG("This is a DEBUG message..."); 34 | cpev::INFO("This is an INFO message"); 35 | cpev::WARN("This is a warning!"); 36 | cpev::ERROR("This is an error"); 37 | cpev::FATAL("This is FATAL!"); 38 | 39 | cpev::INFO("This is a\nmultilines\nmessages....\n"); 40 | 41 | pass "survives"; 42 | 43 | done_testing(); 44 | -------------------------------------------------------------------------------- /t/components-AbsoluteSymlink.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use warnings; 9 | 10 | use FindBin; 11 | 12 | use lib "$FindBin::Bin/../lib"; 13 | 14 | use Test2::Bundle::Extended; 15 | use Test2::Tools::Explain; 16 | use Test2::Plugin::NoWarnings; 17 | use Test::MockModule qw{strict}; 18 | use Test::MockFile qw{strict}; 19 | 20 | use Elevate::Components::AbsoluteSymlinks (); 21 | 22 | my %mocks = map { $_ => Test::MockModule->new($_) } qw{Cpanel::Chdir Cpanel::UUID File::Copy}; 23 | $mocks{'Cpanel::Chdir'}->redefine( "new" => bless {}, "Stretchy::Pants" ); 24 | $mocks{'Cpanel::UUID'}->redefine( "random_uuid" => "what_u_mean" ); 25 | my %cabinet; 26 | $cabinet{'/smang'} = Test::MockFile->symlink( "/home", "/smang" ); 27 | 28 | my $obj = bless {}, 'Elevate::Components::AbsoluteSymlinks'; 29 | ok( !$obj->post_distro_upgrade(), "Nothing to do post distro upgrade" ); 30 | is( { $obj->get_abs_symlinks() }, { '/smang' => '/home' }, "Got expected from get_abs_symlinks" ); 31 | SKIP: { 32 | skip "Test::MockFile doesn't yet properly handle symlinks", 1; 33 | $cabinet{'/smang-what_u_mean'} = Test::MockFile->symlink( undef, "/smang-what_u_mean" ); 34 | 35 | # Test::MockFile doesn't seem to know what to do about overwriting symlinks 36 | # so we need to help out File::Copy here 37 | $mocks{'File::Copy'}->redefine( 38 | "move" => sub { 39 | my ( $from, $to ) = @_; 40 | undef $cabinet{$to}; 41 | my $tarjeta = $cabinet{$from}->readlink(); 42 | $cabinet{$to} = Test::MockFile->symlink( $tarjeta, $to ); 43 | undef $cabinet{$from}; 44 | return 1; 45 | } 46 | ); 47 | 48 | # XXX TODO figure out what the heck is needed to make the `symlink` builtin 49 | # in perl react properly to the existence of Test::MockModule->symlink objects 50 | # As it stands, this makes the symlink then bombs out with 51 | # 'readlink is only supported for symlinks'. 52 | $obj->pre_distro_upgrade(); 53 | is( readlink("/smang"), 'home', "Symlink corrected by pre_distro_upgrade" ); 54 | } 55 | done_testing(); 56 | -------------------------------------------------------------------------------- /t/components-Acronis.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | package test::cpev::components; 9 | 10 | use FindBin; 11 | 12 | use Test2::V0; 13 | use Test2::Tools::Explain; 14 | use Test2::Plugin::NoWarnings; 15 | use Test2::Tools::Exception; 16 | 17 | use Test::MockModule qw/strict/; 18 | 19 | use lib $FindBin::Bin . "/lib"; 20 | use Test::Elevate; 21 | 22 | use cPstrict; 23 | 24 | my $acronis = cpev->new->get_component('Acronis'); 25 | 26 | { 27 | note "Checking pre_distro_upgrade"; 28 | 29 | my $is_installed; 30 | my $yum_remove_called; 31 | my $stage_file_data; 32 | 33 | my $mock_pkgr = Test::MockModule->new('Cpanel::Pkgr'); 34 | $mock_pkgr->redefine( 35 | is_installed => sub { return $is_installed; }, 36 | ); 37 | 38 | my $mock_pkgmgr = Test::MockModule->new( ref Elevate::PkgMgr::instance() ); 39 | $mock_pkgmgr->redefine( 40 | remove => sub { $yum_remove_called = 1; }, 41 | ); 42 | 43 | my $mock_upsf = Test::MockModule->new('Elevate::StageFile'); 44 | $mock_upsf->redefine( 45 | update_stage_file => sub { $stage_file_data = shift; }, 46 | ); 47 | 48 | # Test when Acronis is not installed 49 | $is_installed = 0; 50 | $yum_remove_called = 0; 51 | $stage_file_data = {}; 52 | 53 | $acronis->pre_distro_upgrade(); 54 | is( $yum_remove_called, 0, 'When package not installed: did not uninstall package' ); 55 | is( $stage_file_data, {}, 'When package not installed: did not update the stage file' ); 56 | 57 | # Test when Acronis is installed 58 | $is_installed = 1; 59 | $yum_remove_called = 0; 60 | $stage_file_data = {}; 61 | 62 | $acronis->pre_distro_upgrade(); 63 | is( $yum_remove_called, 1, 'When package is installed: uninstalled package' ); 64 | is( 65 | $stage_file_data, 66 | { 67 | 'reinstall' => { 68 | 'acronis' => 1, 69 | }, 70 | }, 71 | 'When package is installed: updated the stage file' 72 | ); 73 | } 74 | 75 | { 76 | note "Checking post_distro_upgrade"; 77 | 78 | my $stage_file_data; 79 | my $dnf_install_called; 80 | 81 | my $mock_upsf = Test::MockModule->new('Elevate::StageFile'); 82 | $mock_upsf->redefine( 83 | read_stage_file => sub { return $stage_file_data; }, 84 | ); 85 | 86 | my $mock_pkgmgr = Test::MockModule->new( ref Elevate::PkgMgr::instance() ); 87 | $mock_pkgmgr->redefine( 88 | install => sub { $dnf_install_called = 1; }, 89 | ); 90 | 91 | # Test when had not been installed 92 | $stage_file_data = {}; 93 | $dnf_install_called = 0; 94 | 95 | $acronis->post_distro_upgrade(); 96 | is( $dnf_install_called, 0, 'When package was never installed: did not reinstall' ); 97 | 98 | # Test when had been installed 99 | $stage_file_data = { 'acronis' => 1 }; 100 | $dnf_install_called = 1; 101 | 102 | $acronis->post_distro_upgrade(); 103 | is( $dnf_install_called, 1, 'When package had been installed: reinstalled package' ); 104 | } 105 | 106 | done_testing(); 107 | -------------------------------------------------------------------------------- /t/components-BootKernel.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | package test::cpev::components; 9 | 10 | use FindBin; 11 | 12 | use Test2::V0; 13 | use Test2::Tools::Explain; 14 | use Test2::Plugin::NoWarnings; 15 | use Test2::Tools::Exception; 16 | use Test2::Tools::Mock; 17 | 18 | use Test::MockModule qw/strict/; 19 | 20 | use lib $FindBin::Bin . "/lib"; 21 | use Test::Elevate; 22 | 23 | use cPstrict; 24 | 25 | my $mock_cpev = Test::MockModule->new('cpev'); 26 | my $mock_cpanel_kernel_status = Test::MockModule->new('Cpanel::Kernel::Status'); 27 | 28 | my $comp = cpev->new->get_component('BootKernel'); 29 | 30 | { 31 | note 'Test blocker when --upgrade-distro-manually is passed'; 32 | 33 | $mock_cpanel_kernel_status->redefine( 34 | reboot_status => sub { die "should not be called here\n"; }, 35 | ); 36 | 37 | $mock_cpev->redefine( upgrade_distro_manually => 1 ); 38 | is( $comp->check(), 1, 'Should skip when upgrade-distro-manually is passed' ); 39 | } 40 | 41 | { 42 | note 'Test BootKernel behavior'; 43 | 44 | $mock_cpev->redefine( upgrade_distro_manually => 0 ); 45 | 46 | my $rv; 47 | my $bv; 48 | $mock_cpanel_kernel_status->redefine( 49 | reboot_status => sub { 50 | return { 51 | running_version => $rv, 52 | boot_version => $bv, 53 | }; 54 | }, 55 | ); 56 | 57 | $rv = 42; 58 | $bv = 42; 59 | is( $comp->check(), 1, 'No blocker when running version matches boot version' ); 60 | 61 | $rv = 13; 62 | is( $comp->check(), 0, 'Blocker returned when running version does not match boot version' ); 63 | 64 | $mock_cpanel_kernel_status->redefine( 65 | reboot_status => sub { die "fail to do the thing\n" }, 66 | ); 67 | 68 | is( $comp->check(), 0, 'Blocker returned when we fail to determine the running kernel version' ); 69 | } 70 | 71 | done_testing(); 72 | -------------------------------------------------------------------------------- /t/components-Grub2ChecksWorkarounds_parse_shell_variable.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use FindBin; 9 | 10 | use Test2::V0; 11 | use Test2::Tools::Explain; 12 | use Test2::Plugin::NoWarnings; 13 | use Test2::Tools::Exception; 14 | 15 | use cPstrict; 16 | 17 | use Test::MockModule qw/strict/; 18 | 19 | use lib $FindBin::Bin . "/lib"; 20 | use Test::Elevate; 21 | 22 | use File::Temp (); 23 | 24 | my $cpev_mock = Test::MockModule->new('cpev'); 25 | my $cpev = bless {}, 'cpev'; 26 | 27 | subtest "test behavior when the directory doesn't exist" => sub { 28 | my $exception = dies { Elevate::Components::Grub2ChecksWorkarounds::_parse_shell_variable( '/this/path/does/not/exist', 'GRUB_ENABLE_BLSCFG' ) }; 29 | isa_ok( $exception, 'Cpanel::Exception::ProcessFailed::Error' ); 30 | is( eval { $exception->get('error_code') }, 72, "expected return status" ); 31 | }; 32 | 33 | my $test_file = File::Temp->new; 34 | $test_file->autoflush(1); 35 | 36 | chown( scalar getpwnam('nobody'), scalar getgrnam('nobody'), $test_file ); 37 | 38 | is( Elevate::Components::Grub2ChecksWorkarounds::_parse_shell_variable( $test_file->filename, 'GRUB_ENABLE_BLSCFG' ), undef, "_parse_shell_variable returns undef if the variable does not exist in the file" ); 39 | 40 | $test_file->print("GRUB_ENABLE_BLSCFG=true\n"); 41 | is( Elevate::Components::Grub2ChecksWorkarounds::_parse_shell_variable( $test_file->filename, 'GRUB_ENABLE_BLSCFG' ), 'true', "_parse_shell_variable handles bare values" ); 42 | 43 | $test_file->seek( 0, 0 ); 44 | $test_file->print("GRUB_ENABLE_BLSCFG=\"true\"\n"); 45 | is( Elevate::Components::Grub2ChecksWorkarounds::_parse_shell_variable( $test_file->filename, 'GRUB_ENABLE_BLSCFG' ), 'true', "_parse_shell_variable handles double-quoted values" ); 46 | 47 | done_testing; 48 | -------------------------------------------------------------------------------- /t/components-IsContainer.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | package test::cpev::components; 9 | 10 | use FindBin; 11 | 12 | use Test2::V0; 13 | use Test2::Tools::Explain; 14 | use Test2::Plugin::NoWarnings; 15 | use Test2::Tools::Exception; 16 | 17 | use Test::MockFile 0.032; 18 | use Test::MockModule qw/strict/; 19 | 20 | use lib $FindBin::Bin . "/lib", $FindBin::Bin . "/../lib"; 21 | use Test::Elevate; 22 | 23 | use Elevate::Components::IsContainer (); 24 | 25 | use cPstrict; 26 | 27 | my $mock = Test::MockModule->new('Elevate::Components::IsContainer'); 28 | { 29 | note "containers"; 30 | 31 | my $isContainer = cpev->new->get_component('IsContainer'); 32 | 33 | $mock->redefine( '_is_container_envtype' => 1 ); 34 | 35 | my $cpev = cpev->new(); 36 | my $msg = <<~'EOS'; 37 | cPanel thinks that this is a container-like environment. 38 | This script cannot upgrade container environments. 39 | Consider contacting your hypervisor provider for alternative solutions. 40 | EOS 41 | 42 | is( 43 | $isContainer->check(), 44 | { 45 | id => q[Elevate::Components::IsContainer::check], 46 | msg => $msg, 47 | }, 48 | q{Block if this is a container like environment.} 49 | 50 | ); 51 | 52 | $mock->redefine( '_is_container_envtype' => 0 ); 53 | is( $isContainer->check(), 0, q[not a container.] ); 54 | } 55 | 56 | done_testing; 57 | -------------------------------------------------------------------------------- /t/components-JetBackup.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | package test::cpev::components; 9 | 10 | use FindBin; 11 | 12 | use Test2::V0; 13 | use Test2::Tools::Explain; 14 | use Test2::Plugin::NoWarnings; 15 | use Test2::Tools::Exception; 16 | 17 | use Test::MockFile 0.032; 18 | use Test::MockModule qw/strict/; 19 | 20 | use lib $FindBin::Bin . "/lib"; 21 | use Test::Elevate; 22 | 23 | use cPstrict; 24 | 25 | note "checking _use_jetbackup4_or_earlier"; 26 | 27 | my $mock_pkgr = Test::MockModule->new('Cpanel::Pkgr'); 28 | 29 | my $cpev = cpev->new; 30 | my $jb = $cpev->get_blocker('JetBackup'); 31 | 32 | { 33 | note "_use_jetbackup4_or_earlier"; 34 | 35 | $mock_pkgr->redefine( 'is_installed' => 0 ); 36 | ok( !$jb->_use_jetbackup4_or_earlier(), "JetBackup is not installed" ); 37 | 38 | $mock_pkgr->redefine( 'is_installed' => 1 ); 39 | 40 | $mock_pkgr->redefine( 'get_package_version' => '3.2' ); 41 | ok $jb->_use_jetbackup4_or_earlier(), "JetBackup 3.2 is installed"; 42 | 43 | $mock_pkgr->redefine( 'get_package_version' => '4.0' ); 44 | ok $jb->_use_jetbackup4_or_earlier(), "JetBackup 4.0 is installed"; 45 | 46 | $mock_pkgr->redefine( 'get_package_version' => '5.1' ); 47 | ok !$jb->_use_jetbackup4_or_earlier(), "JetBackup 5.1 is installed"; 48 | 49 | $mock_pkgr->redefine( 'get_package_version' => '10' ); 50 | ok !$jb->_use_jetbackup4_or_earlier(), "JetBackup 10 is installed"; 51 | 52 | $mock_pkgr->redefine( 'get_package_version' => '44.1' ); 53 | ok !$jb->_use_jetbackup4_or_earlier(), "JetBackup 44.1 is installed"; 54 | } 55 | 56 | { 57 | note "Jetbackup 4"; 58 | 59 | my $jb_mock = Test::MockModule->new('Elevate::Components::JetBackup'); 60 | 61 | for my $os ( 'cent', 'cloud', 'alma' ) { 62 | set_os_to($os); 63 | 64 | my $expected_target_os = Elevate::OS::upgrade_to_pretty_name(); 65 | 66 | $jb_mock->redefine( '_use_jetbackup4_or_earlier' => 1 ); 67 | is( 68 | $jb->_blocker_old_jetbackup(), 69 | { 70 | id => q[Elevate::Components::JetBackup::_blocker_old_jetbackup], 71 | msg => "$expected_target_os does not support JetBackup prior to version 5.\nPlease upgrade JetBackup before elevate.\n", 72 | }, 73 | q{Block if jetbackup 4 is installed.} 74 | ); 75 | 76 | $jb_mock->redefine( '_use_jetbackup4_or_earlier' => 0 ); 77 | is( $jb->_blocker_old_jetbackup(), 0, 'ok when jetbackup 4 or earlier is not installed.' ); 78 | } 79 | 80 | } 81 | 82 | { 83 | note 'Blocker Jetbackup is supported'; 84 | 85 | my $jb_mock = Test::MockModule->new('Elevate::Components::JetBackup'); 86 | 87 | for my $os ( 'cent', 'cloud', 'ubuntu', 'alma' ) { 88 | set_os_to($os); 89 | 90 | my $expected_target_os = Elevate::OS::upgrade_to_pretty_name(); 91 | is( $jb->_blocker_jetbackup_is_supported(), undef, "JetBackup is supported for upgrades to $expected_target_os" ); 92 | } 93 | } 94 | 95 | done_testing(); 96 | -------------------------------------------------------------------------------- /t/components-Leapp.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | package test::cpev::components; 9 | 10 | use FindBin; 11 | 12 | use Test2::V0; 13 | use Test2::Tools::Explain; 14 | use Test2::Plugin::NoWarnings; 15 | use Test2::Tools::Exception; 16 | use Test2::Tools::Mock; 17 | 18 | use Test::MockModule qw/strict/; 19 | 20 | use lib $FindBin::Bin . "/lib"; 21 | use Test::Elevate; 22 | 23 | use cPstrict; 24 | 25 | my $mock_comp = Test::MockModule->new('Elevate::Components::Leapp'); 26 | my $mock_elevate_comp = Test::MockModule->new('Elevate::Components'); 27 | my $mock_leapp = Test::MockModule->new('Elevate::Leapp'); 28 | 29 | my $comp = cpev->new->get_component('Leapp'); 30 | 31 | { 32 | note 'Test blocker when --upgrade-distro-manually is passed'; 33 | 34 | foreach my $os (qw{ cent alma }) { 35 | set_os_to($os); 36 | 37 | $mock_elevate_comp->redefine( 38 | num_blockers_found => sub { "do not call\n"; }, 39 | ); 40 | 41 | $mock_comp->redefine( 42 | is_check_mode => 1, 43 | ); 44 | 45 | is( $comp->check(), undef, 'Returns early if in check mode' ); 46 | 47 | $mock_comp->redefine( 48 | is_check_mode => 0, 49 | ); 50 | } 51 | 52 | set_os_to('ubuntu'); 53 | is( $comp->check(), undef, 'Returns early if the OS does not rely on leapp to upgrade' ); 54 | 55 | foreach my $os (qw{ cent alma }) { 56 | set_os_to($os); 57 | 58 | $mock_leapp->redefine( 59 | install => sub { die "Do not call\n"; }, 60 | ); 61 | 62 | $mock_elevate_comp->redefine( 63 | num_blockers_found => 1, 64 | ); 65 | 66 | is( $comp->check(), undef, 'Returns early if there are existing blockers found' ); 67 | 68 | my $num_blockers_found = 0; 69 | $mock_elevate_comp->redefine( 70 | num_blockers_found => sub { return $num_blockers_found; }, 71 | ); 72 | 73 | $mock_comp->redefine( 74 | _check_for_inhibitors => sub { $num_blockers_found++; return; }, 75 | _check_for_fatal_errors => 0, 76 | ); 77 | 78 | my $preupgrade_out; 79 | $mock_leapp->redefine( 80 | install => 1, 81 | preupgrade => sub { return $preupgrade_out; }, 82 | ); 83 | 84 | $preupgrade_out = { 85 | status => 0, 86 | }; 87 | 88 | is( $comp->check(), undef, 'No blockers returns if leapp preupgrade returns clean' ); 89 | no_messages_seen(); 90 | 91 | $preupgrade_out = { 92 | status => 42, 93 | }; 94 | 95 | is( $comp->check(), undef, 'Returns undef' ); 96 | message_seen( INFO => qr/Leapp found issues which would prevent the upgrade/ ); 97 | no_messages_seen(); 98 | } 99 | } 100 | 101 | done_testing(); 102 | -------------------------------------------------------------------------------- /t/components-MountPoints.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | package test::cpev::components; 9 | 10 | use FindBin; 11 | 12 | use Test2::V0; 13 | use Test2::Tools::Explain; 14 | use Test2::Plugin::NoWarnings; 15 | use Test2::Tools::Exception; 16 | 17 | use Test::MockFile 0.032; 18 | use Test::MockModule qw/strict/; 19 | 20 | use lib $FindBin::Bin . "/lib"; 21 | use Test::Elevate; 22 | 23 | use cPstrict; 24 | 25 | my $cpev_mock = Test::MockModule->new('cpev'); 26 | 27 | my $cpev = cpev->new; 28 | my $mp = $cpev->get_blocker('MountPoints'); 29 | 30 | my @cmds; 31 | my @stdout; 32 | my @stderr; 33 | my $capture_output_status; 34 | $cpev_mock->redefine( 35 | ssystem_capture_output => sub ( $, @args ) { 36 | push @cmds, [@args]; 37 | return { status => $capture_output_status, stdout => \@stdout, stderr => \@stderr }; 38 | }, 39 | ); 40 | 41 | { 42 | note 'Test _ensure_mount_dash_a_succeeds'; 43 | 44 | undef @cmds; 45 | undef @stdout; 46 | undef @stderr; 47 | $capture_output_status = undef; 48 | 49 | my $mp_mock = Test::MockModule->new('Elevate::Components::MountPoints'); 50 | $mp_mock->redefine( 51 | is_check_mode => 1, 52 | ); 53 | 54 | is( $mp->_ensure_mount_dash_a_succeeds, undef, 'Returns undef when in check mode' ); 55 | is( \@cmds, [], 'No commands are executed when in check mode' ); 56 | 57 | $mp_mock->redefine( 58 | is_check_mode => 0, 59 | ); 60 | 61 | $capture_output_status = 0; 62 | 63 | is( $mp->_ensure_mount_dash_a_succeeds, undef, 'Returns undef when mount -a succeeds' ); 64 | is( 65 | \@cmds, 66 | [ 67 | [ 68 | '/usr/bin/mount', 69 | '-a', 70 | ], 71 | ], 72 | 'The expected command is called', 73 | ); 74 | 75 | $capture_output_status = 42; 76 | $stderr[0] = 'mount: mount point does not exist'; 77 | 78 | my $blocker = dies { $mp->_ensure_mount_dash_a_succeeds() }; 79 | like( 80 | $blocker, 81 | { 82 | id => 'Elevate::Components::MountPoints::_ensure_mount_dash_a_succeeds', 83 | msg => qr/The following command failed to execute successfully on your server/, 84 | }, 85 | 'A blocker is returned when mount -a does not execute successfully', 86 | ); 87 | 88 | no_messages_seen(); 89 | } 90 | 91 | done_testing(); 92 | -------------------------------------------------------------------------------- /t/components-NetworkManager.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | package test::cpev::components; 9 | 10 | use FindBin; 11 | 12 | use Test2::V0; 13 | use Test2::Tools::Explain; 14 | use Test2::Plugin::NoWarnings; 15 | use Test2::Tools::Exception; 16 | 17 | use Test::MockFile 0.032; 18 | use Test::MockModule qw/strict/; 19 | 20 | use lib $FindBin::Bin . "/lib"; 21 | use Test::Elevate; 22 | 23 | use cPstrict; 24 | 25 | my $nm = cpev->new->get_component('NetworkManager'); 26 | 27 | { 28 | note "checking pre_distro_upgrade"; 29 | 30 | my $mock_systemctl = Test::MockModule->new('Elevate::SystemctlService'); 31 | $mock_systemctl->redefine( 32 | is_enabled => sub { die "do not call this yet\n"; }, 33 | ); 34 | 35 | my $mock_nm = Test::MockModule->new('Elevate::Components::NetworkManager'); 36 | $mock_nm->redefine( 37 | upgrade_distro_manually => 1, 38 | ); 39 | 40 | is( $nm->pre_distro_upgrade(), undef, 'Returns early when upgrade manually option is passed' ); 41 | 42 | $mock_nm->redefine( 43 | upgrade_distro_manually => 0, 44 | ); 45 | 46 | foreach my $os (qw{ cent cloud ubuntu }) { 47 | set_os_to($os); 48 | 49 | is( $nm->pre_distro_upgrade(), undef, 'Returns early when network manager does not need to be enabled prior to the distro upgrade' ); 50 | } 51 | 52 | set_os_to('alma'); 53 | 54 | my $called_enabled = 0; 55 | $mock_systemctl->redefine( 56 | is_enabled => 1, 57 | enable => sub { $called_enabled++; }, 58 | ); 59 | 60 | is( $nm->pre_distro_upgrade(), undef, 'Return undef when the network manager service is already enabled' ); 61 | is( $called_enabled, 0, 'Does not attempt to enable network manager if the service is already enabled' ); 62 | 63 | $mock_systemctl->redefine( 64 | is_enabled => 0, 65 | ); 66 | 67 | is( $nm->pre_distro_upgrade(), undef, 'Return undef when it enables the network manager service' ); 68 | is( $called_enabled, 1, 'Enables the network manager service if it is not already enabled' ); 69 | } 70 | 71 | done_testing(); 72 | -------------------------------------------------------------------------------- /t/components-OVH.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | package test::cpev::components; 9 | 10 | use FindBin; 11 | 12 | use Test2::V0; 13 | use Test2::Tools::Explain; 14 | use Test2::Plugin::NoWarnings; 15 | use Test2::Tools::Exception; 16 | 17 | use Test::MockFile 0.032; 18 | use Test::MockModule qw/strict/; 19 | 20 | use lib $FindBin::Bin . "/lib"; 21 | use Test::Elevate; 22 | 23 | use cPstrict; 24 | 25 | my $myip = q[127.0.0.1]; 26 | 27 | my $mock_mainip = Test::MockModule->new('Cpanel::DIp::MainIP')->redefine( getmainip => 42 ); 28 | my $mock_nat = Test::MockModule->new('Cpanel::NAT')->redefine( get_public_ip => sub { return $myip } ); 29 | 30 | my $cpev = cpev->new; 31 | my $blocker = Elevate::Components::OVH->new( cpev => $cpev ); 32 | 33 | subtest '__is_ovh' => sub { 34 | 35 | { 36 | my $mock_pvhrc = Test::MockFile->file('/root/.ovhrc'); 37 | 38 | is $blocker->__is_ovh(), 0, "no .ovhrc"; 39 | 40 | $mock_pvhrc->touch; 41 | is $blocker->__is_ovh(), 1, "with .ovhrc"; 42 | } 43 | 44 | { 45 | my $mock_pvhrc = Test::MockFile->file('/root/.ovhrc'); 46 | 47 | is $blocker->__is_ovh(), 0, "invalid ip"; 48 | 49 | foreach my $ip (qw{54.38.193.69 192.99.6.142}) { 50 | $myip = $ip; 51 | is $blocker->__is_ovh(), 1, "IP $ip belongs to OVH"; 52 | } 53 | } 54 | 55 | return; 56 | }; 57 | 58 | subtest 'ovh blocker' => sub { 59 | my $mock_ovh = Test::MockModule->new('Elevate::Components::OVH'); 60 | $mock_ovh->redefine( '__is_ovh' => 1 ); 61 | 62 | #my $mock_service = Test::MockModule->new('Elevate::Service')->redefine( is_active => 0 ); 63 | 64 | my $mock_touchfile = Test::MockFile->file( Elevate::Constants::OVH_MONITORING_TOUCH_FILE() ); 65 | $mock_touchfile->touch(); 66 | is $blocker->check(), 0, "no blocker when file already touched"; 67 | 68 | $mock_touchfile->unlink(); 69 | 70 | #local @Elevate::Components::BLOCKERS = qw{ OVH }; 71 | my $blockers = Elevate::Components->new( cpev => $cpev ); 72 | 73 | like $blockers->_check_single_blocker('OVH'), object { 74 | prop blessed => 'cpev::Blocker'; 75 | field id => 'Elevate::Components::OVH::check'; 76 | }, "blocker when detecting OVH"; 77 | 78 | return; 79 | }; 80 | 81 | done_testing(); 82 | -------------------------------------------------------------------------------- /t/components-PostgreSQL.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | package test::cpev::PostgreSQL; 9 | 10 | use FindBin; 11 | 12 | use Test2::V0; 13 | use Test2::Tools::Explain; 14 | use Test2::Plugin::NoWarnings; 15 | use Test2::Tools::Exception; 16 | 17 | use Test::MockModule qw/strict/; 18 | use Test::MockFile; 19 | 20 | use lib $FindBin::Bin . "/lib"; 21 | use Test::Elevate; 22 | 23 | use cPstrict; 24 | 25 | use Elevate::Constants (); 26 | 27 | use constant PRE_LEAPP_METHODS => [ 28 | qw( 29 | _store_postgresql_encoding_and_locale 30 | _disable_postgresql_service 31 | _backup_postgresql_datadir 32 | ) 33 | ]; 34 | 35 | use constant POST_LEAPP_METHODS => [ 36 | qw( 37 | _perform_config_workaround 38 | _perform_postgresql_upgrade 39 | _re_enable_service_if_needed 40 | _run_whostmgr_postgres_update_config 41 | ) 42 | ]; 43 | 44 | my $comp_pgsql = bless {}, 'Elevate::Components::PostgreSQL'; 45 | 46 | my $mock_pgsql = Test::MockModule->new('Elevate::Components::PostgreSQL'); 47 | my $mock_stagefile = Test::MockModule->new('Elevate::StageFile'); 48 | 49 | my $mock_pkgr = Test::MockModule->new('Cpanel::Pkgr'); 50 | my $installed; 51 | $mock_pkgr->redefine( 52 | is_installed => sub { return $_[0] eq 'postgresql-server' ? $installed : $mock_pkgr->original('postgresql-server')->(@_) }, 53 | ); 54 | 55 | { 56 | note "Checking pre_distro_upgrade"; 57 | 58 | $installed = 0; 59 | $mock_pgsql->redefine( 60 | map { 61 | $_ => sub { die "shouldn't run" } 62 | } PRE_LEAPP_METHODS->@* 63 | ); 64 | ok( lives { $comp_pgsql->pre_distro_upgrade() }, "Nothing is run if postgresql-server is not installed" ); 65 | $mock_pgsql->unmock( PRE_LEAPP_METHODS->@* ); 66 | 67 | $installed = 1; 68 | } 69 | 70 | { 71 | note "Checking post_distro_upgrade"; 72 | 73 | $installed = 0; 74 | $mock_pgsql->redefine( 75 | map { 76 | $_ => sub { die "shouldn't run" } 77 | } POST_LEAPP_METHODS->@* 78 | ); 79 | ok( lives { $comp_pgsql->post_distro_upgrade() }, "Nothing is run if postgresql-server is not installed" ); 80 | $mock_pgsql->unmock( POST_LEAPP_METHODS->@* ); 81 | 82 | $installed = 1; 83 | 84 | my $mock_pg_conf = Test::MockFile->file( Elevate::Constants::POSTGRESQL_SYSTEM_DATADIR . '/postgresql.conf' ); 85 | 86 | $mock_pg_conf->contents("no matches"); 87 | $comp_pgsql->_perform_config_workaround(); 88 | is( $mock_pg_conf->contents, "no matches", "Nothing changes with postgresql.conf if no unix_socket_directories" ); 89 | 90 | $mock_pg_conf->contents("unix_socket_directories = '/var/run/postgresql, /tmp'"); 91 | $comp_pgsql->_perform_config_workaround(); 92 | is( $mock_pg_conf->contents, "#unix_socket_directories = '/var/run/postgresql, /tmp'\nunix_socket_directory = '/var/run/postgresql'", "Fix applied if unix_socket_directories is present" ); 93 | } 94 | 95 | done_testing(); 96 | -------------------------------------------------------------------------------- /t/components-UpdateReleaseUpgrades.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | package test::cpev::components; 9 | 10 | use FindBin; 11 | 12 | use Test2::V0; 13 | use Test2::Tools::Explain; 14 | use Test2::Plugin::NoWarnings; 15 | use Test2::Tools::Exception; 16 | use Test2::Tools::Mock; 17 | 18 | use Test::MockModule qw/strict/; 19 | use Test::MockFile 0.032; 20 | 21 | use lib $FindBin::Bin . "/lib"; 22 | use Test::Elevate; 23 | 24 | use cPstrict; 25 | 26 | my $release_upgrades_content = <<'EOF'; 27 | # Default behavior for the release upgrader. 28 | 29 | [DEFAULT] 30 | # Default prompting and upgrade behavior, valid options: 31 | # 32 | # never - Never check for, or allow upgrading to, a new release. 33 | # normal - Check to see if a new release is available. If more than one new 34 | # release is found, the release upgrader will attempt to upgrade to 35 | # the supported release that immediately succeeds the 36 | # currently-running release. 37 | # lts - Check to see if a new LTS release is available. The upgrader 38 | # will attempt to upgrade to the first LTS release available after 39 | # the currently-running one. Note that if this option is used and 40 | # the currently-running release is not itself an LTS release the 41 | # upgrader will assume prompt was meant to be normal. 42 | Prompt=never 43 | EOF 44 | 45 | my $mock_install_script = Test::MockFile->file( '/usr/local/cpanel/install/BlockUbuntuUpgrades.pm', '' ); 46 | my $mock_upgrade_file = Test::MockFile->file( '/etc/update-manager/release-upgrades', $release_upgrades_content ); 47 | 48 | my $comp = cpev->new->get_component('UpdateReleaseUpgrades'); 49 | 50 | { 51 | note 'pre_distro_upgrade'; 52 | 53 | set_os_to('alma'); 54 | is( $comp->pre_distro_upgrade(), undef, 'Returns early on systems that do not use do_release_upgrade to perform the distro upgrade' ); 55 | no_messages_seen(); 56 | 57 | set_os_to('ubuntu'); 58 | like( $mock_upgrade_file->contents(), qr/Prompt=never/, 'Update is blocked before pre_distro_upgrade executes' ); 59 | is( $comp->pre_distro_upgrade(), undef, 'Returns undef' ); 60 | message_seen( INFO => qr/Removing install script that blocks upgrades to/ ); 61 | message_seen( INFO => qr/Updating config file to allow upgrades to/ ); 62 | like( $mock_upgrade_file->contents(), qr/Prompt=lts/, 'Update is allowed after pre_distro_upgrade executes' ); 63 | } 64 | 65 | done_testing(); 66 | -------------------------------------------------------------------------------- /t/components-archive_elevate_files.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use cPstrict; 9 | 10 | use Test2::V0; 11 | use Test2::Tools::Explain; 12 | use Test2::Plugin::NoWarnings; 13 | use Test2::Tools::Exception; 14 | 15 | use Test::MockModule qw/strict/; 16 | 17 | use FindBin; 18 | use lib $FindBin::Bin . "/lib"; 19 | 20 | use Test::MockFile 0.032; 21 | 22 | use Test::Elevate; 23 | 24 | use cPstrict; 25 | 26 | my $mock_file_copy = Test::MockModule->new('File::Copy'); 27 | $mock_file_copy->redefine( 28 | mv => sub { die "do not call yet\n"; }, 29 | ); 30 | 31 | my $mock_stages = Test::MockModule->new('Elevate::Stages'); 32 | $mock_stages->redefine( 33 | get_stage => sub { die "do not run yet\n"; }, 34 | ); 35 | 36 | my $components = cpev->new()->components; 37 | 38 | for my $os (qw{ cent cloud ubuntu }) { 39 | set_os_to($os); 40 | 41 | is( $components->archive_elevate_files(), undef, 'Returns early unless log archival is needed' ); 42 | } 43 | 44 | set_os_to('alma'); 45 | 46 | $mock_stages->redefine( 47 | get_stage => 6, 48 | ); 49 | 50 | my $mock_stage_file = Test::MockFile->file('/var/cpanel/elevate'); 51 | my $mock_blockers_file = Test::MockFile->file('/var/cpanel/elevate-blockers'); 52 | my $mock_elevate_log = Test::MockFile->file('/var/log/elevate-cpanel.log'); 53 | 54 | is( $components->archive_elevate_files(), undef, 'Returns early when there are no files to archive' ); 55 | 56 | $mock_stages->redefine( 57 | get_stage => 1, 58 | ); 59 | 60 | my $mock_archive_dir = Test::MockFile->dir('/var/cpanel/elevate_archive'); 61 | my $mock_os_archive_dir = Test::MockFile->dir('/var/cpanel/elevate_archive/CentOS7-to-AlmaLinux8'); 62 | 63 | $mock_stage_file->contents('stuff'); 64 | $mock_blockers_file->contents('foo'); 65 | $mock_elevate_log->contents('bar'); 66 | 67 | is( $components->archive_elevate_files(), undef, 'Returns early unless a successful elevate has completed' ); 68 | 69 | $mock_stages->redefine( 70 | get_stage => 6, 71 | ); 72 | 73 | my %archived_files; 74 | $mock_file_copy->redefine( 75 | mv => sub { 76 | $archived_files{ $_[0] } = $_[1]; 77 | }, 78 | ); 79 | 80 | is( $components->archive_elevate_files(), undef, 'Successfully archives logs when all preconditions are met' ); 81 | is( 82 | \%archived_files, 83 | { 84 | '/var/cpanel/elevate' => '/var/cpanel/elevate_archive/CentOS7-to-AlmaLinux8/_var_cpanel_elevate', 85 | '/var/cpanel/elevate-blockers' => '/var/cpanel/elevate_archive/CentOS7-to-AlmaLinux8/_var_cpanel_elevate-blockers', 86 | '/var/log/elevate-cpanel.log' => '/var/cpanel/elevate_archive/CentOS7-to-AlmaLinux8/_var_log_elevate-cpanel.log', 87 | }, 88 | 'The expected logs are archived to the expected archive locations', 89 | ); 90 | 91 | done_testing; 92 | exit; 93 | -------------------------------------------------------------------------------- /t/components-dns.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | package test::cpev::blockers; 9 | 10 | use FindBin; 11 | 12 | use Test2::V0; 13 | use Test2::Tools::Explain; 14 | use Test2::Plugin::NoWarnings; 15 | use Test2::Tools::Exception; 16 | 17 | use Test::MockFile 0.032; 18 | use Test::MockModule qw/strict/; 19 | 20 | use lib $FindBin::Bin . "/lib"; 21 | use Test::Elevate; 22 | 23 | use cPstrict; 24 | 25 | my $cpev_mock = Test::MockModule->new('cpev'); 26 | my $dns_mock = Test::MockModule->new('Elevate::Components::DNS'); 27 | 28 | my $cpconf; 29 | my $conf_mock = Test::MockModule->new('Cpanel::Config::LoadCpConf'); 30 | $conf_mock->redefine( 31 | loadcpconf => sub { return $cpconf; }, 32 | ); 33 | 34 | my $cpev = cpev->new; 35 | my $dns = $cpev->get_blocker('DNS'); 36 | 37 | { 38 | for my $os ( 'cent', 'cloud', 'ubuntu', 'alma' ) { 39 | set_os_to($os); 40 | my $expected_target_os = Elevate::OS::upgrade_to_pretty_name(); 41 | $cpconf = { 'local_nameserver_type' => 'nsd' }; 42 | like( 43 | $dns->check(), 44 | { 45 | id => q[Elevate::Components::DNS::_blocker_nameserver_not_supported], 46 | msg => qr/^$expected_target_os only supports the following nameservers:/ 47 | }, 48 | 'nsd nameserver is a blocker.' 49 | ); 50 | 51 | $cpconf = { 'local_nameserver_type' => 'mydns' }; 52 | like( 53 | $dns->check(), 54 | { 55 | id => q[Elevate::Components::DNS::_blocker_nameserver_not_supported], 56 | msg => qr/^$expected_target_os only supports the following nameservers:/ 57 | }, 58 | 'mydns nameserver is a blocker.' 59 | ); 60 | 61 | $cpconf = {}; 62 | is( $dns->check(), 0, "Nothing set, we're ok" ); 63 | $cpconf = { 'local_nameserver_type' => 'powerdns' }; 64 | is( $dns->check(), 0, "if they use powerdns, we're ok" ); 65 | $cpconf = { 'local_nameserver_type' => 'disabled' }; 66 | is( $dns->check(), 0, "if they use no dns, we're ok" ); 67 | } 68 | 69 | for my $os ( 'cent', 'cloud', 'alma' ) { 70 | set_os_to($os); 71 | $cpconf = { 'local_nameserver_type' => 'bind' }; 72 | is( $dns->check(), 0, "if they use bind, we're ok" ); 73 | } 74 | 75 | set_os_to('ubuntu'); 76 | my $expected_target_os = Elevate::OS::upgrade_to_pretty_name(); 77 | $cpconf = { 'local_nameserver_type' => 'bind' }; 78 | like( 79 | $dns->check(), 80 | { 81 | id => q[Elevate::Components::DNS::_blocker_nameserver_not_supported], 82 | msg => qr/^$expected_target_os only supports the following nameservers:/ 83 | }, 84 | "bind nameserver is a blocker for $expected_target_os." 85 | ); 86 | } 87 | 88 | done_testing(); 89 | -------------------------------------------------------------------------------- /t/cpanel-setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | set -ex 9 | 10 | echo "No setup right now!" 11 | 12 | -------------------------------------------------------------------------------- /t/cpanfile: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | ## 9 | ## This file is listing extra dependencies not provided by the default cPanel stack 10 | ## 11 | 12 | # test 13 | on 'test' => sub { 14 | 15 | requires "Test::MockFile" => "0.032"; 16 | 17 | # test coverage 18 | recommends "Test::Perl::Critic"; 19 | recommends "Test::PerlTidy"; 20 | recommends "Test::Pod"; 21 | 22 | # fatpack 23 | recommends "Module::Want"; 24 | recommends "Perl::Tidy"; 25 | }; 26 | -------------------------------------------------------------------------------- /t/critic.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | # HARNESS-DURATION-LONG 3 | 4 | # Copyright 2024 WebPros International, LLC 5 | # All rights reserved. 6 | # copyright@cpanel.net http://cpanel.net 7 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 8 | 9 | use FindBin; 10 | use lib $FindBin::Bin . "/lib"; 11 | 12 | my $bin = $FindBin::Bin; 13 | my $perlcriticrc = $bin . '.perlcriticrc'; 14 | 15 | use Test::Perl::Critic -profile => $perlcriticrc; 16 | 17 | my $script_dir = $bin . '/..' . '/script'; 18 | my $lib_dir = $bin . '/..' . '/lib'; 19 | 20 | all_critic_ok( $script_dir, $lib_dir ); 21 | 22 | 1; 23 | -------------------------------------------------------------------------------- /t/elevate-nics.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | package test::cpev::nics; 9 | 10 | use FindBin; 11 | 12 | use Test2::V0; 13 | use Test2::Tools::Explain; 14 | use Test2::Plugin::NoWarnings; 15 | use Test2::Tools::Exception; 16 | 17 | use Test::MockFile 0.032; 18 | 19 | use lib $FindBin::Bin . "/lib"; 20 | use Test::Elevate; 21 | 22 | use Test::MockModule qw/strict/; 23 | 24 | use cPstrict; 25 | 26 | my @mock_files; 27 | for my $i ( 0 .. 2 ) { 28 | my $mock_path = Test::MockFile->symlink( '/virtual', "/sys/class/net/eth$i" ); 29 | push @mock_files, $mock_path; 30 | } 31 | 32 | my ( $nic0, $nic1, $nic2 ); 33 | my $mock_saferun = Test::MockModule->new('Cpanel::SafeRun::Errors'); 34 | $mock_saferun->redefine( 35 | saferunnoerror => sub { return sbin_ip_output( $nic0, $nic1, $nic2 ); }, 36 | ); 37 | 38 | $nic0 = 'lo'; 39 | $nic1 = 'lo'; 40 | $nic2 = 'lo'; 41 | my @nics = Elevate::NICs::get_nics(); 42 | is( 43 | \@nics, 44 | [], 45 | 'Returns expected nics', 46 | ); 47 | 48 | $nic1 = 'eth0'; 49 | @nics = Elevate::NICs::get_nics(); 50 | is( 51 | \@nics, 52 | ['eth0'], 53 | 'Returns expected nics', 54 | ); 55 | 56 | $nic2 = 'eth1'; 57 | @nics = Elevate::NICs::get_nics(); 58 | is( 59 | \@nics, 60 | [ 'eth0', 'eth1' ], 61 | 'Returns expected nics', 62 | ); 63 | 64 | $nic0 = 'eth0'; 65 | $nic1 = 'eth1'; 66 | $nic2 = 'eth2'; 67 | @nics = Elevate::NICs::get_nics(); 68 | is( 69 | \@nics, 70 | [ 'eth0', 'eth1', 'eth2' ], 71 | 'Returns expected nics', 72 | ); 73 | 74 | sub sbin_ip_output ( $nic0 = undef, $nic1 = undef, $nic2 = undef ) { 75 | my $out = <<"EOS"; 76 | 1: $nic0: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 77 | link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 78 | inet 127.0.0.1/8 scope host lo 79 | valid_lft forever preferred_lft forever 80 | inet6 ::1/128 scope host 81 | valid_lft forever preferred_lft forever 82 | 2: $nic1: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 83 | link/ether 46:6b:ce:04:98:99 brd ff:ff:ff:ff:ff:ff 84 | inet 137.184.225.139/20 brd 137.184.239.255 scope global eth0 85 | valid_lft forever preferred_lft forever 86 | inet 10.48.0.6/16 brd 10.48.255.255 scope global eth0 87 | valid_lft forever preferred_lft forever 88 | inet6 fe80::446b:ceff:fe04:9899/64 scope link 89 | valid_lft forever preferred_lft forever 90 | 3: $nic2: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 91 | link/ether ea:b0:b6:c9:51:ea brd ff:ff:ff:ff:ff:ff 92 | inet 10.124.0.3/20 brd 10.124.15.255 scope global eth1 93 | valid_lft forever preferred_lft forever 94 | inet6 fe80::e8b0:b6ff:fec9:51ea/64 scope link 95 | valid_lft forever preferred_lft forever 96 | EOS 97 | 98 | return $out; 99 | } 100 | 101 | done_testing(); 102 | -------------------------------------------------------------------------------- /t/integration/almalinux8-to-almalinux9/complete.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2025 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use lib '/usr/local/cpanel/'; 9 | 10 | use Cpanel::OS (); 11 | 12 | use Test::More; 13 | 14 | is( Cpanel::OS->distro(), 'almalinux', 'System is AlmaLinux after upgrade.' ); 15 | is( Cpanel::OS->major(), '9', 'Verson 9 of OS.' ); 16 | 17 | done_testing(); 18 | -------------------------------------------------------------------------------- /t/integration/centos7-to-almalinux8/complete.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2025 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use lib '/usr/local/cpanel/'; 9 | 10 | use Cpanel::OS (); 11 | 12 | use Test::More; 13 | 14 | is( Cpanel::OS->distro(), 'almalinux', 'System is AlmaLinux after upgrade.' ); 15 | is( Cpanel::OS->major(), '8', 'Verson 8 of OS.' ); 16 | 17 | done_testing(); 18 | -------------------------------------------------------------------------------- /t/integration/cloudlinux7-to-cloudlinux8/complete.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use lib '/usr/local/cpanel/'; 9 | 10 | use Cpanel::OS (); 11 | 12 | use Test::More; 13 | 14 | is( Cpanel::OS->distro(), 'cloudlinux', 'System is CloudLinux after upgrade.' ); 15 | is( Cpanel::OS->major(), '8', 'Verson 8 of OS.' ); 16 | 17 | done_testing(); 18 | -------------------------------------------------------------------------------- /t/integration/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # Copyright 2025 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | # RE-1179 / RE-1244 9 | if [[ ! -f "/etc/scl/prefixes/ea-php80" ]] 10 | then 11 | echo -e "\nFile does not exist at /etc/scl/prefixes/ea-php80; installing ea-php80 via scripts/installpkg" 12 | /usr/local/cpanel/scripts/installpkg ea-php80 13 | else 14 | echo -e "\nFile already exists at /etc/scl/prefixes/ea-php80; skipping install of ea-php80." 15 | fi 16 | 17 | # RE-1416 / RE-1461 18 | echo -e "\nInstalling ea-nginx via scripts/installpkg" 19 | /usr/local/cpanel/scripts/installpkg ea-nginx 20 | 21 | exit 0; 22 | -------------------------------------------------------------------------------- /t/integration/ubuntu20-to-ubuntu22/complete.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use lib '/usr/local/cpanel/'; 9 | 10 | use Cpanel::OS (); 11 | 12 | use Test::More; 13 | 14 | is( Cpanel::OS->distro(), 'ubuntu', 'System is Ubuntu after upgrade.' ); 15 | is( Cpanel::OS->major(), '22', 'Verson 22 of OS.' ); 16 | 17 | done_testing(); 18 | -------------------------------------------------------------------------------- /t/is_experimental.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | package test::cpev::experimental_os; 9 | 10 | use FindBin; 11 | 12 | use Test2::V0; 13 | use Test2::Tools::Explain; 14 | use Test2::Plugin::NoWarnings; 15 | use Test2::Tools::Exception; 16 | use Test2::Tools::Mock; 17 | 18 | use Test::MockModule qw/strict/; 19 | 20 | use lib $FindBin::Bin . "/lib"; 21 | use Test::Elevate; 22 | 23 | my %os_designation = ( 24 | cent => 0, 25 | cloud => 0, 26 | ubuntu => 0, 27 | alma => 0, 28 | ); 29 | 30 | foreach my $os ( sort keys %os_designation ) { 31 | set_os_to($os); 32 | is( Elevate::OS::is_experimental(), $os_designation{$os}, "OS has the expected experimental designation" ); 33 | } 34 | 35 | done_testing(); 36 | -------------------------------------------------------------------------------- /t/leapp_beta.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | 3 | # HARNESS-NO-STREAM 4 | 5 | # Copyright 2024 WebPros International, LLC 6 | # All rights reserved. 7 | # copyright@cpanel.net http://cpanel.net 8 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 9 | 10 | use cPstrict; 11 | use FindBin; 12 | 13 | use Test2::V0; 14 | use Test2::Tools::Explain; 15 | use Test2::Plugin::NoWarnings; 16 | use Test2::Tools::Exception; 17 | 18 | use Test::MockModule qw/strict/; 19 | 20 | use lib $FindBin::Bin . "/lib"; 21 | use Test::Elevate; 22 | 23 | use cPstrict; 24 | 25 | $INC{'scripts/ElevateCpanel.pm'} = '__TEST__'; 26 | 27 | my @ssystem_cmds; 28 | my $leappbeta = 1; 29 | my $mock_elevate = Test::MockModule->new('cpev'); 30 | $mock_elevate->redefine( 31 | ssystem_and_die => sub ( $, @args ) { 32 | 33 | push @ssystem_cmds, [@args]; 34 | note "run: " . join( " ", @args ); 35 | 36 | return; 37 | }, 38 | getopt => sub ( $, $opt ) { return $leappbeta if $opt && $opt eq 'leappbeta' }, 39 | ); 40 | 41 | my $cpev = bless( {}, 'cpev' ); 42 | my $self = Elevate::Leapp->new( 'cpev' => $cpev ); 43 | 44 | set_os_to_cloudlinux_7(); 45 | is( $self->beta_if_enabled, 1, "beta_if_enabled when enabled" ); 46 | is( \@ssystem_cmds, [ [qw{/usr/bin/yum-config-manager --disable cloudlinux-elevate}], [qw{/usr/bin/yum-config-manager --enable cloudlinux-elevate-updates-testing}] ], "Expected commands are run to setup the repos." ) 47 | or diag explain \@ssystem_cmds; 48 | 49 | $leappbeta = 0; 50 | @ssystem_cmds = (); 51 | is( $self->beta_if_enabled, undef, "beta_if_enabled when disabled" ); 52 | is( \@ssystem_cmds, [], "No commands are run to setup the repos." ); 53 | 54 | done_testing; 55 | -------------------------------------------------------------------------------- /t/lib/Perl/Critic/Cpanel.pm: -------------------------------------------------------------------------------- 1 | package Perl::Critic::Cpanel; 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use strict; 9 | use warnings; 10 | 11 | =head1 NAME 12 | 13 | Perl::Critic::Cpanel - Perl::Critic policies for cPanel & WHM 14 | 15 | =cut 16 | 17 | our $VERSION = '0.12'; 18 | 19 | =head1 SYNOPSIS 20 | 21 | $ perlcritic --single-policy Cpanel::CpanelExceptions script.pl 22 | $ perlcritic --single-policy Cpanel::CpanelExceptions lib/ 23 | 24 | =head1 DESCRIPTION 25 | 26 | A set of Perl::Critic policies to enforce the coding standards of cPanel & WHM. 27 | 28 | =head1 POLICIES 29 | 30 | =head2 Perl::Critic::Policy::Cpanel::CpanelExceptions 31 | 32 | Use array references as parameters instead of hash references when invoking C. 33 | 34 | =head2 Perl::Critic::Policy::Cpanel::MultiDimensionalArrayEmulation 35 | 36 | Hash keys should not be multi-valued outside of a hash slice. 37 | 38 | =head2 Perl::Critic::Policy::Cpanel::ProhibitQxAndBackticks 39 | 40 | Prohibit qx and backticks. 41 | 42 | =head2 Perl::Critic::Policy::Cpanel::TransliterationUsage 43 | 44 | Invocations of C do not include attempted character classes like tr/[A-Z]/[a-z]/ 45 | 46 | =head2 Perl::Critic::Policy::Cpanel::TryTinyUsage 47 | 48 | Invocations of L should avoid the fully-qualified form with prototypes. 49 | 50 | =head2 Perl::Critic::Policy::TryTiny::ProhibitExitingSubroutine 51 | 52 | Fork of the L policy from the "Lokku" critic-package. 53 | 54 | Our fork currently has the following bug fixes: 55 | 56 | =over 2 57 | 58 | =item * 59 | 60 | CPANEL-14429: Ensure that the policy does not flag 'last/next/redo' 61 | statements within C loops. 62 | 63 | =item * 64 | 65 | Ensure the policy properly checks C blocks (bug noticed while fixing CPANEL-14429). 66 | 67 | =back 68 | 69 | The L package is abandoned, and since this is the only policy we use from 70 | that package, we are forking and maintaining it. 71 | 72 | =cut 73 | 74 | 1; 75 | -------------------------------------------------------------------------------- /t/lib/Perl/Critic/Policy/Cpanel/CpanelExceptions.pm: -------------------------------------------------------------------------------- 1 | package Perl::Critic::Policy::Cpanel::CpanelExceptions; 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use strict; 9 | use warnings; 10 | 11 | use parent qw(Perl::Critic::Policy); 12 | 13 | use Perl::Critic::Utils qw($SEVERITY_HIGH); 14 | 15 | =head1 NAME 16 | 17 | Perl::Critic::Policy::Cpanel::CpanelExceptions - Provides a Perl::Critic policy to ensure proper Cpanel::Exception usage 18 | 19 | =head1 SYNOPSIS 20 | 21 | $ perlcritic --single-policy Cpanel::CpanelExceptions script.pl 22 | $ perlcritic --single-policy Cpanel::CpanelExceptions lib/ 23 | 24 | =head1 DESCRIPTION 25 | 26 | This policy ensures that invocations of Cpanel::Exception always pass parameters in an ARRAYREF. 27 | 28 | =cut 29 | 30 | our $VERSION = '0.01'; 31 | my $POLICY = 'Cpanel::Exception Usage'; 32 | 33 | sub default_severity { 34 | return $SEVERITY_HIGH; 35 | } 36 | 37 | sub applies_to { 38 | return 'PPI::Token::Word'; 39 | } 40 | 41 | sub violates { 42 | my ( $self, $word ) = @_; 43 | 44 | return if $word->content ne 'Cpanel::Exception::create'; 45 | 46 | my $list = $word->snext_sibling(); 47 | if ( my $param_list = $list->find_first('PPI::Structure::Constructor') ) { 48 | return $self->violation( 'Use ARRAYREFs for parameters', $POLICY, $param_list ) 49 | if $param_list->braces() ne '[]'; 50 | } 51 | 52 | return; 53 | } 54 | 55 | 1; 56 | -------------------------------------------------------------------------------- /t/lib/Perl/Critic/Policy/Cpanel/CpanelOS.pm: -------------------------------------------------------------------------------- 1 | package Perl::Critic::Policy::Cpanel::CpanelOS; 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use strict; 9 | use warnings; 10 | 11 | use parent qw(Perl::Critic::Policy); 12 | 13 | use Perl::Critic::Utils qw{ :severities :classification :ppi $SEVERITY_MEDIUM $TRUE $FALSE }; 14 | 15 | =head1 NAME Perl::Critic::Policy::Cpanel::CpanelOS - Detect discouraged Cpanel::OS calls 16 | 17 | =head1 SYNOPSIS 18 | 19 | $ perlcritic --single-policy Cpanel::CpanelOS script.pl 20 | $ perlcritic --single-policy Cpanel::CpanelOS lib/ 21 | 22 | =head1 DESCRIPTION 23 | 24 | This policy detects discouraged calls of Cpanel::OS methods/functions. 25 | 26 | =over 27 | 28 | =item Cpanel::OS::_instance 29 | 30 | =item Cpanel::OS::build 31 | 32 | =item Cpanel::OS::distro 33 | 34 | =item Cpanel::OS::major 35 | 36 | =item Cpanel::OS::minor 37 | 38 | =item Cpanel::OS::is_apt_based 39 | 40 | =back 41 | 42 | =cut 43 | 44 | our $VERSION = '0.02'; 45 | 46 | use constant POLICY => q[You should avoid calling these Cpanel::OS methods. Please consider using a more generic question.]; 47 | 48 | use constant NOT_RECOMMENDED_METHODS => qw{ 49 | _instance 50 | build 51 | distro 52 | is_apt_based 53 | major 54 | minor 55 | }; 56 | 57 | use constant default_severity => $SEVERITY_HIGH; 58 | use constant applies_to => 'PPI::Token::Word'; 59 | use constant default_themes => qw{cpanel}; 60 | 61 | sub initialize_if_enabled { 62 | my ( $self, $config ) = @_; 63 | 64 | my $list = join( '|', NOT_RECOMMENDED_METHODS ); 65 | 66 | $self->{_regexp} = qr/^Cpanel::OS::(?:$list)$/; 67 | 68 | return $TRUE; 69 | } 70 | 71 | sub violates { 72 | my ( $self, $elem ) = @_; 73 | 74 | return unless defined $self->{_regexp}; 75 | 76 | return if is_hash_key($elem); 77 | 78 | if ( is_method_call($elem) && grep { $_ eq $elem } NOT_RECOMMENDED_METHODS ) { 79 | 80 | my $prev_m1 = eval { $elem->previous_token } or return; 81 | my $prev_m2 = eval { $prev_m1->previous_token } or return; 82 | 83 | if ( $prev_m2 eq 'Cpanel::OS' ) { 84 | return $self->violation( 'Discouraged Cpanel::OS method call:' => POLICY, $elem ); 85 | } 86 | 87 | return; 88 | } 89 | elsif ( $elem =~ $self->{_regexp} ) { 90 | return $self->violation( 'Discouraged Cpanel::OS call: ' => POLICY, $elem ); 91 | } 92 | 93 | return; 94 | } 95 | 96 | 1; 97 | -------------------------------------------------------------------------------- /t/lib/Perl/Critic/Policy/Cpanel/NoExitsFromSubroutines.pm: -------------------------------------------------------------------------------- 1 | package Perl::Critic::Policy::Cpanel::NoExitsFromSubroutines; 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use strict; 9 | use warnings; 10 | 11 | use parent qw(Perl::Critic::Policy); 12 | 13 | use Perl::Critic::Utils qw($SEVERITY_HIGH :booleans); 14 | 15 | =head1 NAME 16 | 17 | Perl::Critic::Policy::Cpanel::NoExitsFromSubroutines - Provides a Perl::Critic policy to report unapproved exits from subroutines 18 | 19 | =head1 SYNOPSIS 20 | 21 | $ perlcritic --single-policy Cpanel::NoExitsFromSubroutines script.pl 22 | $ perlcritic --single-policy Cpanel::NoExitsFromSubroutines lib/ 23 | 24 | =head1 DESCRIPTION 25 | 26 | This policy ensures that exits from subroutines are reported. The policy is configurable to allow exits after certain function calls. 27 | 28 | =cut 29 | 30 | our $VERSION = '0.01'; 31 | my $POLICY = 'Cpanel::NoExitsFromSubroutines'; 32 | 33 | use constant MSG_FORMAT => 'Exit from subroutine detected%s. Please evaluate the usage of this exit and either refactor it away or add a ## no critic with why the exit remains.'; 34 | 35 | sub default_severity { 36 | return $SEVERITY_HIGH; 37 | } 38 | 39 | sub applies_to { 40 | return 'PPI::Statement::Sub'; 41 | } 42 | 43 | sub violates { 44 | my ( $self, $elem ) = @_; 45 | 46 | my $allowed_hr = $self->{_allowed_after}; 47 | my $words = $elem->find('PPI::Token::Word'); 48 | my $exit_element; 49 | my $allowed_because_after; 50 | for my $element (@$words) { 51 | $allowed_because_after = $element->content() if !$allowed_hr->{'none'} && $allowed_hr->{ $element->content() }; 52 | next if $element->content() ne 'exit'; 53 | 54 | $exit_element = $element; 55 | last; # Perl critic can only report on 1 violation per "applies_to" 56 | } 57 | 58 | if ( $exit_element && !$allowed_because_after ) { 59 | my $input_msg = $allowed_hr->{'none'} ? '' : ' not following ' . join( ' or ', sort keys %$allowed_hr ); 60 | return $self->violation( sprintf( MSG_FORMAT(), $input_msg ), $POLICY, $exit_element ); 61 | } 62 | 63 | return; 64 | } 65 | 66 | sub supported_parameters { 67 | return ( 68 | { 69 | name => 'allowed_after', 70 | description => 'Subroutine exits will be allowed if called after the allowed_after method in the same subroutine.', 71 | default_string => 'none', 72 | behavior => 'enumeration', 73 | enumeration_values => [qw{ none exec fork }], 74 | enumeration_allow_multiple_values => 1, 75 | }, 76 | ); 77 | } 78 | 79 | 1; 80 | -------------------------------------------------------------------------------- /t/lib/Perl/Critic/Policy/Cpanel/ProhibitQxAndBackticks.pm: -------------------------------------------------------------------------------- 1 | package Perl::Critic::Policy::Cpanel::ProhibitQxAndBackticks; 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use strict; 9 | use warnings; 10 | 11 | use parent qw(Perl::Critic::Policy); 12 | 13 | use Perl::Critic::Utils qw($SEVERITY_HIGH); 14 | 15 | =head1 NAME 16 | 17 | Perl::Critic::Policy::Cpanel::ProhibitQxAndBackticks - Provides a Perl::Critic policy to prohibit shell unsafe qx and `` operators along with their backing readpipe operator 18 | 19 | =head1 SYNOPSIS 20 | 21 | $ perlcritic --single-policy Cpanel::ProhibitQxAndBackticks script.pl 22 | $ perlcritic --single-policy Cpanel::ProhibitQxAndBackticks lib/ 23 | 24 | =head1 DESCRIPTION 25 | 26 | This policy prohibits the usage of qx or the backticks (``) operators as well as their backing readpipe function. Do not add new usages. 27 | 28 | =cut 29 | 30 | our $VERSION = '0.01'; 31 | 32 | use constant { 33 | DESC => q{Use of qx, backticks, or readpipe}, 34 | EXPL => q{Use Cpanel::SafeRun::Object instead} 35 | }; 36 | 37 | sub default_severity { 38 | return $SEVERITY_HIGH; 39 | } 40 | 41 | sub applies_to { 42 | return qw( PPI::Token::QuoteLike::Backtick PPI::Token::QuoteLike::Command PPI::Statement ); 43 | } 44 | 45 | sub violates { 46 | my ( $self, $elem, undef ) = @_; 47 | 48 | if ( index( $elem->class(), 'PPI::Statement' ) == 0 ) { 49 | my $words = $elem->find('PPI::Token::Word'); 50 | return if 'ARRAY' ne ref $words; 51 | my $readpipe_element; 52 | for my $element (@$words) { 53 | next if $element->content() ne 'readpipe'; 54 | 55 | $readpipe_element = $element; 56 | last; # Perl critic can only report on 1 violation per "applies_to" 57 | } 58 | 59 | return if !$readpipe_element; 60 | 61 | return $self->violation( DESC(), EXPL(), $readpipe_element ); 62 | } 63 | else { 64 | return $self->violation( DESC(), EXPL(), $elem ); 65 | } 66 | } 67 | 68 | 1; 69 | -------------------------------------------------------------------------------- /t/lib/Perl/Critic/Policy/Cpanel/TransliterationUsage.pm: -------------------------------------------------------------------------------- 1 | package Perl::Critic::Policy::Cpanel::TransliterationUsage; 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use strict; 9 | use warnings; 10 | 11 | use parent qw(Perl::Critic::Policy); 12 | 13 | use Perl::Critic::Utils qw($SEVERITY_HIGH); 14 | 15 | =head1 NAME 16 | 17 | Perl::Critic::Policy::Cpanel::TransliterationUsage - Provides a Perl::Critic policy to ensure proper tr/// usage 18 | 19 | =head1 SYNOPSIS 20 | 21 | $ perlcritic --single-policy Cpanel::TransliterationUsage script.pl 22 | $ perlcritic --single-policy Cpanel::TransliterationUsage lib/ 23 | 24 | =head1 DESCRIPTION 25 | 26 | This policy ensures that invocations of tr/// do not include attempted character classes like tr/[A-Z]/[a-z]/ 27 | 28 | =cut 29 | 30 | our $VERSION = '0.02'; 31 | my $POLICY = 'Cpanel::TransliterationUsage Usage'; 32 | 33 | sub default_severity { 34 | return $SEVERITY_HIGH; 35 | } 36 | 37 | sub applies_to { 38 | return 'PPI::Token::Regexp::Transliterate'; 39 | } 40 | 41 | sub violates { 42 | my ( $self, $tr_usage ) = @_; 43 | 44 | if ( _is_regex_character_class( $tr_usage->get_match_string() ) || _is_regex_character_class( $tr_usage->get_substitute_string() ) ) { 45 | return $self->violation( 'Character class, e.g. tr/[A-Z]/[a-z]/, used in transliteration. Use just tr/A-Z/a-z/ instead.', $POLICY, $tr_usage ); 46 | } 47 | 48 | return; 49 | } 50 | 51 | # We only care if it's a regex character class 52 | sub _is_regex_character_class { 53 | my ($string) = @_; 54 | 55 | if ( index( $string, '[' ) == 0 && rindex( $string, ']' ) == ( length($string) - 1 ) && $string =~ /[0-9A-Za-z]-[0-9A-Za-z]/ ) { 56 | return 1; 57 | } 58 | 59 | return 0; 60 | } 61 | 62 | 1; 63 | -------------------------------------------------------------------------------- /t/lib/Perl/Critic/Policy/Cpanel/TryTinyUsage.pm: -------------------------------------------------------------------------------- 1 | package Perl::Critic::Policy::Cpanel::TryTinyUsage; 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use strict; 9 | use warnings; 10 | 11 | use parent qw(Perl::Critic::Policy); 12 | 13 | use Perl::Critic::Utils qw($SEVERITY_HIGH); 14 | 15 | =head1 NAME 16 | 17 | Perl::Critic::Policy::Cpanel::TryTinyUsage - Provides a Perl::Critic policy to check for 18 | error-prone Try::Tiny usage. 19 | 20 | =head1 SYNOPSIS 21 | 22 | $ perlcritic --single-policy Cpanel::CpanelExceptions script.pl 23 | $ perlcritic --single-policy Cpanel::CpanelExceptions lib/ 24 | 25 | =head1 DESCRIPTION 26 | 27 | This policy ensures that invocations of Try::Tiny avoid the fully-qualified form with prototypes. 28 | 29 | Try::Tiny::try { 30 | ... 31 | } Try::Tiny::catch { 32 | ... 33 | } Try::Tiny::finally { 34 | ... 35 | }; 36 | 37 | =cut 38 | 39 | our $VERSION = '0.01'; 40 | my $POLICY = 'DEVRFC-8: You must use the non-prototype form when invoking Try::Tiny in the fully-qualified form. Ex: Try::Tiny::try(sub { ... });'; 41 | 42 | sub default_severity { 43 | return $SEVERITY_HIGH; 44 | } 45 | 46 | sub applies_to { 47 | return 'PPI::Token::Word'; 48 | } 49 | 50 | sub violates { 51 | my ( $self, $word ) = @_; 52 | 53 | return if $word->content !~ m/^Try::Tiny::(?:try|catch|finally)$/; 54 | 55 | my $arg = $word->snext_sibling(); 56 | if ( $arg->isa('PPI::Structure::Block') ) { 57 | return $self->violation( 'Invalid Try::Tiny invocation', $POLICY, $arg ); 58 | } 59 | 60 | return; 61 | } 62 | 63 | 1; 64 | -------------------------------------------------------------------------------- /t/lib/cPstrict.pm: -------------------------------------------------------------------------------- 1 | package cPstrict; 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use strict; 9 | use warnings; 10 | 11 | =pod 12 | 13 | This is importing the following to your namespace 14 | 15 | use strict; 16 | use warnings; 17 | use v5.30; 18 | 19 | use feature 'signatures'; 20 | no warnings 'experimental::signatures'; 21 | 22 | =cut 23 | 24 | sub import { 25 | 26 | # auto import strict and warnings to our caller 27 | 28 | warnings->import(); 29 | strict->import(); 30 | 31 | require feature; 32 | feature->import( ':5.30', 'signatures' ); 33 | warnings->unimport('experimental::signatures'); 34 | 35 | return; 36 | } 37 | 38 | 1; 39 | -------------------------------------------------------------------------------- /t/pod.t: -------------------------------------------------------------------------------- 1 | use Test::More; 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | use Test::Pod; 9 | 10 | all_pod_files_ok(qw/script lib/); 11 | -------------------------------------------------------------------------------- /t/setup/tailwatchd.fake.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | 3 | # Copyright 2024 WebPros International, LLC 4 | # All rights reserved. 5 | # copyright@cpanel.net http://cpanel.net 6 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 7 | 8 | Description=tailwatchd 9 | 10 | [Service] 11 | ExecStart=/usr/bin/perl -E 'sleep 10 while 1' 12 | TimeoutStopSec=10 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /t/tidy.t: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | # HARNESS-DURATION-LONG 3 | 4 | # Copyright 2024 WebPros International, LLC 5 | # All rights reserved. 6 | # copyright@cpanel.net http://cpanel.net 7 | # This code is subject to the cPanel license. Unauthorized copying is prohibited. 8 | 9 | use Test::PerlTidy qw( run_tests ); 10 | 11 | run_tests( exclude => [qr{^\.vscode/}] ); 12 | -------------------------------------------------------------------------------- /version: -------------------------------------------------------------------------------- 1 | 66 --------------------------------------------------------------------------------