From 38f596420532cdcb6c59394feac955fc0894a08b Mon Sep 17 00:00:00 2001 From: Achilleas Koutsou Date: Wed, 20 Mar 2024 13:45:18 +0100 Subject: [PATCH] tools/osbuild-depsolve-dnf: load repos from dir Support loading repositories from a root tree instead of supplying them with the request. The repositories should be in the standard yum repo format. Both repository sources can be defined simultaneously, but at least one is required. The root_dir is expected to contain files necessary for depsolving in the standard paths. These files are: - Repository (.repo) configurations in /etc/yum.repos.d/ - GPG key files in /etc/pki/rpm-gpg/ - This will be used to resolve gpg key paths specified in the .repo files that are relative to the root_dir. - (Optional) Custom dnf config variables in /etc/dnf/vars or /etc/yum/vars. - This is used by CentOS Stream to set the value of $stream. Custom repository configurations in arbitrary (non-root) paths will have to follow this directory structure. A new variable is added to the request, `releasever`, which is mandatory when using `root_dir`. This variable is used in repository URLs and GPG key paths. In the default case, dnf reads this variable by inspecting the rpm database. We will override it in the Solver the same way we override the arch and basearch for variable substitution. In the future, we will make this variable mandatory in all cases, which will make the variable available for repo configs defined in the request as well. The root_dir is used in two ways: - Set the base.conf.reposdir to /etc/yum.repos.d. - Call update_from_etc() with root_dir to read custom variables in /etc/yum/vars and /etc/dnf/vars. --- tools/osbuild-depsolve-dnf | 51 +++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/tools/osbuild-depsolve-dnf b/tools/osbuild-depsolve-dnf index 2cda3316..1533a9a2 100755 --- a/tools/osbuild-depsolve-dnf +++ b/tools/osbuild-depsolve-dnf @@ -20,8 +20,15 @@ import hawkey class Solver(): - # pylint: disable=too-many-arguments - def __init__(self, repos, module_platform_id, persistdir, cachedir, arch): + def __init__(self, request, persistdir, cache_dir): + arch = request["arch"] + releasever = request.get("releasever") + module_platform_id = request["module_platform_id"] + + arguments = request["arguments"] + repos = arguments.get("repos", []) + root_dir = arguments.get("root_dir") + self.base = dnf.Base() # Enable fastestmirror to ensure we choose the fastest mirrors for @@ -49,12 +56,24 @@ class Solver(): self.base.conf.module_platform_id = module_platform_id self.base.conf.config_file_path = "/dev/null" self.base.conf.persistdir = persistdir - self.base.conf.cachedir = cachedir + self.base.conf.cachedir = cache_dir self.base.conf.substitutions['arch'] = arch self.base.conf.substitutions['basearch'] = dnf.rpm.basearch(arch) + if releasever: + self.base.conf.substitutions['releasever'] = releasever for repo in repos: self.base.repos.add(self._dnfrepo(repo, self.base.conf)) + + if root_dir: + # This sets the varsdir to ("{root_dir}/etc/yum/vars/", "{root_dir}/etc/dnf/vars/") for custom variable + # substitution (e.g. CentOS Stream 9's $stream variable) + self.base.conf.substitutions.update_from_etc(root_dir) + + repos_dir = os.path.join(root_dir, "etc/yum.repos.d") + self.base.conf.reposdir = repos_dir + self.base.read_all_repos() + self.base.fill_sack(load_system_repo=False) # pylint: disable=too-many-branches @@ -271,20 +290,12 @@ def setup_cachedir(request): def solve(request, cache_dir): command = request["command"] - arch = request["arch"] - module_platform_id = request["module_platform_id"] arguments = request["arguments"] transactions = arguments.get("transactions") with tempfile.TemporaryDirectory() as persistdir: try: - solver = Solver( - arguments["repos"], - module_platform_id, - persistdir, - cache_dir, - arch - ) + solver = Solver(request, persistdir, cache_dir) if command == "dump": result = solver.dump() elif command == "depsolve": @@ -336,6 +347,7 @@ def respond(result): print(json.dumps(result)) +# pylint: disable=too-many-return-statements def validate_request(request): command = request.get("command") valid_cmds = ("depsolve", "dump", "search") @@ -356,6 +368,7 @@ def validate_request(request): "kind": "InvalidRequest", "reason": "no 'module_platform_id' specified" } + arguments = request.get("arguments") if not arguments: return { @@ -363,10 +376,20 @@ def validate_request(request): "reason": "empty 'arguments'" } - if not arguments.get("repos"): + if not arguments.get("repos") and not arguments.get("root_dir"): return { "kind": "InvalidRequest", - "reason": "no 'repos' specified" + "reason": "no 'repos' or 'root_dir' specified" + } + + # if root_dir is used, we also need releasever + # pylint: disable=fixme + # TODO: Make releasever mandatory in all cases. + # We temporarily keep it tied to root_dir for short-term backwards compatibility + if arguments.get("root_dir") and not request.get("releasever"): + return { + "kind": "InvalidRequest", + "reason": "'root_dir' requires setting 'releasever'" } return None