Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RHEL-10: Fix download of stage2 image from .treeinfo #6109

Open
wants to merge 6 commits into
base: rhel-10
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions dracut/anaconda-lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ config_get() {
\[*\]*) cursec="${line#[}"; cursec="${cursec%%]*}" ;;
*=*) k="${line%%=*}"; v="${line#*=}" ;;
esac
# trim leading and trailing whitespace characters
k=$(echo "$k" | sed 's/^\s*//;s/\s*$//')
v=$(echo "$v" | sed 's/^\s*//;s/\s*$//')

if [ "$cursec" = "$section" ] && [ "$k" == "$key" ]; then
echo "$v"
break
Expand Down Expand Up @@ -108,6 +112,10 @@ anaconda_net_root() {
local repo="$1"
info "anaconda: fetching stage2 from $repo"

# Remove last `/` from repo to enable constructs like ...os/../BaseOS/image/install.img
# Otherwise curl will fail to work with `...os//../BaseOS...`
repo=${repo%/}

# Try to get the local path to stage2 from treeinfo.
treeinfo=$(fetch_url "$repo/.treeinfo" 2> /tmp/treeinfo_err) && \
stage2=$(config_get stage2 mainimage < "$treeinfo")
Expand Down
19 changes: 7 additions & 12 deletions tests/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ Run unit tests with patched pykickstart or other libraries

2. Run the container temporary with your required resources (pykickstart in this example)::

podman run --name=cnt-add --rm -it -v pykickstart:/pykickstart:z quay.io/rhinstaller/anaconda-ci:main sh
podman run --name=cnt-add --rm -it -v ./pykickstart:/pykickstart:z quay.io/rhinstaller/anaconda-ci:main sh

3. Do your required changes in the container (install pykickstart in this example)::

Expand Down Expand Up @@ -177,16 +177,11 @@ ________________________

The `kickstart-tests.yml workflow`_ allows rhinstaller organization members to
run kickstart-tests_ against an anaconda PR (only ``main`` for now). Send a
comment that starts with ``/kickstart-tests <launch options>`` to the pull
request to trigger this. See the `kickstart launch script`_ documentation and
its ``--help`` for details what is supported; the two basic modes are running
a set of individual tests::

/kickstart-tests keyboard [test2 test3 ...]

or running all tests of one or more given types::

/kickstart-tests --testtype network,autopart
comment that starts with ``/kickstart-tests <options>`` to the pull request to
trigger it. It is possible to use tests updated via a kickstart-tests
repository PR. See the `kickstart-tests.yml workflow`_ for supported
options. For more detailed information on tests selection see the
`kickstart launch script`_ documentation and-its ``--help``

Container maintenance
---------------------
Expand Down Expand Up @@ -277,7 +272,7 @@ represents a different class of tests. They are

- *cppcheck/* - static C/C++ code analysis using the *cppcheck* tool;
- *shellcheck/* - shell code analyzer config;
- *dd_tests/* - Python unit tests for driver disk utilities (utils/dd);
- *dd_tests/* - Python unit tests for driver disk utilities (dracut/dd);
- *unit_tests/dracut_tests/* - Python unit tests for the dracut hooks used to configure the
installation environment and load Anaconda;
- *gettext/* - sanity tests of files used for translation; Written in Python and
Expand Down
Empty file.
170 changes: 170 additions & 0 deletions tests/unit_tests/shell_tests/test_dracut_anaconda-lib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#
# Copyright 2025 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use, modify,
# copy, or redistribute it subject to the terms and conditions of the GNU
# General Public License v.2. This program is distributed in the hope that it
# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
# trademarks that are incorporated in the source code or documentation are not
# subject to the GNU General Public License and may only be used or replicated
# with the express permission of Red Hat, Inc.

import os
import re
import subprocess
import unittest
from collections import namedtuple
from tempfile import NamedTemporaryFile, TemporaryDirectory

DISABLE_COMMAND_PREFIX = "disabled-command"


SubprocessReturn = namedtuple("SubprocessReturn",
["returncode", "disabled_cmd_args", "stdout", "stderr"])


class AnacondaLibTestCase(unittest.TestCase):

def setUp(self):
self._temp_dir = TemporaryDirectory()
self._content = ""

def tearDown(self):
self._temp_dir.cleanup()

def _load_script(self, script_name):
with open(os.path.join("../dracut/", script_name), "rt", encoding="utf-8") as f:
self._content = f.read()

def _disable_bash_commands(self, disabled_commands):
disable_list = []
# disable external and problematic commands in Dracut
for disabled_cmd in disabled_commands:
if isinstance(disabled_cmd, list):
disable_list.append(f"""
{disabled_cmd[0]}() {{
echo "{DISABLE_COMMAND_PREFIX}: {disabled_cmd} args: $@" >&2
{disabled_cmd[1]}
}}
""")
if isinstance(disabled_cmd, str):
disable_list.append(f"""
{disabled_cmd}() {{
echo "{DISABLE_COMMAND_PREFIX}: {disabled_cmd} args: $@" >&2
}}
""")

lines = self._content.splitlines()
self._content = lines[0] + "\n" + "\n".join(disable_list) + "\n" + "\n".join(lines[1:])

def _run_shell_command(self, command):
"""Run a shell command and return the output

This function will also split out disabled commands args from the stdout and returns
it as named tuple.

:returns: SubprocessReturn named tuple
"""
command = f"{self._content}\n\n{command}"
ret = subprocess.run(
["bash", "-c", command],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
check=False,
)

disabled_cmd_args, stderr = self._separate_disabled_commands_msgs(ret.stderr)

return SubprocessReturn(
returncode=ret.returncode,
disabled_cmd_args=disabled_cmd_args,
stdout=ret.stdout.strip(),
stderr=stderr
)

def _separate_disabled_commands_msgs(self, stderr):
stderr_final = ""
disabled_cmd_args = {}
for line in stderr.splitlines():
if line.startswith(DISABLE_COMMAND_PREFIX):
match = re.search(fr"{DISABLE_COMMAND_PREFIX}: ([\w-]+) args: (.*)$", line)
if match.group(1) in disabled_cmd_args:
disabled_cmd_args[match.group(1)].append(match.group(2))
else:
disabled_cmd_args[match.group(1)] = [match.group(2)]
continue

stderr_final += line + "\n"

return disabled_cmd_args, stderr_final

def _check_get_text_with_content(self, test_input, expected_stdout):
with NamedTemporaryFile(mode="wt", delete_on_close=False) as test_file:
test_file.write(test_input)
test_file.close()
ret = self._run_shell_command(f"config_get tree arch < {test_file.name}")
assert ret.returncode == 0
assert ret.stdout == expected_stdout

def test_config_get(self):
"""Test bash config_get function to read .treeinfo file"""
self._load_script("anaconda-lib.sh")
self._disable_bash_commands(["command"])

# test multiple values in file
self._check_get_text_with_content(
"""
[tree]
arch=x86_64
[config]
abc=cde
""",
"x86_64",
)

# test space before and after '='
self._check_get_text_with_content(
"""
[tree]
arch = aarch64
[config]
abc=cde
""",
"aarch64",
)

# test multiple spaces before and after '='
self._check_get_text_with_content(
"""
[tree]
arch =\t ppc64
[config]
abc\t=\t\tcde
""",
"ppc64",
)

# test indented section
self._check_get_text_with_content(
"""
[tree]
\tarch = ppc64le
""",
"ppc64le",
)

# test indented value in section
self._check_get_text_with_content(
"""
[tree]
arch = s390
""",
"s390",
)
Loading