1use std::collections::BTreeSet;23use chrono::{DateTime, Utc};45use crate::fleetdata::FleetSecretData;67#[derive(Debug)]8pub struct Expectations {9 pub owners: BTreeSet<String>,10 pub generation_data: serde_json::Value,11 pub public_parts: BTreeSet<String>,12 pub private_parts: BTreeSet<String>,13}1415#[derive(thiserror::Error, Debug)]16pub enum RegenerationReason {17 #[error("owners added: {0:?}")]18 OwnersAdded(BTreeSet<String>),19 #[error("owners added: {0:?}")]20 OwnersRemoved(BTreeSet<String>),21 #[error("unexpected generation data, expected: {expected:?}, found: {found:?}")]22 GenerationData {23 expected: serde_json::Value,24 found: serde_json::Value,25 },26 #[error("unexpected part list, expected: {expected:?}, found: {found:?}")]27 PartList {28 expected: BTreeSet<String>,29 found: BTreeSet<String>,30 },31 #[error("part {0} is expected to be encrypted")]32 ExpectedPrivate(String),33 #[error("part {0} is not expected to be encrypted")]34 ExpectedPublic(String),35 #[error("secret is expired at {0}")]36 Expired(DateTime<Utc>),37}3839pub fn secret_needs_regeneration(40 secret: &FleetSecretData,41 owners: &BTreeSet<String>,42 expectations: &Expectations,43) -> Option<RegenerationReason> {44 if !owners.is_empty() {45 let added: BTreeSet<String> = expectations.owners.difference(owners).cloned().collect();46 if !added.is_empty() {47 return Some(RegenerationReason::OwnersAdded(added));48 }4950 let removed: BTreeSet<String> = owners.difference(&expectations.owners).cloned().collect();51 if !removed.is_empty() {52 return Some(RegenerationReason::OwnersRemoved(removed));53 }54 }5556 if secret.generation_data != expectations.generation_data {57 return Some(RegenerationReason::GenerationData {58 expected: expectations.generation_data.clone(),59 found: secret.generation_data.clone(),60 });61 }6263 if !expectations.public_parts.is_empty() || !expectations.private_parts.is_empty() {64 let expected: BTreeSet<String> = expectations65 .public_parts66 .union(&expectations.private_parts)67 .cloned()68 .collect();69 let found: BTreeSet<String> = secret.parts.keys().cloned().collect();7071 if found != expected {72 return Some(RegenerationReason::PartList { expected, found });73 }7475 for (name, value) in secret.parts.iter() {76 if value.raw.encrypted {77 if !expectations.private_parts.contains(name) {78 return Some(RegenerationReason::ExpectedPrivate(name.clone()));79 }80 } else if !expectations.public_parts.contains(name) {81 return Some(RegenerationReason::ExpectedPublic(name.clone()));82 }83 }84 }8586 if let Some(expiration) = secret.expires_at {87 88 if expiration < Utc::now() {89 return Some(RegenerationReason::Expired(expiration));90 }91 }9293 None94}