From 8024cf1b92cbcb9c4d1127b1280ffff2b99857bc Mon Sep 17 00:00:00 2001 From: "s. rannou" Date: Mon, 20 Nov 2023 23:32:06 +0100 Subject: [PATCH] feat: move config to pydantic --- eth_validator_watcher/config.py | 77 +++++---------- poetry.lock | 163 ++++++++++++++++++++++++++++++- pyproject.toml | 2 + tests/config/test_load_config.py | 39 +++++++- 4 files changed, 226 insertions(+), 55 deletions(-) diff --git a/eth_validator_watcher/config.py b/eth_validator_watcher/config.py index 3602db5..245d52e 100644 --- a/eth_validator_watcher/config.py +++ b/eth_validator_watcher/config.py @@ -1,13 +1,13 @@ from .models import BeaconType -from dataclasses import dataclass, field +from pydantic import BaseModel +from pydantic_settings import BaseSettings, SettingsConfigDict from typing import Any, List, Optional import os import yaml -@dataclass -class WatchedKeyConfig: +class WatchedKeyConfig(BaseModel): """Configuration of a watched key. """ public_key: str @@ -15,34 +15,29 @@ class WatchedKeyConfig: fee_recipient: Optional[str] = None -@dataclass -class Config: +class Config(BaseSettings): """Configuration of the Ethereum Validator Watcher. - - Everything can be configured via a configuration file, there is - the possibility to override parts of the configuration with - environment variables, which ca be useful for secrets. - - Settings here are split in two groups: the ones that can be overriden, - and the more complex ones that can't. """ - beacon_url: Optional[str] = os.getenv('ETH_WATCHER_BEACON_URL') - beacon_type: Optional[str] = os.getenv('ETH_WATCHER_BEACON_TYPE', BeaconType.OTHER) - execution_url: Optional[str] = os.getenv('ETH_WATCHER_EXEC_URL') - config_file: Optional[str] = os.getenv('ETH_WATCHER_CONFIG_FILE', 'etc/config.local.yaml') - web3signer_url: Optional[str] = os.getenv('ETH_WATCHER_WEB3SIGNER_URL') - default_fee_recipient: Optional[str] = os.getenv('ETH_WATCHER_DEFAULT_FEE_RECIPIENT') - slack_channel: Optional[str] = os.getenv('ETH_WATCHER_SLACK_CHANNEL') - slack_token: Optional[str] = os.getenv('ETH_WATCHER_SLACK_TOKEN') - relays: Optional[List[str]] = field(default_factory=lambda: list(filter(bool, os.getenv('ETH_WATCHER_RELAY_URL', '').split(',')))) - liveness_file: Optional[str] = os.getenv('ETH_WATCHER_LIVENESS_FILE') + model_config = SettingsConfigDict(case_sensitive=True, env_prefix='eth_watcher_') - watched_keys: List[WatchedKeyConfig] = field(default_factory=list) + beacon_url: Optional[str] = None + beacon_type: Optional[BeaconType] = None + execution_url: Optional[str] = None + web3signer_url: Optional[str] = None + default_fee_recipient: Optional[str] = None + slack_channel: Optional[str] = None + slack_token: Optional[str] = None + relays: Optional[List[str]] = None + liveness_file: Optional[str] = None + watched_keys: Optional[List[WatchedKeyConfig]] = None def load_config(config_file: str) -> Config: - """Loads the configuration file + """Loads the configuration file from environment and configfile. + Environment variables have priority (can be used to set secrets + and override the config file). + Parameters: config_file : path to the YAML configuration file @@ -50,32 +45,12 @@ def load_config(config_file: str) -> Config: The effective configuration used by the watcher """ with open(config_file, 'r') as fh: - yml = yaml.safe_load(fh) - config = Config() - - def get_value(value: Any, key: str) -> Any: - if value is not None: - return value - return yml.get(key) - - # Settings that can be overriden via environment. - config.beacon_url = get_value(config.beacon_url, 'beacon_url') - config.execution_url = get_value(config.execution_url, 'execution_url') - config.web3signer_url = get_value(config.web3signer_url, 'web3signer_url') - config.default_fee_recipient = get_value(config.default_fee_recipient, 'default_fee_recipient') - config.slack_channel = get_value(config.slack_channel, 'slack_channel') - config.slack_token = get_value(config.slack_token, 'slack_token') - config.beacon_type = get_value(config.beacon_type, 'beacon_type') - config.relays = get_value(config.relays or None, 'relays') - config.liveness_file = get_value(config.liveness_file, 'liveness_file') + config = yaml.safe_load(fh) + + from_env = Config().model_dump() + from_yaml = Config(**config).model_dump() - # More complex settings that can't. - config.watched_keys = [ - WatchedKeyConfig( - public_key=config_key.get('public_key'), - labels=config_key.get('label'), - fee_recipient=config_key.get('fee_recipient', config.default_fee_recipient), - ) for config_key in yml.get('watched_keys') or () - ] + merged = from_yaml.copy() + merged.update({k: v for k, v in from_env.items() if v}) - return config + return Config(**merged) diff --git a/poetry.lock b/poetry.lock index 0a50d76..b970356 100644 --- a/poetry.lock +++ b/poetry.lock @@ -275,6 +275,25 @@ files = [ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] +[[package]] +name = "importlib-metadata" +version = "6.8.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, + {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -556,6 +575,42 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pydantic-settings" +version = "2.1.0" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_settings-2.1.0-py3-none-any.whl", hash = "sha256:7621c0cb5d90d1140d2f0ef557bdf03573aac7035948109adf2574770b77605a"}, + {file = "pydantic_settings-2.1.0.tar.gz", hash = "sha256:26b1492e0a24755626ac5e6d715e9077ab7ad4fb5f19a8b7ed7011d52f36141c"}, +] + +[package.dependencies] +pydantic = ">=2.3.0" +python-dotenv = ">=0.21.0" + +[[package]] +name = "pydantic-yaml" +version = "1.2.0" +description = "Adds some YAML functionality to the excellent `pydantic` library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_yaml-1.2.0-py3-none-any.whl", hash = "sha256:196cd4286a66cbe913bcc2e463251cb6b73ebe5ac9c8af2ceed0b1c72ddc03a2"}, + {file = "pydantic_yaml-1.2.0.tar.gz", hash = "sha256:54bdaf4da25bca95bb9ba790b1a5c6b4b3e93f7cf218fcde46b3073b61779f81"}, +] + +[package.dependencies] +importlib-metadata = "*" +pydantic = ">=1.8" +"ruamel.yaml" = ">=0.16.0,<0.18.0" +typing-extensions = ">=4.5.0" + +[package.extras] +dev = ["black (==23.3.0)", "mypy (==1.5.1)", "pre-commit (==2.21.0)", "pytest (==7.4.2)", "ruff (==0.0.291)", "setuptools (>=61.0.0)", "setuptools-scm[toml] (>=6.2)"] +docs = ["mkdocs", "mkdocs-material", "mkdocstrings[python]", "pygments", "pymdown-extensions"] + [[package]] name = "pytest" version = "7.4.2" @@ -608,6 +663,20 @@ files = [ [package.dependencies] six = ">=1.5" +[[package]] +name = "python-dotenv" +version = "1.0.0" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, + {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + [[package]] name = "pyyaml" version = "6.0.1" @@ -707,6 +776,83 @@ six = "*" fixture = ["fixtures"] test = ["fixtures", "mock", "purl", "pytest", "requests-futures", "sphinx", "testtools"] +[[package]] +name = "ruamel-yaml" +version = "0.17.40" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +optional = false +python-versions = ">=3" +files = [ + {file = "ruamel.yaml-0.17.40-py3-none-any.whl", hash = "sha256:b16b6c3816dff0a93dca12acf5e70afd089fa5acb80604afd1ffa8b465b7722c"}, + {file = "ruamel.yaml-0.17.40.tar.gz", hash = "sha256:6024b986f06765d482b5b07e086cc4b4cd05dd22ddcbc758fa23d54873cf313d"}, +] + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\""} + +[package.extras] +docs = ["mercurial (>5.7)", "ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.8" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +optional = false +python-versions = ">=3.6" +files = [ + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"}, + {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, + {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, +] + [[package]] name = "six" version = "1.16.0" @@ -796,7 +942,22 @@ secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17. socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "zipp" +version = "3.17.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, + {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "2869c7c017f19bd39e16bf2eab971005f62a6457beb0aab6d826af9daa213548" +content-hash = "2792d2dc4d7c44168036a2388952c40f3b02e531c987d1f134f9ec35a931afa5" diff --git a/pyproject.toml b/pyproject.toml index e58a5e4..0b87dda 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,8 @@ typer = "^0.9.0" slack-sdk = "^3.21.3" tenacity = "^8.2.2" pyyaml = "^6.0.1" +pydantic-yaml = "^1.2.0" +pydantic-settings = "^2.1.0" [tool.poetry.group.dev.dependencies] mypy = "^1.2.0" diff --git a/tests/config/test_load_config.py b/tests/config/test_load_config.py index 138bada..63dae06 100644 --- a/tests/config/test_load_config.py +++ b/tests/config/test_load_config.py @@ -1,3 +1,5 @@ +import os + from pathlib import Path from eth_validator_watcher.config import load_config, WatchedKeyConfig from eth_validator_watcher.models import BeaconType @@ -14,11 +16,10 @@ def test_empty_config() -> None: assert config.default_fee_recipient is None assert config.slack_channel is None assert config.slack_token is None - assert config.beacon_type == BeaconType.OTHER + assert config.beacon_type is None assert config.relays is None assert config.liveness_file is None - - assert config.watched_keys == [] + assert config.watched_keys is None def test_filled_config() -> None: @@ -36,3 +37,35 @@ def test_filled_config() -> None: assert config.liveness_file == '/tmp/i-am-alive' assert [k.public_key for k in config.watched_keys] == ['0x832b8286f5d6535fd941c6c4ed8b9b20d214fc6aa726ce4fba1c9dbb4f278132646304f550e557231b6932aa02cf08d3'] + + +def test_filled_config_overriden() -> None: + environ = os.environ.copy() + + os.environ['eth_watcher_beacon_url'] = 'http://override-beacon/' + os.environ['eth_watcher_execution_url'] = 'http://override-exec/' + os.environ['eth_watcher_web3signer_url'] = 'http://override-web3signer/' + os.environ['eth_watcher_default_fee_recipient'] = '0x42' + os.environ['eth_watcher_slack_channel'] = '#ethereum-monitoring-override' + os.environ['eth_watcher_slack_token'] = 'secret-override' + os.environ['eth_watcher_beacon_type'] = 'nimbus' + os.environ['eth_watcher_relays'] = '["http://overriden-relay-1" ,"http://overriden-relay-2"]' + os.environ['eth_watcher_liveness_file'] = '/tmp/override-liveness-file' + + path = Path(assets.__file__).parent / "config.yaml" + config = load_config(path) + + assert config.beacon_url == 'http://override-beacon/' + assert config.execution_url == 'http://override-exec/' + assert config.web3signer_url == 'http://override-web3signer/' + assert config.default_fee_recipient == '0x42' + assert config.slack_channel == '#ethereum-monitoring-override' + assert config.slack_token == 'secret-override' + assert config.beacon_type == BeaconType.NIMBUS + assert config.relays == ['http://overriden-relay-1' ,'http://overriden-relay-2'] + assert config.liveness_file == '/tmp/override-liveness-file' + + assert [k.public_key for k in config.watched_keys] == ['0x832b8286f5d6535fd941c6c4ed8b9b20d214fc6aa726ce4fba1c9dbb4f278132646304f550e557231b6932aa02cf08d3'] + + os.environ.clear() + os.environ.update(environ)