Skip to content

Commit

Permalink
The spm CLI now properly lays down the configuration files required
Browse files Browse the repository at this point in the history
Fixes #137

Signed-off-by: Pedro Algarvio <[email protected]>
  • Loading branch information
s0undt3ch committed Aug 25, 2022
1 parent b3df681 commit 41c201f
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 9 deletions.
1 change: 1 addition & 0 deletions changelog/137.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The `spm` CLI now properly lays down the configuration files required
21 changes: 15 additions & 6 deletions src/saltfactories/bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ class SaltMixin:
salt system paths apply.
"""

id = attr.ib(default=None, init=False) # pylint: disable=invalid-name
config = attr.ib(repr=False)
config_dir = attr.ib(init=False, default=None)
config_file = attr.ib(init=False, default=None)
id = attr.ib(init=False) # pylint: disable=invalid-name
config_file = attr.ib(init=False)
config_dir = attr.ib()
python_executable = attr.ib(default=None)
system_service = attr.ib(repr=False, default=False)
display_name = attr.ib(init=False, default=None)
Expand All @@ -68,11 +68,20 @@ def __attrs_post_init__(self):
self.environ.setdefault("PYTHONUNBUFFERED", "1")
# Don't write .pyc files or create them in __pycache__ directories
self.environ.setdefault("PYTHONDONTWRITEBYTECODE", "1")
self.config_file = self.config["conf_file"]
self.config_dir = os.path.dirname(self.config_file)
self.id = self.config["id"]
self.config = freeze(self.config)

@config_file.default
def _default_config_file(self):
return self.config["conf_file"]

@config_dir.default
def _default_config_dir(self):
return os.path.dirname(self.config_file)

@id.default
def _default_id(self):
return self.config["id"]

def get_display_name(self):
"""
Returns a human readable name for the factory.
Expand Down
121 changes: 121 additions & 0 deletions src/saltfactories/cli/spm.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
"""
``spm`` CLI factory.
"""
import logging
import pathlib
import pprint
import urllib.parse

import attr
import salt.config
import salt.utils.dictupdate
import salt.utils.files
import salt.utils.yaml

from saltfactories.bases import SaltCli
from saltfactories.utils import running_username

log = logging.getLogger(__name__)


@attr.s(kw_only=True, slots=True)
Expand All @@ -19,3 +31,112 @@ def get_minion_tgt(self, minion_tgt=None):
Overridden method because spm does not target minions.
"""
return None

@staticmethod
def default_config(root_dir, master_factory, defaults=None, overrides=None):
"""
Return the default configuration for the daemon.
"""
if defaults is None:
defaults = {}

master_conf_dir = pathlib.Path(master_factory.config_dir)
conf_dir = master_conf_dir / "master.d"
conf_dir.mkdir(parents=True, exist_ok=True)
conf_file = str(conf_dir / "spm.conf")

_defaults = {
"spm_conf_file": conf_file,
"root_dir": str(root_dir),
"formula_path": "srv/formulas",
"pillar_path": "srv/pillar",
"reactor_path": "src/reactor",
"spm_repos_config": str(master_conf_dir / "spm.repos"),
"spm_cache_dir": "cache/spm",
"spm_build_dir": "srv/spm_build",
"spm_db": "cache/spm/packages.db",
"spm_share_dir": "share/spm",
"spm_logfile": "logs/spm.log",
"spm_log_level_logfile": "debug",
"pytest-spm": {
"master-id": master_factory.id,
"log": {"prefix": "{{cli_name}}({})".format(master_factory.id)},
},
}
# Merge in the initial default options with the internal _defaults
salt.utils.dictupdate.update(defaults, _defaults, merge_lists=True)

if overrides:
# Merge in the default options with the master_overrides
salt.utils.dictupdate.update(defaults, overrides, merge_lists=True)

return defaults

@classmethod
def configure(
cls,
master_factory,
root_dir=None,
defaults=None,
overrides=None,
):
"""
Configure the CLI.
"""
return cls.default_config(root_dir, master_factory, defaults=defaults, overrides=overrides)

@classmethod
def verify_config(cls, config):
"""
Verify the configuration dictionary.
"""
prepend_root_dirs = [
"formula_path",
"pillar_path",
"reactor_path",
"spm_db",
"spm_cache_dir",
"spm_build_dir",
"spm_share_dir",
]
for config_key in ("spm_logfile",):
if urllib.parse.urlparse(config.get(config_key, "")).scheme == "":
prepend_root_dirs.append(config_key)
if prepend_root_dirs:
salt.config.prepend_root_dir(config, prepend_root_dirs)
salt.utils.verify.verify_env(
[
str(pathlib.Path(config["spm_logfile"]).parent),
str(pathlib.Path(config["spm_db"]).parent),
config["formula_path"],
config["pillar_path"],
config["reactor_path"],
config["spm_cache_dir"],
config["spm_build_dir"],
config["spm_share_dir"],
],
running_username(),
pki_dir=config.get("pki_dir") or "",
root_dir=config["root_dir"],
)

@classmethod
def write_config(cls, config):
"""
Verify the loaded configuration.
"""
cls.verify_config(config)
config_file = config.pop("spm_conf_file")
log.debug(
"Writing to configuration file %s. Configuration:\n%s",
config_file,
pprint.pformat(config),
)

# Write down the computed configuration into the config file
with salt.utils.files.fopen(config_file, "w") as wfh:
salt.utils.yaml.safe_dump(config, wfh, default_flow_style=False)
# We load the master config, which will include the spm config
return salt.config.spm_config(
str(pathlib.Path(config_file).parent.parent / "master"),
)
16 changes: 14 additions & 2 deletions src/saltfactories/daemons/master.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,14 +427,26 @@ def salt_run_cli(self, factory_class=cli.run.SaltRun, **factory_class_kwargs):
**factory_class_kwargs,
)

def salt_spm_cli(self, factory_class=cli.spm.Spm, **factory_class_kwargs):
def salt_spm_cli(
self, defaults=None, overrides=None, factory_class=cli.spm.Spm, **factory_class_kwargs
):
"""
Return a `spm` CLI process for this master instance.
"""
root_dir = pathlib.Path(self.config["root_dir"])
config = factory_class.configure(
self,
root_dir=root_dir,
defaults=defaults,
overrides=overrides,
)
self.factories_manager.final_spm_config_tweaks(config)
config = factory_class.write_config(config)
script_path = self.factories_manager.get_salt_script_path("spm")
return factory_class(
script_name=script_path,
config=self.config.copy(),
config=config,
config_dir=self.config_dir,
system_service=self.factories_manager.system_service,
python_executable=self.python_executable,
**factory_class_kwargs,
Expand Down
6 changes: 6 additions & 0 deletions src/saltfactories/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@ def final_cloud_config_tweaks(self, config):
"""
self.final_common_config_tweaks(config, "cloud")

def final_spm_config_tweaks(self, config):
"""
Final tweaks to the spm configuration.
"""
self.final_common_config_tweaks(config, "spm")

def final_common_config_tweaks(self, config, role):
"""
Final common tweaks to the configuration.
Expand Down
6 changes: 5 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,16 @@ def pkg_version(package):
return pkg_resources.get_distribution(package).version


# Define the pytest plugins we rely on
pytest_plugins = ["helpers_namespace"]


def pkg_version_info(package):
return tuple(int(part) for part in pkg_version(package).split(".") if part.isdigit())


if pkg_version_info("pytest") >= (6, 2):
pytest_plugins = ["pytester"]
pytest_plugins.append("pytester")
else:

@pytest.fixture
Expand Down
43 changes: 43 additions & 0 deletions tests/functional/factories/cli/test_spm.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,52 @@
"""
import pathlib

import pytest


@pytest.fixture
def spm_formulas_dir(salt_master):
formula_sls = """
install-apache:
pkg.installed:
- name: apache2
"""
formula = """
name: apache
os: RedHat, Debian, Ubuntu, Suse, FreeBSD
os_family: RedHat, Debian, Suse, FreeBSD
version: 201506
release: 2
summary: Formula for installing Apache
description: Formula for installing Apache
"""
with salt_master.state_tree.base.temp_file(
"formulas/apache/apache.sls", formula_sls
), salt_master.state_tree.base.temp_file("formulas/FORMULA", formula):
yield salt_master.state_tree.base.write_path / "formulas"


def test_version_info(salt_master, salt_version):
cli = salt_master.salt_spm_cli()
ret = cli.run("--version")
assert ret.returncode == 0, ret
assert ret.stdout.strip() == "{} {}".format(pathlib.Path(cli.script_name).name, salt_version)


def test_build_and_install(salt_master, spm_formulas_dir):
cli = salt_master.salt_spm_cli()
spm_build_dir = pathlib.Path(cli.config["spm_build_dir"])
formula_path = pathlib.Path(cli.config["formula_path"])
ret = cli.run(
"build",
str(spm_formulas_dir),
)
assert ret.returncode == 0

ret = cli.run(
"install",
str(spm_build_dir / "apache-201506-2.spm"),
"-y",
)
assert ret.returncode == 0
assert formula_path.joinpath("apache", "apache.sls").exists()

0 comments on commit 41c201f

Please sign in to comment.