1use std::collections::{BTreeMap, BTreeSet};23use chrono::{DateTime, Utc};45use crate::fleetdata::{Expectations, FleetSecretData, FleetSecretDistribution, GeneratorPart};67#[derive(thiserror::Error, Debug)]8pub enum RegenerationReason {9 #[error("owners added: {0:?}")]10 OwnersAdded(BTreeSet<String>),11 #[error("owners added: {0:?}")]12 OwnersRemoved(BTreeSet<String>),13 #[error("unexpected generation data, expected: {expected:?}, found: {found:?}")]14 GenerationData {15 expected: serde_json::Value,16 found: serde_json::Value,17 },18 #[error("unexpected part list, expected: {expected:?}, found: {found:?}")]19 PartList {20 expected: BTreeSet<String>,21 found: BTreeSet<String>,22 },23 #[error("part {0} is expected to be encrypted")]24 ExpectedPrivate(String),25 #[error("part {0} is not expected to be encrypted")]26 ExpectedPublic(String),27 #[error("secret is expired at {0}")]28 Expired(DateTime<Utc>),2930 #[error("secret is not generated for this host")]31 Missing,32}3334pub fn secret_needs_regeneration(35 secret: &FleetSecretDistribution,36 expectations: &Expectations,37) -> Option<RegenerationReason> {38 let added: BTreeSet<String> = expectations39 .owners40 .difference(&secret.owners)41 .cloned()42 .collect();43 if !added.is_empty() {44 return Some(RegenerationReason::OwnersAdded(added));45 }4647 let removed: BTreeSet<String> = secret48 .owners49 .difference(&expectations.owners)50 .cloned()51 .collect();52 if !removed.is_empty() {53 return Some(RegenerationReason::OwnersRemoved(removed));54 }5556 if secret.secret.generation_data != expectations.generation_data {57 return Some(RegenerationReason::GenerationData {58 expected: expectations.generation_data.clone(),59 found: secret.secret.generation_data.clone(),60 });61 }6263 let expected: BTreeSet<String> = expectations.parts.keys().cloned().collect();64 let found: BTreeSet<String> = secret.secret.parts.keys().cloned().collect();6566 if found != expected {67 return Some(RegenerationReason::PartList { expected, found });68 }6970 for (name, value) in secret.secret.parts.iter() {71 let expectation = expectations72 .parts73 .get(name)74 .expect("found == expected checked");75 if value.raw.encrypted {76 if !expectation.encrypted {77 return Some(RegenerationReason::ExpectedPrivate(name.clone()));78 }79 } else if expectation.encrypted {80 return Some(RegenerationReason::ExpectedPublic(name.clone()));81 }82 }8384 if let Some(expiration) = secret.secret.expires_at {85 86 if expiration < Utc::now() {87 return Some(RegenerationReason::Expired(expiration));88 }89 }9091 None92}