[system-updater] modify GC strategy

This change modifies the GC strategy in the system-updater. In a
subsequent change, we'll incorporate the retained index so that each GC
can be increasingly more aggressive.

Test: system-updater-integration-tests
Test: system-updater-tests
Docs: updated
Fixed: 77214
Change-Id: Ied9a47bf1105f2ac6d450c5fcdb41d9f5c093f31
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/540965
Commit-Queue: Zach Kirschenbaum <zkbaum@google.com>
Reviewed-by: Kevin Wells <kevinwells@google.com>
diff --git a/docs/concepts/packages/images/second-gc.png b/docs/concepts/packages/images/gc.png
similarity index 100%
rename from docs/concepts/packages/images/second-gc.png
rename to docs/concepts/packages/images/gc.png
Binary files differ
diff --git a/docs/concepts/packages/images/initial-gc.png b/docs/concepts/packages/images/initial-gc.png
deleted file mode 100644
index 8243423..0000000
--- a/docs/concepts/packages/images/initial-gc.png
+++ /dev/null
Binary files differ
diff --git a/docs/concepts/packages/ota.md b/docs/concepts/packages/ota.md
index fc2b261..88d7451 100644
--- a/docs/concepts/packages/ota.md
+++ b/docs/concepts/packages/ota.md
@@ -105,9 +105,8 @@
 
 The update process is divided in the following steps:
 
-* [Initial garbage collection](#initial-garbage-collection)
 * [Fetch update package](#fetch-update-package)
-* [Secondary garbage collection](#secondary-garbage-collection)
+* [Trigger garbage collection](#garbage-collection)
 * [Verify board matches](#verify-board)
 * [Verify epoch is supported](#verify-epoch)
 * [Fetch remaining packages](#fetch-reamaining-packages)
@@ -116,26 +115,11 @@
 * [Reboot](#reboot)
 
 ![Figure: Starting state diagram](images/starting-state.png)
- 
+
 **Figure 3**. The device is currently running hypothetical OS version 1 (on slot A) and begins to
 update to hypothetical OS version 2 (to slot B). *Warning*: this may not be how the disk is
 partitioned in practice.
 
-### Initial garbage collection {#initial-garbage-collection}
-
-Note: This does not garbage collect the old update package because the old
-update package is referenced in the dynamic index.
-
-The `system-updater` instructs `pkg-cache` to perform garbage collection
-which deletes all BLOBs that aren’t referenced in either the static or dynamic
-indexes. This cleans up most of the BLOBs referenced by the old system.
-
-![Figure: Initial garbage collection](images/initial-gc.png)
-
-**Figure 4**. The `system-updater` instructs `pkg-cache` to garbage collect all the blobs referenced
-by slot B. Since slot B currently references version 0, all of the version 0 blobs are garbage
-collected.
-
 ### Fetch update package {#fetch-update-package}
 
 The `system-updater` fetches the [update package], using the provided update package URL.
@@ -155,10 +139,18 @@
 /zedboot.signed
 ```
 
+If the fetch fails because there's not enough space, the `system-updater` will trigger a garbage
+collection to delete all BLOBs that aren’t referenced in either the static or dynamic indexes. After
+the garbage collection, the `system-updater` will retry the fetch. If the retry fails, the
+`system-updater` will trigger a more-aggressive garbage collection and once again retry to fetch
+the update package.
+
 ![Figure: Fetch update package](images/resolve-update-pkg.png)
 
-**Figure 5**. The `system-updater` instructs the `pkg-resolver` to resolve the version 2
-update package.
+**Figure 4**. The `system-updater` instructs the `pkg-resolver` to resolve the version 2
+update package. We assume the `system-updater` failed to fetch the update package because of
+inadequate space, triggered a garbage collection to evict the version 0 blobs referenced by slot B,
+and then retried to successfully fetch the version 2 update package.
 
 Optionally, update packages may contain an `update-mode` file. This file
 determines whether the system update happens in Normal or ForceRecovery
@@ -169,16 +161,15 @@
 marks slots A and B as unbootable, then boots to recovery. For more information,
 see the [implementation of ForceRecovery][recovery-mode-code].
 
-### Secondary garbage collection {#secondary-garbage-collection}
+### Trigger garbage collection {#garbage-collection}
 
-After the old update package is no longer referenced by the dynamic index,
-another garbage collection is triggered to delete the old update package.
+A garbage collection is triggered to delete all BLOBs exclusive to the old system.
 This step frees up additional space for any new packages.
 
-![Figure: Secondary garbage collection (again)](images/second-gc.png)
+![Figure: Garbage collection](images/gc.png)
 
-**Figure 6**. The `system-updater` instructs `pkg-cache` to garbage collect the version 1
-update package to free up space.
+**Figure 5**. The `system-updater` instructs `pkg-cache` to garbage collect all BLOBs exclusive to
+the old system. In this example, it means `pkg-cache` will evict BLOBs exclusively referenced by the version 1 update package.
 
 ### Verify board matches {#verify-board}
 
@@ -188,7 +179,7 @@
 
 ![Figure: Verify board matches](images/verify-board.png)
 
-**Figure 7**. The `system-updater` verifies the board in the update package matches the board
+**Figure 6**. The `system-updater` verifies the board in the update package matches the board
 on slot A.
 
 ### Verify epoch is supported {#verify-epoch}
@@ -200,7 +191,7 @@
 
 ![Figure: Verify epoch is supported](images/verify-epoch.png)
 
-**Figure 8**. The `system-updater` verifies the epoch in the update package is supported by
+**Figure 7**. The `system-updater` verifies the epoch in the update package is supported by
 comparing it to the epoch of the current OS.
 
 ### Fetch remaining packages {#fetch-reamaining-packages}
@@ -231,7 +222,7 @@
 
 ![Figure: Fetch remaining packages](images/resolve-packages.png)
 
-**Figure 9**. The `system-updater` instructs the pkg-resolver to resolve the version 2
+**Figure 8**. The `system-updater` instructs the pkg-resolver to resolve the version 2
 packages referenced in `packages.json`.
 
 ### Write images to block device {#write-images-block-device}
@@ -268,7 +259,7 @@
 
 ![Figure: Write images to block device](images/write-images.png)
 
-**Figure 10**. The `system-updater` writes the version 2 images to slot B via the paver.
+**Figure 9**. The `system-updater` writes the version 2 images to slot B via the paver.
 
 ### Set alternate partition as active {#set-alternate-active}
 
@@ -309,7 +300,7 @@
 
 ![Figure: Set alternate partition as active](images/modify-boot-metadata.png)
 
-**Figure 11**. The `system-updater` sets slot B to Active, so that the device boots into slot B
+**Figure 10**. The `system-updater` sets slot B to Active, so that the device boots into slot B
 on the next boot.
 
 ### Reboot {#reboot}
@@ -319,7 +310,7 @@
 
 ![Figure: Reboot](images/reboot.png)
 
-**Figure 12**. The device reboots into slot B and begins running version 2.
+**Figure 11**. The device reboots into slot B and begins running version 2.
 
 ## Verifying an update {#verifying-update}
 
diff --git a/src/sys/pkg/bin/system-updater/src/update.rs b/src/sys/pkg/bin/system-updater/src/update.rs
index afb0fc5..8aa3e50 100644
--- a/src/sys/pkg/bin/system-updater/src/update.rs
+++ b/src/sys/pkg/bin/system-updater/src/update.rs
@@ -8,7 +8,7 @@
     epoch::EpochFile,
     fidl_fuchsia_io::DirectoryProxy,
     fidl_fuchsia_paver::DataSinkProxy,
-    fidl_fuchsia_pkg::PackageCacheProxy,
+    fidl_fuchsia_pkg::{PackageCacheProxy, PackageResolverProxy},
     fidl_fuchsia_space::ManagerProxy as SpaceManagerProxy,
     fidl_fuchsia_update_installer_ext::{
         FetchFailureReason, Options, PrepareFailureReason, State, UpdateInfo,
@@ -412,19 +412,19 @@
             .await
             .map_err(PrepareError::PreparePartitionMetdata)?;
 
-        if let Err(e) = gc(&self.env.space_manager).await {
-            fx_log_err!("unable to gc packages (1/2): {:#}", anyhow!(e));
-        }
+        let update_pkg = resolve_update_package(
+            &self.env.pkg_resolver,
+            &self.config.update_url,
+            &self.env.space_manager,
+        )
+        .await
+        .map_err(PrepareError::ResolveUpdate)?;
 
-        let update_pkg =
-            resolver::resolve_update_package(&self.env.pkg_resolver, &self.config.update_url)
-                .await
-                .map_err(PrepareError::ResolveUpdate)?;
         *target_version = history::Version::for_update_package(&update_pkg).await;
         let () = update_pkg.verify_name().await.map_err(PrepareError::VerifyName)?;
 
         if let Err(e) = gc(&self.env.space_manager).await {
-            fx_log_err!("unable to gc packages (2/2): {:#}", anyhow!(e));
+            fx_log_err!("unable to gc packages: {:#}", anyhow!(e));
         }
 
         let mode = update_mode(&update_pkg).await.map_err(PrepareError::ParseUpdateMode)?;
@@ -588,6 +588,38 @@
     Ok(())
 }
 
+/// Resolve the update package, incorporating an increasingly aggressive GC and retry strategy.
+async fn resolve_update_package(
+    pkg_resolver: &PackageResolverProxy,
+    update_url: &PkgUrl,
+    space_manager: &SpaceManagerProxy,
+) -> Result<UpdatePackage, ResolveError> {
+    // First, attempt to resolve the update package.
+    match resolver::resolve_update_package(pkg_resolver, update_url).await {
+        Ok(update_pkg) => return Ok(update_pkg),
+        Err(ResolveError::Error(fidl_fuchsia_pkg_ext::ResolveError::NoSpace, _)) => (),
+        Err(e) => return Err(e),
+    }
+
+    // If the first attempt fails with NoSpace, perform a GC and retry.
+    if let Err(e) = gc(space_manager).await {
+        fx_log_err!("unable to gc packages before first resolve retry: {:#}", anyhow!(e));
+    }
+    match resolver::resolve_update_package(pkg_resolver, update_url).await {
+        Ok(update_pkg) => return Ok(update_pkg),
+        Err(ResolveError::Error(fidl_fuchsia_pkg_ext::ResolveError::NoSpace, _)) => (),
+        Err(e) => return Err(e),
+    }
+
+    // If the second attempt fails with NoSpace, perform a GC and retry. If the third attempt fails,
+    // return the error regardless of type.
+    // TODO(fxbug.dev/77367) try harder by releasing retained packages.
+    if let Err(e) = gc(space_manager).await {
+        fx_log_err!("unable to gc packages before second resolve retry: {:#}", anyhow!(e));
+    }
+    resolver::resolve_update_package(pkg_resolver, update_url).await
+}
+
 async fn verify_board<B>(build_info: &B, pkg: &UpdatePackage) -> Result<(), Error>
 where
     B: BuildInfo,
diff --git a/src/sys/pkg/tests/system-updater/src/cobalt_metrics.rs b/src/sys/pkg/tests/system-updater/src/cobalt_metrics.rs
index cd193c8..6a9b98a 100644
--- a/src/sys/pkg/tests/system-updater/src/cobalt_metrics.rs
+++ b/src/sys/pkg/tests/system-updater/src/cobalt_metrics.rs
@@ -104,7 +104,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             BlobfsSync,
diff --git a/src/sys/pkg/tests/system-updater/src/fetch_packages.rs b/src/sys/pkg/tests/system-updater/src/fetch_packages.rs
index 01037da..f08e444 100644
--- a/src/sys/pkg/tests/system-updater/src/fetch_packages.rs
+++ b/src/sys/pkg/tests/system-updater/src/fetch_packages.rs
@@ -39,7 +39,6 @@
             Paver(PaverEvent::BootManagerFlush),
             // The connect succeeds, so the system updater only notices the resolver is not there when
             // it tries to resolve a package
-            Gc
         ]
     );
 }
@@ -89,7 +88,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             PackageResolve(system_image_url.to_string()),
@@ -194,7 +192,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             PackageResolve(SYSTEM_IMAGE_URL.to_string()),
@@ -242,7 +239,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             PackageResolve(SYSTEM_IMAGE_URL.to_string()),
diff --git a/src/sys/pkg/tests/system-updater/src/history.rs b/src/sys/pkg/tests/system-updater/src/history.rs
index da77ef7..9a7a1ec 100644
--- a/src/sys/pkg/tests/system-updater/src/history.rs
+++ b/src/sys/pkg/tests/system-updater/src/history.rs
@@ -57,7 +57,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             BlobfsSync,
diff --git a/src/sys/pkg/tests/system-updater/src/mode_force_recovery.rs b/src/sys/pkg/tests/system-updater/src/mode_force_recovery.rs
index 047cd63..c0835a4 100644
--- a/src/sys/pkg/tests/system-updater/src/mode_force_recovery.rs
+++ b/src/sys/pkg/tests/system-updater/src/mode_force_recovery.rs
@@ -61,7 +61,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             Paver(PaverEvent::WriteAsset {
@@ -165,7 +164,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc
         ]
diff --git a/src/sys/pkg/tests/system-updater/src/mode_normal.rs b/src/sys/pkg/tests/system-updater/src/mode_normal.rs
index adf6560..a354c37 100644
--- a/src/sys/pkg/tests/system-updater/src/mode_normal.rs
+++ b/src/sys/pkg/tests/system-updater/src/mode_normal.rs
@@ -54,7 +54,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             PackageResolve(SYSTEM_IMAGE_URL.to_string()),
@@ -106,7 +105,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             PackageResolve(SYSTEM_IMAGE_URL.to_string()),
@@ -176,7 +174,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             PackageResolve(SYSTEM_IMAGE_URL.to_string()),
diff --git a/src/sys/pkg/tests/system-updater/src/update_package.rs b/src/sys/pkg/tests/system-updater/src/update_package.rs
index 993daf8..316f188 100644
--- a/src/sys/pkg/tests/system-updater/src/update_package.rs
+++ b/src/sys/pkg/tests/system-updater/src/update_package.rs
@@ -2,7 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use {super::*, pretty_assertions::assert_eq};
+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() {
@@ -43,7 +48,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(not_update_package_url.to_string())
         ]
     );
@@ -64,8 +68,7 @@
 async fn fails_if_package_unavailable() {
     let env = TestEnv::builder().build().await;
 
-    env.resolver
-        .mock_resolve_failure(UPDATE_PKG_URL, fidl_fuchsia_pkg::ResolveError::PackageNotFound);
+    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");
@@ -88,7 +91,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
         ]
     );
@@ -126,7 +128,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve("fuchsia-pkg://fuchsia.com/another-update/4".to_string()),
             Gc,
             BlobfsSync,
@@ -142,3 +143,168 @@
         ]
     );
 }
+
+#[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()),
+        ]
+    );
+}
diff --git a/src/sys/pkg/tests/system-updater/src/writes_firmware.rs b/src/sys/pkg/tests/system-updater/src/writes_firmware.rs
index 08aaef0..7534a24 100644
--- a/src/sys/pkg/tests/system-updater/src/writes_firmware.rs
+++ b/src/sys/pkg/tests/system-updater/src/writes_firmware.rs
@@ -35,7 +35,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             BlobfsSync,
@@ -88,7 +87,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             BlobfsSync,
@@ -144,7 +142,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             BlobfsSync,
@@ -210,7 +207,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             BlobfsSync,
diff --git a/src/sys/pkg/tests/system-updater/src/writes_images.rs b/src/sys/pkg/tests/system-updater/src/writes_images.rs
index 391f556..d2772ee 100644
--- a/src/sys/pkg/tests/system-updater/src/writes_images.rs
+++ b/src/sys/pkg/tests/system-updater/src/writes_images.rs
@@ -80,7 +80,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             BlobfsSync,
@@ -130,7 +129,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             BlobfsSync,
@@ -176,7 +174,6 @@
     assert_eq!(
         env.take_interactions(),
         vec![
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             BlobfsSync,
@@ -353,7 +350,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             BlobfsSync,
@@ -407,7 +403,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             BlobfsSync,
@@ -461,7 +456,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             BlobfsSync,
@@ -519,7 +513,6 @@
                 configuration: paver::Configuration::B
             }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             BlobfsSync,
@@ -594,7 +587,6 @@
             Paver(PaverEvent::QueryConfigurationStatus { configuration: current_config }),
             Paver(PaverEvent::SetConfigurationUnbootable { configuration: target_config }),
             Paver(PaverEvent::BootManagerFlush),
-            Gc,
             PackageResolve(UPDATE_PKG_URL.to_string()),
             Gc,
             BlobfsSync,