greentic-operator 0.4.34

Greentic operator CLI for local dev and demo orchestration.
use std::{fs::File, io::Read, path::Path};

use anyhow::{Context, Result, anyhow};
use serde::Deserialize;
use zip::ZipArchive;

const BACKEND_CONFIG_PATHS: &[&str] = &[
    "assets/secrets_backend.json",
    "assets/secrets-backend.json",
    "secrets_backend.json",
    "secrets-backend.json",
];

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SecretsBackendKind {
    DevStore,
    Env,
}

impl std::fmt::Display for SecretsBackendKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let label = match self {
            SecretsBackendKind::DevStore => "dev-store",
            SecretsBackendKind::Env => "env",
        };
        f.write_str(label)
    }
}

#[derive(Deserialize)]
struct PackBackendConfig {
    backend: Option<String>,
}

pub fn backend_kind_from_pack(pack_path: &Path) -> Result<SecretsBackendKind> {
    let file = File::open(pack_path)
        .with_context(|| format!("open secrets manager pack {}", pack_path.display()))?;
    let mut archive = ZipArchive::new(file)
        .with_context(|| format!("read secrets manager pack {}", pack_path.display()))?;
    for entry_name in BACKEND_CONFIG_PATHS {
        if let Ok(mut entry) = archive.by_name(entry_name) {
            let mut contents = String::new();
            entry
                .read_to_string(&mut contents)
                .with_context(|| format!("read backend config {}", entry_name))?;
            let config: PackBackendConfig = serde_json::from_str(&contents)
                .with_context(|| format!("parse secrets backend config in {}", entry_name))?;
            if let Some(kind) = config.backend {
                return match kind.trim().to_ascii_lowercase().as_str() {
                    "" | "default" | "dev-store" | "devstore" => Ok(SecretsBackendKind::DevStore),
                    "env" | "environment" => Ok(SecretsBackendKind::Env),
                    other => Err(anyhow!(
                        "unsupported secrets backend '{other}' in pack {}",
                        pack_path.display()
                    )),
                };
            }
            return Ok(SecretsBackendKind::DevStore);
        }
    }
    Err(anyhow!(
        "missing secrets backend config in pack {}",
        pack_path.display()
    ))
}