From 74fc4685c016b0bb2694307d1d2a496fce4a6479 Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Nov 21 2017 06:43:53 +0000 Subject: Issue #52: Handle missing metadata Adds a new "MissingMetadata" exception to the metadata loader, and special cases that in the CLI machinery. Also adds a new test file for end-to-end CLI test cases that test the full CLI invocation, not just the internal Python API. --- diff --git a/src/_fedmod/__main__.py b/src/_fedmod/__main__.py index 9e88f92..0f7eab1 100644 --- a/src/_fedmod/__main__.py +++ b/src/_fedmod/__main__.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 import sys -from .cli import ModtoolsCLIHelper +from . import cli if __name__ == "__main__": - cli = ModtoolsCLIHelper() sys.exit(cli.run()) diff --git a/src/_fedmod/_repodata.py b/src/_fedmod/_repodata.py index 7f632b3..f5bd8d8 100644 --- a/src/_fedmod/_repodata.py +++ b/src/_fedmod/_repodata.py @@ -28,6 +28,9 @@ _F27_MAIN_REPO = "https://dl.fedoraproject.org/pub/fedora/linux/development/27/E _F27_UPDATES_REPO = "https://dl.fedoraproject.org/pub/fedora/linux/updates/27/" _F27_BOOTSTRAP_MODULEMD = "https://src.fedoraproject.org/modules/bootstrap/raw/master/f/bootstrap.yaml" +class MissingMetadata(Exception): + """Reports failure to find the local metadata cache""" + @attributes class RepoPaths: remote_repo_url = attrib(str) @@ -167,11 +170,20 @@ def _populate_module_reverse_lookup(): return metadata_dir = os.path.join(_x86_64_MODULE_INFO.local_cache_path) repomd_fname = os.path.join(metadata_dir, "repodata", "repomd.xml") + if not os.path.exists(repomd_fname): + msg = f"{repomd_fname!r} does not exist. Run `fedmod fetch-metadata`." + raise MissingMetadata(msg) repomd_xml = etree.parse(repomd_fname) repo_relative_modulemd = _read_repomd_location(repomd_xml, "modules") if repo_relative_modulemd is None: - raise RuntimeError(f"No 'modules' entry found in {repomd_fname}. Is the metadata for a non-modular repo?") + msg = (f"No 'modules' entry found in {repomd_fname!r}. " + "Is the metadata for a non-modular repo?") + raise MissingMetadata(msg) repo_modulemd_fname = os.path.join(metadata_dir, repo_relative_modulemd) + if not os.path.exists(repo_modulemd_fname): + msg = (f"{repo_modulemd_fname!r} does not exist. " + "Try running `fedmod fetch-metadata` again.") + raise MissingMetadata(msg) with gzip.open(repo_modulemd_fname, "r") as modules_yaml_gz: modules_yaml = modules_yaml_gz.read() modules = modulemd.loads_all(modules_yaml) @@ -195,6 +207,10 @@ def _populate_module_reverse_lookup(): _BETTER_RPM_REVERSE_LOOKUP[rpmprefix] = [] _BETTER_RPM_REVERSE_LOOKUP[rpmprefix].append(module.name) # Read the extra RPM bootstrap metadata + if not os.path.exists(_BOOTSTRAP_REVERSE_LOOKUP_CACHE): + msg = (f"{_BOOTSTRAP_REVERSE_LOOKUP_CACHE!r} does not exist. " + "Try running `fedmod fetch-metadata` again.") + raise MissingMetadata(msg) with open(_BOOTSTRAP_REVERSE_LOOKUP_CACHE, "r") as cachefile: _BOOTSTRAP_REVERSE_LOOKUP.update(json.load(cachefile)) diff --git a/src/_fedmod/cli.py b/src/_fedmod/cli.py index 59f3cea..cb099b9 100644 --- a/src/_fedmod/cli.py +++ b/src/_fedmod/cli.py @@ -176,6 +176,9 @@ def run(): except KeyboardInterrupt: print('\nInterrupted by user') + except _repodata.MissingMetadata as e: + print(e, file=sys.stderr) + sys.exit(2) except Exception as e: logging.exception("Unexpected exception") sys.exit(1) diff --git a/tests/test_cli_ux.py b/tests/test_cli_ux.py new file mode 100644 index 0000000..707a6f4 --- /dev/null +++ b/tests/test_cli_ux.py @@ -0,0 +1,33 @@ +"""End-to-end subprocess based testing for the CLI behaviour""" + +import pytest +import os +import re +import sys +import subprocess + +from _fedmod._repodata import CACHEDIR + +def _run_fedmod(args): + # Run via the -m switch to ensure we get the expected version of fedmod + cmd = [sys.executable, "-m", "_fedmod"] + cmd.extend(args) + return subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + +class TestMissingMetadata(object): + + def setup(self): + self._moved_cache_dir = _moved_cache_dir = CACHEDIR + "__" + os.rename(CACHEDIR, _moved_cache_dir) + + def teardown(self): + os.rename(self._moved_cache_dir, CACHEDIR) + + def test_missing_metadata_handling(self, capfd): + result = _run_fedmod(["list-modules"]) + assert result.returncode == 2 + assert result.stdout == b"" + expected_err_msg = b"'' does not exist. Run `fedmod fetch-metadata`.\n" + err_msg = re.sub(b"'.*'", b"''", result.stderr) + assert err_msg == expected_err_msg +