Tuesday, April 12, 2022

[SOLVED] How to (forcefully) install a specific package version on Centos

Issue

In a ci/cd environment, i want to be able to update or downgrade a package to a specific version. To do this with yum, one needs to know the already installed version of a packge, eg one can use yum install to install a new package or update to a newer version. yum downgrade can be used to replace it with an earlier version.

Is there a way to use one command to install a specific version of a package?

Background: We continuously add new package versions to our yum channel on artifactory, so our yum channel doesn't really reflect a stable state. One should be able to install or downgrade a specific package with one yum install <package_name>-<package_version>


Solution

UPDATE#2 (please see below for older answers)

Now going with the following approach:

  1. check the already installed version
  2. if the specified version is already installed, do nothing
  3. if a newer version is installed, do a yum downgrade
  4. else, do a yum install (which also updates)

install-utils.sh:

#!/usr/bin/env bash

EXIT_FAILURE=1

function get_installed_version() {

    pkg_name=$("rpm -q --queryformat \
                          '%{NAME}-%{VERSION}-%{RELEASE}' ${pkg_name}" | tail -1)
    [ $? -ne 0 ] && pkg_name=""
    echo ${pkg_name}
}

function is_version_newer() {
    [[ $1 != $(echo -e "$1\n$2" | sort -V | head -1) ]]
}

function install_package(){
    local pkg_name="$1"
    local pkg_ver_rel="$2"
    local pkg_full_name="${pkg_name}-${pkg_ver_rel}"

    echo "Package to install: $pkg_full_name"
    installed_pkg="$(get_installed_version $pkg_name)"
    echo "Package version installed: $installed_pkg"

    if [[ "$installed_pkg" == "$pkg_full_name" ]]; then
        echo "Requested package version '$pkg_full_name' is " \
             "already installed. Nothing to do."
    elif is_version_newer "$installed_pkg" "$pkg_full_name"; then
        echo "Downgrading to $pkg_full_name ..."
        yum -y downgrade $pkg_full_name \
        && echo "Successfully downgraded to $pkg_full_name" \
        || { echo "Failed to downgrade to $pkg_full_name"; exit $EXIT_FAILURE; }
    else
        echo "Installing $pkg_full_name ..."
        yum -y install $pkg_full_name \
        && echo "Successfully installed package $pkg_full_name" \
        || { echo "Failed to install $pkg_full_name"; exit $EXIT_FAILURE; }
    fi
}

OLDER UPDATE: The (below answer) is not a good approach. Reason: Often, the version to be installed is not fully specified. So instead of giving 'version-release-architecture-epoch' (where architecture and epoch may be omitted) there is only the version specified, meaning: The latest available package for version shall be used. (eg. see this answer to "What's the rule for the Name-Version-Release of RPM?"). The approach given here will lead to an implicit downgrade to the last but one available version.

For example, one wants to install version: '0.11.1', which is already installed with version: 0.11.1-1. And available upstream is '0.11.1-SNAPSHOT20171030093532' and '0.11.1-2'. The install will update to the newest '0.11.1-2', but that would not match with the pkg_full_name, so a yum downgrade is performed, which let's the package manager select the snapshot version to be installed.

INITIAL ANSWER

For now, my best guess is to use some combination of yum install, '(check installed version)' and yum downgrade if it is not the specified version.

Eg:

pkg_name="somepackage"
pkg_full_name="somepackage-1.0.4-SNAPSHOT20171027143208"
echo "Installing $pkg_full_name ..."
yum -y install $pkg_full_name || \
{ echo "Failed to install $pkg_full_name"; exit 1; }
installed_package=$(rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}' ${pkg_name})
if [ "${installed_package}" != "${pkg_full_name}" ]; then
    sudo yum -y downgrade $pkg_full_name || \
    { echo "Failed to downgrade to $pkg_full_name"; exit 1; }
fi
echo "Successfully installed package $pkg_full_name"


Answered By - huch
Answer Checked By - Candace Johnson (WPSolving Volunteer)