lava_operator/
finalizer.rs1use crate::{Condition, Phase, ReconcileOutcome};
12
13pub trait DestroyBackend {
16 fn destroy(&self, source: &crate::Source, bindings: &indexmap::IndexMap<String, String>)
23 -> Result<Vec<String>, String>;
24}
25
26#[must_use]
29pub fn run_finalizer<B: DestroyBackend>(
30 backend: &B,
31 source: &crate::Source,
32 bindings: &indexmap::IndexMap<String, String>,
33) -> ReconcileOutcome {
34 match backend.destroy(source, bindings) {
35 Ok(diagnostics) => ReconcileOutcome {
36 phase: Phase::Finalizing,
37 conditions: {
38 let mut c = vec![Condition::ok("Destroyed", "BackendOk")];
39 for d in diagnostics {
40 c.push(Condition::ok("Diagnostic", d));
41 }
42 c
43 },
44 terraform_json: None,
45 },
46 Err(e) => ReconcileOutcome {
47 phase: Phase::Failed,
48 conditions: vec![Condition::fail("Destroyed", e)],
49 terraform_json: None,
50 },
51 }
52}
53
54pub struct MockDestroy {
56 pub outcome: Result<Vec<String>, String>,
57}
58
59impl MockDestroy {
60 #[must_use]
61 pub fn ok() -> Self {
62 Self {
63 outcome: Ok(vec![]),
64 }
65 }
66 #[must_use]
67 pub fn with_diagnostics(msgs: Vec<String>) -> Self {
68 Self { outcome: Ok(msgs) }
69 }
70 #[must_use]
71 pub fn err(msg: impl Into<String>) -> Self {
72 Self {
73 outcome: Err(msg.into()),
74 }
75 }
76}
77
78impl DestroyBackend for MockDestroy {
79 fn destroy(
80 &self,
81 _src: &crate::Source,
82 _bindings: &indexmap::IndexMap<String, String>,
83 ) -> Result<Vec<String>, String> {
84 self.outcome.clone()
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use indexmap::IndexMap;
92
93 #[test]
94 fn successful_destroy_drives_to_finalizing_phase() {
95 let outcome = run_finalizer(
96 &MockDestroy::ok(),
97 &crate::Source::Inline { inline: "src".into() },
98 &IndexMap::new(),
99 );
100 assert_eq!(outcome.phase, Phase::Finalizing);
101 assert_eq!(outcome.conditions[0].status, "True");
102 }
103
104 #[test]
105 fn destroy_diagnostics_surface_as_conditions() {
106 let outcome = run_finalizer(
107 &MockDestroy::with_diagnostics(vec!["removed 3 resources".into()]),
108 &crate::Source::Inline { inline: "src".into() },
109 &IndexMap::new(),
110 );
111 assert_eq!(outcome.conditions.len(), 2);
112 assert_eq!(outcome.conditions[1].kind, "Diagnostic");
113 }
114
115 #[test]
116 fn failed_destroy_drives_to_failed_phase_with_message() {
117 let outcome = run_finalizer(
118 &MockDestroy::err("provider unreachable"),
119 &crate::Source::Inline { inline: "src".into() },
120 &IndexMap::new(),
121 );
122 assert_eq!(outcome.phase, Phase::Failed);
123 assert_eq!(outcome.conditions[0].status, "False");
124 assert_eq!(
125 outcome.conditions[0].message.as_deref(),
126 Some("provider unreachable"),
127 );
128 }
129}