mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-07-31 06:39:56 +00:00
Compare commits
2 Commits
29bc3cf2da
...
693c6161ef
Author | SHA1 | Date | |
---|---|---|---|
693c6161ef | |||
c13cd029bc |
@ -65,6 +65,8 @@ will try to read value from ``SECRET`` environment variable. In case if the requ
|
|||||||
|
|
||||||
will eventually lead ``key`` option in section ``section1`` to be set to the value of ``HOME`` environment variable (if available).
|
will eventually lead ``key`` option in section ``section1`` to be set to the value of ``HOME`` environment variable (if available).
|
||||||
|
|
||||||
|
Moreover, configuration can be read from environment variables directly by following the same naming convention, e.g. in the example above, one can have environment variable named ``section1:key`` (e.g. ``section1:key=$HOME``) and it will be substituted to the configuration with the highest priority.
|
||||||
|
|
||||||
There is also additional subcommand which will allow to validate configuration and print found errors. In order to do so, run ``service-config-validate`` subcommand, e.g.:
|
There is also additional subcommand which will allow to validate configuration and print found errors. In order to do so, run ``service-config-validate`` subcommand, e.g.:
|
||||||
|
|
||||||
.. code-block:: shell
|
.. code-block:: shell
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#
|
#
|
||||||
# pylint: disable=too-many-public-methods
|
# pylint: disable=too-many-public-methods
|
||||||
import configparser
|
import configparser
|
||||||
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@ -164,6 +165,7 @@ class Configuration(configparser.RawConfigParser):
|
|||||||
"""
|
"""
|
||||||
configuration = cls()
|
configuration = cls()
|
||||||
configuration.load(path)
|
configuration.load(path)
|
||||||
|
configuration.load_environment()
|
||||||
configuration.merge_sections(repository_id)
|
configuration.merge_sections(repository_id)
|
||||||
return configuration
|
return configuration
|
||||||
|
|
||||||
@ -288,6 +290,16 @@ class Configuration(configparser.RawConfigParser):
|
|||||||
self.read(self.path)
|
self.read(self.path)
|
||||||
self.load_includes() # load includes
|
self.load_includes() # load includes
|
||||||
|
|
||||||
|
def load_environment(self) -> None:
|
||||||
|
"""
|
||||||
|
load environment variables into configuration
|
||||||
|
"""
|
||||||
|
for name, value in os.environ.items():
|
||||||
|
if ":" not in name:
|
||||||
|
continue
|
||||||
|
section, key = name.rsplit(":", maxsplit=1)
|
||||||
|
self.set_option(section, key, value)
|
||||||
|
|
||||||
def load_includes(self, path: Path | None = None) -> None:
|
def load_includes(self, path: Path | None = None) -> None:
|
||||||
"""
|
"""
|
||||||
load configuration includes from specified path
|
load configuration includes from specified path
|
||||||
@ -356,11 +368,16 @@ class Configuration(configparser.RawConfigParser):
|
|||||||
"""
|
"""
|
||||||
reload configuration if possible or raise exception otherwise
|
reload configuration if possible or raise exception otherwise
|
||||||
"""
|
"""
|
||||||
|
# get current properties and validate input
|
||||||
path, repository_id = self.check_loaded()
|
path, repository_id = self.check_loaded()
|
||||||
for section in self.sections(): # clear current content
|
|
||||||
|
# clear current content
|
||||||
|
for section in self.sections():
|
||||||
self.remove_section(section)
|
self.remove_section(section)
|
||||||
self.load(path)
|
|
||||||
self.merge_sections(repository_id)
|
# create another instance and copy values from there
|
||||||
|
instance = self.from_path(path, repository_id)
|
||||||
|
self.copy_from(instance)
|
||||||
|
|
||||||
def set_option(self, section: str, option: str, value: str) -> None:
|
def set_option(self, section: str, option: str, value: str) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import configparser
|
import configparser
|
||||||
from io import StringIO
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import os
|
||||||
|
|
||||||
|
from io import StringIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
from unittest.mock import call as MockCall
|
from unittest.mock import call as MockCall
|
||||||
@ -42,12 +42,16 @@ def test_from_path(repository_id: RepositoryId, mocker: MockerFixture) -> None:
|
|||||||
mocker.patch("ahriman.core.configuration.Configuration.get", return_value="ahriman.ini.d")
|
mocker.patch("ahriman.core.configuration.Configuration.get", return_value="ahriman.ini.d")
|
||||||
read_mock = mocker.patch("ahriman.core.configuration.Configuration.read")
|
read_mock = mocker.patch("ahriman.core.configuration.Configuration.read")
|
||||||
load_includes_mock = mocker.patch("ahriman.core.configuration.Configuration.load_includes")
|
load_includes_mock = mocker.patch("ahriman.core.configuration.Configuration.load_includes")
|
||||||
|
merge_mock = mocker.patch("ahriman.core.configuration.Configuration.merge_sections")
|
||||||
|
environment_mock = mocker.patch("ahriman.core.configuration.Configuration.load_environment")
|
||||||
path = Path("path")
|
path = Path("path")
|
||||||
|
|
||||||
configuration = Configuration.from_path(path, repository_id)
|
configuration = Configuration.from_path(path, repository_id)
|
||||||
assert configuration.path == path
|
assert configuration.path == path
|
||||||
read_mock.assert_called_once_with(path)
|
read_mock.assert_called_once_with(path)
|
||||||
load_includes_mock.assert_called_once_with()
|
load_includes_mock.assert_called_once_with()
|
||||||
|
merge_mock.assert_called_once_with(repository_id)
|
||||||
|
environment_mock.assert_called_once_with()
|
||||||
|
|
||||||
|
|
||||||
def test_from_path_file_missing(repository_id: RepositoryId, mocker: MockerFixture) -> None:
|
def test_from_path_file_missing(repository_id: RepositoryId, mocker: MockerFixture) -> None:
|
||||||
@ -324,6 +328,18 @@ def test_gettype_from_section_no_section(configuration: Configuration) -> None:
|
|||||||
configuration.gettype("rsync:x86_64", configuration.repository_id)
|
configuration.gettype("rsync:x86_64", configuration.repository_id)
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_environment(configuration: Configuration) -> None:
|
||||||
|
"""
|
||||||
|
must load environment variables
|
||||||
|
"""
|
||||||
|
os.environ["section:key"] = "value1"
|
||||||
|
os.environ["section:identifier:key"] = "value2"
|
||||||
|
|
||||||
|
configuration.load_environment()
|
||||||
|
assert configuration.get("section", "key") == "value1"
|
||||||
|
assert configuration.get("section:identifier", "key") == "value2"
|
||||||
|
|
||||||
|
|
||||||
def test_load_includes(mocker: MockerFixture) -> None:
|
def test_load_includes(mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must load includes
|
must load includes
|
||||||
@ -444,10 +460,12 @@ def test_reload(configuration: Configuration, mocker: MockerFixture) -> None:
|
|||||||
"""
|
"""
|
||||||
load_mock = mocker.patch("ahriman.core.configuration.Configuration.load")
|
load_mock = mocker.patch("ahriman.core.configuration.Configuration.load")
|
||||||
merge_mock = mocker.patch("ahriman.core.configuration.Configuration.merge_sections")
|
merge_mock = mocker.patch("ahriman.core.configuration.Configuration.merge_sections")
|
||||||
|
environment_mock = mocker.patch("ahriman.core.configuration.Configuration.load_environment")
|
||||||
|
|
||||||
configuration.reload()
|
configuration.reload()
|
||||||
load_mock.assert_called_once_with(configuration.path)
|
load_mock.assert_called_once_with(configuration.path)
|
||||||
merge_mock.assert_called_once_with(configuration.repository_id)
|
merge_mock.assert_called_once_with(configuration.repository_id)
|
||||||
|
environment_mock.assert_called_once_with()
|
||||||
|
|
||||||
|
|
||||||
def test_reload_clear(configuration: Configuration, mocker: MockerFixture) -> None:
|
def test_reload_clear(configuration: Configuration, mocker: MockerFixture) -> None:
|
||||||
|
Reference in New Issue
Block a user