mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Migrate package tests to the main test suite
This commit is contained in:
parent
98dce76198
commit
e2f5e5dbbf
33 changed files with 2805 additions and 0 deletions
0
tests/pytests/pkg/__init__.py
Normal file
0
tests/pytests/pkg/__init__.py
Normal file
573
tests/pytests/pkg/conftest.py
Normal file
573
tests/pytests/pkg/conftest.py
Normal file
|
@ -0,0 +1,573 @@
|
|||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
from pytestskipmarkers.utils import platform
|
||||
from saltfactories.utils import random_string
|
||||
from saltfactories.utils.tempfiles import SaltPillarTree, SaltStateTree
|
||||
|
||||
import salt.config
|
||||
from tests.pytests.pkg.support.helpers import (
|
||||
CODE_DIR,
|
||||
TESTS_DIR,
|
||||
ApiRequest,
|
||||
SaltMaster,
|
||||
SaltMasterWindows,
|
||||
SaltPkgInstall,
|
||||
TestUser,
|
||||
)
|
||||
from tests.support.sminion import create_sminion
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Variable defining a FIPS test run or not
|
||||
FIPS_TESTRUN = os.environ.get("FIPS_TESTRUN", "0") == "1"
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def version(install_salt):
|
||||
"""
|
||||
get version number from artifact
|
||||
"""
|
||||
return install_salt.version
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def sminion():
|
||||
return create_sminion()
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def grains(sminion):
|
||||
return sminion.opts["grains"].copy()
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def _system_up_to_date(
|
||||
grains,
|
||||
shell,
|
||||
):
|
||||
if grains["os_family"] == "Debian":
|
||||
ret = shell.run("apt", "update")
|
||||
assert ret.returncode == 0
|
||||
env = os.environ.copy()
|
||||
env["DEBIAN_FRONTEND"] = "noninteractive"
|
||||
ret = shell.run(
|
||||
"apt",
|
||||
"upgrade",
|
||||
"-y",
|
||||
"-o",
|
||||
"DPkg::Options::=--force-confdef",
|
||||
"-o",
|
||||
"DPkg::Options::=--force-confold",
|
||||
env=env,
|
||||
)
|
||||
assert ret.returncode == 0
|
||||
elif grains["os_family"] == "Redhat":
|
||||
ret = shell.run("yum", "update", "-y")
|
||||
assert ret.returncode == 0
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
"""
|
||||
register argparse-style options and ini-style config values.
|
||||
"""
|
||||
test_selection_group = parser.getgroup("Tests Runtime Selection")
|
||||
test_selection_group.addoption(
|
||||
"--pkg-system-service",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run the daemons as system services",
|
||||
)
|
||||
test_selection_group.addoption(
|
||||
"--upgrade",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Install previous version and then upgrade then run tests",
|
||||
)
|
||||
test_selection_group.addoption(
|
||||
"--downgrade",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Install current version and then downgrade to the previous version and run tests",
|
||||
)
|
||||
test_selection_group.addoption(
|
||||
"--no-install",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Do not install salt and use a previous install Salt package",
|
||||
)
|
||||
test_selection_group.addoption(
|
||||
"--no-uninstall",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Do not uninstall salt packages after test run is complete",
|
||||
)
|
||||
test_selection_group.addoption(
|
||||
"--classic",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Test an upgrade from the classic packages.",
|
||||
)
|
||||
test_selection_group.addoption(
|
||||
"--prev-version",
|
||||
action="store",
|
||||
help="Test an upgrade from the version specified.",
|
||||
)
|
||||
test_selection_group.addoption(
|
||||
"--use-prev-version",
|
||||
action="store_true",
|
||||
help="Tells the test suite to validate the version using the previous version (for downgrades)",
|
||||
)
|
||||
test_selection_group.addoption(
|
||||
"--download-pkgs",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Test package download tests",
|
||||
)
|
||||
|
||||
|
||||
@pytest.hookimpl(tryfirst=True)
|
||||
def pytest_runtest_setup(item):
|
||||
"""
|
||||
Fixtures injection based on markers or test skips based on CLI arguments
|
||||
"""
|
||||
if (
|
||||
str(item.fspath).startswith(str(pathlib.Path(__file__).parent / "download"))
|
||||
and item.config.getoption("--download-pkgs") is False
|
||||
):
|
||||
raise pytest.skip.Exception(
|
||||
"The package download tests are disabled. Pass '--download-pkgs' to pytest "
|
||||
"to enable them.",
|
||||
_use_item_location=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def salt_factories_root_dir(request, tmp_path_factory):
|
||||
root_dir = SaltPkgInstall.salt_factories_root_dir(
|
||||
request.config.getoption("--pkg-system-service")
|
||||
)
|
||||
if root_dir is not None:
|
||||
yield root_dir
|
||||
else:
|
||||
if platform.is_darwin():
|
||||
root_dir = pathlib.Path("/tmp/salt-tests-tmpdir")
|
||||
root_dir.mkdir(mode=0o777, parents=True, exist_ok=True)
|
||||
else:
|
||||
root_dir = tmp_path_factory.mktemp("salt-tests")
|
||||
try:
|
||||
yield root_dir
|
||||
finally:
|
||||
shutil.rmtree(str(root_dir), ignore_errors=True)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def salt_factories_config(salt_factories_root_dir):
|
||||
return {
|
||||
"code_dir": CODE_DIR,
|
||||
"root_dir": salt_factories_root_dir,
|
||||
"system_service": True,
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def install_salt(request, salt_factories_root_dir):
|
||||
with SaltPkgInstall(
|
||||
conf_dir=salt_factories_root_dir / "etc" / "salt",
|
||||
pkg_system_service=request.config.getoption("--pkg-system-service"),
|
||||
upgrade=request.config.getoption("--upgrade"),
|
||||
downgrade=request.config.getoption("--downgrade"),
|
||||
no_uninstall=request.config.getoption("--no-uninstall"),
|
||||
no_install=request.config.getoption("--no-install"),
|
||||
classic=request.config.getoption("--classic"),
|
||||
prev_version=request.config.getoption("--prev-version"),
|
||||
use_prev_version=request.config.getoption("--use-prev-version"),
|
||||
) as fixture:
|
||||
yield fixture
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def salt_factories(salt_factories, salt_factories_root_dir):
|
||||
salt_factories.root_dir = salt_factories_root_dir
|
||||
return salt_factories
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def state_tree():
|
||||
if platform.is_windows():
|
||||
file_root = pathlib.Path("C:/salt/srv/salt")
|
||||
elif platform.is_darwin():
|
||||
file_root = pathlib.Path("/opt/srv/salt")
|
||||
else:
|
||||
file_root = pathlib.Path("/srv/salt")
|
||||
envs = {
|
||||
"base": [
|
||||
str(file_root),
|
||||
str(TESTS_DIR / "pytests" / "pkg" / "files"),
|
||||
],
|
||||
}
|
||||
tree = SaltStateTree(envs=envs)
|
||||
test_sls_contents = """
|
||||
test_foo:
|
||||
test.succeed_with_changes:
|
||||
- name: foo
|
||||
"""
|
||||
states_sls_contents = """
|
||||
update:
|
||||
pkg.installed:
|
||||
- name: bash
|
||||
salt_dude:
|
||||
user.present:
|
||||
- name: dude
|
||||
- fullname: Salt Dude
|
||||
"""
|
||||
win_states_sls_contents = """
|
||||
create_empty_file:
|
||||
file.managed:
|
||||
- name: C://salt/test/txt
|
||||
salt_dude:
|
||||
user.present:
|
||||
- name: dude
|
||||
- fullname: Salt Dude
|
||||
"""
|
||||
with tree.base.temp_file("test.sls", test_sls_contents), tree.base.temp_file(
|
||||
"states.sls", states_sls_contents
|
||||
), tree.base.temp_file("win_states.sls", win_states_sls_contents):
|
||||
yield tree
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def pillar_tree():
|
||||
"""
|
||||
Add pillar files
|
||||
"""
|
||||
if platform.is_windows():
|
||||
pillar_root = pathlib.Path("C:/salt/srv/pillar")
|
||||
elif platform.is_darwin():
|
||||
pillar_root = pathlib.Path("/opt/srv/pillar")
|
||||
else:
|
||||
pillar_root = pathlib.Path("/srv/pillar")
|
||||
pillar_root.mkdir(mode=0o777, parents=True, exist_ok=True)
|
||||
tree = SaltPillarTree(
|
||||
envs={
|
||||
"base": [
|
||||
str(pillar_root),
|
||||
]
|
||||
},
|
||||
)
|
||||
top_file_contents = """
|
||||
base:
|
||||
'*':
|
||||
- test
|
||||
"""
|
||||
test_file_contents = """
|
||||
info: test
|
||||
"""
|
||||
with tree.base.temp_file("top.sls", top_file_contents), tree.base.temp_file(
|
||||
"test.sls", test_file_contents
|
||||
):
|
||||
yield tree
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def sls(state_tree):
|
||||
"""
|
||||
Add an sls file
|
||||
"""
|
||||
test_sls_contents = """
|
||||
test_foo:
|
||||
test.succeed_with_changes:
|
||||
- name: foo
|
||||
"""
|
||||
states_sls_contents = """
|
||||
update:
|
||||
pkg.installed:
|
||||
- name: bash
|
||||
salt_dude:
|
||||
user.present:
|
||||
- name: dude
|
||||
- fullname: Salt Dude
|
||||
"""
|
||||
win_states_sls_contents = """
|
||||
create_empty_file:
|
||||
file.managed:
|
||||
- name: C://salt/test/txt
|
||||
salt_dude:
|
||||
user.present:
|
||||
- name: dude
|
||||
- fullname: Salt Dude
|
||||
"""
|
||||
with state_tree.base.temp_file(
|
||||
"tests.sls", test_sls_contents
|
||||
), state_tree.base.temp_file(
|
||||
"states.sls", states_sls_contents
|
||||
), state_tree.base.temp_file(
|
||||
"win_states.sls", win_states_sls_contents
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def salt_master(salt_factories, install_salt, state_tree, pillar_tree):
|
||||
"""
|
||||
Start up a master
|
||||
"""
|
||||
start_timeout = None
|
||||
# Since the daemons are "packaged" with tiamat, the salt plugins provided
|
||||
# by salt-factories won't be discovered. Provide the required `*_dirs` on
|
||||
# the configuration so that they can still be used.
|
||||
config_defaults = {
|
||||
"engines_dirs": [
|
||||
str(salt_factories.get_salt_engines_path()),
|
||||
],
|
||||
"log_handlers_dirs": [
|
||||
str(salt_factories.get_salt_log_handlers_path()),
|
||||
],
|
||||
}
|
||||
if platform.is_darwin():
|
||||
config_defaults["enable_fqdns_grains"] = False
|
||||
config_overrides = {
|
||||
"timeout": 30,
|
||||
"file_roots": state_tree.as_dict(),
|
||||
"pillar_roots": pillar_tree.as_dict(),
|
||||
"rest_cherrypy": {"port": 8000, "disable_ssl": True},
|
||||
"netapi_enable_clients": ["local"],
|
||||
"external_auth": {"auto": {"saltdev": [".*"]}},
|
||||
"fips_mode": FIPS_TESTRUN,
|
||||
"open_mode": True,
|
||||
}
|
||||
test_user = False
|
||||
master_config = install_salt.config_path / "master"
|
||||
if master_config.exists():
|
||||
with salt.utils.files.fopen(master_config) as fp:
|
||||
data = yaml.safe_load(fp)
|
||||
if data and "user" in data:
|
||||
test_user = True
|
||||
# We are testing a different user, so we need to test the system
|
||||
# configs, or else permissions will not be correct.
|
||||
config_overrides["user"] = data["user"]
|
||||
config_overrides["log_file"] = salt.config.DEFAULT_MASTER_OPTS.get(
|
||||
"log_file"
|
||||
)
|
||||
config_overrides["root_dir"] = salt.config.DEFAULT_MASTER_OPTS.get(
|
||||
"root_dir"
|
||||
)
|
||||
config_overrides["key_logfile"] = salt.config.DEFAULT_MASTER_OPTS.get(
|
||||
"key_logfile"
|
||||
)
|
||||
config_overrides["pki_dir"] = salt.config.DEFAULT_MASTER_OPTS.get(
|
||||
"pki_dir"
|
||||
)
|
||||
config_overrides["api_logfile"] = salt.config.DEFAULT_API_OPTS.get(
|
||||
"api_logfile"
|
||||
)
|
||||
config_overrides["api_pidfile"] = salt.config.DEFAULT_API_OPTS.get(
|
||||
"api_pidfile"
|
||||
)
|
||||
# verify files were set with correct owner/group
|
||||
verify_files = [
|
||||
pathlib.Path("/etc", "salt", "pki", "master"),
|
||||
pathlib.Path("/etc", "salt", "master.d"),
|
||||
pathlib.Path("/var", "cache", "salt", "master"),
|
||||
]
|
||||
for _file in verify_files:
|
||||
assert _file.owner() == "salt"
|
||||
assert _file.group() == "salt"
|
||||
|
||||
master_script = False
|
||||
if platform.is_windows():
|
||||
if install_salt.classic:
|
||||
master_script = True
|
||||
if install_salt.relenv:
|
||||
master_script = True
|
||||
elif not install_salt.upgrade:
|
||||
master_script = True
|
||||
if (
|
||||
not install_salt.relenv
|
||||
and install_salt.use_prev_version
|
||||
and not install_salt.classic
|
||||
):
|
||||
master_script = False
|
||||
|
||||
if master_script:
|
||||
salt_factories.system_service = False
|
||||
salt_factories.generate_scripts = True
|
||||
scripts_dir = salt_factories.root_dir / "Scripts"
|
||||
scripts_dir.mkdir(exist_ok=True)
|
||||
salt_factories.scripts_dir = scripts_dir
|
||||
python_executable = install_salt.bin_dir / "Scripts" / "python.exe"
|
||||
if install_salt.classic:
|
||||
python_executable = install_salt.bin_dir / "python.exe"
|
||||
if install_salt.relenv:
|
||||
python_executable = install_salt.install_dir / "Scripts" / "python.exe"
|
||||
salt_factories.python_executable = python_executable
|
||||
factory = salt_factories.salt_master_daemon(
|
||||
random_string("master-"),
|
||||
defaults=config_defaults,
|
||||
overrides=config_overrides,
|
||||
factory_class=SaltMasterWindows,
|
||||
salt_pkg_install=install_salt,
|
||||
)
|
||||
salt_factories.system_service = True
|
||||
else:
|
||||
|
||||
if install_salt.classic and platform.is_darwin():
|
||||
os.environ["PATH"] += ":/opt/salt/bin"
|
||||
|
||||
factory = salt_factories.salt_master_daemon(
|
||||
random_string("master-"),
|
||||
defaults=config_defaults,
|
||||
overrides=config_overrides,
|
||||
factory_class=SaltMaster,
|
||||
salt_pkg_install=install_salt,
|
||||
)
|
||||
factory.after_terminate(pytest.helpers.remove_stale_master_key, factory)
|
||||
if test_user:
|
||||
# Salt factories calls salt.utils.verify.verify_env
|
||||
# which sets root perms on /etc/salt/pki/master since we are running
|
||||
# the test suite as root, but we want to run Salt master as salt
|
||||
# We ensure those permissions where set by the package earlier
|
||||
subprocess.run(
|
||||
[
|
||||
"chown",
|
||||
"-R",
|
||||
"salt:salt",
|
||||
str(pathlib.Path("/etc", "salt", "pki", "master")),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
# The engines_dirs is created in .nox path. We need to set correct perms
|
||||
# for the user running the Salt Master
|
||||
subprocess.run(
|
||||
["chown", "-R", "salt:salt", str(CODE_DIR.parent / ".nox")], check=False
|
||||
)
|
||||
file_roots = pathlib.Path("/srv/", "salt")
|
||||
pillar_roots = pathlib.Path("/srv/", "pillar")
|
||||
for _dir in [file_roots, pillar_roots]:
|
||||
subprocess.run(["chown", "-R", "salt:salt", str(_dir)], check=False)
|
||||
|
||||
with factory.started(start_timeout=start_timeout):
|
||||
yield factory
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def salt_minion(salt_factories, salt_master, install_salt):
|
||||
"""
|
||||
Start up a minion
|
||||
"""
|
||||
start_timeout = None
|
||||
minion_id = random_string("minion-")
|
||||
# Since the daemons are "packaged" with tiamat, the salt plugins provided
|
||||
# by salt-factories won't be discovered. Provide the required `*_dirs` on
|
||||
# the configuration so that they can still be used.
|
||||
config_defaults = {
|
||||
"engines_dirs": salt_master.config["engines_dirs"].copy(),
|
||||
"log_handlers_dirs": salt_master.config["log_handlers_dirs"].copy(),
|
||||
}
|
||||
if platform.is_darwin():
|
||||
config_defaults["enable_fqdns_grains"] = False
|
||||
config_overrides = {
|
||||
"id": minion_id,
|
||||
"file_roots": salt_master.config["file_roots"].copy(),
|
||||
"pillar_roots": salt_master.config["pillar_roots"].copy(),
|
||||
"fips_mode": FIPS_TESTRUN,
|
||||
"open_mode": True,
|
||||
}
|
||||
if platform.is_windows():
|
||||
config_overrides[
|
||||
"winrepo_dir"
|
||||
] = rf"{salt_factories.root_dir}\srv\salt\win\repo"
|
||||
config_overrides[
|
||||
"winrepo_dir_ng"
|
||||
] = rf"{salt_factories.root_dir}\srv\salt\win\repo_ng"
|
||||
config_overrides["winrepo_source_dir"] = r"salt://win/repo_ng"
|
||||
|
||||
if install_salt.classic and platform.is_windows():
|
||||
salt_factories.python_executable = None
|
||||
|
||||
if install_salt.classic and platform.is_darwin():
|
||||
os.environ["PATH"] += ":/opt/salt/bin"
|
||||
|
||||
factory = salt_master.salt_minion_daemon(
|
||||
minion_id,
|
||||
overrides=config_overrides,
|
||||
defaults=config_defaults,
|
||||
)
|
||||
|
||||
# Salt factories calls salt.utils.verify.verify_env
|
||||
# which sets root perms on /srv/salt and /srv/pillar since we are running
|
||||
# the test suite as root, but we want to run Salt master as salt
|
||||
if not platform.is_windows() and not platform.is_darwin():
|
||||
file_roots = pathlib.Path("/srv/", "salt")
|
||||
pillar_roots = pathlib.Path("/srv/", "pillar")
|
||||
for _dir in [file_roots, pillar_roots]:
|
||||
subprocess.run(["chown", "-R", "salt:salt", str(_dir)], check=True)
|
||||
|
||||
factory.after_terminate(
|
||||
pytest.helpers.remove_stale_minion_key_pkg, salt_master, factory.id
|
||||
)
|
||||
with factory.started(start_timeout=start_timeout):
|
||||
yield factory
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def salt_cli(salt_master):
|
||||
return salt_master.salt_cli()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def salt_key_cli(salt_master):
|
||||
return salt_master.salt_key_cli()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def salt_call_cli(salt_minion):
|
||||
return salt_minion.salt_call_cli()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def test_account(salt_call_cli):
|
||||
with TestUser(salt_call_cli=salt_call_cli) as account:
|
||||
yield account
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def extras_pypath():
|
||||
extras_dir = "extras-{}.{}".format(*sys.version_info)
|
||||
if platform.is_windows():
|
||||
return pathlib.Path(
|
||||
os.getenv("ProgramFiles"), "Salt Project", "Salt", extras_dir
|
||||
)
|
||||
elif platform.is_darwin():
|
||||
return pathlib.Path("/opt", "salt", extras_dir)
|
||||
else:
|
||||
return pathlib.Path("/opt", "saltstack", "salt", extras_dir)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def extras_pypath_bin(extras_pypath):
|
||||
return extras_pypath / "bin"
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def salt_api(salt_master, install_salt, extras_pypath):
|
||||
"""
|
||||
start up and configure salt_api
|
||||
"""
|
||||
shutil.rmtree(str(extras_pypath), ignore_errors=True)
|
||||
start_timeout = None
|
||||
factory = salt_master.salt_api_daemon()
|
||||
with factory.started(start_timeout=start_timeout):
|
||||
yield factory
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def api_request(test_account, salt_api):
|
||||
with ApiRequest(salt_api=salt_api, test_account=test_account) as session:
|
||||
yield session
|
0
tests/pytests/pkg/downgrade/__init__.py
Normal file
0
tests/pytests/pkg/downgrade/__init__.py
Normal file
59
tests/pytests/pkg/downgrade/test_salt_downgrade.py
Normal file
59
tests/pytests/pkg/downgrade/test_salt_downgrade.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
import packaging.version
|
||||
import pytest
|
||||
from pytestskipmarkers.utils import platform
|
||||
|
||||
|
||||
def test_salt_downgrade(salt_call_cli, install_salt):
|
||||
"""
|
||||
Test an upgrade of Salt.
|
||||
"""
|
||||
if not install_salt.downgrade:
|
||||
pytest.skip("Not testing a downgrade, do not run")
|
||||
|
||||
is_downgrade_to_relenv = packaging.version.parse(
|
||||
install_salt.prev_version
|
||||
) >= packaging.version.parse("3006.0")
|
||||
|
||||
if is_downgrade_to_relenv:
|
||||
original_py_version = install_salt.package_python_version()
|
||||
|
||||
# Verify current install version is setup correctly and works
|
||||
ret = salt_call_cli.run("test.version")
|
||||
assert ret.returncode == 0
|
||||
assert packaging.version.parse(ret.data) == packaging.version.parse(
|
||||
install_salt.artifact_version
|
||||
)
|
||||
|
||||
# Test pip install before a downgrade
|
||||
dep = "PyGithub==1.56.0"
|
||||
install = salt_call_cli.run("--local", "pip.install", dep)
|
||||
assert install.returncode == 0
|
||||
|
||||
# Verify we can use the module dependent on the installed package
|
||||
repo = "https://github.com/saltstack/salt.git"
|
||||
use_lib = salt_call_cli.run("--local", "github.get_repo_info", repo)
|
||||
assert "Authentication information could" in use_lib.stderr
|
||||
|
||||
# Downgrade Salt to the previous version and test
|
||||
install_salt.install(downgrade=True)
|
||||
bin_file = "salt"
|
||||
if platform.is_windows():
|
||||
if not is_downgrade_to_relenv:
|
||||
bin_file = install_salt.install_dir / "salt-call.bat"
|
||||
else:
|
||||
bin_file = install_salt.install_dir / "salt-call.exe"
|
||||
elif platform.is_darwin() and install_salt.classic:
|
||||
bin_file = install_salt.bin_dir / "salt-call"
|
||||
|
||||
ret = install_salt.proc.run(bin_file, "--version")
|
||||
assert ret.returncode == 0
|
||||
assert packaging.version.parse(
|
||||
ret.stdout.strip().split()[1]
|
||||
) < packaging.version.parse(install_salt.artifact_version)
|
||||
|
||||
if is_downgrade_to_relenv:
|
||||
new_py_version = install_salt.package_python_version()
|
||||
if new_py_version == original_py_version:
|
||||
# test pip install after a downgrade
|
||||
use_lib = salt_call_cli.run("--local", "github.get_repo_info", repo)
|
||||
assert "Authentication information could" in use_lib.stderr
|
0
tests/pytests/pkg/download/__init__.py
Normal file
0
tests/pytests/pkg/download/__init__.py
Normal file
559
tests/pytests/pkg/download/test_pkg_download.py
Normal file
559
tests/pytests/pkg/download/test_pkg_download.py
Normal file
|
@ -0,0 +1,559 @@
|
|||
"""
|
||||
Test Salt Pkg Downloads
|
||||
"""
|
||||
import contextlib
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import shutil
|
||||
|
||||
import packaging
|
||||
import pytest
|
||||
from pytestskipmarkers.utils import platform
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_salt_test_commands():
|
||||
salt_release = get_salt_release()
|
||||
if platform.is_windows():
|
||||
if packaging.version.parse(salt_release) > packaging.version.parse("3005"):
|
||||
salt_test_commands = [
|
||||
["salt-call.exe", "--local", "test.versions"],
|
||||
["salt-call.exe", "--local", "grains.items"],
|
||||
["salt-minion.exe", "--version"],
|
||||
]
|
||||
else:
|
||||
salt_test_commands = [
|
||||
["salt-call.bat", "--local", "test.versions"],
|
||||
["salt-call.bat", "--local", "grains.items"],
|
||||
["salt.bat", "--version"],
|
||||
["salt-master.bat", "--version"],
|
||||
["salt-minion.bat", "--version"],
|
||||
["salt-ssh.bat", "--version"],
|
||||
["salt-syndic.bat", "--version"],
|
||||
["salt-api.bat", "--version"],
|
||||
["salt-cloud.bat", "--version"],
|
||||
]
|
||||
else:
|
||||
salt_test_commands = [
|
||||
["salt-call", "--local", "test.versions"],
|
||||
["salt-call", "--local", "grains.items"],
|
||||
["salt", "--version"],
|
||||
["salt-master", "--version"],
|
||||
["salt-minion", "--version"],
|
||||
["salt-ssh", "--version"],
|
||||
["salt-syndic", "--version"],
|
||||
["salt-api", "--version"],
|
||||
["salt-cloud", "--version"],
|
||||
]
|
||||
return salt_test_commands
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def root_url(salt_release):
|
||||
if os.environ.get("SALT_REPO_TYPE", "release") == "staging":
|
||||
repo_domain = os.environ.get(
|
||||
"SALT_REPO_DOMAIN_STAGING", "staging.repo.saltproject.io"
|
||||
)
|
||||
else:
|
||||
repo_domain = os.environ.get("SALT_REPO_DOMAIN_RELEASE", "repo.saltproject.io")
|
||||
if "rc" in salt_release:
|
||||
salt_path = "salt_rc/salt"
|
||||
else:
|
||||
salt_path = "salt"
|
||||
salt_repo_user = os.environ.get("SALT_REPO_USER")
|
||||
if salt_repo_user:
|
||||
log.info(
|
||||
"SALT_REPO_USER: %s",
|
||||
salt_repo_user[0] + "*" * (len(salt_repo_user) - 2) + salt_repo_user[-1],
|
||||
)
|
||||
salt_repo_pass = os.environ.get("SALT_REPO_PASS")
|
||||
if salt_repo_pass:
|
||||
log.info(
|
||||
"SALT_REPO_PASS: %s",
|
||||
salt_repo_pass[0] + "*" * (len(salt_repo_pass) - 2) + salt_repo_pass[-1],
|
||||
)
|
||||
if salt_repo_user and salt_repo_pass:
|
||||
repo_domain = f"{salt_repo_user}:{salt_repo_pass}@{repo_domain}"
|
||||
_root_url = f"https://{repo_domain}/{salt_path}/py3"
|
||||
log.info("Repository Root URL: %s", _root_url)
|
||||
return _root_url
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def package_type():
|
||||
return os.environ.get("DOWNLOAD_TEST_PACKAGE_TYPE")
|
||||
|
||||
|
||||
def get_salt_release():
|
||||
salt_release = os.environ.get("SALT_RELEASE")
|
||||
pkg_test_type = os.environ.get("PKG_TEST_TYPE", "install")
|
||||
if salt_release is None:
|
||||
if pkg_test_type == "download-pkgs":
|
||||
log.warning(
|
||||
"Setting salt release to 3006.0rc2 which is probably not what you want."
|
||||
)
|
||||
salt_release = "3006.0rc2"
|
||||
if pkg_test_type == "download-pkgs":
|
||||
if packaging.version.parse(salt_release) < packaging.version.parse("3006.0rc1"):
|
||||
log.warning(f"The salt release being tested, {salt_release!r} looks off.")
|
||||
return salt_release
|
||||
|
||||
|
||||
def get_repo_subpath_params():
|
||||
current_release = packaging.version.parse(get_salt_release())
|
||||
params = ["minor", current_release.major]
|
||||
latest_env_var = os.environ.get("LATEST_SALT_RELEASE")
|
||||
if latest_env_var is not None:
|
||||
latest_release = packaging.version.parse(latest_env_var)
|
||||
if current_release >= latest_release:
|
||||
log.debug(
|
||||
f"Running the tests for the latest release since {str(current_release)} >= {str(latest_release)}"
|
||||
)
|
||||
params.append("latest")
|
||||
return params
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="module",
|
||||
params=get_repo_subpath_params(),
|
||||
)
|
||||
def repo_subpath(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def gpg_key_name(salt_release):
|
||||
if packaging.version.parse(salt_release) > packaging.version.parse("3005"):
|
||||
return "SALT-PROJECT-GPG-PUBKEY-2023.pub"
|
||||
return "salt-archive-keyring.gpg"
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def salt_release():
|
||||
yield get_salt_release()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def onedir_install_path(tmp_path_factory):
|
||||
install_path = tmp_path_factory.mktemp("onedir_install")
|
||||
yield install_path
|
||||
shutil.rmtree(install_path, ignore_errors=True)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def _setup_system(
|
||||
grains,
|
||||
shell,
|
||||
root_url,
|
||||
salt_release,
|
||||
gpg_key_name,
|
||||
repo_subpath,
|
||||
package_type,
|
||||
tmp_path_factory,
|
||||
onedir_install_path,
|
||||
):
|
||||
downloads_path = tmp_path_factory.mktemp("downloads")
|
||||
try:
|
||||
# Windows is a special case, because sometimes we need to uninstall the packages
|
||||
if grains["os_family"] == "Windows":
|
||||
with setup_windows(
|
||||
shell,
|
||||
root_url=root_url,
|
||||
salt_release=salt_release,
|
||||
downloads_path=downloads_path,
|
||||
repo_subpath=repo_subpath,
|
||||
package_type=package_type,
|
||||
onedir_install_path=onedir_install_path,
|
||||
):
|
||||
yield
|
||||
else:
|
||||
if grains["os_family"] == "MacOS":
|
||||
setup_macos(
|
||||
shell,
|
||||
root_url=root_url,
|
||||
salt_release=salt_release,
|
||||
downloads_path=downloads_path,
|
||||
repo_subpath=repo_subpath,
|
||||
package_type=package_type,
|
||||
onedir_install_path=onedir_install_path,
|
||||
)
|
||||
elif grains["os"] == "Amazon":
|
||||
setup_redhat_family(
|
||||
shell,
|
||||
os_name=grains["os"].lower(),
|
||||
os_version=grains["osmajorrelease"],
|
||||
root_url=root_url,
|
||||
salt_release=salt_release,
|
||||
downloads_path=downloads_path,
|
||||
gpg_key_name=gpg_key_name,
|
||||
repo_subpath=repo_subpath,
|
||||
)
|
||||
elif grains["os"] == "Fedora":
|
||||
setup_redhat_family(
|
||||
shell,
|
||||
os_name=grains["os"].lower(),
|
||||
os_version=grains["osmajorrelease"],
|
||||
root_url=root_url,
|
||||
salt_release=salt_release,
|
||||
downloads_path=downloads_path,
|
||||
gpg_key_name=gpg_key_name,
|
||||
repo_subpath=repo_subpath,
|
||||
)
|
||||
elif grains["os"] == "VMware Photon OS":
|
||||
setup_redhat_family(
|
||||
shell,
|
||||
os_name="photon",
|
||||
os_version=grains["osmajorrelease"],
|
||||
root_url=root_url,
|
||||
salt_release=salt_release,
|
||||
downloads_path=downloads_path,
|
||||
gpg_key_name=gpg_key_name,
|
||||
repo_subpath=repo_subpath,
|
||||
)
|
||||
elif grains["os_family"] == "RedHat":
|
||||
setup_redhat_family(
|
||||
shell,
|
||||
os_name="redhat",
|
||||
os_version=grains["osmajorrelease"],
|
||||
root_url=root_url,
|
||||
salt_release=salt_release,
|
||||
downloads_path=downloads_path,
|
||||
gpg_key_name=gpg_key_name,
|
||||
repo_subpath=repo_subpath,
|
||||
)
|
||||
elif grains["os_family"] == "Debian":
|
||||
setup_debian_family(
|
||||
shell,
|
||||
os_name=grains["os"].lower(),
|
||||
os_version=grains["osrelease"],
|
||||
os_codename=grains["oscodename"],
|
||||
root_url=root_url,
|
||||
salt_release=salt_release,
|
||||
downloads_path=downloads_path,
|
||||
gpg_key_name=gpg_key_name,
|
||||
repo_subpath=repo_subpath,
|
||||
package_type=package_type,
|
||||
onedir_install_path=onedir_install_path,
|
||||
)
|
||||
else:
|
||||
pytest.fail("Don't know how to handle %s", grains["osfinger"])
|
||||
yield
|
||||
finally:
|
||||
shutil.rmtree(downloads_path, ignore_errors=True)
|
||||
|
||||
|
||||
def setup_redhat_family(
|
||||
shell,
|
||||
os_name,
|
||||
os_version,
|
||||
root_url,
|
||||
salt_release,
|
||||
downloads_path,
|
||||
gpg_key_name,
|
||||
repo_subpath,
|
||||
):
|
||||
arch = os.environ.get("SALT_REPO_ARCH") or "x86_64"
|
||||
|
||||
if repo_subpath == "minor":
|
||||
repo_url_base = (
|
||||
f"{root_url}/{os_name}/{os_version}/{arch}/{repo_subpath}/{salt_release}"
|
||||
)
|
||||
else:
|
||||
repo_url_base = f"{root_url}/{os_name}/{os_version}/{arch}/{repo_subpath}"
|
||||
|
||||
gpg_file_url = f"{root_url}/{os_name}/{os_version}/{arch}/{gpg_key_name}"
|
||||
|
||||
try:
|
||||
pytest.helpers.download_file(gpg_file_url, downloads_path / gpg_key_name)
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
pytest.fail(f"Failed to download {gpg_file_url}: {exc}")
|
||||
|
||||
ret = shell.run("rpm", "--import", str(downloads_path / gpg_key_name), check=False)
|
||||
if ret.returncode != 0:
|
||||
pytest.fail("Failed to import gpg key")
|
||||
|
||||
repo_file = pytest.helpers.download_file(
|
||||
f"{repo_url_base}.repo", downloads_path / f"salt-{os_name}.repo"
|
||||
)
|
||||
|
||||
commands = [
|
||||
("mv", str(repo_file), "/etc/yum.repos.d/salt.repo"),
|
||||
("yum", "clean", "all" if os_name == "photon" else "expire-cache"),
|
||||
(
|
||||
"yum",
|
||||
"install",
|
||||
"-y",
|
||||
"salt-master",
|
||||
"salt-minion",
|
||||
"salt-ssh",
|
||||
"salt-syndic",
|
||||
"salt-cloud",
|
||||
"salt-api",
|
||||
"salt-debuginfo",
|
||||
),
|
||||
]
|
||||
|
||||
for cmd in commands:
|
||||
ret = shell.run(*cmd, check=False)
|
||||
if ret.returncode != 0:
|
||||
pytest.fail(f"Failed to run '{' '.join(cmd)!r}':\n{ret}")
|
||||
|
||||
|
||||
def setup_debian_family(
|
||||
shell,
|
||||
os_name,
|
||||
os_version,
|
||||
os_codename,
|
||||
root_url,
|
||||
salt_release,
|
||||
downloads_path,
|
||||
gpg_key_name,
|
||||
repo_subpath,
|
||||
package_type,
|
||||
onedir_install_path,
|
||||
):
|
||||
arch = os.environ.get("SALT_REPO_ARCH") or "amd64"
|
||||
ret = shell.run("apt-get", "update", "-y", check=False)
|
||||
if ret.returncode != 0:
|
||||
pytest.fail(str(ret))
|
||||
|
||||
if package_type == "package":
|
||||
if arch == "aarch64":
|
||||
arch = "arm64"
|
||||
elif arch == "x86_64":
|
||||
arch = "amd64"
|
||||
|
||||
if repo_subpath == "minor":
|
||||
repo_url_base = f"{root_url}/{os_name}/{os_version}/{arch}/{repo_subpath}/{salt_release}"
|
||||
else:
|
||||
repo_url_base = f"{root_url}/{os_name}/{os_version}/{arch}/{repo_subpath}"
|
||||
gpg_file_url = f"{root_url}/{os_name}/{os_version}/{arch}/{gpg_key_name}"
|
||||
|
||||
try:
|
||||
pytest.helpers.download_file(gpg_file_url, downloads_path / gpg_key_name)
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
pytest.fail(f"Failed to download {gpg_file_url}: {exc}")
|
||||
|
||||
salt_sources_path = downloads_path / "salt.list"
|
||||
salt_sources_path.write_text(
|
||||
f"deb [signed-by=/usr/share/keyrings/{gpg_key_name} arch={arch}] {repo_url_base} {os_codename} main\n"
|
||||
)
|
||||
commands = [
|
||||
(
|
||||
"mv",
|
||||
str(downloads_path / gpg_key_name),
|
||||
f"/usr/share/keyrings/{gpg_key_name}",
|
||||
),
|
||||
(
|
||||
"mv",
|
||||
str(salt_sources_path),
|
||||
"/etc/apt/sources.list.d/salt.list",
|
||||
),
|
||||
("apt-get", "install", "-y", "ca-certificates"),
|
||||
("update-ca-certificates",),
|
||||
("apt-get", "update"),
|
||||
(
|
||||
"apt-get",
|
||||
"install",
|
||||
"-y",
|
||||
"salt-master",
|
||||
"salt-minion",
|
||||
"salt-ssh",
|
||||
"salt-syndic",
|
||||
"salt-cloud",
|
||||
"salt-api",
|
||||
"salt-dbg",
|
||||
),
|
||||
]
|
||||
for cmd in commands:
|
||||
ret = shell.run(*cmd)
|
||||
if ret.returncode != 0:
|
||||
pytest.fail(str(ret))
|
||||
else:
|
||||
# We are testing the onedir download
|
||||
onedir_name = f"salt-{salt_release}-onedir-linux-{arch}.tar.xz"
|
||||
if repo_subpath == "minor":
|
||||
repo_url_base = f"{root_url}/onedir/{repo_subpath}/{salt_release}"
|
||||
else:
|
||||
repo_url_base = f"{root_url}/onedir/{repo_subpath}"
|
||||
onedir_url = f"{repo_url_base}/{onedir_name}"
|
||||
onedir_location = downloads_path / onedir_name
|
||||
onedir_extracted = onedir_install_path
|
||||
|
||||
try:
|
||||
pytest.helpers.download_file(onedir_url, onedir_location)
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
pytest.fail(f"Failed to download {onedir_url}: {exc}")
|
||||
|
||||
shell.run("tar", "xvf", str(onedir_location), "-C", str(onedir_extracted))
|
||||
|
||||
|
||||
def setup_macos(
|
||||
shell,
|
||||
root_url,
|
||||
salt_release,
|
||||
downloads_path,
|
||||
repo_subpath,
|
||||
package_type,
|
||||
onedir_install_path,
|
||||
):
|
||||
arch = os.environ.get("SALT_REPO_ARCH") or "x86_64"
|
||||
if package_type == "package":
|
||||
|
||||
if packaging.version.parse(salt_release) > packaging.version.parse("3005"):
|
||||
mac_pkg = f"salt-{salt_release}-py3-{arch}.pkg"
|
||||
if repo_subpath == "minor":
|
||||
mac_pkg_url = (
|
||||
f"{root_url}/macos/{repo_subpath}/{salt_release}/{mac_pkg}"
|
||||
)
|
||||
else:
|
||||
mac_pkg_url = f"{root_url}/macos/{repo_subpath}/{mac_pkg}"
|
||||
else:
|
||||
mac_pkg_url = f"{root_url}/macos/{salt_release}/{mac_pkg}"
|
||||
mac_pkg = f"salt-{salt_release}-macos-{arch}.pkg"
|
||||
|
||||
mac_pkg_path = downloads_path / mac_pkg
|
||||
pytest.helpers.download_file(mac_pkg_url, mac_pkg_path)
|
||||
|
||||
ret = shell.run(
|
||||
"installer",
|
||||
"-pkg",
|
||||
str(mac_pkg_path),
|
||||
"-target",
|
||||
"/",
|
||||
check=False,
|
||||
)
|
||||
assert ret.returncode == 0, ret
|
||||
else:
|
||||
# We are testing the onedir download
|
||||
onedir_name = f"salt-{salt_release}-onedir-darwin-{arch}.tar.xz"
|
||||
if repo_subpath == "minor":
|
||||
repo_url_base = f"{root_url}/onedir/{repo_subpath}/{salt_release}"
|
||||
else:
|
||||
repo_url_base = f"{root_url}/onedir/{repo_subpath}"
|
||||
onedir_url = f"{repo_url_base}/{onedir_name}"
|
||||
onedir_location = downloads_path / onedir_name
|
||||
onedir_extracted = onedir_install_path
|
||||
|
||||
try:
|
||||
pytest.helpers.download_file(onedir_url, onedir_location)
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
pytest.fail(f"Failed to download {onedir_url}: {exc}")
|
||||
|
||||
shell.run("tar", "xvf", str(onedir_location), "-C", str(onedir_extracted))
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def setup_windows(
|
||||
shell,
|
||||
root_url,
|
||||
salt_release,
|
||||
downloads_path,
|
||||
repo_subpath,
|
||||
package_type,
|
||||
onedir_install_path,
|
||||
):
|
||||
try:
|
||||
arch = os.environ.get("SALT_REPO_ARCH") or "amd64"
|
||||
if package_type != "onedir":
|
||||
root_dir = pathlib.Path(r"C:\Program Files\Salt Project\Salt")
|
||||
|
||||
if packaging.version.parse(salt_release) > packaging.version.parse("3005"):
|
||||
if package_type.lower() == "nsis":
|
||||
if arch.lower() != "x86":
|
||||
arch = arch.upper()
|
||||
win_pkg = f"Salt-Minion-{salt_release}-Py3-{arch}-Setup.exe"
|
||||
else:
|
||||
if arch.lower() != "x86":
|
||||
arch = arch.upper()
|
||||
win_pkg = f"Salt-Minion-{salt_release}-Py3-{arch}.msi"
|
||||
if repo_subpath == "minor":
|
||||
win_pkg_url = (
|
||||
f"{root_url}/windows/{repo_subpath}/{salt_release}/{win_pkg}"
|
||||
)
|
||||
else:
|
||||
win_pkg_url = f"{root_url}/windows/{repo_subpath}/{win_pkg}"
|
||||
ssm_bin = root_dir / "ssm.exe"
|
||||
else:
|
||||
win_pkg = f"salt-{salt_release}-windows-{arch}.exe"
|
||||
win_pkg_url = f"{root_url}/windows/{salt_release}/{win_pkg}"
|
||||
ssm_bin = root_dir / "bin" / "ssm_bin"
|
||||
|
||||
pkg_path = downloads_path / win_pkg
|
||||
|
||||
pytest.helpers.download_file(win_pkg_url, pkg_path)
|
||||
if package_type.lower() == "nsis":
|
||||
ret = shell.run(str(pkg_path), "/start-minion=0", "/S", check=False)
|
||||
else:
|
||||
ret = shell.run(
|
||||
"msiexec", "/qn", "/i", str(pkg_path), 'START_MINION=""'
|
||||
)
|
||||
assert ret.returncode == 0, ret
|
||||
|
||||
log.debug("Removing installed salt-minion service")
|
||||
ret = shell.run(
|
||||
"cmd",
|
||||
"/c",
|
||||
str(ssm_bin),
|
||||
"remove",
|
||||
"salt-minion",
|
||||
"confirm",
|
||||
check=False,
|
||||
)
|
||||
assert ret.returncode == 0, ret
|
||||
else:
|
||||
# We are testing the onedir download
|
||||
onedir_name = f"salt-{salt_release}-onedir-windows-{arch}.zip"
|
||||
if repo_subpath == "minor":
|
||||
repo_url_base = f"{root_url}/onedir/{repo_subpath}/{salt_release}"
|
||||
else:
|
||||
repo_url_base = f"{root_url}/onedir/{repo_subpath}"
|
||||
onedir_url = f"{repo_url_base}/{onedir_name}"
|
||||
onedir_location = downloads_path / onedir_name
|
||||
onedir_extracted = onedir_install_path
|
||||
|
||||
try:
|
||||
pytest.helpers.download_file(onedir_url, onedir_location)
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
pytest.fail(f"Failed to download {onedir_url}: {exc}")
|
||||
|
||||
shell.run("unzip", str(onedir_location), "-d", str(onedir_extracted))
|
||||
yield
|
||||
finally:
|
||||
# We need to uninstall the MSI packages, otherwise they will not install correctly
|
||||
if package_type.lower() == "msi":
|
||||
ret = shell.run("msiexec", "/qn", "/x", str(pkg_path))
|
||||
assert ret.returncode == 0, ret
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def install_dir(_setup_system, package_type, onedir_install_path):
|
||||
if package_type != "onedir":
|
||||
if platform.is_windows():
|
||||
return pathlib.Path(
|
||||
os.getenv("ProgramFiles"), "Salt Project", "Salt"
|
||||
).resolve()
|
||||
if platform.is_darwin():
|
||||
return pathlib.Path("/opt", "salt")
|
||||
return pathlib.Path("/opt", "saltstack", "salt")
|
||||
else:
|
||||
# We are testing the onedir
|
||||
return onedir_install_path / "salt"
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def salt_test_command(request, install_dir):
|
||||
command = request.param
|
||||
command[0] = str(install_dir / command[0])
|
||||
return command
|
||||
|
||||
|
||||
@pytest.mark.parametrize("salt_test_command", get_salt_test_commands(), indirect=True)
|
||||
def test_download(shell, salt_test_command):
|
||||
"""
|
||||
Test downloading of Salt packages and running various commands.
|
||||
"""
|
||||
ret = shell.run(*salt_test_command, check=False)
|
||||
assert ret.returncode == 0, ret
|
0
tests/pytests/pkg/integration/__init__.py
Normal file
0
tests/pytests/pkg/integration/__init__.py
Normal file
22
tests/pytests/pkg/integration/test_check_imports.py
Normal file
22
tests/pytests/pkg/integration/test_check_imports.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
import logging
|
||||
|
||||
import pytest
|
||||
from saltfactories.utils.functional import MultiStateResult
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skip_on_windows,
|
||||
]
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def test_check_imports(salt_cli, salt_minion):
|
||||
"""
|
||||
Test imports
|
||||
"""
|
||||
ret = salt_cli.run("state.sls", "check_imports", minion_tgt=salt_minion.id)
|
||||
assert ret.returncode == 0
|
||||
assert ret.data
|
||||
result = MultiStateResult(raw=ret.data)
|
||||
for state_ret in result:
|
||||
assert state_ret.result is True
|
61
tests/pytests/pkg/integration/test_clean_zmq_teardown.py
Normal file
61
tests/pytests/pkg/integration/test_clean_zmq_teardown.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
import logging
|
||||
import pathlib
|
||||
import shutil
|
||||
import textwrap
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skip_on_windows,
|
||||
]
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _skip_on_non_relenv(install_salt):
|
||||
if not install_salt.relenv:
|
||||
pytest.skip("This test is for relenv versions of salt")
|
||||
|
||||
|
||||
def test_check_no_import_error(salt_call_cli, salt_master):
|
||||
"""
|
||||
Test that we don't have any errors on teardown of python when using a py-rendered sls file
|
||||
This is a package test because the issue was not reproducible in our normal test suite
|
||||
"""
|
||||
init_sls = textwrap.dedent(
|
||||
"""#!py
|
||||
|
||||
|
||||
def run():
|
||||
return {
|
||||
"file_foobar": {
|
||||
"file.managed": [
|
||||
{
|
||||
"name": "/foobar"
|
||||
},
|
||||
{
|
||||
"template": "jinja"
|
||||
},
|
||||
{
|
||||
"context": {
|
||||
"foobar": "baz",
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "salt://breaks/foobar.jinja",
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
base_tree = pathlib.Path(salt_master.config["file_roots"]["base"][0])
|
||||
breaks_tree = base_tree / "breaks"
|
||||
breaks_tree.mkdir(exist_ok=True)
|
||||
(breaks_tree / "init.sls").write_text(init_sls)
|
||||
(breaks_tree / "foobar.jinja").write_text("{{ foobar }}")
|
||||
output = salt_call_cli.run("state.apply", "breaks", "--output-diff", "test=true")
|
||||
log.debug(output.stderr)
|
||||
shutil.rmtree(str(breaks_tree), ignore_errors=True)
|
||||
assert not output.stderr
|
40
tests/pytests/pkg/integration/test_enabled_disabled.py
Normal file
40
tests/pytests/pkg/integration/test_enabled_disabled.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
import pytest
|
||||
from pytestskipmarkers.utils import platform
|
||||
|
||||
|
||||
@pytest.mark.skip_on_windows(reason="Linux test only")
|
||||
def test_services(install_salt, salt_cli, salt_minion):
|
||||
"""
|
||||
Check if Services are enabled/disabled
|
||||
"""
|
||||
if install_salt.distro_id in ("ubuntu", "debian"):
|
||||
services_enabled = ["salt-master", "salt-minion", "salt-syndic", "salt-api"]
|
||||
services_disabled = []
|
||||
elif install_salt.distro_id in ("centos", "redhat", "amzn", "fedora"):
|
||||
services_enabled = []
|
||||
services_disabled = ["salt-master", "salt-minion", "salt-syndic", "salt-api"]
|
||||
elif install_salt.distro_id == "photon":
|
||||
if float(install_salt.distro_version) < 5:
|
||||
services_enabled = []
|
||||
services_disabled = [
|
||||
"salt-master",
|
||||
"salt-minion",
|
||||
"salt-syndic",
|
||||
"salt-api",
|
||||
]
|
||||
else:
|
||||
services_enabled = ["salt-master", "salt-minion", "salt-syndic", "salt-api"]
|
||||
services_disabled = []
|
||||
elif platform.is_darwin():
|
||||
services_enabled = ["salt-minion"]
|
||||
services_disabled = []
|
||||
else:
|
||||
pytest.fail(f"Don't know how to handle os_family={install_salt.distro_id}")
|
||||
|
||||
for service in services_enabled:
|
||||
ret = salt_cli.run("service.enabled", service, minion_tgt=salt_minion.id)
|
||||
assert "true" in ret.stdout
|
||||
|
||||
for service in services_disabled:
|
||||
ret = salt_cli.run("service.disabled", service, minion_tgt=salt_minion.id)
|
||||
assert "true" in ret.stdout
|
25
tests/pytests/pkg/integration/test_help.py
Normal file
25
tests/pytests/pkg/integration/test_help.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
import subprocess
|
||||
|
||||
|
||||
def test_help(install_salt):
|
||||
"""
|
||||
Test --help works for all salt cmds
|
||||
"""
|
||||
for cmd in install_salt.binary_paths.values():
|
||||
cmd = [str(x) for x in cmd]
|
||||
|
||||
if len(cmd) > 1 and "shell" in cmd[1]:
|
||||
# Singlebin build, unable to get the version
|
||||
continue
|
||||
|
||||
if "python" in cmd[0] and len(cmd) == 1:
|
||||
ret = install_salt.proc.run(
|
||||
*cmd, "--version", stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
assert "Python" in ret.stdout
|
||||
else:
|
||||
ret = install_salt.proc.run(
|
||||
*cmd, "--help", stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
assert "Usage" in ret.stdout
|
||||
assert ret.returncode == 0
|
46
tests/pytests/pkg/integration/test_logrotate_config.py
Normal file
46
tests/pytests/pkg/integration/test_logrotate_config.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
"""
|
||||
Tests for logrotate config
|
||||
"""
|
||||
|
||||
import pathlib
|
||||
|
||||
import packaging.version
|
||||
import pytest
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skip_unless_on_linux,
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def logrotate_config_file(grains):
|
||||
"""
|
||||
Fixture for logrotate config file path
|
||||
"""
|
||||
if grains["os_family"] == "RedHat":
|
||||
return pathlib.Path("/etc/logrotate.d", "salt")
|
||||
elif grains["os_family"] == "Debian":
|
||||
return pathlib.Path("/etc/logrotate.d", "salt-common")
|
||||
|
||||
|
||||
def test_logrotate_config(logrotate_config_file):
|
||||
"""
|
||||
Test that logrotate config has been installed in correctly
|
||||
"""
|
||||
assert logrotate_config_file.is_file()
|
||||
assert logrotate_config_file.owner() == "root"
|
||||
assert logrotate_config_file.group() == "root"
|
||||
|
||||
|
||||
def test_issue_65231_etc_logrotate_salt_dir_removed(install_salt):
|
||||
"""
|
||||
Test that /etc/logrotate.d/salt is not a directory
|
||||
"""
|
||||
if install_salt.prev_version and packaging.version.parse(
|
||||
install_salt.prev_version
|
||||
) <= packaging.version.parse("3006.4"):
|
||||
pytest.skip("Testing a downgrade to 3006.4, do not run")
|
||||
|
||||
path = pathlib.Path("/etc/logrotate.d/salt")
|
||||
if path.exists():
|
||||
assert path.is_dir() is False
|
138
tests/pytests/pkg/integration/test_multi_minion.py
Normal file
138
tests/pytests/pkg/integration/test_multi_minion.py
Normal file
|
@ -0,0 +1,138 @@
|
|||
import os
|
||||
import pathlib
|
||||
import subprocess
|
||||
|
||||
import packaging.version
|
||||
import psutil
|
||||
import pytest
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skip_unless_on_windows,
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _skip_on_less_than_3006_1(install_salt):
|
||||
if packaging.version.parse(install_salt.version) <= packaging.version.parse(
|
||||
"3006.1"
|
||||
):
|
||||
pytest.skip(
|
||||
"Multi-minion script only available on versions greater than 3006.1"
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mm_script(install_salt):
|
||||
yield install_salt.ssm_bin.parent / "multi-minion.ps1"
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def mm_conf(mm_script):
|
||||
yield pathlib.Path(os.getenv("LocalAppData"), "Salt Project", "Salt", "conf")
|
||||
subprocess.run(
|
||||
["powershell", str(mm_script).replace(" ", "' '"), "-d"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=False,
|
||||
universal_newlines=True,
|
||||
)
|
||||
|
||||
|
||||
def test_script_present(mm_script):
|
||||
"""
|
||||
Ensure the multi-minion.ps1 file is present in the root of the installation
|
||||
"""
|
||||
assert mm_script.exists()
|
||||
|
||||
|
||||
def test_install(mm_script, mm_conf):
|
||||
"""
|
||||
Install a second minion with default settings. Should create a minion config
|
||||
file in Local AppData
|
||||
"""
|
||||
ret = subprocess.run(
|
||||
["powershell", str(mm_script).replace(" ", "' '")],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=False,
|
||||
universal_newlines=True,
|
||||
)
|
||||
assert ret.returncode == 0, ret.stderr
|
||||
conf_file = mm_conf / "minion"
|
||||
assert conf_file.exists()
|
||||
assert conf_file.read_text().find("master: salt") > -1
|
||||
|
||||
|
||||
def test_install_master(mm_script, mm_conf):
|
||||
"""
|
||||
Install a second minion and set the master to spongebob
|
||||
"""
|
||||
ret = subprocess.run(
|
||||
["powershell", str(mm_script).replace(" ", "' '"), "-m", "spongebob"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=False,
|
||||
universal_newlines=True,
|
||||
)
|
||||
assert ret.returncode == 0, ret.stderr
|
||||
conf_file = mm_conf / "minion"
|
||||
assert conf_file.exists()
|
||||
assert conf_file.read_text().find("master: spongebob") > -1
|
||||
|
||||
|
||||
def test_install_prefix(mm_script, mm_conf):
|
||||
"""
|
||||
Install a second minion and add a prefix to the minion id
|
||||
"""
|
||||
ret = subprocess.run(
|
||||
["powershell", str(mm_script).replace(" ", "' '"), "-p", "squarepants"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=False,
|
||||
universal_newlines=True,
|
||||
)
|
||||
assert ret.returncode == 0, ret.stderr
|
||||
conf_file = mm_conf / "minion"
|
||||
assert conf_file.exists()
|
||||
assert conf_file.read_text().find("id: squarepants") > -1
|
||||
|
||||
|
||||
def test_install_log_level(mm_script, mm_conf):
|
||||
"""
|
||||
Install a second minion and set the log level in the log file to debug
|
||||
"""
|
||||
ret = subprocess.run(
|
||||
["powershell", str(mm_script).replace(" ", "' '"), "-l", "debug"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=False,
|
||||
universal_newlines=True,
|
||||
)
|
||||
assert ret.returncode == 0, ret.stderr
|
||||
conf_file = mm_conf / "minion"
|
||||
assert conf_file.exists()
|
||||
assert conf_file.read_text().find("log_level_logfile: debug") > -1
|
||||
|
||||
|
||||
def test_install_start(mm_script, mm_conf):
|
||||
"""
|
||||
Install a second minion and start that minion in a hidden process
|
||||
"""
|
||||
ret = subprocess.run(
|
||||
["powershell", str(mm_script).replace(" ", "' '"), "-s"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=False,
|
||||
universal_newlines=True,
|
||||
)
|
||||
assert ret.returncode == 0, ret.stderr
|
||||
conf_file = mm_conf / "minion"
|
||||
assert conf_file.exists()
|
||||
assert conf_file.read_text().find("master: salt") > -1
|
||||
|
||||
found = False
|
||||
for p in psutil.process_iter(["cmdline", "name"]):
|
||||
if p.info["name"] and p.info["name"] == "salt-minion.exe":
|
||||
if f"{mm_conf}" in p.info["cmdline"]:
|
||||
found = True
|
||||
assert found is True
|
206
tests/pytests/pkg/integration/test_pip.py
Normal file
206
tests/pytests/pkg/integration/test_pip.py
Normal file
|
@ -0,0 +1,206 @@
|
|||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
from pytestskipmarkers.utils import platform
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pypath():
|
||||
if platform.is_windows():
|
||||
return pathlib.Path(os.getenv("ProgramFiles"), "Salt Project", "Salt")
|
||||
else:
|
||||
return pathlib.Path("/opt", "saltstack", "salt", "pypath", "bin")
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def wipe_pydeps(shell, install_salt, extras_pypath):
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
# Note, uninstalling anything with an associated script will leave the script.
|
||||
# This is due to a bug in pip.
|
||||
for dep in ["pep8", "PyGithub"]:
|
||||
shell.run(
|
||||
*(install_salt.binary_paths["pip"] + ["uninstall", "-y", dep]),
|
||||
)
|
||||
# Let's remove everything under the extras directory, uninstalling doesn't get dependencies
|
||||
dirs = []
|
||||
files = []
|
||||
for filename in extras_pypath.glob("**/**"):
|
||||
if filename != extras_pypath and filename.exists():
|
||||
if filename.is_dir():
|
||||
dirs.append(filename)
|
||||
else:
|
||||
files.append(filename)
|
||||
for fp in files:
|
||||
fp.unlink()
|
||||
for dirname in dirs:
|
||||
shutil.rmtree(dirname, ignore_errors=True)
|
||||
|
||||
|
||||
def test_pip_install(salt_call_cli, install_salt, shell):
|
||||
"""
|
||||
Test pip.install and ensure module can use installed library
|
||||
"""
|
||||
dep = "PyGithub==1.56.0"
|
||||
repo = "https://github.com/saltstack/salt.git"
|
||||
|
||||
try:
|
||||
install = salt_call_cli.run("--local", "pip.install", dep)
|
||||
assert install.returncode == 0
|
||||
|
||||
use_lib = salt_call_cli.run("--local", "github.get_repo_info", repo)
|
||||
assert "Authentication information could" in use_lib.stderr
|
||||
finally:
|
||||
ret = salt_call_cli.run("--local", "pip.uninstall", dep)
|
||||
assert ret.returncode == 0
|
||||
use_lib = salt_call_cli.run("--local", "github.get_repo_info", repo)
|
||||
assert "The github execution module cannot be loaded" in use_lib.stderr
|
||||
|
||||
|
||||
def test_pip_install_extras(shell, install_salt, extras_pypath_bin):
|
||||
"""
|
||||
Test salt-pip installs into the correct directory
|
||||
"""
|
||||
if not install_salt.relenv:
|
||||
pytest.skip("The extras directory is only in relenv versions")
|
||||
dep = "pep8"
|
||||
extras_keyword = "extras-3"
|
||||
if platform.is_windows():
|
||||
check_path = extras_pypath_bin / f"{dep}.exe"
|
||||
else:
|
||||
check_path = extras_pypath_bin / dep
|
||||
|
||||
install_ret = shell.run(*(install_salt.binary_paths["pip"] + ["install", dep]))
|
||||
assert install_ret.returncode == 0
|
||||
|
||||
ret = shell.run(*(install_salt.binary_paths["pip"] + ["list", "--format=json"]))
|
||||
assert ret.returncode == 0
|
||||
assert ret.data # We can parse the JSON output
|
||||
for pkg in ret.data:
|
||||
if pkg["name"] == dep:
|
||||
break
|
||||
else:
|
||||
pytest.fail(
|
||||
f"The {dep!r} package was not found installed. Packages Installed: {ret.data}"
|
||||
)
|
||||
|
||||
show_ret = shell.run(*(install_salt.binary_paths["pip"] + ["show", dep]))
|
||||
assert show_ret.returncode == 0
|
||||
assert extras_keyword in show_ret.stdout
|
||||
assert check_path.exists()
|
||||
|
||||
ret = shell.run(str(check_path), "--version")
|
||||
assert ret.returncode == 0
|
||||
|
||||
|
||||
def demote(user_uid, user_gid):
|
||||
def result():
|
||||
# os.setgid does not remove group membership, so we remove them here so they are REALLY non-root
|
||||
os.setgroups([])
|
||||
os.setgid(user_gid)
|
||||
os.setuid(user_uid)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@pytest.mark.skip_on_windows(reason="We can't easily demote users on Windows")
|
||||
def test_pip_non_root(shell, install_salt, test_account, extras_pypath_bin, pypath):
|
||||
if install_salt.classic:
|
||||
pytest.skip("We can install non-root for classic packages")
|
||||
check_path = extras_pypath_bin / "pep8"
|
||||
if not install_salt.relenv and not install_salt.classic:
|
||||
check_path = pypath / "pep8"
|
||||
# We should be able to issue a --help without being root
|
||||
ret = subprocess.run(
|
||||
install_salt.binary_paths["salt"] + ["--help"],
|
||||
preexec_fn=demote(test_account.uid, test_account.gid),
|
||||
env=test_account.env,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=False,
|
||||
universal_newlines=True,
|
||||
)
|
||||
assert ret.returncode == 0, ret.stderr
|
||||
assert "Usage" in ret.stdout
|
||||
|
||||
# Let tiamat-pip create the pypath directory for us
|
||||
ret = subprocess.run(
|
||||
install_salt.binary_paths["pip"] + ["install", "-h"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=False,
|
||||
universal_newlines=True,
|
||||
)
|
||||
assert ret.returncode == 0, ret.stderr
|
||||
|
||||
# Now, we should still not be able to install as non-root
|
||||
ret = subprocess.run(
|
||||
install_salt.binary_paths["pip"] + ["install", "pep8"],
|
||||
preexec_fn=demote(test_account.uid, test_account.gid),
|
||||
env=test_account.env,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=False,
|
||||
universal_newlines=True,
|
||||
)
|
||||
assert ret.returncode != 0, ret.stderr
|
||||
# But we should be able to install as root
|
||||
ret = subprocess.run(
|
||||
install_salt.binary_paths["pip"] + ["install", "pep8"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=False,
|
||||
universal_newlines=True,
|
||||
)
|
||||
|
||||
assert check_path.exists(), shutil.which("pep8")
|
||||
|
||||
assert ret.returncode == 0, ret.stderr
|
||||
|
||||
|
||||
def test_pip_install_salt_extension_in_extras(install_salt, extras_pypath, shell):
|
||||
"""
|
||||
Test salt-pip installs into the correct directory and the salt extension
|
||||
is properly loaded.
|
||||
"""
|
||||
if not install_salt.relenv:
|
||||
pytest.skip("The extras directory is only in relenv versions")
|
||||
dep = "salt-analytics-framework"
|
||||
dep_version = "0.1.0"
|
||||
|
||||
install_ret = shell.run(
|
||||
*(install_salt.binary_paths["pip"] + ["install", f"{dep}=={dep_version}"]),
|
||||
)
|
||||
assert install_ret.returncode == 0
|
||||
|
||||
ret = shell.run(
|
||||
*(install_salt.binary_paths["pip"] + ["list", "--format=json"]),
|
||||
)
|
||||
assert ret.returncode == 0
|
||||
pkgs_installed = json.loads(ret.stdout.strip())
|
||||
for pkg in pkgs_installed:
|
||||
if pkg["name"] == dep:
|
||||
break
|
||||
else:
|
||||
pytest.fail(
|
||||
f"The {dep!r} package was not found installed. Packages Installed: {pkgs_installed}"
|
||||
)
|
||||
|
||||
show_ret = shell.run(
|
||||
*(install_salt.binary_paths["pip"] + ["show", dep]),
|
||||
)
|
||||
assert show_ret.returncode == 0
|
||||
|
||||
assert extras_pypath.joinpath("saf").is_dir()
|
||||
|
||||
ret = shell.run(
|
||||
*(install_salt.binary_paths["minion"] + ["--versions-report"]),
|
||||
)
|
||||
assert show_ret.returncode == 0
|
||||
assert "Salt Extensions" in ret.stdout
|
||||
assert f"{dep}: {dep_version}" in ret.stdout
|
96
tests/pytests/pkg/integration/test_pip_upgrade.py
Normal file
96
tests/pytests/pkg/integration/test_pip_upgrade.py
Normal file
|
@ -0,0 +1,96 @@
|
|||
import logging
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skip_on_windows,
|
||||
]
|
||||
|
||||
|
||||
def test_pip_install(install_salt, salt_call_cli):
|
||||
"""
|
||||
Test pip.install and ensure that a package included in the tiamat build can be upgraded
|
||||
"""
|
||||
ret = subprocess.run(
|
||||
install_salt.binary_paths["salt"] + ["--versions-report"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
universal_newlines=True,
|
||||
check=True,
|
||||
shell=False,
|
||||
)
|
||||
assert ret.returncode == 0
|
||||
|
||||
possible_upgrades = [
|
||||
"docker-py",
|
||||
"msgpack",
|
||||
"pycparser",
|
||||
"python-gnupg",
|
||||
"pyyaml",
|
||||
"pyzmq",
|
||||
"jinja2",
|
||||
]
|
||||
found_new = False
|
||||
for dep in possible_upgrades:
|
||||
get_latest = salt_call_cli.run("--local", "pip.list_all_versions", dep)
|
||||
if not get_latest.data:
|
||||
# No information available
|
||||
continue
|
||||
dep_version = get_latest.data[-1]
|
||||
installed_version = None
|
||||
for line in ret.stdout.splitlines():
|
||||
if dep in line.lower():
|
||||
installed_version = line.lower().strip().split(":")[-1].strip()
|
||||
break
|
||||
else:
|
||||
pytest.fail(f"Failed to find {dep} in the versions report output")
|
||||
|
||||
if dep_version == installed_version:
|
||||
log.warning(f"The {dep} dependency is already latest")
|
||||
else:
|
||||
found_new = True
|
||||
break
|
||||
|
||||
if found_new:
|
||||
try:
|
||||
install = salt_call_cli.run(
|
||||
"--local", "pip.install", f"{dep}=={dep_version}"
|
||||
)
|
||||
assert install
|
||||
log.warning(install)
|
||||
# The assert is commented out because pip will actually trigger a failure since
|
||||
# we're breaking the dependency tree, but, for the purpose of this test, we can
|
||||
# ignore it.
|
||||
#
|
||||
# assert install.returncode == 0
|
||||
|
||||
ret = subprocess.run(
|
||||
install_salt.binary_paths["salt"] + ["--versions-report"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
universal_newlines=True,
|
||||
check=True,
|
||||
shell=False,
|
||||
)
|
||||
assert ret.returncode == 0
|
||||
for line in ret.stdout.splitlines():
|
||||
if dep in line.lower():
|
||||
new_version = line.lower().strip().split(":")[-1].strip()
|
||||
if new_version == installed_version:
|
||||
pytest.fail(
|
||||
f"The newly installed version of {dep} does not show in the versions report"
|
||||
)
|
||||
assert new_version == dep_version
|
||||
break
|
||||
else:
|
||||
pytest.fail(f"Failed to find {dep} in the versions report output")
|
||||
finally:
|
||||
log.info(f"Uninstalling {dep_version}")
|
||||
assert salt_call_cli.run(
|
||||
"--local", "pip.uninstall", f"{dep}=={dep_version}"
|
||||
)
|
||||
else:
|
||||
pytest.skip("Did not find an upgrade version for any of the dependencies")
|
36
tests/pytests/pkg/integration/test_pkg.py
Normal file
36
tests/pytests/pkg/integration/test_pkg.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
import sys
|
||||
import time
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def pkg_name(salt_call_cli, grains):
|
||||
if sys.platform.startswith("win"):
|
||||
ret = salt_call_cli.run("--local", "winrepo.update_git_repos")
|
||||
assert ret.returncode == 0
|
||||
attempts = 3
|
||||
while attempts:
|
||||
attempts -= 1
|
||||
ret = salt_call_cli.run("--local", "pkg.refresh_db")
|
||||
if ret.returncode:
|
||||
time.sleep(5)
|
||||
continue
|
||||
break
|
||||
else:
|
||||
pytest.fail("Failed to run 'pkg.refresh_db' 3 times.")
|
||||
return "putty"
|
||||
elif grains["os_family"] == "RedHat":
|
||||
if grains["os"] == "VMware Photon OS":
|
||||
return "snoopy"
|
||||
elif grains["osfinger"] == "Amazon Linux-2023":
|
||||
return "dnf-utils"
|
||||
return "units"
|
||||
elif grains["os_family"] == "Debian":
|
||||
return "ifenslave"
|
||||
return "figlet"
|
||||
|
||||
|
||||
def test_pkg_install(salt_call_cli, pkg_name):
|
||||
ret = salt_call_cli.run("--local", "state.single", "pkg.installed", pkg_name)
|
||||
assert ret.returncode == 0
|
49
tests/pytests/pkg/integration/test_python.py
Normal file
49
tests/pytests/pkg/integration/test_python.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.pytests.pkg.support.helpers import TESTS_DIR
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def python_script_bin(install_salt):
|
||||
# Tiamat builds run scripts via `salt python`
|
||||
if not install_salt.relenv and not install_salt.classic:
|
||||
return install_salt.binary_paths["python"][:1] + ["python"]
|
||||
return install_salt.binary_paths["python"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("exp_ret,user_arg", [(1, "false"), (0, "true")])
|
||||
def test_python_script(install_salt, exp_ret, user_arg, python_script_bin):
|
||||
ret = install_salt.proc.run(
|
||||
*(
|
||||
python_script_bin
|
||||
+ [
|
||||
str(TESTS_DIR / "pytests" / "pkg" / "files" / "check_python.py"),
|
||||
user_arg,
|
||||
]
|
||||
),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=False,
|
||||
universal_newlines=True,
|
||||
)
|
||||
|
||||
assert ret.returncode == exp_ret, ret.stderr
|
||||
|
||||
|
||||
def test_python_script_exception(install_salt, python_script_bin):
|
||||
ret = install_salt.proc.run(
|
||||
*(
|
||||
python_script_bin
|
||||
+ [
|
||||
str(TESTS_DIR / "pytests" / "pkg" / "files" / "check_python.py"),
|
||||
"raise",
|
||||
]
|
||||
),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=False,
|
||||
universal_newlines=True,
|
||||
)
|
||||
assert "Exception: test" in ret.stderr
|
21
tests/pytests/pkg/integration/test_salt_api.py
Normal file
21
tests/pytests/pkg/integration/test_salt_api.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
import pytest
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skip_on_windows,
|
||||
]
|
||||
|
||||
|
||||
def test_salt_api(api_request):
|
||||
"""
|
||||
Test running a command against the salt api
|
||||
"""
|
||||
ret = api_request.post(
|
||||
"/run",
|
||||
data={
|
||||
"client": "local",
|
||||
"tgt": "*",
|
||||
"fun": "test.arg",
|
||||
"arg": ["foo", "bar"],
|
||||
},
|
||||
)
|
||||
assert ret["args"] == ["foo", "bar"]
|
59
tests/pytests/pkg/integration/test_salt_call.py
Normal file
59
tests/pytests/pkg/integration/test_salt_call.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
import pytest
|
||||
|
||||
|
||||
def test_salt_call_local(salt_call_cli):
|
||||
"""
|
||||
Test salt-call --local test.ping
|
||||
"""
|
||||
ret = salt_call_cli.run("--local", "test.ping")
|
||||
assert ret.data is True
|
||||
assert ret.returncode == 0
|
||||
|
||||
|
||||
def test_salt_call(salt_call_cli):
|
||||
"""
|
||||
Test salt-call test.ping
|
||||
"""
|
||||
ret = salt_call_cli.run("test.ping")
|
||||
assert ret.data is True
|
||||
assert ret.returncode == 0
|
||||
|
||||
|
||||
def test_sls(salt_call_cli):
|
||||
"""
|
||||
Test calling a sls file
|
||||
"""
|
||||
ret = salt_call_cli.run("state.apply", "test")
|
||||
assert ret.data, ret
|
||||
sls_ret = ret.data[next(iter(ret.data))]
|
||||
assert sls_ret["changes"]["testing"]["new"] == "Something pretended to change"
|
||||
assert ret.returncode == 0
|
||||
|
||||
|
||||
def test_salt_call_local_sys_doc_none(salt_call_cli):
|
||||
"""
|
||||
Test salt-call --local sys.doc none
|
||||
"""
|
||||
ret = salt_call_cli.run("--local", "sys.doc", "none")
|
||||
assert not ret.data
|
||||
assert ret.returncode == 0
|
||||
|
||||
|
||||
def test_salt_call_local_sys_doc_aliases(salt_call_cli):
|
||||
"""
|
||||
Test salt-call --local sys.doc aliases
|
||||
"""
|
||||
ret = salt_call_cli.run("--local", "sys.doc", "aliases.list_aliases")
|
||||
assert "aliases.list_aliases" in ret.data
|
||||
assert ret.returncode == 0
|
||||
|
||||
|
||||
@pytest.mark.skip_on_windows()
|
||||
def test_salt_call_cmd_run_id_runas(salt_call_cli, test_account, caplog):
|
||||
"""
|
||||
Test salt-call --local cmd_run id with runas
|
||||
"""
|
||||
ret = salt_call_cli.run("--local", "cmd.run", "id", runas=test_account.username)
|
||||
assert "Environment could not be retrieved for user" not in caplog.text
|
||||
assert str(test_account.uid) in ret.stdout
|
||||
assert str(test_account.gid) in ret.stdout
|
38
tests/pytests/pkg/integration/test_salt_exec.py
Normal file
38
tests/pytests/pkg/integration/test_salt_exec.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
from sys import platform
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skip_on_windows,
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def cat_file(tmp_path):
|
||||
fp = tmp_path / "cat-file"
|
||||
fp.write_text(str(fp))
|
||||
return fp
|
||||
|
||||
|
||||
def test_salt_cmd_run(salt_cli, salt_minion, cat_file):
|
||||
"""
|
||||
Test salt cmd.run 'ipconfig' or 'cat <file>'
|
||||
"""
|
||||
ret = None
|
||||
if platform.startswith("win"):
|
||||
ret = salt_cli.run("cmd.run", "ipconfig", minion_tgt=salt_minion.id)
|
||||
else:
|
||||
ret = salt_cli.run("cmd.run", f"cat {str(cat_file)}", minion_tgt=salt_minion.id)
|
||||
assert ret
|
||||
assert ret.stdout
|
||||
|
||||
|
||||
def test_salt_list_users(salt_cli, salt_minion):
|
||||
"""
|
||||
Test salt user.list_users
|
||||
"""
|
||||
ret = salt_cli.run("user.list_users", minion_tgt=salt_minion.id)
|
||||
if platform.startswith("win"):
|
||||
assert "Administrator" in ret.stdout
|
||||
else:
|
||||
assert "root" in ret.stdout
|
41
tests/pytests/pkg/integration/test_salt_grains.py
Normal file
41
tests/pytests/pkg/integration/test_salt_grains.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
import pytest
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skip_on_windows,
|
||||
]
|
||||
|
||||
|
||||
def test_grains_items(salt_cli, salt_minion):
|
||||
"""
|
||||
Test grains.items
|
||||
"""
|
||||
ret = salt_cli.run("grains.items", minion_tgt=salt_minion.id)
|
||||
assert ret.data, ret
|
||||
assert "osrelease" in ret.data
|
||||
|
||||
|
||||
def test_grains_item_os(salt_cli, salt_minion):
|
||||
"""
|
||||
Test grains.item os
|
||||
"""
|
||||
ret = salt_cli.run("grains.item", "os", minion_tgt=salt_minion.id)
|
||||
assert ret.data, ret
|
||||
assert "os" in ret.data
|
||||
|
||||
|
||||
def test_grains_item_pythonversion(salt_cli, salt_minion):
|
||||
"""
|
||||
Test grains.item pythonversion
|
||||
"""
|
||||
ret = salt_cli.run("grains.item", "pythonversion", minion_tgt=salt_minion.id)
|
||||
assert ret.data, ret
|
||||
assert "pythonversion" in ret.data
|
||||
|
||||
|
||||
def test_grains_setval_key_val(salt_cli, salt_minion):
|
||||
"""
|
||||
Test grains.setval key val
|
||||
"""
|
||||
ret = salt_cli.run("grains.setval", "key", "val", minion_tgt=salt_minion.id)
|
||||
assert ret.data, ret
|
||||
assert "key" in ret.data
|
14
tests/pytests/pkg/integration/test_salt_key.py
Normal file
14
tests/pytests/pkg/integration/test_salt_key.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
import pytest
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skip_on_windows,
|
||||
]
|
||||
|
||||
|
||||
def test_salt_key(salt_key_cli, salt_minion):
|
||||
"""
|
||||
Test running salt-key -L
|
||||
"""
|
||||
ret = salt_key_cli.run("-L")
|
||||
assert ret.data
|
||||
assert salt_minion.id in ret.data["minions"]
|
26
tests/pytests/pkg/integration/test_salt_minion.py
Normal file
26
tests/pytests/pkg/integration/test_salt_minion.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
import pytest
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skip_on_windows,
|
||||
]
|
||||
|
||||
|
||||
def test_salt_minion_ping(salt_cli, salt_minion):
|
||||
"""
|
||||
Test running a command against a targeted minion
|
||||
"""
|
||||
ret = salt_cli.run("test.ping", minion_tgt=salt_minion.id)
|
||||
assert ret.returncode == 0
|
||||
assert ret.data is True
|
||||
|
||||
|
||||
def test_salt_minion_setproctitle(salt_cli, salt_minion):
|
||||
"""
|
||||
Test that setproctitle is working
|
||||
for the running Salt minion
|
||||
"""
|
||||
ret = salt_cli.run(
|
||||
"ps.pgrep", "MinionProcessManager", full=True, minion_tgt=salt_minion.id
|
||||
)
|
||||
assert ret.returncode == 0
|
||||
assert ret.data != ""
|
19
tests/pytests/pkg/integration/test_salt_output.py
Normal file
19
tests/pytests/pkg/integration/test_salt_output.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
import pytest
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skip_on_windows,
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("output_fmt", ["yaml", "json"])
|
||||
def test_salt_output(salt_cli, salt_minion, output_fmt):
|
||||
"""
|
||||
Test --output
|
||||
"""
|
||||
ret = salt_cli.run(
|
||||
f"--output={output_fmt}", "test.fib", "7", minion_tgt=salt_minion.id
|
||||
)
|
||||
if output_fmt == "json":
|
||||
assert 13 in ret.data
|
||||
else:
|
||||
ret.stdout.matcher.fnmatch_lines(["*- 13*"])
|
13
tests/pytests/pkg/integration/test_salt_pillar.py
Normal file
13
tests/pytests/pkg/integration/test_salt_pillar.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
import pytest
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skip_on_windows,
|
||||
]
|
||||
|
||||
|
||||
def test_salt_pillar(salt_cli, salt_minion):
|
||||
"""
|
||||
Test pillar.items
|
||||
"""
|
||||
ret = salt_cli.run("pillar.items", minion_tgt=salt_minion.id)
|
||||
assert "info" in ret.data
|
24
tests/pytests/pkg/integration/test_salt_state_file.py
Normal file
24
tests/pytests/pkg/integration/test_salt_state_file.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skip_on_windows,
|
||||
]
|
||||
|
||||
|
||||
def test_salt_state_file(salt_cli, salt_minion):
|
||||
"""
|
||||
Test state file
|
||||
"""
|
||||
if sys.platform.startswith("win"):
|
||||
ret = salt_cli.run("state.apply", "win_states", minion_tgt=salt_minion.id)
|
||||
else:
|
||||
ret = salt_cli.run("state.apply", "states", minion_tgt=salt_minion.id)
|
||||
|
||||
assert ret.data, ret
|
||||
if ret.stdout and "Minion did not return" in ret.stdout:
|
||||
pytest.skip("Skipping test, state took too long to apply")
|
||||
sls_ret = ret.data[next(iter(ret.data))]
|
||||
assert "changes" in sls_ret
|
||||
assert "name" in sls_ret
|
38
tests/pytests/pkg/integration/test_salt_ufw.py
Normal file
38
tests/pytests/pkg/integration/test_salt_ufw.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
import pathlib
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.skip_on_windows
|
||||
@pytest.mark.skip_if_binaries_missing("ufw")
|
||||
def test_salt_ufw(salt_master, salt_call_cli, install_salt):
|
||||
"""
|
||||
Test salt.ufw for Debian/Ubuntu salt-master
|
||||
"""
|
||||
if install_salt.distro_id not in ("debian", "ubuntu"):
|
||||
pytest.skip("Only tests Debian / Ubuntu packages")
|
||||
|
||||
# check that the salt_master is running
|
||||
assert salt_master.is_running()
|
||||
|
||||
ufw_master_path = pathlib.Path("/etc/ufw/applications.d/salt.ufw")
|
||||
assert ufw_master_path.exists()
|
||||
assert ufw_master_path.is_file()
|
||||
|
||||
ufw_list_cmd = "/usr/sbin/ufw app list"
|
||||
ret = salt_call_cli.run("--local", "cmd.run", ufw_list_cmd)
|
||||
assert "Available applications" in ret.stdout
|
||||
assert "Salt" in ret.stdout
|
||||
ufw_upd_cmd = "/usr/sbin/ufw app update Salt"
|
||||
ret = salt_call_cli.run("--local", "cmd.run", ufw_upd_cmd)
|
||||
assert ret.returncode == 0
|
||||
expected_info = """Profile: Salt
|
||||
Title: salt
|
||||
Description: fast and powerful configuration management and remote
|
||||
execution
|
||||
|
||||
Ports:
|
||||
4505,4506/tcp"""
|
||||
ufw_info_cmd = "/usr/sbin/ufw app info Salt"
|
||||
ret = salt_call_cli.run("--local", "cmd.run", ufw_info_cmd)
|
||||
assert expected_info in ret.data
|
359
tests/pytests/pkg/integration/test_salt_user.py
Normal file
359
tests/pytests/pkg/integration/test_salt_user.py
Normal file
|
@ -0,0 +1,359 @@
|
|||
import os
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import packaging.version
|
||||
import psutil
|
||||
import pytest
|
||||
from saltfactories.utils.tempfiles import temp_directory
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skip_on_windows,
|
||||
pytest.mark.skip_on_darwin,
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pkg_paths():
|
||||
"""
|
||||
Paths created by package installs
|
||||
"""
|
||||
paths = [
|
||||
"/etc/salt",
|
||||
"/var/cache/salt",
|
||||
"/var/log/salt",
|
||||
"/var/run/salt",
|
||||
"/opt/saltstack/salt",
|
||||
]
|
||||
return paths
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pkg_paths_salt_user():
|
||||
"""
|
||||
Paths created by package installs and owned by salt user
|
||||
"""
|
||||
return [
|
||||
"/etc/salt/cloud.deploy.d",
|
||||
"/var/log/salt/cloud",
|
||||
"/opt/saltstack/salt/lib/python{}.{}/site-packages/salt/cloud/deploy".format(
|
||||
*sys.version_info
|
||||
),
|
||||
"/etc/salt/pki/master",
|
||||
"/etc/salt/master.d",
|
||||
"/var/log/salt/master",
|
||||
"/var/log/salt/api",
|
||||
"/var/log/salt/key",
|
||||
"/var/cache/salt/master",
|
||||
"/var/run/salt/master",
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pkg_paths_salt_user_exclusions():
|
||||
"""
|
||||
Exclusions from paths created by package installs and owned by salt user
|
||||
"""
|
||||
paths = [
|
||||
"/var/cache/salt/master/.root_key" # written by salt, salt-run and salt-key as root
|
||||
]
|
||||
return paths
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _skip_on_non_relenv(install_salt):
|
||||
if not install_salt.relenv:
|
||||
pytest.skip("The salt user only exists on relenv versions of salt")
|
||||
|
||||
|
||||
def test_salt_user_master(salt_master, install_salt):
|
||||
"""
|
||||
Test the correct user is running the Salt Master
|
||||
"""
|
||||
match = False
|
||||
for proc in psutil.Process(salt_master.pid).children():
|
||||
assert proc.username() == "salt"
|
||||
match = True
|
||||
|
||||
assert match
|
||||
|
||||
|
||||
def test_salt_user_home(install_salt):
|
||||
"""
|
||||
Test the salt user's home is /opt/saltstack/salt
|
||||
"""
|
||||
proc = subprocess.run(
|
||||
["getent", "passwd", "salt"], check=False, capture_output=True
|
||||
)
|
||||
assert proc.returncode == 0
|
||||
home = ""
|
||||
try:
|
||||
home = proc.stdout.decode().split(":")[5]
|
||||
except Exception: # pylint: disable=broad-except
|
||||
pass
|
||||
assert home == "/opt/saltstack/salt"
|
||||
|
||||
|
||||
def test_salt_user_group(install_salt):
|
||||
"""
|
||||
Test the salt user is in the salt group
|
||||
"""
|
||||
proc = subprocess.run(["id", "salt"], check=False, capture_output=True)
|
||||
assert proc.returncode == 0
|
||||
in_group = False
|
||||
try:
|
||||
for group in proc.stdout.decode().split(" "):
|
||||
if "salt" in group:
|
||||
in_group = True
|
||||
except Exception: # pylint: disable=broad-except
|
||||
pass
|
||||
assert in_group is True
|
||||
|
||||
|
||||
def test_salt_user_shell(install_salt):
|
||||
"""
|
||||
Test the salt user's login shell
|
||||
"""
|
||||
proc = subprocess.run(
|
||||
["getent", "passwd", "salt"], check=False, capture_output=True
|
||||
)
|
||||
assert proc.returncode == 0
|
||||
shell = ""
|
||||
shell_exists = False
|
||||
try:
|
||||
shell = proc.stdout.decode().split(":")[6].strip()
|
||||
shell_exists = pathlib.Path(shell).exists()
|
||||
except Exception: # pylint: disable=broad-except
|
||||
pass
|
||||
assert shell_exists is True
|
||||
|
||||
|
||||
def test_pkg_paths(
|
||||
install_salt, pkg_paths, pkg_paths_salt_user, pkg_paths_salt_user_exclusions
|
||||
):
|
||||
"""
|
||||
Test package paths ownership
|
||||
"""
|
||||
if packaging.version.parse(install_salt.version) <= packaging.version.parse(
|
||||
"3006.4"
|
||||
):
|
||||
pytest.skip("Package path ownership was changed in salt 3006.4")
|
||||
salt_user_subdirs = []
|
||||
for _path in pkg_paths:
|
||||
pkg_path = pathlib.Path(_path)
|
||||
assert pkg_path.exists()
|
||||
for dirpath, sub_dirs, files in os.walk(pkg_path):
|
||||
path = pathlib.Path(dirpath)
|
||||
# Directories owned by salt:salt or their subdirs/files
|
||||
if (
|
||||
str(path) in pkg_paths_salt_user or str(path) in salt_user_subdirs
|
||||
) and str(path) not in pkg_paths_salt_user_exclusions:
|
||||
assert path.owner() == "salt"
|
||||
assert path.group() == "salt"
|
||||
salt_user_subdirs.extend(
|
||||
[str(path.joinpath(sub_dir)) for sub_dir in sub_dirs]
|
||||
)
|
||||
# Individual files owned by salt user
|
||||
for file in files:
|
||||
file_path = path.joinpath(file)
|
||||
if str(file_path) not in pkg_paths_salt_user_exclusions:
|
||||
assert file_path.owner() == "salt"
|
||||
# Directories owned by root:root
|
||||
else:
|
||||
assert path.owner() == "root"
|
||||
assert path.group() == "root"
|
||||
for file in files:
|
||||
file_path = path.joinpath(file)
|
||||
# Individual files owned by salt user
|
||||
if str(file_path) in pkg_paths_salt_user:
|
||||
assert file_path.owner() == "salt"
|
||||
else:
|
||||
assert file_path.owner() == "root"
|
||||
assert file_path.group() == "root"
|
||||
|
||||
|
||||
@pytest.mark.skip_if_binaries_missing("logrotate")
|
||||
def test_paths_log_rotation(
|
||||
salt_master, salt_minion, salt_call_cli, install_salt, test_account
|
||||
):
|
||||
"""
|
||||
Test the correct ownership is assigned when log rotation occurs
|
||||
Change the user in the Salt Master, chage ownership, force logrotation
|
||||
Check ownership and premissions.
|
||||
Assumes test_pkg_paths successful
|
||||
"""
|
||||
if packaging.version.parse(install_salt.version) <= packaging.version.parse(
|
||||
"3006.4"
|
||||
):
|
||||
pytest.skip("Package path ownership was changed in salt 3006.4")
|
||||
|
||||
if install_salt.distro_id not in ("centos", "redhat", "amzn", "fedora"):
|
||||
pytest.skip(
|
||||
"Only tests RedHat family packages till logrotation paths are resolved on Ubuntu/Debian, see issue 65231"
|
||||
)
|
||||
|
||||
# check that the salt_master is running
|
||||
assert salt_master.is_running()
|
||||
match = False
|
||||
for proc in psutil.Process(salt_master.pid).children():
|
||||
assert proc.username() == "salt"
|
||||
match = True
|
||||
|
||||
assert match
|
||||
|
||||
# Paths created by package installs with adjustment for current conf_dir /etc/salt
|
||||
log_pkg_paths = [
|
||||
install_salt.conf_dir, # "bkup0"
|
||||
"/var/cache/salt", # "bkup1"
|
||||
"/var/log/salt", # "bkup2"
|
||||
"/var/run/salt", # "bkup3"
|
||||
"/opt/saltstack/salt", # "bkup4"
|
||||
]
|
||||
|
||||
# backup those about to change
|
||||
bkup_count = 0
|
||||
bkup_count_max = 5
|
||||
with temp_directory("bkup0") as temp_dir_path_0:
|
||||
with temp_directory("bkup1") as temp_dir_path_1:
|
||||
with temp_directory("bkup2") as temp_dir_path_2:
|
||||
with temp_directory("bkup3") as temp_dir_path_3:
|
||||
with temp_directory("bkup4") as temp_dir_path_4:
|
||||
|
||||
assert temp_dir_path_0.is_dir()
|
||||
assert temp_dir_path_1.is_dir()
|
||||
assert temp_dir_path_2.is_dir()
|
||||
assert temp_dir_path_3.is_dir()
|
||||
assert temp_dir_path_4.is_dir()
|
||||
|
||||
# stop the salt_master, so can change user
|
||||
with salt_master.stopped():
|
||||
assert salt_master.is_running() is False
|
||||
|
||||
for _path in log_pkg_paths:
|
||||
if bkup_count == 0:
|
||||
cmd_to_run = (
|
||||
f"cp -a {_path}/* {str(temp_dir_path_0)}/"
|
||||
)
|
||||
elif bkup_count == 1:
|
||||
cmd_to_run = (
|
||||
f"cp -a {_path}/* {str(temp_dir_path_1)}/"
|
||||
)
|
||||
elif bkup_count == 2:
|
||||
cmd_to_run = (
|
||||
f"cp -a {_path}/* {str(temp_dir_path_2)}/"
|
||||
)
|
||||
elif bkup_count == 3:
|
||||
cmd_to_run = (
|
||||
f"cp -a {_path}/* {str(temp_dir_path_3)}/"
|
||||
)
|
||||
elif bkup_count == 4:
|
||||
cmd_to_run = (
|
||||
f"cp -a {_path}/* {str(temp_dir_path_4)}/"
|
||||
)
|
||||
elif bkup_count > 5:
|
||||
assert bkupcount < bkup_count_max # force assertion
|
||||
|
||||
ret = salt_call_cli.run(
|
||||
"--local", "cmd.run", cmd_to_run
|
||||
)
|
||||
bkup_count += 1
|
||||
assert ret.returncode == 0
|
||||
|
||||
# change the user in the master's config file.
|
||||
ret = salt_call_cli.run(
|
||||
"--local",
|
||||
"file.replace",
|
||||
f"{install_salt.conf_dir}/master",
|
||||
"user: salt",
|
||||
f"user: {test_account.username}",
|
||||
"flags=['IGNORECASE']",
|
||||
"append_if_not_found=True",
|
||||
)
|
||||
assert ret.returncode == 0
|
||||
|
||||
# change ownership of appropriate paths to user
|
||||
for _path in log_pkg_paths:
|
||||
chg_ownership_cmd = (
|
||||
f"chown -R {test_account.username} {_path}"
|
||||
)
|
||||
ret = salt_call_cli.run(
|
||||
"--local", "cmd.run", chg_ownership_cmd
|
||||
)
|
||||
assert ret.returncode == 0
|
||||
|
||||
# restart the salt_master
|
||||
with salt_master.started():
|
||||
assert salt_master.is_running() is True
|
||||
|
||||
# ensure some data in files
|
||||
log_files_list = [
|
||||
"/var/log/salt/api",
|
||||
"/var/log/salt/key",
|
||||
"/var/log/salt/master",
|
||||
]
|
||||
for _path in log_files_list:
|
||||
log_path = pathlib.Path(_path)
|
||||
assert log_path.exists()
|
||||
with log_path.open("a") as f:
|
||||
f.write("This is a log rotation test\n")
|
||||
|
||||
# force log rotation
|
||||
logr_conf_file = "/etc/logrotate.d/salt"
|
||||
logr_conf_path = pathlib.Path(logr_conf_file)
|
||||
if not logr_conf_path.exists():
|
||||
logr_conf_file = "/etc/logrotate.conf"
|
||||
logr_conf_path = pathlib.Path(logr_conf_file)
|
||||
assert logr_conf_path.exists()
|
||||
|
||||
# force log rotation
|
||||
log_rotate_cmd = f"logrotate -f {logr_conf_file}"
|
||||
ret = salt_call_cli.run(
|
||||
"--local", "cmd.run", log_rotate_cmd
|
||||
)
|
||||
assert ret.returncode == 0
|
||||
|
||||
for _path in log_files_list:
|
||||
log_path = pathlib.Path(_path)
|
||||
assert log_path.exists()
|
||||
assert log_path.owner() == test_account.username
|
||||
assert log_path.stat().st_mode & 0o7777 == 0o640
|
||||
|
||||
# cleanup
|
||||
assert salt_master.is_running() is False
|
||||
|
||||
# change the user in the master's config file.
|
||||
ret = salt_call_cli.run(
|
||||
"--local",
|
||||
"file.replace",
|
||||
f"{install_salt.conf_dir}/master",
|
||||
f"user: {test_account.username}",
|
||||
"user: salt",
|
||||
"flags=['IGNORECASE']",
|
||||
"append_if_not_found=True",
|
||||
)
|
||||
assert ret.returncode == 0
|
||||
|
||||
# restore from backed up
|
||||
bkup_count = 0
|
||||
for _path in log_pkg_paths:
|
||||
if bkup_count == 0:
|
||||
cmd_to_run = f"cp -a --force {str(temp_dir_path_0)}/* {_path}/"
|
||||
elif bkup_count == 1:
|
||||
cmd_to_run = f"cp -a --force {str(temp_dir_path_1)}/* {_path}/"
|
||||
elif bkup_count == 2:
|
||||
cmd_to_run = f"cp -a --force {str(temp_dir_path_2)}/* {_path}/"
|
||||
elif bkup_count == 3:
|
||||
cmd_to_run = f"cp -a --force {str(temp_dir_path_3)}/* {_path}/"
|
||||
elif bkup_count == 4:
|
||||
# use --update since /opt/saltstack/salt and would get SIGSEGV since mucking with running code
|
||||
cmd_to_run = f"cp -a --update --force {str(temp_dir_path_4)}/* {_path}/"
|
||||
elif bkup_count > 5:
|
||||
assert bkupcount < bkup_count_max # force assertion
|
||||
|
||||
ret = salt_call_cli.run(
|
||||
"--local", "cmd.run", cmd_to_run
|
||||
)
|
||||
|
||||
bkup_count += 1
|
||||
assert ret.returncode == 0
|
15
tests/pytests/pkg/integration/test_ssm.py
Normal file
15
tests/pytests/pkg/integration/test_ssm.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skip_unless_on_windows,
|
||||
]
|
||||
|
||||
|
||||
def test_ssm_present(install_salt):
|
||||
"""
|
||||
The ssm.exe binary needs to be present in both the zip and the exe/msi
|
||||
builds
|
||||
"""
|
||||
assert os.path.exists(install_salt.ssm_bin)
|
42
tests/pytests/pkg/integration/test_systemd_config.py
Normal file
42
tests/pytests/pkg/integration/test_systemd_config.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skip_on_windows(reason="Linux test only"),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("salt_minion")
|
||||
def test_system_config(grains):
|
||||
"""
|
||||
Test system config
|
||||
"""
|
||||
if grains["os_family"] == "RedHat":
|
||||
if grains["osfinger"] in (
|
||||
"CentOS Stream-8",
|
||||
"CentOS Linux-8",
|
||||
"CentOS Stream-9",
|
||||
"Fedora Linux-36",
|
||||
"VMware Photon OS-3",
|
||||
"VMware Photon OS-4",
|
||||
"VMware Photon OS-5",
|
||||
"Amazon Linux-2023",
|
||||
):
|
||||
expected_retcode = 0
|
||||
else:
|
||||
expected_retcode = 1
|
||||
ret = subprocess.call(
|
||||
"systemctl show -p ${config} salt-minion.service", shell=True
|
||||
)
|
||||
assert ret == expected_retcode
|
||||
|
||||
elif grains["os_family"] == "Debian":
|
||||
if grains["osfinger"] == "Debian-9":
|
||||
expected_retcode = 1
|
||||
else:
|
||||
expected_retcode = 0
|
||||
ret = subprocess.call(
|
||||
"systemctl show -p ${config} salt-minion.service", shell=True
|
||||
)
|
||||
assert ret == expected_retcode
|
142
tests/pytests/pkg/integration/test_version.py
Normal file
142
tests/pytests/pkg/integration/test_version.py
Normal file
|
@ -0,0 +1,142 @@
|
|||
import os.path
|
||||
import pathlib
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
from pytestskipmarkers.utils import platform
|
||||
|
||||
|
||||
@pytest.mark.skip_on_windows
|
||||
def test_salt_version(version, install_salt):
|
||||
"""
|
||||
Test version output from salt --version
|
||||
"""
|
||||
test_bin = os.path.join(*install_salt.binary_paths["salt"])
|
||||
ret = install_salt.proc.run(test_bin, "--version")
|
||||
actual = ret.stdout.strip().split(" ")[:2]
|
||||
expected = ["salt", version]
|
||||
assert actual == expected
|
||||
|
||||
|
||||
@pytest.mark.skip_on_windows
|
||||
def test_salt_versions_report_master(install_salt):
|
||||
"""
|
||||
Test running --versions-report on master
|
||||
"""
|
||||
if not install_salt.relenv and not install_salt.classic:
|
||||
pytest.skip("Unable to get the python version dynamically from tiamat builds")
|
||||
test_bin = os.path.join(*install_salt.binary_paths["master"])
|
||||
python_bin = os.path.join(*install_salt.binary_paths["python"])
|
||||
ret = install_salt.proc.run(test_bin, "--versions-report")
|
||||
ret.stdout.matcher.fnmatch_lines(["*Salt Version:*"])
|
||||
py_version = subprocess.run(
|
||||
[str(python_bin), "--version"],
|
||||
check=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
).stdout
|
||||
py_version = py_version.decode().strip().replace(" ", ": ")
|
||||
ret.stdout.matcher.fnmatch_lines([f"*{py_version}*"])
|
||||
|
||||
|
||||
@pytest.mark.skip_on_windows
|
||||
def test_salt_versions_report_minion(salt_cli, salt_minion):
|
||||
"""
|
||||
Test running test.versions_report on minion
|
||||
"""
|
||||
# Make sure the minion is running
|
||||
assert salt_minion.is_running()
|
||||
# Make sure we can ping the minion ...
|
||||
ret = salt_cli.run(
|
||||
"--timeout=240", "test.ping", minion_tgt=salt_minion.id, _timeout=240
|
||||
)
|
||||
assert ret.returncode == 0
|
||||
assert ret.data is True
|
||||
ret = salt_cli.run(
|
||||
"--hard-crash",
|
||||
"--failhard",
|
||||
"--timeout=240",
|
||||
"test.versions_report",
|
||||
minion_tgt=salt_minion.id,
|
||||
_timeout=240,
|
||||
)
|
||||
ret.stdout.matcher.fnmatch_lines(["*Salt Version:*"])
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"binary", ["master", "cloud", "syndic", "minion", "call", "api"]
|
||||
)
|
||||
def test_compare_versions(version, binary, install_salt):
|
||||
"""
|
||||
Test compare versions
|
||||
"""
|
||||
if binary in install_salt.binary_paths:
|
||||
ret = install_salt.proc.run(
|
||||
*install_salt.binary_paths[binary],
|
||||
"--version",
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
ret.stdout.matcher.fnmatch_lines([f"*{version}*"])
|
||||
else:
|
||||
if platform.is_windows():
|
||||
pytest.skip(f"Binary not available on windows: {binary}")
|
||||
pytest.fail(
|
||||
f"Platform is not Windows and yet the binary {binary!r} is not available"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip_unless_on_darwin()
|
||||
@pytest.mark.parametrize(
|
||||
"symlink",
|
||||
[
|
||||
# We can't create a salt symlink because there is a salt directory
|
||||
"salt",
|
||||
"salt-api",
|
||||
"salt-call",
|
||||
"salt-cloud",
|
||||
"salt-cp",
|
||||
"salt-key",
|
||||
"salt-master",
|
||||
"salt-minion",
|
||||
"salt-proxy",
|
||||
"salt-run",
|
||||
"spm",
|
||||
"salt-ssh",
|
||||
"salt-syndic",
|
||||
],
|
||||
)
|
||||
def test_symlinks_created(version, symlink, install_salt):
|
||||
"""
|
||||
Test symlinks created
|
||||
"""
|
||||
if install_salt.classic:
|
||||
pytest.skip("Symlinks not created for classic macos builds, we adjust the path")
|
||||
if not install_salt.relenv and symlink == "spm":
|
||||
symlink = "salt-spm"
|
||||
ret = install_salt.proc.run(pathlib.Path("/usr/local/sbin") / symlink, "--version")
|
||||
ret.stdout.matcher.fnmatch_lines([f"*{version}*"])
|
||||
|
||||
|
||||
@pytest.mark.skip_on_windows()
|
||||
def test_compare_pkg_versions_redhat_rc(version, install_salt):
|
||||
"""
|
||||
Test compare pkg versions for redhat RC packages. A tilde should be included
|
||||
in RC Packages and it should test to be a lower version than a non RC
|
||||
package of the same version. For example, v3004~rc1 should be less than
|
||||
v3004.
|
||||
"""
|
||||
if install_salt.distro_id not in ("centos", "redhat", "amzn", "fedora", "photon"):
|
||||
pytest.skip("Only tests rpm packages")
|
||||
|
||||
pkg = [x for x in install_salt.pkgs if "rpm" in x]
|
||||
if not pkg:
|
||||
pytest.skip("Not testing rpm packages")
|
||||
pkg = pkg[0].split("/")[-1]
|
||||
if "rc" not in ".".join(pkg.split(".")[:2]):
|
||||
pytest.skip("Not testing an RC package")
|
||||
assert "~" in pkg
|
||||
comp_pkg = pkg.split("~")[0]
|
||||
ret = install_salt.proc.run("rpmdev-vercmp", pkg, comp_pkg)
|
||||
ret.stdout.matcher.fnmatch_lines([f"{pkg} < {comp_pkg}"])
|
0
tests/pytests/pkg/upgrade/__init__.py
Normal file
0
tests/pytests/pkg/upgrade/__init__.py
Normal file
44
tests/pytests/pkg/upgrade/test_salt_upgrade.py
Normal file
44
tests/pytests/pkg/upgrade/test_salt_upgrade.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
import packaging.version
|
||||
import pytest
|
||||
|
||||
|
||||
def test_salt_upgrade(salt_call_cli, install_salt):
|
||||
"""
|
||||
Test an upgrade of Salt.
|
||||
"""
|
||||
if not install_salt.upgrade:
|
||||
pytest.skip("Not testing an upgrade, do not run")
|
||||
|
||||
if install_salt.relenv:
|
||||
original_py_version = install_salt.package_python_version()
|
||||
|
||||
# Verify previous install version is setup correctly and works
|
||||
ret = salt_call_cli.run("test.version")
|
||||
assert ret.returncode == 0
|
||||
assert packaging.version.parse(ret.data) < packaging.version.parse(
|
||||
install_salt.artifact_version
|
||||
)
|
||||
|
||||
# Test pip install before an upgrade
|
||||
dep = "PyGithub==1.56.0"
|
||||
install = salt_call_cli.run("--local", "pip.install", dep)
|
||||
assert install.returncode == 0
|
||||
|
||||
# Verify we can use the module dependent on the installed package
|
||||
repo = "https://github.com/saltstack/salt.git"
|
||||
use_lib = salt_call_cli.run("--local", "github.get_repo_info", repo)
|
||||
assert "Authentication information could" in use_lib.stderr
|
||||
|
||||
# Upgrade Salt from previous version and test
|
||||
install_salt.install(upgrade=True)
|
||||
ret = salt_call_cli.run("test.version")
|
||||
assert ret.returncode == 0
|
||||
assert packaging.version.parse(ret.data) == packaging.version.parse(
|
||||
install_salt.artifact_version
|
||||
)
|
||||
|
||||
if install_salt.relenv:
|
||||
new_py_version = install_salt.package_python_version()
|
||||
if new_py_version == original_py_version:
|
||||
# test pip install after an upgrade
|
||||
use_lib = salt_call_cli.run("--local", "github.get_repo_info", repo)
|
Loading…
Add table
Reference in a new issue