diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 739f87db..e27e172f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,13 @@ Changelog ========= +10.1.1 +====== + +* [FIX] Add check for the existence of ssh control path directory +* [FIX] Handle is_installed for rpm package when rpm database is corrupted +* [FIX] Fix service.exists + 10.1.0 ====== diff --git a/images/debian_bookworm/Dockerfile b/images/debian_bookworm/Dockerfile index 33584c00..8fc65c15 100644 --- a/images/debian_bookworm/Dockerfile +++ b/images/debian_bookworm/Dockerfile @@ -66,7 +66,12 @@ RUN /v/bin/pip install -U pip RUN /v/bin/pip install 'requests==2.30.0' # install salt -RUN python3 -m pip install --break-system-packages --no-cache salt +ARG _BUILD_DEPS="gcc g++ libc6-dev python3-dev" +RUN apt update && apt install -y $_BUILD_DEPS && \ + python3 -m pip install --break-system-packages --no-cache salt && \ + apt -y purge $_BUILD_DEPS && \ + apt -y autoremove --purge && \ + rm -rf /var/lib/apt/lists/* ENV LANG fr_FR.ISO-8859-15 ENV LANGUAGE fr_FR diff --git a/test/test_modules.py b/test/test_modules.py index 3106479f..23a1f2f2 100644 --- a/test/test_modules.py +++ b/test/test_modules.py @@ -59,6 +59,17 @@ def test_held_package(host): assert python.version.startswith("3.11.") +@pytest.mark.destructive +@pytest.mark.testinfra_hosts("docker://rockylinux9") +def test_rpmdb_corrupted(host): + host.check_output("dd if=/dev/zero of=/var/lib/rpm/rpmdb.sqlite bs=1024 count=1") + with pytest.raises(RuntimeError) as excinfo: + host.package("zsh").is_installed + assert ( + "Could not check if RPM package 'zsh' is installed. error: sqlite failure:" + ) in str(excinfo.value) + + @pytest.mark.testinfra_hosts("docker://rockylinux9") def test_non_default_package_tool(host): # Make non default pkg tool binary present diff --git a/testinfra/backend/ssh.py b/testinfra/backend/ssh.py index a43d4a7f..aae144d5 100644 --- a/testinfra/backend/ssh.py +++ b/testinfra/backend/ssh.py @@ -27,7 +27,7 @@ def __init__( ssh_config: Optional[str] = None, ssh_identity_file: Optional[str] = None, timeout: int = 10, - controlpath: str = "", + controlpath: Optional[str] = None, controlpersist: int = 60, ssh_extra_args: Optional[str] = None, *args: Any, diff --git a/testinfra/modules/package.py b/testinfra/modules/package.py index 39ef248d..f9f93515 100644 --- a/testinfra/modules/package.py +++ b/testinfra/modules/package.py @@ -165,7 +165,15 @@ def version(self): class RpmPackage(Package): @property def is_installed(self): - return self.run_test("rpm -q %s", self.name).rc == 0 + result = self.run_test("rpm -q --quiet %s 2>&1", self.name) + if result.succeeded: + return True + elif result.failed and result.stdout == "": + return False + else: + raise RuntimeError( + f"Could not check if RPM package '{self.name}' is installed. {result.stdout}" + ) @property def version(self): diff --git a/testinfra/modules/service.py b/testinfra/modules/service.py index 413b2433..9a3d704a 100644 --- a/testinfra/modules/service.py +++ b/testinfra/modules/service.py @@ -176,7 +176,7 @@ def _has_systemd_suffix(self): @property def exists(self): - cmd = self.run_test('systemctl list-unit-files | grep -q"^%s"', self.name) + cmd = self.run_test('systemctl list-unit-files | grep -q "^%s"', self.name) return cmd.rc == 0 @property diff --git a/testinfra/utils/ansible_runner.py b/testinfra/utils/ansible_runner.py index 084c03b6..dc315141 100644 --- a/testinfra/utils/ansible_runner.py +++ b/testinfra/utils/ansible_runner.py @@ -187,13 +187,18 @@ def get_config( control_path = config.get("ssh_connection", "control_path", fallback="", raw=True) if control_path: - directory = config.get( + control_path_dir = config.get( "persistent_connection", "control_path_dir", fallback="~/.ansible/cp" ) - control_path = control_path % ({"directory": directory}) # noqa: S001 - # restore original "%%" - control_path = control_path.replace("%", "%%") - kwargs["controlpath"] = control_path + control_path_dir = os.path.expanduser(control_path_dir) + control_path_dir = os.path.normpath(control_path_dir) + + if os.path.isdir(control_path_dir): + control_path = control_path % ( # noqa: S001 + {"directory": control_path_dir} + ) + control_path = control_path.replace("%", "%%") # restore original "%%" + kwargs["controlpath"] = control_path spec = "{}://".format(connection)