Coverage for arclith / infrastructure / secret_factory.py: 100%
34 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-25 15:02 +0100
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-25 15:02 +0100
1from __future__ import annotations
3import os
4from pathlib import Path
6from arclith.domain.ports.secret_resolver import SecretResolver
9def build_secret_resolver(raw_config: dict, base_path: Path | None = None) -> SecretResolver | None:
10 """Build a SecretResolver from raw config dict (before Pydantic validation).
12 Returns None when no mappings are declared (nothing to resolve).
13 VAULT_ADDR env var overrides secrets.vault.addr from config.
14 """
15 secrets = raw_config.get("secrets") or {}
16 mappings: dict = secrets.get("mappings") or {}
17 if not mappings:
18 return None
20 resolver_type: str = secrets.get("resolver", "yaml")
22 def _make(name: str) -> SecretResolver:
23 match name:
24 case "vault":
25 vault_cfg: dict = secrets.get("vault") or {}
26 addr = os.environ.get("VAULT_ADDR") or vault_cfg.get("addr", "http://127.0.0.1:8200")
27 mount: str = vault_cfg.get("mount", "kv")
28 from arclith.adapters.output.vault.secret_adapter import VaultSecretAdapter
29 return VaultSecretAdapter(addr=addr, mount=mount)
30 case "yaml":
31 yaml_cfg: dict = secrets.get("yaml") or {}
32 default_path = str(base_path / "secrets.yaml") if base_path else "secrets.yaml"
33 path: str = yaml_cfg.get("path", default_path)
34 from arclith.adapters.output.yaml.secret_adapter import YamlSecretAdapter
35 return YamlSecretAdapter(path=path)
36 case "env":
37 from arclith.adapters.output.env.secret_adapter import EnvSecretAdapter
38 return EnvSecretAdapter()
39 case _:
40 raise ValueError(f"Unknown secret resolver: '{name}'")
42 if resolver_type == "chain":
43 chain_names: list[str] = secrets.get("chain") or ["yaml"]
44 from arclith.adapters.output.chain.secret_adapter import ChainSecretAdapter
45 return ChainSecretAdapter([_make(n) for n in chain_names])
47 return _make(resolver_type)