Files
recipe-maintainer/lib/secrets.py
autonomic-bot f283a371bb recipe-maintainer: public snapshot (secrets + deployment plans removed, single commit)
Sanitized single-commit public mirror of recipe-maintainer.
- Removed test-ssh/.testenv (live creds); added test-ssh/.testenv.example placeholders.
- Removed plans/ and planned-updates/ (deployment-planning docs) so no client/
  deployment domains appear in the public repo.
- All other secret stores were already gitignored.
- docs.coopcloud.tech retained as a submodule (public upstream).
2026-06-16 20:18:24 +00:00

83 lines
2.5 KiB
Python

"""Secret resolution, syncing, and reading from containers.
Replaces the bash scripts resolve-secrets.sh and sync-secrets.sh
with Python equivalents that work with the new deployment model.
"""
from pathlib import Path
from lib.config import paths, load_recipe_toml
from lib.env import read_env_file
from lib.ssh import read_container_secrets
def secrets_dir() -> Path:
"""Return the secrets directory: recipe-info/testsecrets/."""
return paths.TESTSECRETS_DIR
def load_secrets(domain: str) -> dict[str, str]:
"""Load locally-stored secrets for a deployment.
Reads from recipe-info/testsecrets/<domain>.
Returns empty dict if no secrets file exists.
"""
p = secrets_dir() / domain
if not p.exists():
return {}
return read_env_file(p)
def save_secrets(domain: str, secrets: dict[str, str]) -> Path:
"""Save secrets to recipe-info/testsecrets/<domain>."""
d = secrets_dir()
d.mkdir(parents=True, exist_ok=True)
p = d / domain
with open(p, "w") as f:
for name in sorted(secrets.keys()):
f.write(f"{name}={secrets[name]}\n")
return p
def sync_secrets_for_deployment(server: str, domain: str) -> dict[str, str]:
"""Sync secrets from running containers to local file.
SSHes into the server, reads /run/secrets/ from all containers
of the stack, and saves to recipe-info/testsecrets/<domain>.
Returns the secrets dict.
"""
stack_prefix = domain.replace(".", "_")
secrets = read_container_secrets(server, stack_prefix)
if secrets:
save_secrets(domain, secrets)
print(f" Wrote {len(secrets)} secrets for {domain}", flush=True)
else:
print(f" No secrets found for {domain}", flush=True)
return secrets
def sync_all_secrets(server: str) -> dict[str, dict]:
"""Sync secrets for all recipes that have a recipe.toml.
Returns {domain: {secret_name: value}}.
"""
from lib.models import load_default_instance
inst = load_default_instance()
recipe_info_dir = paths.RECIPE_INFO_DIR
if not recipe_info_dir.exists():
return {}
results = {}
for d in sorted(recipe_info_dir.iterdir()):
recipe_toml = d / "recipe.toml"
if d.is_dir() and recipe_toml.exists():
recipe_name = d.name
domain = f"{recipe_name}.{inst.domain_suffix}"
print(f"=== {recipe_name} ({domain}) ===", flush=True)
results[domain] = sync_secrets_for_deployment(
server, domain
)
return results