system_update_configurator/
health.rs1use crate::bridge::{Bridge, BridgeError, OptOutPreference};
6use async_trait::async_trait;
7use fuchsia_inspect::health::Reporter;
8use std::cell::RefCell;
9
10pub struct HealthStatus {
11 node: RefCell<fuchsia_inspect::health::Node>,
12}
13
14impl HealthStatus {
15 pub fn new(root: &fuchsia_inspect::Node) -> Self {
16 let mut node = fuchsia_inspect::health::Node::new(root);
17 node.set_ok();
18 Self { node: RefCell::new(node) }
19 }
20
21 fn set_bridge_error(&self, err: Option<&BridgeError>) {
22 let mut node = self.node.borrow_mut();
23
24 match err {
25 Some(err) => node.set_unhealthy(&format!("Error interacting with storage: {err:#}")),
26 None => node.set_ok(),
27 }
28 }
29
30 pub fn wrap_bridge<I>(self, bridge: I) -> BridgeWithHealthStatus<I> {
31 BridgeWithHealthStatus { status: self, inner: bridge }
32 }
33}
34
35pub struct BridgeWithHealthStatus<I> {
36 status: HealthStatus,
37 inner: I,
38}
39
40#[async_trait(?Send)]
41impl<I> Bridge for BridgeWithHealthStatus<I>
42where
43 I: Bridge,
44{
45 async fn get_opt_out(&self) -> Result<OptOutPreference, BridgeError> {
46 let res = self.inner.get_opt_out().await;
47 self.status.set_bridge_error(res.as_ref().err());
48 res
49 }
50
51 async fn set_opt_out(&mut self, value: OptOutPreference) -> Result<(), BridgeError> {
52 let res = self.inner.set_opt_out(value).await;
53 self.status.set_bridge_error(res.as_ref().err());
54 res
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use crate::bridge;
62 use diagnostics_assertions::{assert_data_tree, AnyProperty};
63 use fuchsia_inspect::Inspector;
64
65 fn verify_healthy(inspector: &Inspector) {
66 assert_data_tree!(
67 inspector,
68 root: {
69 "fuchsia.inspect.Health": {
70 "start_timestamp_nanos": AnyProperty,
71 "status": "OK",
72 }
73 }
74 );
75 }
76
77 fn verify_unhealthy(inspector: &Inspector) {
78 assert_data_tree!(
79 inspector,
80 root: {
81 "fuchsia.inspect.Health": {
82 "start_timestamp_nanos": AnyProperty,
83 "status": "UNHEALTHY",
84 "message": AnyProperty,
85 }
86 }
87 );
88 }
89
90 #[fuchsia::test]
91 async fn starts_healthy() {
92 let inspector = Inspector::default();
93
94 let storage = bridge::testing::Fake::new(OptOutPreference::AllowAllUpdates);
95 let _storage = HealthStatus::new(inspector.root()).wrap_bridge(storage);
96
97 verify_healthy(&inspector);
98 }
99
100 #[fuchsia::test]
101 async fn updates_health_on_storage_error() {
102 let inspector = Inspector::default();
103
104 let (storage, fail_requests) =
105 bridge::testing::Fake::new_with_error_toggle(OptOutPreference::AllowAllUpdates);
106 let storage = HealthStatus::new(inspector.root()).wrap_bridge(storage);
107
108 verify_healthy(&inspector);
109
110 fail_requests.set(true);
111 storage.get_opt_out().await.unwrap_err();
112
113 verify_unhealthy(&inspector);
114 }
115
116 #[fuchsia::test]
117 async fn updates_health_on_storage_success() {
118 let inspector = Inspector::default();
119
120 let (storage, fail_requests) =
121 bridge::testing::Fake::new_with_error_toggle(OptOutPreference::AllowAllUpdates);
122 let storage = HealthStatus::new(inspector.root()).wrap_bridge(storage);
123
124 fail_requests.set(true);
125 storage.get_opt_out().await.unwrap_err();
126
127 fail_requests.set(false);
128 storage.get_opt_out().await.unwrap();
129
130 verify_healthy(&inspector);
131 }
132}