Skip to main content

system_update_committer/metadata/
commit.rs

1// Copyright 2020 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 super::configuration_without_recovery::ConfigurationWithoutRecovery;
6use super::errors::{BootManagerError, BootManagerResultExt};
7use fidl_fuchsia_paver as paver;
8
9/// Commit the slot by updating the boot metadata.
10pub async fn do_commit(
11    boot_manager: &paver::BootManagerProxy,
12    current_config: &ConfigurationWithoutRecovery,
13) -> Result<(), BootManagerError> {
14    // Do all writes inside this function to ensure that we call flush no matter what.
15    async fn internal_write(
16        boot_manager: &paver::BootManagerProxy,
17        current_config: &ConfigurationWithoutRecovery,
18    ) -> Result<(), BootManagerError> {
19        let alternate_config = current_config.to_alternate().into();
20
21        let () = boot_manager
22            .set_configuration_healthy(current_config.into())
23            .await
24            .into_boot_manager_result("set_configuration_healthy")?;
25        let () = boot_manager
26            .set_configuration_unbootable(alternate_config)
27            .await
28            .into_boot_manager_result("set_configuration_unbootable")?;
29        Ok(())
30    }
31
32    // Capture the result of the writes so we can return it after we flush.
33    let write_result = internal_write(boot_manager, current_config).await;
34
35    let () = boot_manager.flush().await.into_boot_manager_result("flush")?;
36
37    write_result
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43    use assert_matches::assert_matches;
44    use mock_paver::{MockPaverServiceBuilder, PaverEvent, hooks as mphooks};
45    use std::sync::Arc;
46    use zx::Status;
47
48    /// Helper fn to verify that do_commit succeeds.
49    async fn run_success_test(current_config: &ConfigurationWithoutRecovery) {
50        let paver = Arc::new(MockPaverServiceBuilder::new().build());
51        let () = do_commit(&paver.spawn_boot_manager_service(), current_config).await.unwrap();
52
53        assert_eq!(
54            paver.take_events(),
55            vec![
56                PaverEvent::SetConfigurationHealthy { configuration: current_config.into() },
57                PaverEvent::SetConfigurationUnbootable {
58                    configuration: current_config.to_alternate().into()
59                },
60                PaverEvent::BootManagerFlush,
61            ]
62        );
63    }
64
65    #[fuchsia::test]
66    async fn test_success_current_a() {
67        run_success_test(&ConfigurationWithoutRecovery::A).await;
68    }
69
70    #[fuchsia::test]
71    async fn test_success_current_b() {
72        run_success_test(&ConfigurationWithoutRecovery::B).await;
73    }
74
75    #[fuchsia::test]
76    async fn test_fails_when_set_healthy_fails() {
77        let paver = Arc::new(
78            MockPaverServiceBuilder::new()
79                .insert_hook(mphooks::return_error(|e| match e {
80                    PaverEvent::SetConfigurationHealthy { .. } => Err(Status::OUT_OF_RANGE),
81                    _ => Ok(()),
82                }))
83                .build(),
84        );
85
86        assert_matches!(
87            do_commit(&paver.spawn_boot_manager_service(), &ConfigurationWithoutRecovery::A).await,
88            Err(BootManagerError::Status {
89                method_name: "set_configuration_healthy",
90                status: Status::OUT_OF_RANGE
91            })
92        );
93
94        assert_eq!(
95            paver.take_events(),
96            vec![
97                PaverEvent::SetConfigurationHealthy { configuration: paver::Configuration::A },
98                PaverEvent::BootManagerFlush,
99            ]
100        );
101    }
102}