2021-09-27 12:08:32 +02:00
|
|
|
From 1ccca51897eb7c7cf1bace7015a4307aa0be7215 Mon Sep 17 00:00:00 2001
|
2019-01-17 10:18:02 +01:00
|
|
|
From: Bo Maryniuk <bo@suse.de>
|
|
|
|
Date: Tue, 20 Nov 2018 16:06:31 +0100
|
|
|
|
Subject: [PATCH] Debian info_installed compatibility (#50453)
|
|
|
|
|
|
|
|
Remove unused variable
|
|
|
|
|
|
|
|
Get unit ticks installation time
|
|
|
|
|
|
|
|
Pass on unix ticks installation date time
|
|
|
|
|
|
|
|
Implement function to figure out package build time
|
|
|
|
|
|
|
|
Unify arch attribute
|
|
|
|
|
|
|
|
Add 'attr' support.
|
|
|
|
|
|
|
|
Use attr parameter in aptpkg
|
|
|
|
|
|
|
|
Add 'all_versions' output structure backward compatibility
|
|
|
|
|
|
|
|
Fix docstring
|
|
|
|
|
|
|
|
Add UT for generic test of function 'info'
|
|
|
|
|
|
|
|
Add UT for 'info' function with the parameter 'attr'
|
|
|
|
|
|
|
|
Add UT for info_installed's 'attr' param
|
|
|
|
|
|
|
|
Fix docstring
|
|
|
|
|
|
|
|
Add returned type check
|
|
|
|
|
|
|
|
Add UT for info_installed with 'all_versions=True' output structure
|
|
|
|
|
|
|
|
Refactor UT for 'owner' function
|
|
|
|
|
|
|
|
Refactor UT: move to decorators, add more checks
|
|
|
|
|
|
|
|
Schedule TODO for next refactoring of UT 'show' function
|
|
|
|
|
|
|
|
Refactor UT: get rid of old assertion way, flatten tests
|
|
|
|
|
|
|
|
Refactor UT: move to native assertions, cleanup noise, flatten complexity for better visibility what is tested
|
|
|
|
|
|
|
|
Lintfix: too many empty lines
|
|
|
|
|
|
|
|
Adjust architecture getter according to the lowpkg info
|
|
|
|
|
|
|
|
Fix wrong Git merge: missing function signature
|
2021-09-27 12:08:32 +02:00
|
|
|
|
|
|
|
Reintroducing reverted changes
|
|
|
|
|
|
|
|
Reintroducing changes from commit e20362f6f053eaa4144583604e6aac3d62838419
|
|
|
|
that got partially reverted by this commit:
|
|
|
|
https://github.com/openSUSE/salt/commit/d0ef24d113bdaaa29f180031b5da384cffe08c64#diff-820e6ce667fe3afddbc1b9cf1682fdef
|
2019-01-17 10:18:02 +01:00
|
|
|
---
|
2021-09-27 12:08:32 +02:00
|
|
|
salt/modules/aptpkg.py | 24 +-
|
|
|
|
salt/modules/dpkg_lowpkg.py | 108 ++-
|
|
|
|
tests/unit/modules/test_aptpkg.py | 981 +++++++++++++++++++++++++
|
|
|
|
tests/unit/modules/test_dpkg_lowpkg.py | 189 +++--
|
|
|
|
4 files changed, 1203 insertions(+), 99 deletions(-)
|
|
|
|
create mode 100644 tests/unit/modules/test_aptpkg.py
|
2019-01-17 10:18:02 +01:00
|
|
|
|
|
|
|
diff --git a/salt/modules/aptpkg.py b/salt/modules/aptpkg.py
|
2021-09-27 12:08:32 +02:00
|
|
|
index 86c85bb95c..06db908d3d 100644
|
2019-01-17 10:18:02 +01:00
|
|
|
--- a/salt/modules/aptpkg.py
|
|
|
|
+++ b/salt/modules/aptpkg.py
|
2021-09-27 12:08:32 +02:00
|
|
|
@@ -2920,6 +2920,15 @@ def info_installed(*names, **kwargs):
|
2019-01-17 10:18:02 +01:00
|
|
|
|
|
|
|
.. versionadded:: 2016.11.3
|
|
|
|
|
|
|
|
+ attr
|
|
|
|
+ Comma-separated package attributes. If no 'attr' is specified, all available attributes returned.
|
|
|
|
+
|
|
|
|
+ Valid attributes are:
|
|
|
|
+ version, vendor, release, build_date, build_date_time_t, install_date, install_date_time_t,
|
|
|
|
+ build_host, group, source_rpm, arch, epoch, size, license, signature, packager, url, summary, description.
|
|
|
|
+
|
|
|
|
+ .. versionadded:: Neon
|
|
|
|
+
|
2021-09-27 12:08:32 +02:00
|
|
|
CLI Example:
|
2019-01-17 10:18:02 +01:00
|
|
|
|
|
|
|
.. code-block:: bash
|
2021-09-27 12:08:32 +02:00
|
|
|
@@ -2930,11 +2939,19 @@ def info_installed(*names, **kwargs):
|
2021-01-08 13:41:50 +01:00
|
|
|
"""
|
2019-01-17 10:18:02 +01:00
|
|
|
kwargs = salt.utils.args.clean_kwargs(**kwargs)
|
2021-01-08 13:41:50 +01:00
|
|
|
failhard = kwargs.pop("failhard", True)
|
|
|
|
+ kwargs.pop("errors", None) # Only for compatibility with RPM
|
|
|
|
+ attr = kwargs.pop("attr", None) # Package attributes to return
|
|
|
|
+ all_versions = kwargs.pop(
|
|
|
|
+ "all_versions", False
|
|
|
|
+ ) # This is for backward compatible structure only
|
2019-01-17 10:18:02 +01:00
|
|
|
+
|
|
|
|
if kwargs:
|
|
|
|
salt.utils.args.invalid_kwargs(kwargs)
|
|
|
|
|
|
|
|
ret = dict()
|
2021-01-08 13:41:50 +01:00
|
|
|
- for pkg_name, pkg_nfo in __salt__["lowpkg.info"](*names, failhard=failhard).items():
|
|
|
|
+ for pkg_name, pkg_nfo in __salt__["lowpkg.info"](
|
|
|
|
+ *names, failhard=failhard, attr=attr
|
|
|
|
+ ).items():
|
2019-01-17 10:18:02 +01:00
|
|
|
t_nfo = dict()
|
2021-01-08 13:41:50 +01:00
|
|
|
if pkg_nfo.get("status", "ii")[1] != "i":
|
|
|
|
continue # return only packages that are really installed
|
2021-09-27 12:08:32 +02:00
|
|
|
@@ -2955,7 +2972,10 @@ def info_installed(*names, **kwargs):
|
2019-01-17 10:18:02 +01:00
|
|
|
else:
|
|
|
|
t_nfo[key] = value
|
|
|
|
|
|
|
|
- ret[pkg_name] = t_nfo
|
|
|
|
+ if all_versions:
|
|
|
|
+ ret.setdefault(pkg_name, []).append(t_nfo)
|
|
|
|
+ else:
|
|
|
|
+ ret[pkg_name] = t_nfo
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
2019-04-12 11:57:21 +02:00
|
|
|
diff --git a/salt/modules/dpkg_lowpkg.py b/salt/modules/dpkg_lowpkg.py
|
2021-09-27 12:08:32 +02:00
|
|
|
index 6a88573a8f..afbd619490 100644
|
2019-04-12 11:57:21 +02:00
|
|
|
--- a/salt/modules/dpkg_lowpkg.py
|
|
|
|
+++ b/salt/modules/dpkg_lowpkg.py
|
2021-09-27 12:08:32 +02:00
|
|
|
@@ -234,6 +234,44 @@ def file_dict(*packages, **kwargs):
|
2021-01-08 13:41:50 +01:00
|
|
|
return {"errors": errors, "packages": ret}
|
2019-01-17 10:18:02 +01:00
|
|
|
|
|
|
|
|
|
|
|
+def _get_pkg_build_time(name):
|
2021-01-08 13:41:50 +01:00
|
|
|
+ """
|
2019-01-17 10:18:02 +01:00
|
|
|
+ Get package build time, if possible.
|
|
|
|
+
|
|
|
|
+ :param name:
|
|
|
|
+ :return:
|
2021-01-08 13:41:50 +01:00
|
|
|
+ """
|
2019-01-17 10:18:02 +01:00
|
|
|
+ iso_time = iso_time_t = None
|
2021-01-08 13:41:50 +01:00
|
|
|
+ changelog_dir = os.path.join("/usr/share/doc", name)
|
2019-01-17 10:18:02 +01:00
|
|
|
+ if os.path.exists(changelog_dir):
|
|
|
|
+ for fname in os.listdir(changelog_dir):
|
|
|
|
+ try:
|
|
|
|
+ iso_time_t = int(os.path.getmtime(os.path.join(changelog_dir, fname)))
|
2021-01-08 13:41:50 +01:00
|
|
|
+ iso_time = (
|
|
|
|
+ datetime.datetime.utcfromtimestamp(iso_time_t).isoformat() + "Z"
|
|
|
|
+ )
|
2019-01-17 10:18:02 +01:00
|
|
|
+ break
|
|
|
|
+ except OSError:
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+ # Packager doesn't care about Debian standards, therefore Plan B: brute-force it.
|
|
|
|
+ if not iso_time:
|
2021-01-08 13:41:50 +01:00
|
|
|
+ for pkg_f_path in __salt__["cmd.run"](
|
|
|
|
+ "dpkg-query -L {}".format(name)
|
|
|
|
+ ).splitlines():
|
|
|
|
+ if "changelog" in pkg_f_path.lower() and os.path.exists(pkg_f_path):
|
2019-01-17 10:18:02 +01:00
|
|
|
+ try:
|
|
|
|
+ iso_time_t = int(os.path.getmtime(pkg_f_path))
|
2021-01-08 13:41:50 +01:00
|
|
|
+ iso_time = (
|
|
|
|
+ datetime.datetime.utcfromtimestamp(iso_time_t).isoformat() + "Z"
|
|
|
|
+ )
|
2019-01-17 10:18:02 +01:00
|
|
|
+ break
|
|
|
|
+ except OSError:
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+ return iso_time, iso_time_t
|
|
|
|
+
|
|
|
|
+
|
|
|
|
def _get_pkg_info(*packages, **kwargs):
|
2021-01-08 13:41:50 +01:00
|
|
|
"""
|
2019-01-17 10:18:02 +01:00
|
|
|
Return list of package information. If 'packages' parameter is empty,
|
2021-09-27 12:08:32 +02:00
|
|
|
@@ -257,7 +295,7 @@ def _get_pkg_info(*packages, **kwargs):
|
2021-01-08 13:41:50 +01:00
|
|
|
cmd = (
|
|
|
|
"dpkg-query -W -f='package:" + bin_var + "\\n"
|
|
|
|
"revision:${binary:Revision}\\n"
|
|
|
|
- "architecture:${Architecture}\\n"
|
|
|
|
+ "arch:${Architecture}\\n"
|
|
|
|
"maintainer:${Maintainer}\\n"
|
|
|
|
"summary:${Summary}\\n"
|
|
|
|
"source:${source:Package}\\n"
|
2021-09-27 12:08:32 +02:00
|
|
|
@@ -296,9 +334,16 @@ def _get_pkg_info(*packages, **kwargs):
|
2019-01-17 10:18:02 +01:00
|
|
|
key, value = pkg_info_line.split(":", 1)
|
|
|
|
if value:
|
|
|
|
pkg_data[key] = value
|
2021-01-08 13:41:50 +01:00
|
|
|
- install_date = _get_pkg_install_time(pkg_data.get("package"))
|
2019-01-17 10:18:02 +01:00
|
|
|
- if install_date:
|
2021-01-08 13:41:50 +01:00
|
|
|
- pkg_data["install_date"] = install_date
|
|
|
|
+ install_date, install_date_t = _get_pkg_install_time(
|
|
|
|
+ pkg_data.get("package"), pkg_data.get("arch")
|
|
|
|
+ )
|
2019-01-17 10:18:02 +01:00
|
|
|
+ if install_date:
|
2021-01-08 13:41:50 +01:00
|
|
|
+ pkg_data["install_date"] = install_date
|
|
|
|
+ pkg_data["install_date_time_t"] = install_date_t # Unix ticks
|
|
|
|
+ build_date, build_date_t = _get_pkg_build_time(pkg_data.get("package"))
|
2019-01-17 10:18:02 +01:00
|
|
|
+ if build_date:
|
2021-01-08 13:41:50 +01:00
|
|
|
+ pkg_data["build_date"] = build_date
|
|
|
|
+ pkg_data["build_date_time_t"] = build_date_t
|
|
|
|
pkg_data["description"] = pkg_descr.split(":", 1)[-1]
|
2019-01-17 10:18:02 +01:00
|
|
|
ret.append(pkg_data)
|
|
|
|
|
2021-09-27 12:08:32 +02:00
|
|
|
@@ -324,24 +369,34 @@ def _get_pkg_license(pkg):
|
2019-01-17 10:18:02 +01:00
|
|
|
return ", ".join(sorted(licenses))
|
|
|
|
|
|
|
|
|
|
|
|
-def _get_pkg_install_time(pkg):
|
|
|
|
+def _get_pkg_install_time(pkg, arch):
|
2021-01-08 13:41:50 +01:00
|
|
|
"""
|
2019-01-17 10:18:02 +01:00
|
|
|
Return package install time, based on the /var/lib/dpkg/info/<package>.list
|
|
|
|
|
|
|
|
:return:
|
2021-01-08 13:41:50 +01:00
|
|
|
"""
|
2019-01-17 10:18:02 +01:00
|
|
|
- iso_time = None
|
|
|
|
+ iso_time = iso_time_t = None
|
2021-01-08 13:41:50 +01:00
|
|
|
+ loc_root = "/var/lib/dpkg/info"
|
2019-01-17 10:18:02 +01:00
|
|
|
if pkg is not None:
|
2021-01-08 13:41:50 +01:00
|
|
|
- location = "/var/lib/dpkg/info/{}.list".format(pkg)
|
2019-01-17 10:18:02 +01:00
|
|
|
- if os.path.exists(location):
|
2021-01-08 13:41:50 +01:00
|
|
|
- iso_time = (
|
|
|
|
- datetime.datetime.utcfromtimestamp(
|
|
|
|
- int(os.path.getmtime(location))
|
|
|
|
- ).isoformat()
|
|
|
|
- + "Z"
|
|
|
|
- )
|
2019-01-17 10:18:02 +01:00
|
|
|
+ locations = []
|
2021-01-08 13:41:50 +01:00
|
|
|
+ if arch is not None and arch != "all":
|
|
|
|
+ locations.append(os.path.join(loc_root, "{}:{}.list".format(pkg, arch)))
|
|
|
|
|
|
|
|
- return iso_time
|
|
|
|
+ locations.append(os.path.join(loc_root, "{}.list".format(pkg)))
|
2019-01-17 10:18:02 +01:00
|
|
|
+ for location in locations:
|
|
|
|
+ try:
|
|
|
|
+ iso_time_t = int(os.path.getmtime(location))
|
2021-01-08 13:41:50 +01:00
|
|
|
+ iso_time = (
|
|
|
|
+ datetime.datetime.utcfromtimestamp(iso_time_t).isoformat() + "Z"
|
|
|
|
+ )
|
2019-01-17 10:18:02 +01:00
|
|
|
+ break
|
|
|
|
+ except OSError:
|
|
|
|
+ pass
|
2021-01-08 13:41:50 +01:00
|
|
|
+
|
2019-01-17 10:18:02 +01:00
|
|
|
+ if iso_time is None:
|
|
|
|
+ log.debug('Unable to get package installation time for package "%s".', pkg)
|
|
|
|
+
|
|
|
|
+ return iso_time, iso_time_t
|
|
|
|
|
|
|
|
|
|
|
|
def _get_pkg_ds_avail():
|
2021-09-27 12:08:32 +02:00
|
|
|
@@ -391,6 +446,15 @@ def info(*packages, **kwargs):
|
2019-01-17 10:18:02 +01:00
|
|
|
|
|
|
|
.. versionadded:: 2016.11.3
|
|
|
|
|
|
|
|
+ attr
|
|
|
|
+ Comma-separated package attributes. If no 'attr' is specified, all available attributes returned.
|
|
|
|
+
|
|
|
|
+ Valid attributes are:
|
|
|
|
+ version, vendor, release, build_date, build_date_time_t, install_date, install_date_time_t,
|
|
|
|
+ build_host, group, source_rpm, arch, epoch, size, license, signature, packager, url, summary, description.
|
|
|
|
+
|
|
|
|
+ .. versionadded:: Neon
|
|
|
|
+
|
2021-09-27 12:08:32 +02:00
|
|
|
CLI Example:
|
2019-01-17 10:18:02 +01:00
|
|
|
|
|
|
|
.. code-block:: bash
|
2021-09-27 12:08:32 +02:00
|
|
|
@@ -405,6 +469,10 @@ def info(*packages, **kwargs):
|
2019-01-17 10:18:02 +01:00
|
|
|
|
|
|
|
kwargs = salt.utils.args.clean_kwargs(**kwargs)
|
2021-01-08 13:41:50 +01:00
|
|
|
failhard = kwargs.pop("failhard", True)
|
|
|
|
+ attr = kwargs.pop("attr", None) or None
|
2019-01-17 10:18:02 +01:00
|
|
|
+ if attr:
|
2021-01-08 13:41:50 +01:00
|
|
|
+ attr = attr.split(",")
|
2019-01-17 10:18:02 +01:00
|
|
|
+
|
|
|
|
if kwargs:
|
|
|
|
salt.utils.args.invalid_kwargs(kwargs)
|
|
|
|
|
2021-09-27 12:08:32 +02:00
|
|
|
@@ -432,6 +500,14 @@ def info(*packages, **kwargs):
|
2021-01-08 13:41:50 +01:00
|
|
|
lic = _get_pkg_license(pkg["package"])
|
2019-01-17 10:18:02 +01:00
|
|
|
if lic:
|
2021-01-08 13:41:50 +01:00
|
|
|
pkg["license"] = lic
|
|
|
|
- ret[pkg["package"]] = pkg
|
2019-01-17 10:18:02 +01:00
|
|
|
+
|
|
|
|
+ # Remove keys that aren't in attrs
|
2021-01-08 13:41:50 +01:00
|
|
|
+ pkg_name = pkg["package"]
|
2019-01-17 10:18:02 +01:00
|
|
|
+ if attr:
|
|
|
|
+ for k in list(pkg.keys())[:]:
|
|
|
|
+ if k not in attr:
|
|
|
|
+ del pkg[k]
|
|
|
|
+
|
|
|
|
+ ret[pkg_name] = pkg
|
|
|
|
|
|
|
|
return ret
|
|
|
|
diff --git a/tests/unit/modules/test_aptpkg.py b/tests/unit/modules/test_aptpkg.py
|
2021-09-27 12:08:32 +02:00
|
|
|
new file mode 100644
|
|
|
|
index 0000000000..3c9744e224
|
|
|
|
--- /dev/null
|
2019-01-17 10:18:02 +01:00
|
|
|
+++ b/tests/unit/modules/test_aptpkg.py
|
2021-09-27 12:08:32 +02:00
|
|
|
@@ -0,0 +1,981 @@
|
|
|
|
+"""
|
|
|
|
+ :synopsis: Unit Tests for Advanced Packaging Tool module 'module.aptpkg'
|
|
|
|
+ :platform: Linux
|
|
|
|
+ :maturity: develop
|
|
|
|
+ versionadded:: 2017.7.0
|
|
|
|
+"""
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+import copy
|
|
|
|
+import logging
|
|
|
|
+import textwrap
|
|
|
|
+
|
|
|
|
+import pytest
|
|
|
|
+import salt.modules.aptpkg as aptpkg
|
|
|
|
+from salt.exceptions import CommandExecutionError, SaltInvocationError
|
2021-01-08 13:41:50 +01:00
|
|
|
+from salt.ext import six
|
2021-09-27 12:08:32 +02:00
|
|
|
+from tests.support.mixins import LoaderModuleMockMixin
|
|
|
|
+from tests.support.mock import MagicMock, Mock, call, patch
|
|
|
|
+from tests.support.unit import TestCase, skipIf
|
|
|
|
+
|
|
|
|
+log = logging.getLogger(__name__)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+APT_KEY_LIST = r"""
|
|
|
|
+pub:-:1024:17:46181433FBB75451:1104433784:::-:::scSC:
|
|
|
|
+fpr:::::::::C5986B4F1257FFA86632CBA746181433FBB75451:
|
|
|
|
+uid:-::::1104433784::B4D41942D4B35FF44182C7F9D00C99AF27B93AD0::Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>:
|
|
|
|
+"""
|
|
|
|
+
|
|
|
|
+REPO_KEYS = {
|
|
|
|
+ "46181433FBB75451": {
|
|
|
|
+ "algorithm": 17,
|
|
|
|
+ "bits": 1024,
|
|
|
|
+ "capability": "scSC",
|
|
|
|
+ "date_creation": 1104433784,
|
|
|
|
+ "date_expiration": None,
|
|
|
|
+ "fingerprint": "C5986B4F1257FFA86632CBA746181433FBB75451",
|
|
|
|
+ "keyid": "46181433FBB75451",
|
|
|
|
+ "uid": "Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>",
|
|
|
|
+ "uid_hash": "B4D41942D4B35FF44182C7F9D00C99AF27B93AD0",
|
|
|
|
+ "validity": "-",
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+PACKAGES = {"wget": "1.15-1ubuntu1.14.04.2"}
|
|
|
|
+
|
|
|
|
+LOWPKG_FILES = {
|
|
|
|
+ "errors": {},
|
|
|
|
+ "packages": {
|
|
|
|
+ "wget": [
|
|
|
|
+ "/.",
|
|
|
|
+ "/etc",
|
|
|
|
+ "/etc/wgetrc",
|
|
|
|
+ "/usr",
|
|
|
|
+ "/usr/bin",
|
|
|
|
+ "/usr/bin/wget",
|
|
|
|
+ "/usr/share",
|
|
|
|
+ "/usr/share/info",
|
|
|
|
+ "/usr/share/info/wget.info.gz",
|
|
|
|
+ "/usr/share/doc",
|
|
|
|
+ "/usr/share/doc/wget",
|
|
|
|
+ "/usr/share/doc/wget/MAILING-LIST",
|
|
|
|
+ "/usr/share/doc/wget/NEWS.gz",
|
|
|
|
+ "/usr/share/doc/wget/AUTHORS",
|
|
|
|
+ "/usr/share/doc/wget/copyright",
|
|
|
|
+ "/usr/share/doc/wget/changelog.Debian.gz",
|
|
|
|
+ "/usr/share/doc/wget/README",
|
|
|
|
+ "/usr/share/man",
|
|
|
|
+ "/usr/share/man/man1",
|
|
|
|
+ "/usr/share/man/man1/wget.1.gz",
|
|
|
|
+ ]
|
|
|
|
+ },
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+LOWPKG_INFO = {
|
|
|
|
+ "wget": {
|
|
|
|
+ "architecture": "amd64",
|
|
|
|
+ "description": "retrieves files from the web",
|
|
|
|
+ "homepage": "http://www.gnu.org/software/wget/",
|
|
|
|
+ "install_date": "2016-08-30T22:20:15Z",
|
|
|
|
+ "maintainer": "Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>",
|
|
|
|
+ "name": "wget",
|
|
|
|
+ "section": "web",
|
|
|
|
+ "source": "wget",
|
|
|
|
+ "version": "1.15-1ubuntu1.14.04.2",
|
|
|
|
+ "status": "ii",
|
|
|
|
+ },
|
|
|
|
+ "apache2": {
|
|
|
|
+ "architecture": "amd64",
|
|
|
|
+ "description": """Apache HTTP Server
|
|
|
|
+ The Apache HTTP Server Project's goal is to build a secure, efficient and
|
|
|
|
+ extensible HTTP server as standards-compliant open source software. The
|
|
|
|
+ result has long been the number one web server on the Internet.
|
|
|
|
+ .
|
|
|
|
+ Installing this package results in a full installation, including the
|
|
|
|
+ configuration files, init scripts and support scripts.""",
|
|
|
|
+ "homepage": "http://httpd.apache.org/",
|
|
|
|
+ "install_date": "2016-08-30T22:20:15Z",
|
|
|
|
+ "maintainer": "Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>",
|
|
|
|
+ "name": "apache2",
|
|
|
|
+ "section": "httpd",
|
|
|
|
+ "source": "apache2",
|
|
|
|
+ "version": "2.4.18-2ubuntu3.9",
|
|
|
|
+ "status": "rc",
|
|
|
|
+ },
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+APT_Q_UPDATE = """
|
|
|
|
+Get:1 http://security.ubuntu.com trusty-security InRelease [65 kB]
|
|
|
|
+Get:2 http://security.ubuntu.com trusty-security/main Sources [120 kB]
|
|
|
|
+Get:3 http://security.ubuntu.com trusty-security/main amd64 Packages [548 kB]
|
|
|
|
+Get:4 http://security.ubuntu.com trusty-security/main i386 Packages [507 kB]
|
|
|
|
+Hit http://security.ubuntu.com trusty-security/main Translation-en
|
|
|
|
+Fetched 1240 kB in 10s (124 kB/s)
|
|
|
|
+Reading package lists...
|
|
|
|
+"""
|
|
|
|
+
|
|
|
|
+APT_Q_UPDATE_ERROR = """
|
|
|
|
+Err http://security.ubuntu.com trusty InRelease
|
|
|
|
+
|
|
|
|
+Err http://security.ubuntu.com trusty Release.gpg
|
|
|
|
+Unable to connect to security.ubuntu.com:http:
|
|
|
|
+Reading package lists...
|
|
|
|
+W: Failed to fetch http://security.ubuntu.com/ubuntu/dists/trusty/InRelease
|
|
|
|
+
|
|
|
|
+W: Failed to fetch http://security.ubuntu.com/ubuntu/dists/trusty/Release.gpg Unable to connect to security.ubuntu.com:http:
|
|
|
|
+
|
|
|
|
+W: Some index files failed to download. They have been ignored, or old ones used instead.
|
|
|
|
+"""
|
|
|
|
+
|
|
|
|
+AUTOREMOVE = """
|
|
|
|
+Reading package lists... Done
|
|
|
|
+Building dependency tree
|
|
|
|
+Reading state information... Done
|
|
|
|
+0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
|
|
|
|
+"""
|
|
|
|
+
|
|
|
|
+UPGRADE = """
|
|
|
|
+Reading package lists...
|
|
|
|
+Building dependency tree...
|
|
|
|
+Reading state information...
|
|
|
|
+0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
|
|
|
|
+"""
|
|
|
|
+
|
|
|
|
+UNINSTALL = {"tmux": {"new": "", "old": "1.8-5"}}
|
|
|
|
+INSTALL = {"tmux": {"new": "1.8-5", "old": ""}}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+def _get_uri(repo):
|
|
|
|
+ """
|
|
|
|
+ Get the URI portion of the a string
|
|
|
|
+ """
|
|
|
|
+ splits = repo.split()
|
|
|
|
+ for val in splits:
|
|
|
|
+ if any(val.startswith(x) for x in ("http://", "https://", "ftp://")):
|
|
|
|
+ return val
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class MockSourceEntry:
|
|
|
|
+ def __init__(self, uri, source_type, line, invalid, file=None):
|
|
|
|
+ self.uri = uri
|
|
|
|
+ self.type = source_type
|
|
|
|
+ self.line = line
|
|
|
|
+ self.invalid = invalid
|
|
|
|
+ self.file = file
|
|
|
|
+ self.disabled = False
|
|
|
|
+ self.dist = ""
|
|
|
|
+
|
|
|
|
+ def mysplit(self, line):
|
|
|
|
+ return line.split()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class MockSourceList:
|
|
|
|
+ def __init__(self):
|
|
|
|
+ self.list = []
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class AptPkgTestCase(TestCase, LoaderModuleMockMixin):
|
|
|
|
+ """
|
|
|
|
+ Test cases for salt.modules.aptpkg
|
|
|
|
+ """
|
|
|
|
+
|
|
|
|
+ def setup_loader_modules(self):
|
|
|
|
+ return {aptpkg: {"__grains__": {}}}
|
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ @patch(
|
|
|
|
+ "salt.modules.aptpkg.__salt__",
|
|
|
|
+ {
|
|
|
|
+ "pkg_resource.version": MagicMock(
|
|
|
|
+ return_value=LOWPKG_INFO["wget"]["version"]
|
|
|
|
+ )
|
|
|
|
+ },
|
|
|
|
+ )
|
2021-09-27 12:08:32 +02:00
|
|
|
+ def test_version(self):
|
|
|
|
+ """
|
|
|
|
+ Test - Returns a string representing the package version or an empty string if
|
|
|
|
+ not installed.
|
|
|
|
+ """
|
2021-01-08 13:41:50 +01:00
|
|
|
+ assert aptpkg.version(*["wget"]) == aptpkg.__salt__["pkg_resource.version"]()
|
2021-09-27 12:08:32 +02:00
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ @patch("salt.modules.aptpkg.latest_version", MagicMock(return_value=""))
|
2021-09-27 12:08:32 +02:00
|
|
|
+ def test_upgrade_available(self):
|
|
|
|
+ """
|
|
|
|
+ Test - Check whether or not an upgrade is available for a given package.
|
|
|
|
+ """
|
2021-01-08 13:41:50 +01:00
|
|
|
+ assert not aptpkg.upgrade_available("wget")
|
2021-09-27 12:08:32 +02:00
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ @patch("salt.modules.aptpkg.get_repo_keys", MagicMock(return_value=REPO_KEYS))
|
|
|
|
+ @patch(
|
|
|
|
+ "salt.modules.aptpkg.__salt__",
|
|
|
|
+ {"cmd.run_all": MagicMock(return_value={"retcode": 0, "stdout": "OK"})},
|
|
|
|
+ )
|
2021-09-27 12:08:32 +02:00
|
|
|
+ def test_add_repo_key(self):
|
|
|
|
+ """
|
|
|
|
+ Test - Add a repo key.
|
|
|
|
+ """
|
2021-01-08 13:41:50 +01:00
|
|
|
+ assert aptpkg.add_repo_key(keyserver="keyserver.ubuntu.com", keyid="FBB75451")
|
2021-09-27 12:08:32 +02:00
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ @patch("salt.modules.aptpkg.get_repo_keys", MagicMock(return_value=REPO_KEYS))
|
|
|
|
+ @patch(
|
|
|
|
+ "salt.modules.aptpkg.__salt__",
|
|
|
|
+ {"cmd.run_all": MagicMock(return_value={"retcode": 0, "stdout": "OK"})},
|
|
|
|
+ )
|
2021-09-27 12:08:32 +02:00
|
|
|
+ def test_add_repo_key_failed(self):
|
|
|
|
+ """
|
|
|
|
+ Test - Add a repo key using incomplete input data.
|
|
|
|
+ """
|
2019-01-17 10:18:02 +01:00
|
|
|
+ with pytest.raises(SaltInvocationError) as ex:
|
2021-01-08 13:41:50 +01:00
|
|
|
+ aptpkg.add_repo_key(keyserver="keyserver.ubuntu.com")
|
|
|
|
+ assert (
|
|
|
|
+ " No keyid or keyid too short for keyserver: keyserver.ubuntu.com"
|
|
|
|
+ in str(ex)
|
|
|
|
+ )
|
2021-09-27 12:08:32 +02:00
|
|
|
+
|
|
|
|
+ def test_get_repo_keys(self):
|
|
|
|
+ """
|
|
|
|
+ Test - List known repo key details.
|
|
|
|
+ """
|
|
|
|
+ mock = MagicMock(return_value={"retcode": 0, "stdout": APT_KEY_LIST})
|
|
|
|
+ with patch.dict(aptpkg.__salt__, {"cmd.run_all": mock}):
|
|
|
|
+ self.assertEqual(aptpkg.get_repo_keys(), REPO_KEYS)
|
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ @patch(
|
|
|
|
+ "salt.modules.aptpkg.__salt__",
|
|
|
|
+ {"lowpkg.file_dict": MagicMock(return_value=LOWPKG_FILES)},
|
|
|
|
+ )
|
2021-09-27 12:08:32 +02:00
|
|
|
+ def test_file_dict(self):
|
|
|
|
+ """
|
|
|
|
+ Test - List the files that belong to a package, grouped by package.
|
|
|
|
+ """
|
2021-01-08 13:41:50 +01:00
|
|
|
+ assert aptpkg.file_dict("wget") == LOWPKG_FILES
|
2021-09-27 12:08:32 +02:00
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ @patch(
|
|
|
|
+ "salt.modules.aptpkg.__salt__",
|
|
|
|
+ {
|
|
|
|
+ "lowpkg.file_list": MagicMock(
|
|
|
|
+ return_value={
|
|
|
|
+ "errors": LOWPKG_FILES["errors"],
|
|
|
|
+ "files": LOWPKG_FILES["packages"]["wget"],
|
|
|
|
+ }
|
|
|
|
+ )
|
|
|
|
+ },
|
|
|
|
+ )
|
2021-09-27 12:08:32 +02:00
|
|
|
+ def test_file_list(self):
|
|
|
|
+ """
|
2019-01-17 10:18:02 +01:00
|
|
|
+ Test 'file_list' function, which is just an alias to the lowpkg 'file_list'
|
|
|
|
+
|
2021-09-27 12:08:32 +02:00
|
|
|
+ """
|
2021-01-08 13:41:50 +01:00
|
|
|
+ assert aptpkg.file_list("wget") == aptpkg.__salt__["lowpkg.file_list"]()
|
2021-09-27 12:08:32 +02:00
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ @patch(
|
|
|
|
+ "salt.modules.aptpkg.__salt__",
|
|
|
|
+ {"cmd.run_stdout": MagicMock(return_value="wget\t\t\t\t\t\tinstall")},
|
|
|
|
+ )
|
2021-09-27 12:08:32 +02:00
|
|
|
+ def test_get_selections(self):
|
|
|
|
+ """
|
|
|
|
+ Test - View package state from the dpkg database.
|
|
|
|
+ """
|
2021-01-08 13:41:50 +01:00
|
|
|
+ assert aptpkg.get_selections("wget") == {"install": ["wget"]}
|
2021-09-27 12:08:32 +02:00
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ @patch(
|
|
|
|
+ "salt.modules.aptpkg.__salt__",
|
|
|
|
+ {"lowpkg.info": MagicMock(return_value=LOWPKG_INFO)},
|
|
|
|
+ )
|
2021-09-27 12:08:32 +02:00
|
|
|
+ def test_info_installed(self):
|
|
|
|
+ """
|
|
|
|
+ Test - Return the information of the named package(s) installed on the system.
|
|
|
|
+ """
|
|
|
|
+ names = {"group": "section", "packager": "maintainer", "url": "homepage"}
|
|
|
|
+
|
|
|
|
+ installed = copy.deepcopy({"wget": LOWPKG_INFO["wget"]})
|
|
|
|
+ for name in names:
|
|
|
|
+ if installed["wget"].get(names[name], False):
|
|
|
|
+ installed["wget"][name] = installed["wget"].pop(names[name])
|
|
|
|
+
|
|
|
|
+ del installed["wget"]["status"]
|
|
|
|
+ self.assertEqual(aptpkg.info_installed("wget"), installed)
|
|
|
|
+ self.assertEqual(len(aptpkg.info_installed()), 1)
|
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ @patch(
|
|
|
|
+ "salt.modules.aptpkg.__salt__",
|
|
|
|
+ {"lowpkg.info": MagicMock(return_value=LOWPKG_INFO)},
|
|
|
|
+ )
|
2019-01-17 10:18:02 +01:00
|
|
|
+ def test_info_installed_attr(self):
|
2021-01-08 13:41:50 +01:00
|
|
|
+ """
|
2019-01-17 10:18:02 +01:00
|
|
|
+ Test info_installed 'attr'.
|
|
|
|
+ This doesn't test 'attr' behaviour per se, since the underlying function is in dpkg.
|
|
|
|
+ The test should simply not raise exceptions for invalid parameter.
|
|
|
|
+
|
|
|
|
+ :return:
|
2021-01-08 13:41:50 +01:00
|
|
|
+ """
|
|
|
|
+ ret = aptpkg.info_installed("emacs", attr="foo,bar")
|
2019-01-17 10:18:02 +01:00
|
|
|
+ assert isinstance(ret, dict)
|
2021-01-08 13:41:50 +01:00
|
|
|
+ assert "wget" in ret
|
|
|
|
+ assert isinstance(ret["wget"], dict)
|
|
|
|
+
|
|
|
|
+ wget_pkg = ret["wget"]
|
|
|
|
+ expected_pkg = {
|
|
|
|
+ "url": "http://www.gnu.org/software/wget/",
|
|
|
|
+ "packager": "Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>",
|
|
|
|
+ "name": "wget",
|
|
|
|
+ "install_date": "2016-08-30T22:20:15Z",
|
|
|
|
+ "description": "retrieves files from the web",
|
|
|
|
+ "version": "1.15-1ubuntu1.14.04.2",
|
|
|
|
+ "architecture": "amd64",
|
|
|
|
+ "group": "web",
|
|
|
|
+ "source": "wget",
|
|
|
|
+ }
|
2019-01-17 10:18:02 +01:00
|
|
|
+ for k in wget_pkg:
|
|
|
|
+ assert k in expected_pkg
|
|
|
|
+ assert wget_pkg[k] == expected_pkg[k]
|
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ @patch(
|
|
|
|
+ "salt.modules.aptpkg.__salt__",
|
|
|
|
+ {"lowpkg.info": MagicMock(return_value=LOWPKG_INFO)},
|
|
|
|
+ )
|
2019-01-17 10:18:02 +01:00
|
|
|
+ def test_info_installed_all_versions(self):
|
2021-01-08 13:41:50 +01:00
|
|
|
+ """
|
2019-01-17 10:18:02 +01:00
|
|
|
+ Test info_installed 'all_versions'.
|
|
|
|
+ Since Debian won't return same name packages with the different names,
|
|
|
|
+ this should just return different structure, backward compatible with
|
|
|
|
+ the RPM equivalents.
|
|
|
|
+
|
|
|
|
+ :return:
|
2021-01-08 13:41:50 +01:00
|
|
|
+ """
|
2019-01-17 10:18:02 +01:00
|
|
|
+ print()
|
2021-01-08 13:41:50 +01:00
|
|
|
+ ret = aptpkg.info_installed("emacs", all_versions=True)
|
2019-01-17 10:18:02 +01:00
|
|
|
+ assert isinstance(ret, dict)
|
2021-01-08 13:41:50 +01:00
|
|
|
+ assert "wget" in ret
|
|
|
|
+ assert isinstance(ret["wget"], list)
|
2019-04-12 11:57:21 +02:00
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ pkgs = ret["wget"]
|
2019-01-17 10:18:02 +01:00
|
|
|
+
|
|
|
|
+ assert len(pkgs) == 1
|
|
|
|
+ assert isinstance(pkgs[0], dict)
|
|
|
|
+
|
|
|
|
+ wget_pkg = pkgs[0]
|
2021-01-08 13:41:50 +01:00
|
|
|
+ expected_pkg = {
|
|
|
|
+ "url": "http://www.gnu.org/software/wget/",
|
|
|
|
+ "packager": "Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>",
|
|
|
|
+ "name": "wget",
|
|
|
|
+ "install_date": "2016-08-30T22:20:15Z",
|
|
|
|
+ "description": "retrieves files from the web",
|
|
|
|
+ "version": "1.15-1ubuntu1.14.04.2",
|
|
|
|
+ "architecture": "amd64",
|
|
|
|
+ "group": "web",
|
|
|
|
+ "source": "wget",
|
|
|
|
+ }
|
2019-01-17 10:18:02 +01:00
|
|
|
+ for k in wget_pkg:
|
|
|
|
+ assert k in expected_pkg
|
|
|
|
+ assert wget_pkg[k] == expected_pkg[k]
|
2021-01-08 13:41:50 +01:00
|
|
|
+
|
|
|
|
+ @patch(
|
|
|
|
+ "salt.modules.aptpkg.__salt__",
|
|
|
|
+ {"cmd.run_stdout": MagicMock(return_value="wget: /usr/bin/wget")},
|
|
|
|
+ )
|
2021-09-27 12:08:32 +02:00
|
|
|
+ def test_owner(self):
|
|
|
|
+ """
|
|
|
|
+ Test - Return the name of the package that owns the file.
|
|
|
|
+ """
|
2021-01-08 13:41:50 +01:00
|
|
|
+ assert aptpkg.owner("/usr/bin/wget") == "wget"
|
2021-09-27 12:08:32 +02:00
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ @patch("salt.utils.pkg.clear_rtag", MagicMock())
|
|
|
|
+ @patch(
|
|
|
|
+ "salt.modules.aptpkg.__salt__",
|
|
|
|
+ {
|
|
|
|
+ "cmd.run_all": MagicMock(
|
|
|
|
+ return_value={"retcode": 0, "stdout": APT_Q_UPDATE}
|
|
|
|
+ ),
|
|
|
|
+ "config.get": MagicMock(return_value=False),
|
|
|
|
+ },
|
|
|
|
+ )
|
2021-09-27 12:08:32 +02:00
|
|
|
+ def test_refresh_db(self):
|
|
|
|
+ """
|
|
|
|
+ Test - Updates the APT database to latest packages based upon repositories.
|
|
|
|
+ """
|
|
|
|
+ refresh_db = {
|
|
|
|
+ "http://security.ubuntu.com trusty-security InRelease": True,
|
|
|
|
+ "http://security.ubuntu.com trusty-security/main Sources": True,
|
|
|
|
+ "http://security.ubuntu.com trusty-security/main Translation-en": None,
|
|
|
|
+ "http://security.ubuntu.com trusty-security/main amd64 Packages": True,
|
|
|
|
+ "http://security.ubuntu.com trusty-security/main i386 Packages": True,
|
|
|
|
+ }
|
|
|
|
+ mock = MagicMock(return_value={"retcode": 0, "stdout": APT_Q_UPDATE})
|
|
|
|
+ with patch("salt.utils.pkg.clear_rtag", MagicMock()):
|
|
|
|
+ with patch.dict(
|
|
|
|
+ aptpkg.__salt__,
|
|
|
|
+ {"cmd.run_all": mock, "config.get": MagicMock(return_value=False)},
|
|
|
|
+ ):
|
|
|
|
+ self.assertEqual(aptpkg.refresh_db(), refresh_db)
|
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ @patch("salt.utils.pkg.clear_rtag", MagicMock())
|
|
|
|
+ @patch(
|
|
|
|
+ "salt.modules.aptpkg.__salt__",
|
|
|
|
+ {
|
|
|
|
+ "cmd.run_all": MagicMock(
|
|
|
|
+ return_value={"retcode": 0, "stdout": APT_Q_UPDATE_ERROR}
|
|
|
|
+ ),
|
|
|
|
+ "config.get": MagicMock(return_value=False),
|
|
|
|
+ },
|
|
|
|
+ )
|
2021-09-27 12:08:32 +02:00
|
|
|
+ def test_refresh_db_failed(self):
|
|
|
|
+ """
|
|
|
|
+ Test - Update the APT database using unreachable repositories.
|
|
|
|
+ """
|
|
|
|
+ kwargs = {"failhard": True}
|
|
|
|
+ mock = MagicMock(return_value={"retcode": 0, "stdout": APT_Q_UPDATE_ERROR})
|
|
|
|
+ with patch("salt.utils.pkg.clear_rtag", MagicMock()):
|
|
|
|
+ with patch.dict(
|
|
|
|
+ aptpkg.__salt__,
|
|
|
|
+ {"cmd.run_all": mock, "config.get": MagicMock(return_value=False)},
|
|
|
|
+ ):
|
|
|
|
+ self.assertRaises(CommandExecutionError, aptpkg.refresh_db, **kwargs)
|
|
|
|
+
|
|
|
|
+ def test_autoremove(self):
|
|
|
|
+ """
|
|
|
|
+ Test - Remove packages not required by another package.
|
|
|
|
+ """
|
|
|
|
+ with patch("salt.modules.aptpkg.list_pkgs", MagicMock(return_value=PACKAGES)):
|
|
|
|
+ patch_kwargs = {
|
|
|
|
+ "__salt__": {
|
|
|
|
+ "config.get": MagicMock(return_value=True),
|
|
|
|
+ "cmd.run_all": MagicMock(
|
|
|
|
+ return_value=MagicMock(return_value=AUTOREMOVE)
|
|
|
|
+ ),
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ with patch.multiple(aptpkg, **patch_kwargs):
|
|
|
|
+ assert aptpkg.autoremove() == {}
|
|
|
|
+ assert aptpkg.autoremove(purge=True) == {}
|
|
|
|
+ assert aptpkg.autoremove(list_only=True) == []
|
|
|
|
+ assert aptpkg.autoremove(list_only=True, purge=True) == []
|
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ @patch("salt.modules.aptpkg._uninstall", MagicMock(return_value=UNINSTALL))
|
2021-09-27 12:08:32 +02:00
|
|
|
+ def test_remove(self):
|
|
|
|
+ """
|
|
|
|
+ Test - Remove packages.
|
|
|
|
+ """
|
2021-01-08 13:41:50 +01:00
|
|
|
+ assert aptpkg.remove(name="tmux") == UNINSTALL
|
2021-09-27 12:08:32 +02:00
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ @patch("salt.modules.aptpkg._uninstall", MagicMock(return_value=UNINSTALL))
|
2021-09-27 12:08:32 +02:00
|
|
|
+ def test_purge(self):
|
|
|
|
+ """
|
|
|
|
+ Test - Remove packages along with all configuration files.
|
|
|
|
+ """
|
2021-01-08 13:41:50 +01:00
|
|
|
+ assert aptpkg.purge(name="tmux") == UNINSTALL
|
|
|
|
+
|
|
|
|
+ @patch("salt.utils.pkg.clear_rtag", MagicMock())
|
|
|
|
+ @patch("salt.modules.aptpkg.list_pkgs", MagicMock(return_value=UNINSTALL))
|
|
|
|
+ @patch.multiple(
|
|
|
|
+ aptpkg,
|
|
|
|
+ **{
|
|
|
|
+ "__salt__": {
|
|
|
|
+ "config.get": MagicMock(return_value=True),
|
|
|
|
+ "cmd.run_all": MagicMock(
|
|
|
|
+ return_value={"retcode": 0, "stdout": UPGRADE}
|
|
|
|
+ ),
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ )
|
2021-09-27 12:08:32 +02:00
|
|
|
+ def test_upgrade(self):
|
|
|
|
+ """
|
|
|
|
+ Test - Upgrades all packages.
|
|
|
|
+ """
|
|
|
|
+ with patch("salt.utils.pkg.clear_rtag", MagicMock()):
|
|
|
|
+ with patch(
|
|
|
|
+ "salt.modules.aptpkg.list_pkgs", MagicMock(return_value=UNINSTALL)
|
|
|
|
+ ):
|
|
|
|
+ mock_cmd = MagicMock(return_value={"retcode": 0, "stdout": UPGRADE})
|
|
|
|
+ patch_kwargs = {
|
|
|
|
+ "__salt__": {
|
|
|
|
+ "config.get": MagicMock(return_value=True),
|
|
|
|
+ "cmd.run_all": mock_cmd,
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ with patch.multiple(aptpkg, **patch_kwargs):
|
|
|
|
+ self.assertEqual(aptpkg.upgrade(), dict())
|
|
|
|
+ kwargs = {"force_conf_new": True}
|
|
|
|
+ self.assertEqual(aptpkg.upgrade(**kwargs), dict())
|
|
|
|
+
|
|
|
|
+ def test_upgrade_downloadonly(self):
|
|
|
|
+ """
|
|
|
|
+ Tests the download-only options for upgrade.
|
|
|
|
+ """
|
|
|
|
+ with patch("salt.utils.pkg.clear_rtag", MagicMock()):
|
|
|
|
+ with patch(
|
|
|
|
+ "salt.modules.aptpkg.list_pkgs", MagicMock(return_value=UNINSTALL)
|
|
|
|
+ ):
|
|
|
|
+ mock_cmd = MagicMock(return_value={"retcode": 0, "stdout": UPGRADE})
|
|
|
|
+ patch_kwargs = {
|
|
|
|
+ "__salt__": {
|
|
|
|
+ "config.get": MagicMock(return_value=True),
|
|
|
|
+ "cmd.run_all": mock_cmd,
|
|
|
|
+ },
|
|
|
|
+ }
|
|
|
|
+ with patch.multiple(aptpkg, **patch_kwargs):
|
|
|
|
+ aptpkg.upgrade()
|
|
|
|
+ args_matching = [
|
|
|
|
+ True
|
|
|
|
+ for args in patch_kwargs["__salt__"]["cmd.run_all"].call_args[0]
|
|
|
|
+ if "--download-only" in args
|
|
|
|
+ ]
|
|
|
|
+ # Here we shouldn't see the parameter and args_matching should be empty.
|
|
|
|
+ self.assertFalse(any(args_matching))
|
|
|
|
+
|
|
|
|
+ aptpkg.upgrade(downloadonly=True)
|
|
|
|
+ args_matching = [
|
|
|
|
+ True
|
|
|
|
+ for args in patch_kwargs["__salt__"]["cmd.run_all"].call_args[0]
|
|
|
|
+ if "--download-only" in args
|
|
|
|
+ ]
|
|
|
|
+ # --download-only should be in the args list and we should have at least on True in the list.
|
|
|
|
+ self.assertTrue(any(args_matching))
|
|
|
|
+
|
|
|
|
+ aptpkg.upgrade(download_only=True)
|
|
|
|
+ args_matching = [
|
|
|
|
+ True
|
|
|
|
+ for args in patch_kwargs["__salt__"]["cmd.run_all"].call_args[0]
|
|
|
|
+ if "--download-only" in args
|
|
|
|
+ ]
|
|
|
|
+ # --download-only should be in the args list and we should have at least on True in the list.
|
|
|
|
+ self.assertTrue(any(args_matching))
|
|
|
|
+
|
|
|
|
+ def test_show(self):
|
|
|
|
+ """
|
|
|
|
+ Test that the pkg.show function properly parses apt-cache show output.
|
|
|
|
+ This test uses an abridged output per package, for simplicity.
|
|
|
|
+ """
|
|
|
|
+ show_mock_success = MagicMock(
|
|
|
|
+ return_value={
|
|
|
|
+ "retcode": 0,
|
|
|
|
+ "pid": 12345,
|
|
|
|
+ "stderr": "",
|
|
|
|
+ "stdout": textwrap.dedent(
|
|
|
|
+ """\
|
|
|
|
+ Package: foo1.0
|
|
|
|
+ Architecture: amd64
|
|
|
|
+ Version: 1.0.5-3ubuntu4
|
|
|
|
+ Description: A silly package (1.0 release cycle)
|
|
|
|
+ Provides: foo
|
|
|
|
+ Suggests: foo-doc
|
|
|
|
+
|
|
|
|
+ Package: foo1.0
|
|
|
|
+ Architecture: amd64
|
|
|
|
+ Version: 1.0.4-2ubuntu1
|
|
|
|
+ Description: A silly package (1.0 release cycle)
|
|
|
|
+ Provides: foo
|
|
|
|
+ Suggests: foo-doc
|
|
|
|
+
|
|
|
|
+ Package: foo-doc
|
|
|
|
+ Architecture: all
|
|
|
|
+ Version: 1.0.5-3ubuntu4
|
|
|
|
+ Description: Silly documentation for a silly package (1.0 release cycle)
|
|
|
|
+
|
|
|
|
+ Package: foo-doc
|
|
|
|
+ Architecture: all
|
|
|
|
+ Version: 1.0.4-2ubuntu1
|
|
|
|
+ Description: Silly documentation for a silly package (1.0 release cycle)
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ ),
|
|
|
|
+ }
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ show_mock_failure = MagicMock(
|
|
|
|
+ return_value={
|
|
|
|
+ "retcode": 1,
|
|
|
|
+ "pid": 12345,
|
|
|
|
+ "stderr": textwrap.dedent(
|
|
|
|
+ """\
|
|
|
|
+ N: Unable to locate package foo*
|
|
|
|
+ N: Couldn't find any package by glob 'foo*'
|
|
|
|
+ N: Couldn't find any package by regex 'foo*'
|
|
|
|
+ E: No packages found
|
|
|
|
+ """
|
|
|
|
+ ),
|
|
|
|
+ "stdout": "",
|
|
|
|
+ }
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ refresh_mock = Mock()
|
|
|
|
+
|
|
|
|
+ expected = {
|
|
|
|
+ "foo1.0": {
|
|
|
|
+ "1.0.5-3ubuntu4": {
|
|
|
|
+ "Architecture": "amd64",
|
|
|
|
+ "Description": "A silly package (1.0 release cycle)",
|
|
|
|
+ "Provides": "foo",
|
|
|
|
+ "Suggests": "foo-doc",
|
|
|
|
+ },
|
|
|
|
+ "1.0.4-2ubuntu1": {
|
|
|
|
+ "Architecture": "amd64",
|
|
|
|
+ "Description": "A silly package (1.0 release cycle)",
|
|
|
|
+ "Provides": "foo",
|
|
|
|
+ "Suggests": "foo-doc",
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ "foo-doc": {
|
|
|
|
+ "1.0.5-3ubuntu4": {
|
|
|
|
+ "Architecture": "all",
|
|
|
|
+ "Description": "Silly documentation for a silly package (1.0 release cycle)",
|
|
|
|
+ },
|
|
|
|
+ "1.0.4-2ubuntu1": {
|
|
|
|
+ "Architecture": "all",
|
|
|
|
+ "Description": "Silly documentation for a silly package (1.0 release cycle)",
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ # Make a copy of the above dict and strip out some keys to produce the
|
|
|
|
+ # expected filtered result.
|
|
|
|
+ filtered = copy.deepcopy(expected)
|
|
|
|
+ for k1 in filtered:
|
|
|
|
+ for k2 in filtered[k1]:
|
|
|
|
+ # Using list() because we will modify the dict during iteration
|
|
|
|
+ for k3 in list(filtered[k1][k2]):
|
|
|
|
+ if k3 not in ("Description", "Provides"):
|
|
|
|
+ filtered[k1][k2].pop(k3)
|
|
|
|
+
|
|
|
|
+ with patch.dict(
|
|
|
|
+ aptpkg.__salt__, {"cmd.run_all": show_mock_success}
|
|
|
|
+ ), patch.object(aptpkg, "refresh_db", refresh_mock):
|
|
|
|
+
|
|
|
|
+ # Test success (no refresh)
|
|
|
|
+ self.assertEqual(aptpkg.show("foo*"), expected)
|
|
|
|
+ refresh_mock.assert_not_called()
|
|
|
|
+ refresh_mock.reset_mock()
|
|
|
|
+
|
|
|
|
+ # Test success (with refresh)
|
|
|
|
+ self.assertEqual(aptpkg.show("foo*", refresh=True), expected)
|
|
|
|
+ self.assert_called_once(refresh_mock)
|
|
|
|
+ refresh_mock.reset_mock()
|
|
|
|
+
|
|
|
|
+ # Test filtered return
|
|
|
|
+ self.assertEqual(
|
|
|
|
+ aptpkg.show("foo*", filter="description,provides"), filtered
|
|
|
|
+ )
|
|
|
|
+ refresh_mock.assert_not_called()
|
|
|
|
+ refresh_mock.reset_mock()
|
|
|
|
+
|
|
|
|
+ with patch.dict(
|
|
|
|
+ aptpkg.__salt__, {"cmd.run_all": show_mock_failure}
|
|
|
|
+ ), patch.object(aptpkg, "refresh_db", refresh_mock):
|
|
|
|
+
|
|
|
|
+ # Test failure (no refresh)
|
|
|
|
+ self.assertEqual(aptpkg.show("foo*"), {})
|
|
|
|
+ refresh_mock.assert_not_called()
|
|
|
|
+ refresh_mock.reset_mock()
|
|
|
|
+
|
|
|
|
+ # Test failure (with refresh)
|
|
|
|
+ self.assertEqual(aptpkg.show("foo*", refresh=True), {})
|
|
|
|
+ self.assert_called_once(refresh_mock)
|
|
|
|
+ refresh_mock.reset_mock()
|
|
|
|
+
|
|
|
|
+ def test_mod_repo_enabled(self):
|
|
|
|
+ """
|
|
|
|
+ Checks if a repo is enabled or disabled depending on the passed kwargs.
|
|
|
|
+ """
|
|
|
|
+ with patch.dict(
|
|
|
|
+ aptpkg.__salt__,
|
|
|
|
+ {"config.option": MagicMock(), "no_proxy": MagicMock(return_value=False)},
|
|
|
|
+ ):
|
|
|
|
+ with patch("salt.modules.aptpkg._check_apt", MagicMock(return_value=True)):
|
|
|
|
+ with patch(
|
|
|
|
+ "salt.modules.aptpkg.refresh_db", MagicMock(return_value={})
|
|
|
|
+ ):
|
|
|
|
+ with patch(
|
|
|
|
+ "salt.utils.data.is_true", MagicMock(return_value=True)
|
|
|
|
+ ) as data_is_true:
|
|
|
|
+ with patch(
|
|
|
|
+ "salt.modules.aptpkg.sourceslist", MagicMock(), create=True
|
|
|
|
+ ):
|
|
|
|
+ repo = aptpkg.mod_repo("foo", enabled=False)
|
|
|
|
+ data_is_true.assert_called_with(False)
|
|
|
|
+ # with disabled=True; should call salt.utils.data.is_true True
|
|
|
|
+ data_is_true.reset_mock()
|
|
|
|
+ repo = aptpkg.mod_repo("foo", disabled=True)
|
|
|
|
+ data_is_true.assert_called_with(True)
|
|
|
|
+ # with enabled=True; should call salt.utils.data.is_true with False
|
|
|
|
+ data_is_true.reset_mock()
|
|
|
|
+ repo = aptpkg.mod_repo("foo", enabled=True)
|
|
|
|
+ data_is_true.assert_called_with(True)
|
|
|
|
+ # with disabled=True; should call salt.utils.data.is_true False
|
|
|
|
+ data_is_true.reset_mock()
|
|
|
|
+ repo = aptpkg.mod_repo("foo", disabled=False)
|
|
|
|
+ data_is_true.assert_called_with(False)
|
|
|
|
+
|
|
|
|
+ @patch(
|
|
|
|
+ "salt.utils.path.os_walk", MagicMock(return_value=[("test", "test", "test")])
|
|
|
|
+ )
|
|
|
|
+ @patch("os.path.getsize", MagicMock(return_value=123456))
|
|
|
|
+ @patch("os.path.getctime", MagicMock(return_value=1234567890.123456))
|
|
|
|
+ @patch(
|
|
|
|
+ "fnmatch.filter",
|
|
|
|
+ MagicMock(return_value=["/var/cache/apt/archive/test_package.rpm"]),
|
|
|
|
+ )
|
|
|
|
+ def test_list_downloaded(self):
|
|
|
|
+ """
|
|
|
|
+ Test downloaded packages listing.
|
|
|
|
+ :return:
|
|
|
|
+ """
|
|
|
|
+ DOWNLOADED_RET = {
|
|
|
|
+ "test-package": {
|
|
|
|
+ "1.0": {
|
|
|
|
+ "path": "/var/cache/apt/archive/test_package.rpm",
|
|
|
|
+ "size": 123456,
|
|
|
|
+ "creation_date_time_t": 1234567890,
|
|
|
|
+ "creation_date_time": "2009-02-13T23:31:30",
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ with patch.dict(
|
|
|
|
+ aptpkg.__salt__,
|
|
|
|
+ {
|
|
|
|
+ "lowpkg.bin_pkg_info": MagicMock(
|
|
|
|
+ return_value={"name": "test-package", "version": "1.0"}
|
|
|
|
+ )
|
|
|
|
+ },
|
|
|
|
+ ):
|
|
|
|
+ list_downloaded = aptpkg.list_downloaded()
|
|
|
|
+ self.assertEqual(len(list_downloaded), 1)
|
|
|
|
+ self.assertDictEqual(list_downloaded, DOWNLOADED_RET)
|
|
|
|
+
|
|
|
|
+ def test__skip_source(self):
|
|
|
|
+ """
|
|
|
|
+ Test __skip_source.
|
|
|
|
+ :return:
|
|
|
|
+ """
|
|
|
|
+ # Valid source
|
|
|
|
+ source_type = "deb"
|
|
|
|
+ source_uri = "http://cdn-aws.deb.debian.org/debian"
|
|
|
|
+ source_line = "deb http://cdn-aws.deb.debian.org/debian stretch main\n"
|
|
|
|
+
|
|
|
|
+ mock_source = MockSourceEntry(source_uri, source_type, source_line, False)
|
|
|
|
+
|
|
|
|
+ ret = aptpkg._skip_source(mock_source)
|
|
|
|
+ self.assertFalse(ret)
|
|
|
|
+
|
|
|
|
+ # Invalid source type
|
|
|
|
+ source_type = "ded"
|
|
|
|
+ source_uri = "http://cdn-aws.deb.debian.org/debian"
|
|
|
|
+ source_line = "deb http://cdn-aws.deb.debian.org/debian stretch main\n"
|
|
|
|
+
|
|
|
|
+ mock_source = MockSourceEntry(source_uri, source_type, source_line, True)
|
|
|
|
+
|
|
|
|
+ ret = aptpkg._skip_source(mock_source)
|
|
|
|
+ self.assertTrue(ret)
|
|
|
|
+
|
|
|
|
+ # Invalid source type , not skipped
|
|
|
|
+ source_type = "deb"
|
|
|
|
+ source_uri = "http://cdn-aws.deb.debian.org/debian"
|
|
|
|
+ source_line = "deb [http://cdn-aws.deb.debian.org/debian] stretch main\n"
|
|
|
|
+
|
|
|
|
+ mock_source = MockSourceEntry(source_uri, source_type, source_line, True)
|
|
|
|
+
|
|
|
|
+ ret = aptpkg._skip_source(mock_source)
|
|
|
|
+ self.assertFalse(ret)
|
|
|
|
+
|
|
|
|
+ def test_normalize_name(self):
|
|
|
|
+ """
|
|
|
|
+ Test that package is normalized only when it should be
|
|
|
|
+ """
|
|
|
|
+ with patch.dict(aptpkg.__grains__, {"osarch": "amd64"}):
|
|
|
|
+ result = aptpkg.normalize_name("foo")
|
|
|
|
+ assert result == "foo", result
|
|
|
|
+ result = aptpkg.normalize_name("foo:amd64")
|
|
|
|
+ assert result == "foo", result
|
|
|
|
+ result = aptpkg.normalize_name("foo:any")
|
|
|
|
+ assert result == "foo", result
|
|
|
|
+ result = aptpkg.normalize_name("foo:i386")
|
|
|
|
+ assert result == "foo:i386", result
|
|
|
|
+
|
|
|
|
+ def test_list_repos(self):
|
|
|
|
+ """
|
|
|
|
+ Checks results from list_repos
|
|
|
|
+ """
|
|
|
|
+ # Valid source
|
|
|
|
+ source_type = "deb"
|
|
|
|
+ source_uri = "http://cdn-aws.deb.debian.org/debian/"
|
|
|
|
+ source_line = "deb http://cdn-aws.deb.debian.org/debian/ stretch main\n"
|
|
|
|
+
|
|
|
|
+ mock_source = MockSourceEntry(source_uri, source_type, source_line, False)
|
|
|
|
+ mock_source_list = MockSourceList()
|
|
|
|
+ mock_source_list.list = [mock_source]
|
|
|
|
+
|
|
|
|
+ with patch("salt.modules.aptpkg._check_apt", MagicMock(return_value=True)):
|
|
|
|
+ with patch("salt.modules.aptpkg.sourceslist", MagicMock(), create=True):
|
|
|
|
+ with patch(
|
|
|
|
+ "salt.modules.aptpkg.sourceslist.SourcesList",
|
|
|
|
+ MagicMock(return_value=mock_source_list),
|
|
|
|
+ create=True,
|
|
|
|
+ ):
|
|
|
|
+ repos = aptpkg.list_repos()
|
|
|
|
+ self.assertIn(source_uri, repos)
|
|
|
|
+
|
|
|
|
+ assert isinstance(repos[source_uri], list)
|
|
|
|
+ assert len(repos[source_uri]) == 1
|
|
|
|
+
|
|
|
|
+ # Make sure last character in of the URI in line is still a /
|
|
|
|
+ self.assertIn("line", repos[source_uri][0])
|
|
|
|
+ _uri = _get_uri(repos[source_uri][0]["line"])
|
|
|
|
+ self.assertEqual(_uri[-1], "/")
|
|
|
|
+
|
|
|
|
+ # Make sure last character in URI is still a /
|
|
|
|
+ self.assertIn("uri", repos[source_uri][0])
|
|
|
|
+ self.assertEqual(repos[source_uri][0]["uri"][-1], "/")
|
|
|
|
+
|
|
|
|
+ def test_expand_repo_def(self):
|
|
|
|
+ """
|
|
|
|
+ Checks results from expand_repo_def
|
|
|
|
+ """
|
|
|
|
+ source_type = "deb"
|
|
|
|
+ source_uri = "http://cdn-aws.deb.debian.org/debian/"
|
|
|
|
+ source_line = "deb http://cdn-aws.deb.debian.org/debian/ stretch main\n"
|
|
|
|
+ source_file = "/etc/apt/sources.list"
|
|
|
|
+
|
|
|
|
+ mock_source = MockSourceEntry(
|
|
|
|
+ source_uri, source_type, source_line, False, file=source_file
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ # Valid source
|
|
|
|
+ with patch("salt.modules.aptpkg._check_apt", MagicMock(return_value=True)):
|
|
|
|
+ with patch("salt.modules.aptpkg.sourceslist", MagicMock(), create=True):
|
|
|
|
+ with patch(
|
|
|
|
+ "salt.modules.aptpkg.sourceslist.SourceEntry",
|
|
|
|
+ MagicMock(return_value=mock_source),
|
|
|
|
+ create=True,
|
|
|
|
+ ):
|
|
|
|
+ repo = "deb http://cdn-aws.deb.debian.org/debian/ stretch main\n"
|
|
|
|
+ sanitized = aptpkg.expand_repo_def(repo=repo, file=source_file)
|
|
|
|
+
|
|
|
|
+ assert isinstance(sanitized, dict)
|
|
|
|
+ self.assertIn("uri", sanitized)
|
|
|
|
+
|
|
|
|
+ # Make sure last character in of the URI is still a /
|
|
|
|
+ self.assertEqual(sanitized["uri"][-1], "/")
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+@skipIf(pytest is None, "PyTest is missing")
|
|
|
|
+class AptUtilsTestCase(TestCase, LoaderModuleMockMixin):
|
|
|
|
+ """
|
|
|
|
+ apt utils test case
|
|
|
|
+ """
|
|
|
|
+
|
|
|
|
+ def setup_loader_modules(self):
|
|
|
|
+ return {aptpkg: {}}
|
|
|
|
+
|
|
|
|
+ def test_call_apt_default(self):
|
|
|
|
+ """
|
|
|
|
+ Call default apt.
|
|
|
|
+ :return:
|
|
|
|
+ """
|
|
|
|
+ with patch.dict(
|
|
|
|
+ aptpkg.__salt__,
|
|
|
|
+ {"cmd.run_all": MagicMock(), "config.get": MagicMock(return_value=False)},
|
|
|
|
+ ):
|
|
|
|
+ aptpkg._call_apt(["apt-get", "install", "emacs"]) # pylint: disable=W0106
|
|
|
|
+ aptpkg.__salt__["cmd.run_all"].assert_called_once_with(
|
|
|
|
+ ["apt-get", "install", "emacs"],
|
|
|
|
+ env={},
|
|
|
|
+ output_loglevel="trace",
|
|
|
|
+ python_shell=False,
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ @patch("salt.utils.systemd.has_scope", MagicMock(return_value=True))
|
|
|
|
+ def test_call_apt_in_scope(self):
|
|
|
|
+ """
|
|
|
|
+ Call apt within the scope.
|
|
|
|
+ :return:
|
|
|
|
+ """
|
|
|
|
+ with patch.dict(
|
|
|
|
+ aptpkg.__salt__,
|
|
|
|
+ {"cmd.run_all": MagicMock(), "config.get": MagicMock(return_value=True)},
|
|
|
|
+ ):
|
|
|
|
+ aptpkg._call_apt(["apt-get", "purge", "vim"]) # pylint: disable=W0106
|
|
|
|
+ aptpkg.__salt__["cmd.run_all"].assert_called_once_with(
|
|
|
|
+ [
|
|
|
|
+ "systemd-run",
|
|
|
|
+ "--scope",
|
|
|
|
+ "--description",
|
|
|
|
+ '"salt.modules.aptpkg"',
|
|
|
|
+ "apt-get",
|
|
|
|
+ "purge",
|
|
|
|
+ "vim",
|
|
|
|
+ ],
|
|
|
|
+ env={},
|
|
|
|
+ output_loglevel="trace",
|
|
|
|
+ python_shell=False,
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ def test_call_apt_with_kwargs(self):
|
|
|
|
+ """
|
|
|
|
+ Call apt with the optinal keyword arguments.
|
|
|
|
+ :return:
|
|
|
|
+ """
|
|
|
|
+ with patch.dict(
|
|
|
|
+ aptpkg.__salt__,
|
|
|
|
+ {"cmd.run_all": MagicMock(), "config.get": MagicMock(return_value=False)},
|
|
|
|
+ ):
|
|
|
|
+ aptpkg._call_apt(
|
|
|
|
+ ["dpkg", "-l", "python"],
|
|
|
|
+ python_shell=True,
|
|
|
|
+ output_loglevel="quiet",
|
|
|
|
+ ignore_retcode=False,
|
|
|
|
+ username="Darth Vader",
|
|
|
|
+ ) # pylint: disable=W0106
|
|
|
|
+ aptpkg.__salt__["cmd.run_all"].assert_called_once_with(
|
|
|
|
+ ["dpkg", "-l", "python"],
|
|
|
|
+ env={},
|
|
|
|
+ ignore_retcode=False,
|
|
|
|
+ output_loglevel="quiet",
|
|
|
|
+ python_shell=True,
|
|
|
|
+ username="Darth Vader",
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ def test_call_apt_dpkg_lock(self):
|
|
|
|
+ """
|
|
|
|
+ Call apt and ensure the dpkg locking is handled
|
|
|
|
+ :return:
|
|
|
|
+ """
|
|
|
|
+ cmd_side_effect = [
|
|
|
|
+ {"stderr": "Could not get lock"},
|
|
|
|
+ {"stderr": "Could not get lock"},
|
|
|
|
+ {"stderr": "Could not get lock"},
|
|
|
|
+ {"stderr": "Could not get lock"},
|
|
|
|
+ {"stderr": "", "stdout": ""},
|
|
|
|
+ ]
|
|
|
|
+
|
|
|
|
+ cmd_mock = MagicMock(side_effect=cmd_side_effect)
|
|
|
|
+ cmd_call = (
|
|
|
|
+ call(
|
|
|
|
+ ["dpkg", "-l", "python"],
|
|
|
|
+ env={},
|
|
|
|
+ ignore_retcode=False,
|
|
|
|
+ output_loglevel="quiet",
|
|
|
|
+ python_shell=True,
|
|
|
|
+ username="Darth Vader",
|
|
|
|
+ ),
|
|
|
|
+ )
|
|
|
|
+ expected_calls = [cmd_call * 5]
|
|
|
|
+
|
|
|
|
+ with patch.dict(
|
|
|
|
+ aptpkg.__salt__,
|
|
|
|
+ {"cmd.run_all": cmd_mock, "config.get": MagicMock(return_value=False)},
|
|
|
|
+ ):
|
|
|
|
+ with patch("salt.modules.aptpkg.time.sleep", MagicMock()) as sleep_mock:
|
|
|
|
+ aptpkg._call_apt(
|
|
|
|
+ ["dpkg", "-l", "python"],
|
|
|
|
+ python_shell=True,
|
|
|
|
+ output_loglevel="quiet",
|
|
|
|
+ ignore_retcode=False,
|
|
|
|
+ username="Darth Vader",
|
|
|
|
+ ) # pylint: disable=W0106
|
|
|
|
+
|
|
|
|
+ # We should have sleept at least 4 times
|
|
|
|
+ assert sleep_mock.call_count >= 4
|
|
|
|
+
|
|
|
|
+ # We should attempt to call the cmd 5 times
|
|
|
|
+ self.assertEqual(cmd_mock.call_count, 5)
|
|
|
|
+ cmd_mock.has_calls(expected_calls)
|
2019-04-12 11:57:21 +02:00
|
|
|
diff --git a/tests/unit/modules/test_dpkg_lowpkg.py b/tests/unit/modules/test_dpkg_lowpkg.py
|
2021-01-08 13:41:50 +01:00
|
|
|
index 071c0f0742..160bbcd5b1 100644
|
2019-04-12 11:57:21 +02:00
|
|
|
--- a/tests/unit/modules/test_dpkg_lowpkg.py
|
|
|
|
+++ b/tests/unit/modules/test_dpkg_lowpkg.py
|
2021-01-08 13:41:50 +01:00
|
|
|
@@ -1,18 +1,12 @@
|
|
|
|
-# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
|
|
:codeauthor: Jayesh Kariya <jayeshk@saltstack.com>
|
|
|
|
"""
|
|
|
|
|
|
|
|
-# Import Python libs
|
|
|
|
-from __future__ import absolute_import, print_function, unicode_literals
|
|
|
|
|
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
|
|
|
|
-# Import Salt Libs
|
|
|
|
import salt.modules.dpkg_lowpkg as dpkg
|
|
|
|
-
|
|
|
|
-# Import Salt Testing Libs
|
|
|
|
from tests.support.mixins import LoaderModuleMockMixin
|
|
|
|
from tests.support.mock import MagicMock, patch
|
|
|
|
from tests.support.unit import TestCase
|
|
|
|
@@ -65,6 +59,51 @@ class DpkgTestCase(TestCase, LoaderModuleMockMixin):
|
|
|
|
package = cmd[2]
|
|
|
|
return DPKG_L_OUTPUT[package]
|
|
|
|
|
2019-01-17 10:18:02 +01:00
|
|
|
+ dselect_pkg = {
|
2021-01-08 13:41:50 +01:00
|
|
|
+ "emacs": {
|
|
|
|
+ "priority": "optional",
|
|
|
|
+ "filename": "pool/main/e/emacs-defaults/emacs_46.1_all.deb",
|
|
|
|
+ "description": "GNU Emacs editor (metapackage)",
|
|
|
|
+ "md5sum": "766eb2cee55ba0122dac64c4cea04445",
|
|
|
|
+ "sha256": "d172289b9a1608820eddad85c7ffc15f346a6e755c3120de0f64739c4bbc44ce",
|
|
|
|
+ "description-md5": "21fb7da111336097a2378959f6d6e6a8",
|
|
|
|
+ "bugs": "https://bugs.launchpad.net/springfield/+filebug",
|
|
|
|
+ "depends": "emacs24 | emacs24-lucid | emacs24-nox",
|
|
|
|
+ "origin": "Simpsons",
|
|
|
|
+ "version": "46.1",
|
|
|
|
+ "task": "ubuntu-usb, edubuntu-usb",
|
|
|
|
+ "original-maintainer": "Homer Simpson <homer@springfield.org>",
|
|
|
|
+ "package": "emacs",
|
|
|
|
+ "architecture": "all",
|
|
|
|
+ "size": "1692",
|
|
|
|
+ "sha1": "9271bcec53c1f7373902b1e594d9fc0359616407",
|
|
|
|
+ "source": "emacs-defaults",
|
|
|
|
+ "maintainer": "Simpsons Developers <simpsons-devel-discuss@lists.springfield.org>",
|
|
|
|
+ "supported": "9m",
|
|
|
|
+ "section": "editors",
|
|
|
|
+ "installed-size": "25",
|
|
|
|
+ }
|
2019-01-17 10:18:02 +01:00
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pkgs_info = [
|
2021-01-08 13:41:50 +01:00
|
|
|
+ {
|
|
|
|
+ "version": "46.1",
|
|
|
|
+ "arch": "all",
|
|
|
|
+ "build_date": "2014-08-07T16:51:48Z",
|
|
|
|
+ "install_date_time_t": 1481745778,
|
|
|
|
+ "section": "editors",
|
|
|
|
+ "description": "GNU Emacs editor (metapackage)\n GNU Emacs is the extensible "
|
|
|
|
+ "self-documenting text editor.\n This is a metapackage that will always "
|
|
|
|
+ "depend on the latest\n recommended Emacs release.\n",
|
|
|
|
+ "package": "emacs",
|
|
|
|
+ "source": "emacs-defaults",
|
|
|
|
+ "maintainer": "Simpsons Developers <simpsons-devel-discuss@lists.springfield.org>",
|
|
|
|
+ "build_date_time_t": 1407430308,
|
|
|
|
+ "installed_size": "25",
|
|
|
|
+ "install_date": "2016-12-14T20:02:58Z",
|
|
|
|
+ }
|
2019-01-17 10:18:02 +01:00
|
|
|
+ ]
|
|
|
|
+
|
|
|
|
def setup_loader_modules(self):
|
|
|
|
return {dpkg: {}}
|
|
|
|
|
2021-01-08 13:41:50 +01:00
|
|
|
@@ -269,83 +308,71 @@ class DpkgTestCase(TestCase, LoaderModuleMockMixin):
|
|
|
|
dpkg.bin_pkg_info("package.deb")["name"], "package_name"
|
|
|
|
)
|
2020-04-07 14:14:01 +02:00
|
|
|
|
2021-01-08 13:41:50 +01:00
|
|
|
+ @patch("salt.modules.dpkg._get_pkg_ds_avail", MagicMock(return_value=dselect_pkg))
|
|
|
|
+ @patch("salt.modules.dpkg._get_pkg_info", MagicMock(return_value=pkgs_info))
|
|
|
|
+ @patch("salt.modules.dpkg._get_pkg_license", MagicMock(return_value="BSD v3"))
|
2020-04-07 14:14:01 +02:00
|
|
|
def test_info(self):
|
2021-01-08 13:41:50 +01:00
|
|
|
"""
|
2020-04-07 14:14:01 +02:00
|
|
|
- Test package info
|
2019-01-17 10:18:02 +01:00
|
|
|
+ Test info
|
|
|
|
+ :return:
|
2021-01-08 13:41:50 +01:00
|
|
|
"""
|
|
|
|
- mock = MagicMock(
|
|
|
|
- return_value={
|
|
|
|
- "retcode": 0,
|
|
|
|
- "stderr": "",
|
|
|
|
- "stdout": os.linesep.join(
|
|
|
|
- [
|
|
|
|
- "package:bash",
|
|
|
|
- "revision:",
|
|
|
|
- "architecture:amd64",
|
|
|
|
- "maintainer:Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>",
|
|
|
|
- "summary:",
|
|
|
|
- "source:bash",
|
|
|
|
- "version:4.4.18-2ubuntu1",
|
|
|
|
- "section:shells",
|
|
|
|
- "installed_size:1588",
|
|
|
|
- "size:",
|
|
|
|
- "MD5:",
|
|
|
|
- "SHA1:",
|
|
|
|
- "SHA256:",
|
|
|
|
- "origin:",
|
|
|
|
- "homepage:http://tiswww.case.edu/php/chet/bash/bashtop.html",
|
|
|
|
- "status:ii ",
|
|
|
|
- "======",
|
|
|
|
- "description:GNU Bourne Again SHell",
|
|
|
|
- " Bash is an sh-compatible command language interpreter that executes",
|
|
|
|
- " commands read from the standard input or from a file. Bash also",
|
|
|
|
- " incorporates useful features from the Korn and C shells (ksh and csh).",
|
|
|
|
- " .",
|
|
|
|
- " Bash is ultimately intended to be a conformant implementation of the",
|
|
|
|
- " IEEE POSIX Shell and Tools specification (IEEE Working Group 1003.2).",
|
|
|
|
- " .",
|
|
|
|
- " The Programmable Completion Code, by Ian Macdonald, is now found in",
|
|
|
|
- " the bash-completion package.",
|
|
|
|
- "------",
|
|
|
|
- ]
|
|
|
|
- ),
|
|
|
|
- }
|
|
|
|
+ ret = dpkg.info("emacs")
|
2019-01-17 10:18:02 +01:00
|
|
|
+
|
|
|
|
+ assert isinstance(ret, dict)
|
|
|
|
+ assert len(ret.keys()) == 1
|
2021-01-08 13:41:50 +01:00
|
|
|
+ assert "emacs" in ret
|
2019-01-17 10:18:02 +01:00
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ pkg_data = ret["emacs"]
|
2019-01-17 10:18:02 +01:00
|
|
|
+
|
|
|
|
+ assert isinstance(pkg_data, dict)
|
2021-01-08 13:41:50 +01:00
|
|
|
+ for pkg_section in [
|
|
|
|
+ "section",
|
|
|
|
+ "architecture",
|
|
|
|
+ "original-maintainer",
|
|
|
|
+ "maintainer",
|
|
|
|
+ "package",
|
|
|
|
+ "installed-size",
|
|
|
|
+ "build_date_time_t",
|
|
|
|
+ "sha256",
|
|
|
|
+ "origin",
|
|
|
|
+ "build_date",
|
|
|
|
+ "size",
|
|
|
|
+ "source",
|
|
|
|
+ "version",
|
|
|
|
+ "install_date_time_t",
|
|
|
|
+ "license",
|
|
|
|
+ "priority",
|
|
|
|
+ "description",
|
|
|
|
+ "md5sum",
|
|
|
|
+ "supported",
|
|
|
|
+ "filename",
|
|
|
|
+ "sha1",
|
|
|
|
+ "install_date",
|
|
|
|
+ "arch",
|
|
|
|
+ ]:
|
2019-01-17 10:18:02 +01:00
|
|
|
+ assert pkg_section in pkg_data
|
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ assert pkg_data["section"] == "editors"
|
|
|
|
+ assert (
|
|
|
|
+ pkg_data["maintainer"]
|
|
|
|
+ == "Simpsons Developers <simpsons-devel-discuss@lists.springfield.org>"
|
|
|
|
)
|
|
|
|
+ assert pkg_data["license"] == "BSD v3"
|
|
|
|
|
|
|
|
- with patch.dict(dpkg.__salt__, {"cmd.run_all": mock}), patch.dict(
|
|
|
|
- dpkg.__grains__, {"os": "Ubuntu", "osrelease_info": (18, 4)}
|
|
|
|
- ), patch("salt.utils.path.which", MagicMock(return_value=False)), patch(
|
|
|
|
- "os.path.exists", MagicMock(return_value=False)
|
|
|
|
- ), patch(
|
|
|
|
- "os.path.getmtime", MagicMock(return_value=1560199259.0)
|
|
|
|
- ):
|
|
|
|
- self.assertDictEqual(
|
|
|
|
- dpkg.info("bash"),
|
|
|
|
- {
|
|
|
|
- "bash": {
|
|
|
|
- "architecture": "amd64",
|
|
|
|
- "description": os.linesep.join(
|
|
|
|
- [
|
|
|
|
- "GNU Bourne Again SHell",
|
|
|
|
- " Bash is an sh-compatible command language interpreter that executes",
|
|
|
|
- " commands read from the standard input or from a file. Bash also",
|
|
|
|
- " incorporates useful features from the Korn and C shells (ksh and csh).",
|
|
|
|
- " .",
|
|
|
|
- " Bash is ultimately intended to be a conformant implementation of the",
|
|
|
|
- " IEEE POSIX Shell and Tools specification (IEEE Working Group 1003.2).",
|
|
|
|
- " .",
|
|
|
|
- " The Programmable Completion Code, by Ian Macdonald, is now found in",
|
|
|
|
- " the bash-completion package." + os.linesep,
|
|
|
|
- ]
|
|
|
|
- ),
|
|
|
|
- "homepage": "http://tiswww.case.edu/php/chet/bash/bashtop.html",
|
|
|
|
- "maintainer": "Ubuntu Developers "
|
|
|
|
- "<ubuntu-devel-discuss@lists.ubuntu.com>",
|
|
|
|
- "package": "bash",
|
|
|
|
- "section": "shells",
|
|
|
|
- "source": "bash",
|
|
|
|
- "status": "ii",
|
|
|
|
- "version": "4.4.18-2ubuntu1",
|
|
|
|
- }
|
|
|
|
- },
|
|
|
|
- )
|
|
|
|
+ @patch("salt.modules.dpkg._get_pkg_ds_avail", MagicMock(return_value=dselect_pkg))
|
|
|
|
+ @patch("salt.modules.dpkg._get_pkg_info", MagicMock(return_value=pkgs_info))
|
|
|
|
+ @patch("salt.modules.dpkg._get_pkg_license", MagicMock(return_value="BSD v3"))
|
2019-01-17 10:18:02 +01:00
|
|
|
+ def test_info_attr(self):
|
2021-01-08 13:41:50 +01:00
|
|
|
+ """
|
2019-01-17 10:18:02 +01:00
|
|
|
+ Test info with 'attr' parameter
|
|
|
|
+ :return:
|
2021-01-08 13:41:50 +01:00
|
|
|
+ """
|
|
|
|
+ ret = dpkg.info("emacs", attr="arch,license,version")
|
2019-01-17 10:18:02 +01:00
|
|
|
+ assert isinstance(ret, dict)
|
2021-01-08 13:41:50 +01:00
|
|
|
+ assert "emacs" in ret
|
|
|
|
+ for attr in ["arch", "license", "version"]:
|
|
|
|
+ assert attr in ret["emacs"]
|
2019-01-17 10:18:02 +01:00
|
|
|
+
|
2021-01-08 13:41:50 +01:00
|
|
|
+ assert ret["emacs"]["arch"] == "all"
|
|
|
|
+ assert ret["emacs"]["license"] == "BSD v3"
|
|
|
|
+ assert ret["emacs"]["version"] == "46.1"
|
2019-01-17 10:18:02 +01:00
|
|
|
--
|
2021-09-27 12:08:32 +02:00
|
|
|
2.33.0
|
2019-01-17 10:18:02 +01:00
|
|
|
|
|
|
|
|