From 34e3f90035894568111497e5701ca9214abae848 Mon Sep 17 00:00:00 2001 From: Megan Wilhite Date: Fri, 9 Jun 2023 12:22:04 -0600 Subject: [PATCH] Add Salt package type to versions report and grain --- changelog/62589.added.md | 1 + changelog/62961.added.md | 1 + doc/ref/grains/all/index.rst | 1 + doc/ref/grains/all/salt.grains.package.rst | 5 ++ salt/grains/package.py | 25 ++++++++ salt/modules/pip.py | 59 ++++++++----------- salt/utils/package.py | 11 ++++ salt/version.py | 13 ++++ tests/pytests/functional/cli/test_salt.py | 8 ++- .../pytests/functional/modules/test_grains.py | 24 ++++++++ tests/pytests/unit/grains/test_package.py | 14 +++++ tests/pytests/unit/utils/test_package.py | 11 ++++ 12 files changed, 136 insertions(+), 37 deletions(-) create mode 100644 changelog/62589.added.md create mode 100644 changelog/62961.added.md create mode 100644 doc/ref/grains/all/salt.grains.package.rst create mode 100644 salt/grains/package.py create mode 100644 salt/utils/package.py create mode 100644 tests/pytests/functional/modules/test_grains.py create mode 100644 tests/pytests/unit/grains/test_package.py create mode 100644 tests/pytests/unit/utils/test_package.py diff --git a/changelog/62589.added.md b/changelog/62589.added.md new file mode 100644 index 00000000000..f7785c3d17f --- /dev/null +++ b/changelog/62589.added.md @@ -0,0 +1 @@ +Added new grain to detect the Salt package type: system or onedir diff --git a/changelog/62961.added.md b/changelog/62961.added.md new file mode 100644 index 00000000000..7f635dbb734 --- /dev/null +++ b/changelog/62961.added.md @@ -0,0 +1 @@ +Add salt package type information. Either system or onedir. diff --git a/doc/ref/grains/all/index.rst b/doc/ref/grains/all/index.rst index 80ee46f7189..15cad059532 100644 --- a/doc/ref/grains/all/index.rst +++ b/doc/ref/grains/all/index.rst @@ -31,6 +31,7 @@ grains modules nvme nxos opts + package panos pending_reboot philips_hue diff --git a/doc/ref/grains/all/salt.grains.package.rst b/doc/ref/grains/all/salt.grains.package.rst new file mode 100644 index 00000000000..7a56421ec6b --- /dev/null +++ b/doc/ref/grains/all/salt.grains.package.rst @@ -0,0 +1,5 @@ +salt.grains.package +=================== + +.. automodule:: salt.grains.package + :members: diff --git a/salt/grains/package.py b/salt/grains/package.py new file mode 100644 index 00000000000..be44fb5c3ce --- /dev/null +++ b/salt/grains/package.py @@ -0,0 +1,25 @@ +""" +Grains for detecting what type of package Salt is using + +.. versionadded:: 3007.0 +""" +import logging + +import salt.utils.package + +log = logging.getLogger(__name__) + + +__virtualname__ = "package" + + +def __virtual__(): + return __virtualname__ + + +def package(): + """ + Function to determine if the user is currently using + onedir package or system level package of Salt. + """ + return {"package": salt.utils.package.pkg_type()} diff --git a/salt/modules/pip.py b/salt/modules/pip.py index c4de0c29846..faa52ef030f 100644 --- a/salt/modules/pip.py +++ b/salt/modules/pip.py @@ -90,6 +90,7 @@ import salt.utils.data import salt.utils.files import salt.utils.json import salt.utils.locales +import salt.utils.package import salt.utils.platform import salt.utils.stringutils import salt.utils.url @@ -136,7 +137,7 @@ def _clear_context(bin_env=None): """ contextkey = "pip.version" if bin_env is not None: - contextkey = "{}.{}".format(contextkey, bin_env) + contextkey = f"{contextkey}.{bin_env}" __context__.pop(contextkey, None) @@ -144,7 +145,7 @@ def _check_bundled(): """ Gather run-time information to indicate if we are running from source or bundled. """ - if hasattr(sys, "RELENV"): + if salt.utils.package.type() == "onedir": return True return False @@ -189,7 +190,7 @@ def _get_pip_bin(bin_env): bin_path, ) raise CommandNotFoundError( - "Could not find a pip binary in virtualenv {}".format(bin_env) + f"Could not find a pip binary in virtualenv {bin_env}" ) # bin_env is the python or pip binary @@ -201,12 +202,10 @@ def _get_pip_bin(bin_env): # We have been passed a pip binary, use the pip binary. return [os.path.normpath(bin_env)] - raise CommandExecutionError( - "Could not find a pip binary within {}".format(bin_env) - ) + raise CommandExecutionError(f"Could not find a pip binary within {bin_env}") else: raise CommandNotFoundError( - "Access denied to {}, could not find a pip binary".format(bin_env) + f"Access denied to {bin_env}, could not find a pip binary" ) @@ -412,9 +411,7 @@ def _format_env_vars(env_vars): val = str(val) ret[key] = val else: - raise CommandExecutionError( - "env_vars {} is not a dictionary".format(env_vars) - ) + raise CommandExecutionError(f"env_vars {env_vars} is not a dictionary") return ret @@ -464,7 +461,7 @@ def install( cache_dir=None, no_binary=None, disable_version_check=False, - **kwargs + **kwargs, ): """ Install packages with pip @@ -757,9 +754,9 @@ def install( if log: if os.path.isdir(log): - raise OSError("'{}' is a directory. Use --log path_to_file".format(log)) + raise OSError(f"'{log}' is a directory. Use --log path_to_file") elif not os.access(log, os.W_OK): - raise OSError("'{}' is not writeable".format(log)) + raise OSError(f"'{log}' is not writeable") cmd.extend(["--log", log]) @@ -784,9 +781,7 @@ def install( raise ValueError("Timeout cannot be a float") int(timeout) except ValueError: - raise ValueError( - "'{}' is not a valid timeout, must be an integer".format(timeout) - ) + raise ValueError(f"'{timeout}' is not a valid timeout, must be an integer") cmd.extend(["--timeout", timeout]) if find_links: @@ -797,9 +792,7 @@ def install( if not ( salt.utils.url.validate(link, VALID_PROTOS) or os.path.exists(link) ): - raise CommandExecutionError( - "'{}' is not a valid URL or path".format(link) - ) + raise CommandExecutionError(f"'{link}' is not a valid URL or path") cmd.extend(["--find-links", link]) if no_index and (index_url or extra_index_url): @@ -809,14 +802,12 @@ def install( if index_url: if not salt.utils.url.validate(index_url, VALID_PROTOS): - raise CommandExecutionError("'{}' is not a valid URL".format(index_url)) + raise CommandExecutionError(f"'{index_url}' is not a valid URL") cmd.extend(["--index-url", index_url]) if extra_index_url: if not salt.utils.url.validate(extra_index_url, VALID_PROTOS): - raise CommandExecutionError( - "'{}' is not a valid URL".format(extra_index_url) - ) + raise CommandExecutionError(f"'{extra_index_url}' is not a valid URL") cmd.extend(["--extra-index-url", extra_index_url]) if no_index: @@ -836,7 +827,7 @@ def install( cmd.append("--use-mirrors") for mirror in mirrors: if not mirror.startswith("http://"): - raise CommandExecutionError("'{}' is not a valid URL".format(mirror)) + raise CommandExecutionError(f"'{mirror}' is not a valid URL") cmd.extend(["--mirrors", mirror]) if disable_version_check: @@ -994,7 +985,7 @@ def install( # Don't allow any recursion into keyword arg definitions # Don't allow multiple definitions of a keyword if isinstance(val, (dict, list)): - raise TypeError("Too many levels in: {}".format(key)) + raise TypeError(f"Too many levels in: {key}") # This is a a normal one-to-one keyword argument cmd.extend([key, val]) # It is a positional argument, append it to the list @@ -1107,7 +1098,7 @@ def uninstall( # TODO make this check if writeable os.path.exists(log) except OSError: - raise OSError("'{}' is not writeable".format(log)) + raise OSError(f"'{log}' is not writeable") cmd.extend(["--log", log]) @@ -1132,9 +1123,7 @@ def uninstall( raise ValueError("Timeout cannot be a float") int(timeout) except ValueError: - raise ValueError( - "'{}' is not a valid timeout, must be an integer".format(timeout) - ) + raise ValueError(f"'{timeout}' is not a valid timeout, must be an integer") cmd.extend(["--timeout", timeout]) if pkgs: @@ -1336,7 +1325,7 @@ def list_(prefix=None, bin_env=None, user=None, cwd=None, env_vars=None, **kwarg user=user, cwd=cwd, env_vars=env_vars, - **kwargs + **kwargs, ) cmd = _get_pip_bin(bin_env) @@ -1394,7 +1383,7 @@ def version(bin_env=None, cwd=None, user=None): cwd = _pip_bin_env(cwd, bin_env) contextkey = "pip.version" if bin_env is not None: - contextkey = "{}.{}".format(contextkey, bin_env) + contextkey = f"{contextkey}.{bin_env}" if contextkey in __context__: return __context__[contextkey] @@ -1650,14 +1639,12 @@ def list_all_versions( if index_url: if not salt.utils.url.validate(index_url, VALID_PROTOS): - raise CommandExecutionError("'{}' is not a valid URL".format(index_url)) + raise CommandExecutionError(f"'{index_url}' is not a valid URL") cmd.extend(["--index-url", index_url]) if extra_index_url: if not salt.utils.url.validate(extra_index_url, VALID_PROTOS): - raise CommandExecutionError( - "'{}' is not a valid URL".format(extra_index_url) - ) + raise CommandExecutionError(f"'{extra_index_url}' is not a valid URL") cmd.extend(["--extra-index-url", extra_index_url]) # Is the `pip index` command available @@ -1669,7 +1656,7 @@ def list_all_versions( if salt.utils.versions.compare(ver1=pip_version, oper=">=", ver2="20.3"): cmd.append("--use-deprecated=legacy-resolver") regex = re.compile(r"\s*Could not find a version.* \(from versions: (.*)\)") - cmd.extend(["install", "{}==versions".format(pkg)]) + cmd.extend(["install", f"{pkg}==versions"]) cmd_kwargs = dict( cwd=cwd, runas=user, output_loglevel="quiet", redirect_stderr=True diff --git a/salt/utils/package.py b/salt/utils/package.py new file mode 100644 index 00000000000..4e76c88e46a --- /dev/null +++ b/salt/utils/package.py @@ -0,0 +1,11 @@ +import sys + + +def pkg_type(): + """ + Gather run-time information to indicate if we are running from onedir or . + """ + if hasattr(sys, "RELENV"): + return "onedir" + else: + return "system" diff --git a/salt/version.py b/salt/version.py index e174d8bfb1f..e37bf4eeea6 100644 --- a/salt/version.py +++ b/salt/version.py @@ -685,6 +685,16 @@ def salt_information(): yield "Salt", __version__ +def package_information(): + + """ + Report package type + """ + import salt.utils.package + + yield "Package Type", salt.utils.package.pkg_type() + + def dependency_information(include_salt_cloud=False): """ Report versions of library dependencies. @@ -867,12 +877,14 @@ def versions_information(include_salt_cloud=False, include_extensions=True): salt_info = list(salt_information()) lib_info = list(dependency_information(include_salt_cloud)) sys_info = list(system_information()) + package_info = list(package_information()) info = { "Salt Version": dict(salt_info), "Python Version": dict(py_info), "Dependency Versions": dict(lib_info), "System Versions": dict(sys_info), + "Salt Package Information": dict(package_info), } if include_extensions: extensions_info = extensions_information() @@ -905,6 +917,7 @@ def versions_report(include_salt_cloud=False, include_extensions=True): "Python Version", "Dependency Versions", "Salt Extensions", + "Salt Package Information", "System Versions", ): if ver_type == "Salt Extensions" and ver_type not in ver_info: diff --git a/tests/pytests/functional/cli/test_salt.py b/tests/pytests/functional/cli/test_salt.py index acd820a17c0..03bae43a46c 100644 --- a/tests/pytests/functional/cli/test_salt.py +++ b/tests/pytests/functional/cli/test_salt.py @@ -1,6 +1,7 @@ import logging import os import shutil +import sys import pytest @@ -56,7 +57,7 @@ def test_versions_report(salt_cli): ret_lines = [line.strip() for line in ret_lines] for header in expected: - assert "{}:".format(header) in ret_lines + assert f"{header}:" in ret_lines ret_dict = {} expected_keys = set() @@ -80,6 +81,11 @@ def test_versions_report(salt_cli): assert key in expected_keys expected_keys.remove(key) assert not expected_keys + if hasattr(sys, "RELENV"): + assert "onedir" in ret_dict["Salt Package Information"]["Package Type"] + else: + assert "system" in ret_dict["Salt Package Information"]["Package Type"] + if os.environ.get("ONEDIR_TESTRUN", "0") == "0": # Stop any more testing return diff --git a/tests/pytests/functional/modules/test_grains.py b/tests/pytests/functional/modules/test_grains.py new file mode 100644 index 00000000000..5992b866eac --- /dev/null +++ b/tests/pytests/functional/modules/test_grains.py @@ -0,0 +1,24 @@ +import sys + +import pytest + + +@pytest.fixture(scope="module") +def grains(modules): + return modules.grains + + +def test_grains_items(grains): + """ + Test running grains.items and assert + certain information is included in + the return + """ + ret = grains.items() + if hasattr(sys, "RELENV"): + assert ret["package"] == "onedir" + else: + assert ret["package"] == "system" + + for key in ["num_cpus", "cpu_model", "os_family"]: + assert key in ret.keys() diff --git a/tests/pytests/unit/grains/test_package.py b/tests/pytests/unit/grains/test_package.py new file mode 100644 index 00000000000..62973b799b9 --- /dev/null +++ b/tests/pytests/unit/grains/test_package.py @@ -0,0 +1,14 @@ +import sys + +import salt.grains.package + + +def test_grain_package_type(tmp_path): + """ + Test grains.package_type for both package types + """ + ret = salt.grains.package.package()["package"] + if hasattr(sys, "RELENV"): + assert ret == "onedir" + else: + assert ret == "system" diff --git a/tests/pytests/unit/utils/test_package.py b/tests/pytests/unit/utils/test_package.py new file mode 100644 index 00000000000..7edca9806c2 --- /dev/null +++ b/tests/pytests/unit/utils/test_package.py @@ -0,0 +1,11 @@ +import sys + +import salt.utils.package + + +def test_pkg_type(): + ret = salt.utils.package.pkg_type() + if hasattr(sys, "RELENV"): + assert ret == "onedir" + else: + assert ret == "system"