| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| super::*, |
| fidl_fuchsia_pkg::ResolveError, |
| fidl_fuchsia_update_installer_ext::{PrepareFailureReason, State}, |
| pretty_assertions::assert_eq, |
| }; |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn rejects_invalid_package_name() { |
| let env = TestEnv::builder().build().await; |
| |
| // Name the update package something other than "update" and assert that the process fails to |
| // validate the update package. |
| env.resolver |
| .register_custom_package("not_update", "not_update", "upd4t3", "fuchsia.com") |
| .add_file("packages.json", make_packages_json([SYSTEM_IMAGE_URL])) |
| .add_file("zbi", "fake zbi") |
| .add_file("zedboot", "new recovery") |
| .add_file("version", "build version"); |
| |
| let not_update_package_url = "fuchsia-pkg://fuchsia.com/not_update"; |
| |
| let result = env.run_update_with_options(not_update_package_url, default_options()).await; |
| assert!(result.is_err(), "system updater succeeded when it should fail"); |
| |
| // Expect to have failed prior to downloading images. |
| // The overall result should be similar to an invalid board, and we should have used |
| // the not_update package URL, not `fuchsia.com/update`. |
| assert_eq!( |
| env.take_interactions(), |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::VerifiedBootMetadata |
| }), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::Kernel |
| }), |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::QueryConfigurationStatus { configuration: paver::Configuration::A }), |
| Paver(PaverEvent::SetConfigurationUnbootable { |
| configuration: paver::Configuration::B |
| }), |
| Paver(PaverEvent::BootManagerFlush), |
| PackageResolve(not_update_package_url.to_string()) |
| ] |
| ); |
| |
| assert_eq!( |
| env.get_ota_metrics().await, |
| OtaMetrics { |
| initiator: metrics::OtaResultAttemptsMetricDimensionInitiator::UserInitiatedCheck |
| as u32, |
| phase: metrics::OtaResultAttemptsMetricDimensionPhase::Tufupdate as u32, |
| status_code: metrics::OtaResultAttemptsMetricDimensionStatusCode::Error as u32, |
| target: "build version".into(), |
| } |
| ); |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn fails_if_package_unavailable() { |
| let env = TestEnv::builder().build().await; |
| |
| env.resolver.mock_resolve_failure(UPDATE_PKG_URL, ResolveError::PackageNotFound); |
| |
| let result = env.run_update().await; |
| assert!(result.is_err(), "system updater succeeded when it should fail"); |
| |
| assert_eq!( |
| env.take_interactions(), |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::VerifiedBootMetadata |
| }), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::Kernel |
| }), |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::QueryConfigurationStatus { configuration: paver::Configuration::A }), |
| Paver(PaverEvent::SetConfigurationUnbootable { |
| configuration: paver::Configuration::B |
| }), |
| Paver(PaverEvent::BootManagerFlush), |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| ] |
| ); |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn uses_custom_update_package() { |
| let env = TestEnv::builder().build().await; |
| |
| env.resolver |
| .register_custom_package("another-update/4", "update", "upd4t3r", "fuchsia.com") |
| .add_file("packages.json", make_packages_json([])) |
| .add_file("epoch.json", make_epoch_json(SOURCE_EPOCH)) |
| .add_file("zbi", "fake zbi"); |
| |
| env.run_update_with_options("fuchsia-pkg://fuchsia.com/another-update/4", default_options()) |
| .await |
| .expect("run system updater"); |
| |
| assert_eq!( |
| env.take_interactions(), |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::VerifiedBootMetadata |
| }), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::Kernel |
| }), |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::QueryConfigurationStatus { configuration: paver::Configuration::A }), |
| Paver(PaverEvent::SetConfigurationUnbootable { |
| configuration: paver::Configuration::B |
| }), |
| Paver(PaverEvent::BootManagerFlush), |
| PackageResolve("fuchsia-pkg://fuchsia.com/another-update/4".to_string()), |
| Gc, |
| BlobfsSync, |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::B, |
| asset: paver::Asset::Kernel, |
| payload: b"fake zbi".to_vec(), |
| }), |
| Paver(PaverEvent::SetConfigurationActive { configuration: paver::Configuration::B }), |
| Paver(PaverEvent::DataSinkFlush), |
| Paver(PaverEvent::BootManagerFlush), |
| Reboot, |
| ] |
| ); |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn retry_update_package_resolve_once() { |
| let env = TestEnv::builder().build().await; |
| |
| env.resolver.url(UPDATE_PKG_URL).respond_serially(vec![ |
| // First resolve should fail with NoSpace. |
| Err(ResolveError::NoSpace), |
| // Second resolve should succeed. |
| Ok(env |
| .resolver |
| .package("update", "upd4t3") |
| .add_file("packages.json", make_packages_json([])) |
| .add_file("epoch.json", make_epoch_json(SOURCE_EPOCH)) |
| .add_file("zbi", "fake zbi")), |
| ]); |
| |
| env.run_update().await.expect("run system updater"); |
| |
| assert_eq!( |
| env.take_interactions(), |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::VerifiedBootMetadata |
| }), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::Kernel |
| }), |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::QueryConfigurationStatus { configuration: paver::Configuration::A }), |
| Paver(PaverEvent::SetConfigurationUnbootable { |
| configuration: paver::Configuration::B |
| }), |
| Paver(PaverEvent::BootManagerFlush), |
| // First resolve should fail with NoSpace, so we GC and try the resolve again. |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| // Second resolve should succeed! |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| BlobfsSync, |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::B, |
| asset: paver::Asset::Kernel, |
| payload: b"fake zbi".to_vec(), |
| }), |
| Paver(PaverEvent::SetConfigurationActive { configuration: paver::Configuration::B }), |
| Paver(PaverEvent::DataSinkFlush), |
| Paver(PaverEvent::BootManagerFlush), |
| Reboot, |
| ] |
| ); |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn retry_update_package_resolve_twice() { |
| let env = TestEnv::builder().build().await; |
| |
| env.resolver.url(UPDATE_PKG_URL).respond_serially(vec![ |
| // First two resolves should fail with NoSpace. |
| Err(ResolveError::NoSpace), |
| Err(ResolveError::NoSpace), |
| // Third resolve should succeed. |
| Ok(env |
| .resolver |
| .package("update", "upd4t3") |
| .add_file("packages.json", make_packages_json([])) |
| .add_file("epoch.json", make_epoch_json(SOURCE_EPOCH)) |
| .add_file("zbi", "fake zbi")), |
| ]); |
| |
| env.run_update().await.expect("run system updater"); |
| |
| assert_eq!( |
| env.take_interactions(), |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::VerifiedBootMetadata |
| }), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::Kernel |
| }), |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::QueryConfigurationStatus { configuration: paver::Configuration::A }), |
| Paver(PaverEvent::SetConfigurationUnbootable { |
| configuration: paver::Configuration::B |
| }), |
| Paver(PaverEvent::BootManagerFlush), |
| // First resolve should fail with NoSpace, so we GC and try the resolve again. |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| // Second resolve should fail with NoSpace, so we GC and try the resolve again. |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| // Third resolve should succeed! |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| BlobfsSync, |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::B, |
| asset: paver::Asset::Kernel, |
| payload: b"fake zbi".to_vec(), |
| }), |
| Paver(PaverEvent::SetConfigurationActive { configuration: paver::Configuration::B }), |
| Paver(PaverEvent::DataSinkFlush), |
| Paver(PaverEvent::BootManagerFlush), |
| Reboot, |
| ] |
| ); |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn retry_update_package_resolve_thrice_fails_update_attempt() { |
| let env = TestEnv::builder().build().await; |
| |
| env.resolver.url(UPDATE_PKG_URL).respond_serially(vec![ |
| // Each resolve should fail with NoSpace. |
| Err(ResolveError::NoSpace), |
| Err(ResolveError::NoSpace), |
| Err(ResolveError::NoSpace), |
| ]); |
| |
| let mut attempt = env.start_update().await.unwrap(); |
| |
| assert_eq!(attempt.next().await.unwrap().unwrap(), State::Prepare); |
| assert_eq!( |
| attempt.next().await.unwrap().unwrap(), |
| State::FailPrepare(PrepareFailureReason::OutOfSpace) |
| ); |
| |
| assert_eq!( |
| env.take_interactions(), |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::VerifiedBootMetadata |
| }), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::Kernel |
| }), |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::QueryConfigurationStatus { configuration: paver::Configuration::A }), |
| Paver(PaverEvent::SetConfigurationUnbootable { |
| configuration: paver::Configuration::B |
| }), |
| Paver(PaverEvent::BootManagerFlush), |
| // First resolve should fail with out of space, so we GC and try the resolve again. |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| // Second resolve should fail with out of space, so we GC and try the resolve again. |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| // Third resolve should fail with out of space, so the update fails. |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| ] |
| ); |
| } |