system_update_configurator/
health.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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}