forked from rpms/leapp-repository
207 lines
7.6 KiB
Diff
207 lines
7.6 KiB
Diff
From 097981dd9505d1609b91fbed4e6d28e960161926 Mon Sep 17 00:00:00 2001
|
|
From: Michal Hecko <mhecko@redhat.com>
|
|
Date: Mon, 14 Apr 2025 13:02:40 +0200
|
|
Subject: [PATCH 28/37] feat(command_utils): allow missing minor/major version
|
|
for CentOS
|
|
|
|
CentOS versioning consists only of the major version number. Therefore,
|
|
in case we are running on CentOS, we do not want to enforce version to
|
|
have the format MAJOR.MINOR. Rather, we want to check that the version
|
|
is of the form MAJOR. This patch introduces the necessary
|
|
infrastructure, allowing easy extensibility of version format checking
|
|
for also other distributions. In case we do not know what version
|
|
format should a distro use, we default to MINOR.MAJOR.
|
|
|
|
Jira-ref: RHEL-80334
|
|
---
|
|
commands/command_utils.py | 92 ++++++++++++++++++++++++++++++---------
|
|
commands/upgrade/util.py | 4 +-
|
|
2 files changed, 74 insertions(+), 22 deletions(-)
|
|
|
|
diff --git a/commands/command_utils.py b/commands/command_utils.py
|
|
index a13ca59b..155bacad 100644
|
|
--- a/commands/command_utils.py
|
|
+++ b/commands/command_utils.py
|
|
@@ -3,6 +3,8 @@ import json
|
|
import os
|
|
import re
|
|
import resource
|
|
+from collections import namedtuple
|
|
+from enum import Enum
|
|
|
|
from leapp.actors import config as actor_config
|
|
from leapp.exceptions import CommandError
|
|
@@ -16,26 +18,52 @@ LEAPP_UPGRADE_FLAVOUR_DEFAULT = 'default'
|
|
LEAPP_UPGRADE_FLAVOUR_SAP_HANA = 'saphana'
|
|
LEAPP_UPGRADE_PATHS = 'upgrade_paths.json'
|
|
|
|
-VERSION_REGEX = re.compile(r"^([1-9]\d*)\.(\d+)$")
|
|
|
|
+_VersionFormat = namedtuple('VersionFormat', ('human_readable', 'regex'))
|
|
|
|
-def check_version(version):
|
|
+
|
|
+class VersionFormats(Enum):
|
|
+ MAJOR_ONLY = _VersionFormat('MAJOR_VER', re.compile(r'^[1-9]\d*$'))
|
|
+ MAJOR_MINOR = _VersionFormat('MAJOR_VER.MINOR_VER', re.compile(r"^([1-9]\d*)\.(\d+)$"))
|
|
+
|
|
+
|
|
+class _VersionKind(str, Enum):
|
|
+ """ Enum encoding information whether the given OS version is source or target. """
|
|
+ SOURCE = 'source'
|
|
+ TARGET = 'target'
|
|
+
|
|
+
|
|
+class DistroIDs(str, Enum):
|
|
+ RHEL = 'rhel'
|
|
+ CENTOS = 'centos'
|
|
+
|
|
+
|
|
+_DISTRO_VERSION_FORMATS = {
|
|
+ DistroIDs.RHEL: VersionFormats.MAJOR_MINOR,
|
|
+ DistroIDs.CENTOS: VersionFormats.MAJOR_ONLY,
|
|
+}
|
|
+"""
|
|
+Maps distro ID to the expected OS version format.
|
|
+
|
|
+If a distro is not listed in the dictionary, then VersionFormats.MAJOR_MINOR
|
|
+is used as a default.
|
|
+"""
|
|
+
|
|
+
|
|
+def assert_version_format(version_str, desired_format, version_kind):
|
|
"""
|
|
- Versioning schema: MAJOR.MINOR
|
|
- In case version contains an invalid version string, an CommandError will be raised.
|
|
+ Check whether a given version_str has the given desired format.
|
|
+
|
|
+ In case the version does not conform to the desired_format, an CommandError will be raised.
|
|
|
|
:raises: CommandError
|
|
- :return: release tuple
|
|
"""
|
|
- if not re.match(VERSION_REGEX, version):
|
|
- raise CommandError(
|
|
- "Unexpected format of target version: {}. "
|
|
- "The required format is 'X.Y' (major and minor version).".format(version)
|
|
- )
|
|
- return version.split('.')
|
|
+ if not re.match(desired_format.regex, version_str):
|
|
+ error_str = 'Unexpected format of target version: {0}. The required format is \'{1}\'.'
|
|
+ raise CommandError(error_str.format(version_str, desired_format.human_readable))
|
|
|
|
|
|
-def get_major_version(version):
|
|
+def get_major_version_from_a_valid_version(version):
|
|
"""
|
|
Return the major version from the given version string.
|
|
|
|
@@ -45,7 +73,7 @@ def get_major_version(version):
|
|
:rtype: str
|
|
:returns: The major version from the given version string.
|
|
"""
|
|
- return str(check_version(version)[0])
|
|
+ return version.split('.')[0]
|
|
|
|
|
|
def detect_sap_hana():
|
|
@@ -71,7 +99,7 @@ def get_upgrade_flavour():
|
|
return LEAPP_UPGRADE_FLAVOUR_DEFAULT
|
|
|
|
|
|
-def _retrieve_os_release_contents(_os_release_path='/etc/os-release'):
|
|
+def _retrieve_os_release_contents(_os_release_path='/etc/os-release', strip_double_quotes=True):
|
|
"""
|
|
Retrieve the contents of /etc/os-release
|
|
|
|
@@ -79,7 +107,20 @@ def _retrieve_os_release_contents(_os_release_path='/etc/os-release'):
|
|
"""
|
|
with open(_os_release_path) as os_release_handle:
|
|
lines = os_release_handle.readlines()
|
|
- return dict(line.strip().split('=', 1) for line in lines if '=' in line)
|
|
+
|
|
+ os_release_contents = {}
|
|
+ for line in lines:
|
|
+ if '=' not in line:
|
|
+ continue
|
|
+
|
|
+ key, value = line.strip().split('=', 1)
|
|
+
|
|
+ if strip_double_quotes:
|
|
+ value = value.strip('"')
|
|
+
|
|
+ os_release_contents[key] = value
|
|
+
|
|
+ return os_release_contents
|
|
|
|
|
|
def get_os_release_version_id(filepath):
|
|
@@ -88,7 +129,7 @@ def get_os_release_version_id(filepath):
|
|
|
|
:return: `str` version_id
|
|
"""
|
|
- return _retrieve_os_release_contents(_os_release_path=filepath).get('VERSION_ID', '').strip('"')
|
|
+ return _retrieve_os_release_contents(_os_release_path=filepath).get('VERSION_ID', '')
|
|
|
|
|
|
def get_upgrade_paths_config():
|
|
@@ -117,15 +158,20 @@ def get_supported_target_versions(flavour=get_upgrade_flavour()):
|
|
"""
|
|
|
|
os_release_contents = _retrieve_os_release_contents()
|
|
- current_version_id = os_release_contents.get('VERSION_ID', '').strip('"')
|
|
- distro_id = os_release_contents.get('ID', '').strip('"')
|
|
+ current_version_id = os_release_contents.get('VERSION_ID', '')
|
|
+ distro_id = os_release_contents.get('ID', '')
|
|
+
|
|
+ # We want to guarantee our actors that if they see 'centos'/'rhel'/...
|
|
+ # then they will always see expected version format
|
|
+ expected_version_format = _DISTRO_VERSION_FORMATS.get(distro_id, VersionFormats.MAJOR_MINOR).value
|
|
+ assert_version_format(current_version_id, expected_version_format, _VersionKind.SOURCE)
|
|
|
|
target_versions = get_target_versions_from_config(current_version_id, distro_id, flavour)
|
|
if not target_versions:
|
|
# If we cannot find a particular major.minor version in the map,
|
|
# we fallback to pick a target version just based on a major version.
|
|
# This can happen for example when testing not yet released versions
|
|
- major_version = get_major_version(current_version_id)
|
|
+ major_version = get_major_version_from_a_valid_version(current_version_id)
|
|
target_versions = get_target_versions_from_config(major_version, distro_id, flavour)
|
|
|
|
return target_versions
|
|
@@ -145,9 +191,15 @@ def vet_upgrade_path(args):
|
|
"""
|
|
flavor = get_upgrade_flavour()
|
|
env_version_override = os.getenv('LEAPP_DEVEL_TARGET_RELEASE')
|
|
+
|
|
if env_version_override:
|
|
- check_version(env_version_override)
|
|
+ os_release_contents = _retrieve_os_release_contents()
|
|
+ distro_id = os_release_contents.get('ID', '')
|
|
+ expected_version_format = _DISTRO_VERSION_FORMATS.get(distro_id, VersionFormats.MAJOR_MINOR).value
|
|
+ assert_version_format(env_version_override, expected_version_format, _VersionKind.TARGET)
|
|
+
|
|
return (env_version_override, flavor)
|
|
+
|
|
target_release = args.target or get_target_version(flavor)
|
|
return (target_release, flavor)
|
|
|
|
diff --git a/commands/upgrade/util.py b/commands/upgrade/util.py
|
|
index 6cdfa6d8..b54b0b34 100644
|
|
--- a/commands/upgrade/util.py
|
|
+++ b/commands/upgrade/util.py
|
|
@@ -260,8 +260,8 @@ def prepare_configuration(args):
|
|
|
|
current_version = command_utils.get_os_release_version_id('/etc/os-release')
|
|
os.environ['LEAPP_IPU_IN_PROGRESS'] = '{source}to{target}'.format(
|
|
- source=command_utils.get_major_version(current_version),
|
|
- target=command_utils.get_major_version(target_version)
|
|
+ source=command_utils.get_major_version_from_a_valid_version(current_version),
|
|
+ target=command_utils.get_major_version_from_a_valid_version(target_version)
|
|
)
|
|
|
|
configuration = {
|
|
--
|
|
2.49.0
|
|
|