From 6f686ff9d6b6303b43a69c84c1694afa59f78261 Mon Sep 17 00:00:00 2001 From: "Brian C. Lane" Date: Tue, 13 Aug 2019 18:24:21 -0700 Subject: [PATCH] lorax-composer: Add support for dnf variables to repo sources This loads the system dnf vars from /etc/dnf/vars at startup if system repos are enabled, and it substitutes the values in the sources when loaded, and when a new source is added. Also includes tests. --- src/pylorax/api/dnfbase.py | 5 + src/pylorax/api/projects.py | 152 +++++++++++++++----- src/pylorax/api/v0.py | 43 +----- src/pylorax/api/v1.py | 51 +------ tests/pylorax/source/test-repo-v1-vars.toml | 7 + tests/pylorax/source/test-repo-vars.toml | 6 + tests/pylorax/test_projects.py | 72 +++++++++- tests/pylorax/test_server.py | 60 +++++++- 8 files changed, 272 insertions(+), 124 deletions(-) create mode 100644 tests/pylorax/source/test-repo-v1-vars.toml create mode 100644 tests/pylorax/source/test-repo-vars.toml diff --git a/src/pylorax/api/dnfbase.py b/src/pylorax/api/dnfbase.py index cbd8dadb..1d72b28a 100644 --- a/src/pylorax/api/dnfbase.py +++ b/src/pylorax/api/dnfbase.py @@ -104,6 +104,11 @@ def get_base_object(conf): if conf.has_option("dnf", "sslverify") and not conf.getboolean("dnf", "sslverify"): dbc.sslverify = False + # If the system repos are enabled read the dnf vars from /etc/dnf/vars/ + if not conf.has_option("repos", "use_system_repos") or conf.getboolean("repos", "use_system_repos"): + dbc.substitutions.update_from_etc("/") + log.info("dnf vars: %s", dbc.substitutions) + _releasever = conf.get_default("composer", "releasever", None) if not _releasever: # Use the releasever of the host system diff --git a/src/pylorax/api/projects.py b/src/pylorax/api/projects.py index 00474502..a9cc1a21 100644 --- a/src/pylorax/api/projects.py +++ b/src/pylorax/api/projects.py @@ -24,6 +24,7 @@ import os import time from pylorax.api.bisect import insort_left +from pylorax.sysutils import joinpaths TIME_FORMAT = "%Y-%m-%dT%H:%M:%S" @@ -477,6 +478,58 @@ def repo_to_source(repo, system_source, api=1): return source +def source_to_repodict(source): + """Return a tuple suitable for use with dnf.add_new_repo + + :param source: A Weldr source dict + :type source: dict + :returns: A tuple of dnf.Repo attributes + :rtype: (str, list, dict) + + Return a tuple with (id, baseurl|(), kwargs) that can be used + with dnf.repos.add_new_repo + """ + kwargs = {} + if "id" in source: + # This is an API v1 source definition + repoid = source["id"] + if "name" in source: + kwargs["name"] = source["name"] + else: + repoid = source["name"] + + # This will allow errors to be raised so we can catch them + # without this they are logged, but the repo is silently disabled + kwargs["skip_if_unavailable"] = False + + if source["type"] == "yum-baseurl": + baseurl = [source["url"]] + elif source["type"] == "yum-metalink": + kwargs["metalink"] = source["url"] + baseurl = () + elif source["type"] == "yum-mirrorlist": + kwargs["mirrorlist"] = source["url"] + baseurl = () + + if "proxy" in source: + kwargs["proxy"] = source["proxy"] + + if source["check_ssl"]: + kwargs["sslverify"] = True + else: + kwargs["sslverify"] = False + + if source["check_gpg"]: + kwargs["gpgcheck"] = True + else: + kwargs["gpgcheck"] = False + + if "gpgkey_urls" in source: + kwargs["gpgkey"] = tuple(source["gpgkey_urls"]) + + return (repoid, baseurl, kwargs) + + def source_to_repo(source, dnf_conf): """Return a dnf Repo object created from a source dict @@ -506,39 +559,14 @@ def source_to_repo(source, dnf_conf): If the ``id`` field is included it is used for the repo id, otherwise ``name`` is used. v0 of the API only used ``name``, v1 added the distinction between ``id`` and ``name``. """ - if "id" in source: - # This is an API v1 source definition - repo = dnf.repo.Repo(source["id"], dnf_conf) - if "name" in source: - repo.name = source["name"] - else: - repo = dnf.repo.Repo(source["name"], dnf_conf) - # This will allow errors to be raised so we can catch them - # without this they are logged, but the repo is silently disabled - repo.skip_if_unavailable = False + repoid, baseurl, kwargs = source_to_repodict(source) + repo = dnf.repo.Repo(repoid, dnf_conf) + if baseurl: + repo.baseurl = baseurl - if source["type"] == "yum-baseurl": - repo.baseurl = source["url"] - elif source["type"] == "yum-metalink": - repo.metalink = source["url"] - elif source["type"] == "yum-mirrorlist": - repo.mirrorlist = source["url"] - - if "proxy" in source: - repo.proxy = source["proxy"] - - if source["check_ssl"]: - repo.sslverify = True - else: - repo.sslverify = False - - if source["check_gpg"]: - repo.gpgcheck = True - else: - repo.gpgcheck = False - - if "gpgkey_urls" in source: - repo.gpgkey = tuple(source["gpgkey_urls"]) + # Apply the rest of the kwargs to the Repo object + for k, v in kwargs.items(): + setattr(repo, k, v) repo.enable() @@ -607,3 +635,63 @@ def delete_repo_source(source_glob, source_id): raise ProjectsError("Problem deleting repo source %s: %s" % (source_id, str(e))) if not found: raise ProjectsError("source %s not found" % source_id) + +def new_repo_source(dbo, repoid, source, repo_dir): + """Add a new repo source from a Weldr source dict + + :param dbo: dnf base object + :type dbo: dnf.Base + :param id: The repo id (API v0 uses the name, v1 uses the id) + :type id: str + :param source: A Weldr source dict + :type source: dict + :returns: None + :raises: ... + + Make sure access to the dbo has been locked before calling this. + The `id` parameter will the the 'name' field for API v0, and the 'id' field for API v1 + + DNF variables will be substituted at load time, and on restart. + """ + try: + # Remove it from the RepoDict (NOTE that this isn't explicitly supported by the DNF API) + # If this repo already exists, delete it and replace it with the new one + repos = list(r.id for r in dbo.repos.iter_enabled()) + if repoid in repos: + del dbo.repos[repoid] + + # Add the repo and substitute any dnf variables + _, baseurl, kwargs = source_to_repodict(source) + log.debug("repoid=%s, baseurl=%s, kwargs=%s", repoid, baseurl, kwargs) + r = dbo.repos.add_new_repo(repoid, dbo.conf, baseurl, **kwargs) + r.enable() + + log.info("Updating repository metadata after adding %s", repoid) + dbo.fill_sack(load_system_repo=False) + dbo.read_comps() + + # Remove any previous sources with this id, ignore it if it isn't found + try: + delete_repo_source(joinpaths(repo_dir, "*.repo"), repoid) + except ProjectsError: + pass + + # Make sure the source id can't contain a path traversal by taking the basename + source_path = joinpaths(repo_dir, os.path.basename("%s.repo" % repoid)) + # Write the un-substituted version of the repo to disk + with open(source_path, "w") as f: + repo = source_to_repo(source, dbo.conf) + f.write(dnf_repo_to_file_repo(repo)) + except Exception as e: + log.error("(new_repo_source) adding %s failed: %s", repoid, str(e)) + + # Cleanup the mess, if loading it failed we don't want to leave it in memory + repos = list(r.id for r in dbo.repos.iter_enabled()) + if repoid in repos: + del dbo.repos[repoid] + + log.info("Updating repository metadata after adding %s failed", repoid) + dbo.fill_sack(load_system_repo=False) + dbo.read_comps() + + raise diff --git a/src/pylorax/api/v0.py b/src/pylorax/api/v0.py index e5a8e4f4..31a2bce2 100644 --- a/src/pylorax/api/v0.py +++ b/src/pylorax/api/v0.py @@ -62,7 +62,7 @@ from pylorax.api.errors import * # pylint: disable from pylorax.api.flask_blueprint import BlueprintSkip from pylorax.api.projects import projects_list, projects_info, projects_depsolve from pylorax.api.projects import modules_list, modules_info, ProjectsError, repo_to_source -from pylorax.api.projects import get_repo_sources, delete_repo_source, source_to_repo, dnf_repo_to_file_repo +from pylorax.api.projects import get_repo_sources, delete_repo_source, new_repo_source from pylorax.api.queue import queue_status, build_status, uuid_delete, uuid_status, uuid_info from pylorax.api.queue import uuid_tar, uuid_image, uuid_cancel, uuid_log from pylorax.api.recipes import list_branch_files, read_recipe_commit, recipe_filename, list_commits @@ -1206,46 +1206,9 @@ def v0_projects_source_new(): try: # Remove it from the RepoDict (NOTE that this isn't explicitly supported by the DNF API) with api.config["DNFLOCK"].lock: - dbo = api.config["DNFLOCK"].dbo - # If this repo already exists, delete it and replace it with the new one - repos = list(r.id for r in dbo.repos.iter_enabled()) - if source["name"] in repos: - del dbo.repos[source["name"]] - - repo = source_to_repo(source, dbo.conf) - dbo.repos.add(repo) - - log.info("Updating repository metadata after adding %s", source["name"]) - dbo.fill_sack(load_system_repo=False) - dbo.read_comps() - - # Write the new repo to disk, replacing any existing ones - repo_dir = api.config["COMPOSER_CFG"].get("composer", "repo_dir") - - # Remove any previous sources with this name, ignore it if it isn't found - try: - delete_repo_source(joinpaths(repo_dir, "*.repo"), source["name"]) - except ProjectsError: - pass - - # Make sure the source name can't contain a path traversal by taking the basename - source_path = joinpaths(repo_dir, os.path.basename("%s.repo" % source["name"])) - with open(source_path, "w") as f: - f.write(dnf_repo_to_file_repo(repo)) + repo_dir = api.config["COMPOSER_CFG"].get("composer", "repo_dir") + new_repo_source(api.config["DNFLOCK"].dbo, source["name"], source, repo_dir) except Exception as e: - log.error("(v0_projects_source_add) adding %s failed: %s", source["name"], str(e)) - - # Cleanup the mess, if loading it failed we don't want to leave it in memory - repos = list(r.id for r in dbo.repos.iter_enabled()) - if source["name"] in repos: - with api.config["DNFLOCK"].lock: - dbo = api.config["DNFLOCK"].dbo - del dbo.repos[source["name"]] - - log.info("Updating repository metadata after adding %s failed", source["name"]) - dbo.fill_sack(load_system_repo=False) - dbo.read_comps() - return jsonify(status=False, errors=[{"id": PROJECTS_ERROR, "msg": str(e)}]), 400 return jsonify(status=True) diff --git a/src/pylorax/api/v1.py b/src/pylorax/api/v1.py index 7b0d19d9..bba96bf6 100644 --- a/src/pylorax/api/v1.py +++ b/src/pylorax/api/v1.py @@ -19,7 +19,6 @@ """ import logging log = logging.getLogger("lorax-composer") -import os from flask import jsonify, request from flask import current_app as api @@ -27,12 +26,9 @@ from flask import current_app as api from pylorax.api.checkparams import checkparams from pylorax.api.errors import INVALID_CHARS, PROJECTS_ERROR, SYSTEM_SOURCE, UNKNOWN_SOURCE from pylorax.api.flask_blueprint import BlueprintSkip -from pylorax.api.projects import delete_repo_source, dnf_repo_to_file_repo, get_repo_sources, repo_to_source -from pylorax.api.projects import source_to_repo -from pylorax.api.projects import ProjectsError +from pylorax.api.projects import get_repo_sources, new_repo_source, repo_to_source from pylorax.api.regexes import VALID_API_STRING import pylorax.api.toml as toml -from pylorax.sysutils import joinpaths # Create the v1 routes Blueprint with skip_routes support v1_api = BlueprintSkip("v1_routes", __name__) @@ -153,9 +149,7 @@ def v1_projects_source_new(): else: source = request.get_json(cache=False) - # XXX TODO # Check for id in source, return error if not - # Add test for that if "id" not in source: return jsonify(status=False, errors=[{"id": UNKNOWN_SOURCE, "msg": "'id' field is missing from API v1 request."}]), 400 @@ -166,48 +160,9 @@ def v1_projects_source_new(): try: # Remove it from the RepoDict (NOTE that this isn't explicitly supported by the DNF API) with api.config["DNFLOCK"].lock: - dbo = api.config["DNFLOCK"].dbo - # If this repo already exists, delete it and replace it with the new one - repos = list(r.id for r in dbo.repos.iter_enabled()) - if source["id"] in repos: - del dbo.repos[source["id"]] - - repo = source_to_repo(source, dbo.conf) - dbo.repos.add(repo) - - log.info("Updating repository metadata after adding %s", source["id"]) - dbo.fill_sack(load_system_repo=False) - dbo.read_comps() - - # Write the new repo to disk, replacing any existing ones - repo_dir = api.config["COMPOSER_CFG"].get("composer", "repo_dir") - - # Remove any previous sources with this id, ignore it if it isn't found - try: - delete_repo_source(joinpaths(repo_dir, "*.repo"), source["id"]) - except ProjectsError: - pass - - # Make sure the source id can't contain a path traversal by taking the basename - source_path = joinpaths(repo_dir, os.path.basename("%s.repo" % source["id"])) - with open(source_path, "w") as f: - f.write(dnf_repo_to_file_repo(repo)) + repo_dir = api.config["COMPOSER_CFG"].get("composer", "repo_dir") + new_repo_source(api.config["DNFLOCK"].dbo, source["id"], source, repo_dir) except Exception as e: - log.error("(v0_projects_source_add) adding %s failed: %s", source["id"], str(e)) - - # Cleanup the mess, if loading it failed we don't want to leave it in memory - repos = list(r.id for r in dbo.repos.iter_enabled()) - if source["id"] in repos: - with api.config["DNFLOCK"].lock: - dbo = api.config["DNFLOCK"].dbo - del dbo.repos[source["id"]] - - log.info("Updating repository metadata after adding %s failed", source["id"]) - dbo.fill_sack(load_system_repo=False) - dbo.read_comps() - return jsonify(status=False, errors=[{"id": PROJECTS_ERROR, "msg": str(e)}]), 400 return jsonify(status=True) - - diff --git a/tests/pylorax/source/test-repo-v1-vars.toml b/tests/pylorax/source/test-repo-v1-vars.toml new file mode 100644 index 00000000..a8ddac2a --- /dev/null +++ b/tests/pylorax/source/test-repo-v1-vars.toml @@ -0,0 +1,7 @@ +id = "new-repo-2-v1-vars" +name = "API v1 toml new repo with vars" +url = "file:///tmp/lorax-empty-repo-v1-$releasever-$basearch/" +type = "yum-baseurl" +check_ssl = true +check_gpg = true +gpgkey_urls = ["file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-$releasever-$basearch"] diff --git a/tests/pylorax/source/test-repo-vars.toml b/tests/pylorax/source/test-repo-vars.toml new file mode 100644 index 00000000..676940fd --- /dev/null +++ b/tests/pylorax/source/test-repo-vars.toml @@ -0,0 +1,6 @@ +name = "new-repo-2-vars" +url = "file:///tmp/lorax-empty-repo-$releasever-$basearch/" +type = "yum-baseurl" +check_ssl = true +check_gpg = true +gpgkey_urls = ["file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-$releasever-$basearch"] diff --git a/tests/pylorax/test_projects.py b/tests/pylorax/test_projects.py index 8c1309c2..c2a6554e 100644 --- a/tests/pylorax/test_projects.py +++ b/tests/pylorax/test_projects.py @@ -28,7 +28,7 @@ from pylorax.api.projects import api_time, api_changelog, pkg_to_project, pkg_to from pylorax.api.projects import proj_to_module, projects_list, projects_info, projects_depsolve from pylorax.api.projects import modules_list, modules_info, ProjectsError, dep_evra, dep_nevra from pylorax.api.projects import repo_to_source, get_repo_sources, delete_repo_source, source_to_repo -from pylorax.api.projects import dnf_repo_to_file_repo +from pylorax.api.projects import source_to_repodict, dnf_repo_to_file_repo from pylorax.api.dnfbase import get_base_object class Package(object): @@ -202,7 +202,6 @@ class ProjectsTest(unittest.TestCase): self.assertTrue("ctags" in names) # default package self.assertFalse("cmake" in names) # optional package - class ConfigureTest(unittest.TestCase): @classmethod def setUpClass(self): @@ -341,6 +340,25 @@ def singlerepo_v1(): d["name"] = "One repo in the file" return d +def singlerepo_vars_v0(): + return { + "check_gpg": True, + "check_ssl": True, + "gpgkey_urls": [ + "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-$releasever-$basearch" + ], + "name": "single-repo", + "system": False, + "type": "yum-baseurl", + "url": "file:///tmp/lorax-empty-repo-$releasever-$basearch/" + } + +def singlerepo_vars_v1(): + d = singlerepo_v0() + d["id"] = "single-repo" + d["name"] = "One repo in the file" + return d + class SourceTest(unittest.TestCase): @classmethod def setUpClass(self): @@ -443,51 +461,101 @@ class SourceTest(unittest.TestCase): repo = source_to_repo(fakerepo_baseurl_v0(), self.dbo.conf) self.assertEqual(repo.baseurl[0], fakerepo_baseurl_v0()["url"]) + def test_source_to_repodict_baseurl(self): + """Test creating a repodict with a baseurl API v0""" + repo = source_to_repodict(fakerepo_baseurl_v0()) + self.assertEqual(repo[1][0], fakerepo_baseurl_v0()["url"]) + def test_source_to_repo_baseurl_v1(self): """Test creating a dnf.Repo with a baseurl API v1""" repo = source_to_repo(fakerepo_baseurl_v1(), self.dbo.conf) self.assertEqual(repo.baseurl[0], fakerepo_baseurl_v1()["url"]) + def test_source_to_repodict_baseurl_v1(self): + """Test creating a repodict with a baseurl API v1""" + repo = source_to_repodict(fakerepo_baseurl_v1()) + self.assertEqual(repo[1][0], fakerepo_baseurl_v1()["url"]) + def test_source_to_repo_metalink(self): """Test creating a dnf.Repo with a metalink API v0""" repo = source_to_repo(fakerepo_metalink_v0(), self.dbo.conf) self.assertEqual(repo.metalink, fakerepo_metalink_v0()["url"]) + def test_source_to_repodict_metalink(self): + """Test creating a repodict with a metalink API v0""" + repo = source_to_repodict(fakerepo_metalink_v0()) + self.assertEqual(repo[2]["metalink"], fakerepo_metalink_v0()["url"]) + def test_source_to_repo_metalink_v1(self): """Test creating a dnf.Repo with a metalink API v1""" repo = source_to_repo(fakerepo_metalink_v1(), self.dbo.conf) self.assertEqual(repo.metalink, fakerepo_metalink_v1()["url"]) + def test_source_to_repodict_metalink_v1(self): + """Test creating a repodict with a metalink API v1""" + repo = source_to_repodict(fakerepo_metalink_v1()) + self.assertEqual(repo[2]["metalink"], fakerepo_metalink_v1()["url"]) + def test_source_to_repo_mirrorlist(self): """Test creating a dnf.Repo with a mirrorlist API v0""" repo = source_to_repo(fakerepo_mirrorlist_v0(), self.dbo.conf) self.assertEqual(repo.mirrorlist, fakerepo_mirrorlist_v0()["url"]) + def test_source_to_repodict_mirrorlist(self): + """Test creating a repodict with a mirrorlist API v0""" + repo = source_to_repodict(fakerepo_mirrorlist_v0()) + self.assertEqual(repo[2]["mirrorlist"], fakerepo_mirrorlist_v0()["url"]) + def test_source_to_repo_mirrorlist_v1(self): """Test creating a dnf.Repo with a mirrorlist""" repo = source_to_repo(fakerepo_mirrorlist_v1(), self.dbo.conf) self.assertEqual(repo.mirrorlist, fakerepo_mirrorlist_v1()["url"]) + def test_source_to_repodict_mirrorlist_v1(self): + """Test creating a repodict with a mirrorlist""" + repo = source_to_repodict(fakerepo_mirrorlist_v1()) + self.assertEqual(repo[2]["mirrorlist"], fakerepo_mirrorlist_v1()["url"]) + def test_source_to_repo_proxy(self): """Test creating a dnf.Repo with a proxy API v0""" repo = source_to_repo(fakerepo_proxy_v0(), self.dbo.conf) self.assertEqual(repo.proxy, fakerepo_proxy_v0()["proxy"]) + def test_source_to_repodict_proxy(self): + """Test creating a repodict with a proxy API v0""" + repo = source_to_repodict(fakerepo_proxy_v0()) + self.assertEqual(repo[2]["proxy"], fakerepo_proxy_v0()["proxy"]) + def test_source_to_repo_proxy_v1(self): """Test creating a dnf.Repo with a proxy API v1""" repo = source_to_repo(fakerepo_proxy_v1(), self.dbo.conf) self.assertEqual(repo.proxy, fakerepo_proxy_v1()["proxy"]) + def test_source_to_repodict_proxy_v1(self): + """Test creating a repodict with a proxy API v1""" + repo = source_to_repodict(fakerepo_proxy_v1()) + self.assertEqual(repo[2]["proxy"], fakerepo_proxy_v1()["proxy"]) + def test_source_to_repo_gpgkey(self): """Test creating a dnf.Repo with a proxy API v0""" repo = source_to_repo(fakerepo_gpgkey_v0(), self.dbo.conf) self.assertEqual(repo.gpgkey[0], fakerepo_gpgkey_v0()["gpgkey_urls"][0]) + def test_source_to_repodict_gpgkey(self): + """Test creating a repodict with a proxy API v0""" + repo = source_to_repodict(fakerepo_gpgkey_v0()) + self.assertEqual(repo[2]["gpgkey"][0], fakerepo_gpgkey_v0()["gpgkey_urls"][0]) + def test_source_to_repo_gpgkey_v1(self): """Test creating a dnf.Repo with a proxy API v1""" repo = source_to_repo(fakerepo_gpgkey_v1(), self.dbo.conf) self.assertEqual(repo.gpgkey[0], fakerepo_gpgkey_v1()["gpgkey_urls"][0]) + def test_source_to_repodict_gpgkey_v1(self): + """Test creating a repodict with a proxy API v1""" + repo = source_to_repodict(fakerepo_gpgkey_v1()) + self.assertEqual(repo[2]["gpgkey"][0], fakerepo_gpgkey_v1()["gpgkey_urls"][0]) + def test_drtfr_baseurl(self): """Test creating a dnf .repo file from a baseurl Repo object""" self.assertEqual(dnf_repo_to_file_repo(self.dbo.repos.get("fake-repo-baseurl")), diff --git a/tests/pylorax/test_server.py b/tests/pylorax/test_server.py index 6d692632..303a3ca9 100644 --- a/tests/pylorax/test_server.py +++ b/tests/pylorax/test_server.py @@ -18,6 +18,7 @@ import os from configparser import ConfigParser, NoOptionError from contextlib import contextmanager +import dnf from glob import glob from rpmfluff import SimpleRpmBuild, expectedArch import shutil @@ -127,10 +128,14 @@ class ServerTestCase(unittest.TestCase): if os.path.exists("/etc/yum.repos.d/fedora-rawhide.repo"): self.rawhide = True + # Need the substitution values to create the directories before we can create the dnf.Base for real + dbo = dnf.Base() + repo_dirs = ["/tmp/lorax-empty-repo-%s-%s" % (dbo.conf.substitutions["releasever"], dbo.conf.substitutions["basearch"]), + "/tmp/lorax-empty-repo-v1-%s-%s" % (dbo.conf.substitutions["releasever"], dbo.conf.substitutions["basearch"])] # dnf repo baseurl has to point to an absolute directory, so we use /tmp/lorax-empty-repo/ in the files # and create an empty repository. We now remove duplicate repo entries so we need a number of them. - for d in ["/tmp/lorax-empty-repo/", "/tmp/lorax-other-empty-repo/", "/tmp/lorax-empty-repo-1/", - "/tmp/lorax-empty-repo-2/", "/tmp/lorax-empty-repo-3/", "/tmp/lorax-empty-repo-4/"]: + for d in repo_dirs + ["/tmp/lorax-empty-repo/", "/tmp/lorax-other-empty-repo/", "/tmp/lorax-empty-repo-1/", + "/tmp/lorax-empty-repo-2/", "/tmp/lorax-empty-repo-3/", "/tmp/lorax-empty-repo-4/"]: os.makedirs(d) rc = os.system("createrepo_c %s" % d) if rc != 0: @@ -139,6 +144,13 @@ class ServerTestCase(unittest.TestCase): server.config["DNFLOCK"] = DNFLock(server.config["COMPOSER_CFG"]) + # Grab the substitution values for later + with server.config["DNFLOCK"].lock: + self.substitutions = server.config["DNFLOCK"].dbo.conf.substitutions + + if "releasever" not in self.substitutions or "basearch" not in self.substitutions: + raise RuntimeError("DNF is missing the releasever and basearch substitutions") + # Include a message in /api/status output server.config["TEMPLATE_ERRORS"] = ["Test message"] @@ -831,6 +843,29 @@ class ServerTestCase(unittest.TestCase): sources = data["sources"] self.assertTrue("new-repo-2" in sources) + def test_projects_source_00_new_toml_vars(self): + """Test /api/v0/projects/source/new with a new toml source using vars""" + toml_source = open("./tests/pylorax/source/test-repo-vars.toml").read() + self.assertTrue(len(toml_source) > 0) + resp = self.server.post("/api/v0/projects/source/new", + data=toml_source, + content_type="text/x-toml") + data = json.loads(resp.data) + self.assertEqual(data, {"status":True}) + + # Was it added, and was is it correct? + resp = self.server.get("/api/v1/projects/source/info/new-repo-2-vars") + data = json.loads(resp.data) + self.assertNotEqual(data, None) + sources = data["sources"] + print(sources) + self.assertTrue("new-repo-2-vars" in sources) + + self.assertTrue(self.substitutions["releasever"] in sources["new-repo-2-vars"]["url"]) + self.assertTrue(self.substitutions["basearch"] in sources["new-repo-2-vars"]["url"]) + self.assertTrue(self.substitutions["releasever"] in sources["new-repo-2-vars"]["gpgkey_urls"][0]) + self.assertTrue(self.substitutions["basearch"] in sources["new-repo-2-vars"]["gpgkey_urls"][0]) + def test_projects_source_01_new_toml(self): """Test /api/v1/projects/source/new with a new toml source""" toml_source = open("./tests/pylorax/source/test-repo-v1.toml").read() @@ -852,6 +887,27 @@ class ServerTestCase(unittest.TestCase): self.assertTrue("name" in sources["new-repo-2-v1"]) self.assertEqual(sources["new-repo-2-v1"]["name"], "API v1 toml new repo") + def test_projects_source_01_new_toml_vars(self): + """Test /api/v1/projects/source/new with a new toml source using vars""" + toml_source = open("./tests/pylorax/source/test-repo-v1-vars.toml").read() + self.assertTrue(len(toml_source) > 0) + resp = self.server.post("/api/v1/projects/source/new", + data=toml_source, + content_type="text/x-toml") + data = json.loads(resp.data) + self.assertEqual(data, {"status":True}) + + # Was it added, and was is it correct? + resp = self.server.get("/api/v1/projects/source/info/new-repo-2-v1-vars") + data = json.loads(resp.data) + self.assertNotEqual(data, None) + sources = data["sources"] + self.assertTrue("new-repo-2-v1-vars" in sources) + self.assertTrue(self.substitutions["releasever"] in sources["new-repo-2-v1-vars"]["url"]) + self.assertTrue(self.substitutions["basearch"] in sources["new-repo-2-v1-vars"]["url"]) + self.assertTrue(self.substitutions["releasever"] in sources["new-repo-2-v1-vars"]["gpgkey_urls"][0]) + self.assertTrue(self.substitutions["basearch"] in sources["new-repo-2-v1-vars"]["gpgkey_urls"][0]) + def test_projects_source_02_new_toml(self): """Test /api/v1/projects/source/new with a new toml source w/o id field""" toml_source = open("./tests/pylorax/source/test-repo.toml").read()