1use std::str::FromStr;23use crate::command::MyCommand;4use crate::host::Config;5use anyhow::{anyhow, Result};6use tracing::warn;78impl Config {9 pub fn cached_key(&self, host: &str) -> Option<String> {10 let data = self.data();11 let key = data.hosts.get(host).map(|h| &h.encryption_key);12 if let Some(key) = key {13 if key.is_empty() {14 return None;15 }16 }17 key.cloned()18 }19 pub fn update_key(&self, host: &str, key: String) {20 let mut data = self.data_mut();21 let host = data.hosts.entry(host.to_string()).or_default();22 host.encryption_key = key.trim().to_string();23 }2425 pub async fn key(&self, host: &str) -> anyhow::Result<String> {26 if let Some(key) = self.cached_key(host) {27 Ok(key)28 } else {29 warn!("Loading key for {}", host);30 let mut cmd = MyCommand::new("cat");31 cmd.arg("/etc/ssh/ssh_host_ed25519_key.pub");32 let key = self.run_string_on(host, cmd, false).await?;33 self.update_key(host, key.clone());34 Ok(key)35 }36 }37 38 pub async fn recipient(&self, host: &str) -> anyhow::Result<age::ssh::Recipient> {39 let key = self.key(host).await?;40 age::ssh::Recipient::from_str(&key).map_err(|e| anyhow!("parse recipient error: {:?}", e))41 }4243 pub async fn orphaned_data(&self) -> Result<Vec<String>> {44 let mut out = Vec::new();45 let host_names = self.list_hosts().await?;46 for hostname in self47 .data()48 .hosts49 .iter()50 .filter(|(_, host)| !host.encryption_key.is_empty())51 .map(|(n, _)| n)52 {53 if !host_names.contains(hostname) {54 out.push(hostname.to_owned())55 }56 }5758 Ok(out)59 }60}