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

[master] Add Salt package type to versions report and grain #64446

Merged
merged 10 commits into from
Aug 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/62589.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added new grain to detect the Salt package type: onedir, pip or system
1 change: 1 addition & 0 deletions changelog/62961.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add salt package type information. Either onedir, pip or system.
1 change: 1 addition & 0 deletions doc/ref/grains/all/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ grains modules
nvme
nxos
opts
package
panos
pending_reboot
philips_hue
Expand Down
5 changes: 5 additions & 0 deletions doc/ref/grains/all/salt.grains.package.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
salt.grains.package
===================

.. automodule:: salt.grains.package
:members:
7 changes: 7 additions & 0 deletions doc/topics/packaging/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ Docker Containers
The Salt Project uses docker containers to build our deb and rpm packages. If you are building your own packages you can use
the same containers we build with in the Github piplines. These containers are documented `here <https://github.com/saltstack/salt-ci-containers/tree/main/custom/packaging>`_.

Package Grain
=============
In the 3007.0 release a new package grain was added. This detects how Salt was installed using the `_pkg.txt`
in the root of the Salt repo. By default this is set to ``pip``, but it is set to ``onedir`` when ``tools pkg build salt-onedir``
is run in our pipelines when building our onedir packages. If you are building your own custom packages, please ensure you set
``_pkg.txt`` contents to be the type of package you are creating. The options are ``pip``, ``onedir`` or ``system``.


How to build onedir only
========================
Expand Down
5 changes: 5 additions & 0 deletions doc/topics/releases/templates/3007.0.md.template
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ Support for python 3.7 has been dropped since it reached end-of-line in 27 Jun 2

Starting from Salt version 3007.0, the Azure functionality previously available in the Salt code base is fully removed. To continue using Salt's features for interacting with Azure resources, users are required to utilize the Azure Salt extension. For more information, refer to the [Azure Salt Extension GitHub repository](https://github.com/salt-extensions/saltext-azurerm).

## New Package Grain
A new ``package`` grain was added in 3007.0 This detects how Salt was installed using the ``_pkg.txt`` in the root of
the directory. If you are building packages of Salt you need to ensure this file is set to the correct package type
that you are building. The options are ``pip``, ``onedir``, or ``system``. By default this file is already set to ``pip``.

<!--
Do not edit the changelog below.
This is auto generated
Expand Down
9 changes: 9 additions & 0 deletions pkg/tests/integration/test_salt_grains.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,12 @@ def test_grains_setval_key_val(salt_cli, salt_minion):
ret = salt_cli.run("grains.setval", "key", "val", minion_tgt=salt_minion.id)
assert ret.data, ret
assert "key" in ret.data


def test_grains_package_onedir(salt_cli, salt_minion):
"""
Test that the package grain returns onedir
"""
ret = salt_cli.run("grains.get", "package", minion_tgt=salt_minion.id)
assert ret.data == "onedir"
s0undt3ch marked this conversation as resolved.
Show resolved Hide resolved
assert ret.data, ret
1 change: 1 addition & 0 deletions salt/_pkg.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pip
25 changes: 25 additions & 0 deletions salt/grains/package.py
Original file line number Diff line number Diff line change
@@ -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, pip or system level package of Salt.
"""
return {"package": salt.utils.package.pkg_type()}
59 changes: 23 additions & 36 deletions salt/modules/pip.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
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
Expand Down Expand Up @@ -136,15 +137,15 @@ 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)


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.bundled():
return True
return False

Expand Down Expand Up @@ -189,7 +190,7 @@ def _search_paths(*basedirs):
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
Expand All @@ -201,12 +202,10 @@ def _search_paths(*basedirs):
# 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"
)


Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -464,7 +461,7 @@ def install(
cache_dir=None,
no_binary=None,
disable_version_check=False,
**kwargs
**kwargs,
):
"""
Install packages with pip
Expand Down Expand Up @@ -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])

Expand All @@ -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:
Expand All @@ -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):
Expand All @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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])

Expand All @@ -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:
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
27 changes: 27 additions & 0 deletions salt/utils/package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import pathlib
import sys

import salt.utils.files


def bundled():
"""
Gather run-time information to indicate if we are running from relenv onedir
"""
if hasattr(sys, "RELENV"):
return True
else:
return False


def pkg_type():
"""
Utility to find out how Salt was installed.
"""
pkg_file = pathlib.Path(__file__).parent.parent / "_pkg.txt"
if pkg_file.is_file():
with salt.utils.files.fopen(pkg_file) as _fp:
content = _fp.read()
if content:
return content.strip()
return None
13 changes: 13 additions & 0 deletions salt/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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:
Expand Down
5 changes: 4 additions & 1 deletion tests/pytests/functional/cli/test_salt.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,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()
Expand All @@ -80,10 +80,13 @@ def test_versions_report(salt_cli):
assert key in expected_keys
expected_keys.remove(key)
assert not expected_keys

if os.environ.get("ONEDIR_TESTRUN", "0") == "0":
assert "pip" in ret_dict["Salt Package Information"]["Package Type"]
# Stop any more testing
return

assert "onedir" in ret_dict["Salt Package Information"]["Package Type"]
assert "relenv" in ret_dict["Dependency Versions"]
assert "Salt Extensions" in ret_dict
assert "salt-analytics-framework" in ret_dict["Salt Extensions"]
2 changes: 1 addition & 1 deletion tests/pytests/functional/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def master_opts(

@pytest.fixture(scope="module")
def loaders(minion_opts):
return Loaders(minion_opts, loaded_base_name="{}.loaded".format(__name__))
return Loaders(minion_opts, loaded_base_name=f"{__name__}.loaded")


@pytest.fixture(autouse=True)
Expand Down