diff --git a/CHANGELOG.txt b/CHANGELOG.txt index e05a5471..0f3d3e1a 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.1.0] - 2023-04-05 +### Changed + - Credential files error reporting to help users identify the credentials issues + ## [1.0.9] - 2023-01-25 ### Added - Add `tentaclio.streams.api.make_empty_safe` to modify the standard behavoir of creating diff --git a/setup.py b/setup.py index 3708a129..77e6cfb7 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ from setuptools.command.install import install -VERSION = "1.0.9" +VERSION = "1.1.0" REPO_ROOT = pathlib.Path(__file__).parent diff --git a/src/tentaclio/credentials/__init__.py b/src/tentaclio/credentials/__init__.py index a1634c46..3d2bbb81 100644 --- a/src/tentaclio/credentials/__init__.py +++ b/src/tentaclio/credentials/__init__.py @@ -1,3 +1,4 @@ """Credential management module.""" from .api import * # noqa from .injection import * # noqa +from .reader import TentaclioFileError # noqa diff --git a/src/tentaclio/credentials/reader.py b/src/tentaclio/credentials/reader.py index a591b2b8..b297e7b0 100644 --- a/src/tentaclio/credentials/reader.py +++ b/src/tentaclio/credentials/reader.py @@ -15,6 +15,28 @@ TENTACLIO_SECRETS_FILE = "TENTACLIO__SECRETS_FILE" +class TentaclioFileError(Exception): + """Tentaclio secrets file errors.""" + + TENTACLIO_FILE = os.getenv(TENTACLIO_SECRETS_FILE, "") + ERROR_TEMPLATE = """ +######################################### + +Your tentaclio secrets file is malformed: + +File: {tentaclio_file} + +{message} + +Check https://github.com/octoenergy/tentaclio#credentials-file for more info about this file +""" + + def __init__(self, message: str): + """Intialise a new TentaclioFileError.""" + message = self.ERROR_TEMPLATE.format(message=message, tentaclio_file=self.TENTACLIO_FILE) + super().__init__(message) + + def add_credentials_from_env_file( injector: injection.CredentialsInjector, ) -> injection.CredentialsInjector: @@ -29,10 +51,27 @@ def add_credentials_from_env_file( return injector +def _process_mark_error(error: yaml.MarkedYAMLError) -> str: + error_str = "" + if error.problem_mark is not None: + error_str += str(error.problem) + "\n" + error_str += str(error.problem_mark) + "\n" + if error.context_mark is not None: + error_str += str(error.context) + "\n" + error_str += str(error.context_mark) + "\n" + return error_str + + def _load_creds_from_yaml(yaml_reader: protocols.Reader) -> dict: - loaded_data = yaml.safe_load(io.StringIO(yaml_reader.read())) + try: + loaded_data = yaml.safe_load(io.StringIO(yaml_reader.read())) + except yaml.MarkedYAMLError as error: + raise TentaclioFileError(_process_mark_error(error)) + if SECRETS not in loaded_data: - raise KeyError(f"no secrets in yaml data. Make sure the file has a '{SECRETS}' element") + raise TentaclioFileError( + "No secrets in yaml data. Make sure the file has a `secrets:` element" + ) return loaded_data[SECRETS] @@ -43,8 +82,7 @@ def _load_from_file( with open(path, "r") as f: return add_credentials_from_reader(injector, f) except IOError as e: - logger.error("Error while loading secrets file {path}") - raise e + raise TentaclioFileError("File not found") from e def add_credentials_from_reader( diff --git a/tests/unit/credentials/test_reader.py b/tests/unit/credentials/test_reader.py index 962e349a..6256f127 100644 --- a/tests/unit/credentials/test_reader.py +++ b/tests/unit/credentials/test_reader.py @@ -3,7 +3,7 @@ import pytest from tentaclio import urls -from tentaclio.credentials import injection, reader +from tentaclio.credentials import TentaclioFileError, injection, reader @pytest.fixture @@ -32,13 +32,13 @@ def creds_yaml_bad_url(): def test_bad_yaml(): - with pytest.raises(Exception): + with pytest.raises(TentaclioFileError): data = io.StringIO("sadfsaf") reader.add_credentials_from_reader(injection.CredentialsInjector(), data) def test_no_credentials_in_file(no_creds_yaml): - with pytest.raises(KeyError): + with pytest.raises(TentaclioFileError): data = io.StringIO(no_creds_yaml) reader.add_credentials_from_reader(injection.CredentialsInjector(), data)