|
2 | 2 | //! |
3 | 3 | //! Create a merged filesystem tree with the image and mounted configmaps. |
4 | 4 |
|
| 5 | +use std::collections::HashSet; |
5 | 6 | use std::io::{BufRead, Write}; |
6 | 7 |
|
7 | 8 | use anyhow::Ok; |
@@ -268,53 +269,76 @@ pub(crate) async fn pull( |
268 | 269 | Ok(Box::new((*import).into())) |
269 | 270 | } |
270 | 271 |
|
| 272 | +/// Gather all bound images in all deployments, then prune the image store, |
| 273 | +/// using the gathered images as the roots (that will not be GC'd). |
| 274 | +pub(crate) async fn prune_container_store(sysroot: &Storage) -> Result<()> { |
| 275 | + let deployments = sysroot.deployments(); |
| 276 | + let mut all_bound_images = Vec::new(); |
| 277 | + for deployment in deployments { |
| 278 | + let bound = crate::boundimage::query_bound_images_for_deployment(sysroot, &deployment)?; |
| 279 | + all_bound_images.extend(bound.into_iter()); |
| 280 | + } |
| 281 | + // Convert to a hashset of just the image names |
| 282 | + let image_names = HashSet::from_iter(all_bound_images.iter().map(|img| img.image.as_str())); |
| 283 | + let pruned = sysroot.imgstore.prune_except_roots(&image_names).await?; |
| 284 | + tracing::debug!("Pruned images: {}", pruned.len()); |
| 285 | + Ok(()) |
| 286 | +} |
| 287 | + |
271 | 288 | pub(crate) async fn cleanup(sysroot: &Storage) -> Result<()> { |
| 289 | + let bound_prune = prune_container_store(sysroot); |
| 290 | + |
272 | 291 | // We create clones (just atomic reference bumps) here to move to the thread. |
273 | 292 | let repo = sysroot.repo(); |
274 | 293 | let sysroot = sysroot.sysroot.clone(); |
275 | | - ostree_ext::tokio_util::spawn_blocking_cancellable_flatten(move |cancellable| { |
276 | | - let locked_sysroot = &SysrootLock::from_assumed_locked(&sysroot); |
277 | | - let cancellable = Some(cancellable); |
278 | | - let repo = &repo; |
279 | | - let txn = repo.auto_transaction(cancellable)?; |
280 | | - let repo = txn.repo(); |
281 | | - |
282 | | - // Regenerate our base references. First, we delete the ones that exist |
283 | | - for ref_entry in repo |
284 | | - .list_refs_ext( |
285 | | - Some(BASE_IMAGE_PREFIX), |
286 | | - ostree::RepoListRefsExtFlags::NONE, |
287 | | - cancellable, |
288 | | - ) |
289 | | - .context("Listing refs")? |
290 | | - .keys() |
291 | | - { |
292 | | - repo.transaction_set_refspec(ref_entry, None); |
293 | | - } |
| 294 | + let repo_prune = |
| 295 | + ostree_ext::tokio_util::spawn_blocking_cancellable_flatten(move |cancellable| { |
| 296 | + let locked_sysroot = &SysrootLock::from_assumed_locked(&sysroot); |
| 297 | + let cancellable = Some(cancellable); |
| 298 | + let repo = &repo; |
| 299 | + let txn = repo.auto_transaction(cancellable)?; |
| 300 | + let repo = txn.repo(); |
| 301 | + |
| 302 | + // Regenerate our base references. First, we delete the ones that exist |
| 303 | + for ref_entry in repo |
| 304 | + .list_refs_ext( |
| 305 | + Some(BASE_IMAGE_PREFIX), |
| 306 | + ostree::RepoListRefsExtFlags::NONE, |
| 307 | + cancellable, |
| 308 | + ) |
| 309 | + .context("Listing refs")? |
| 310 | + .keys() |
| 311 | + { |
| 312 | + repo.transaction_set_refspec(ref_entry, None); |
| 313 | + } |
| 314 | + |
| 315 | + // Then, for each deployment which is derived (e.g. has configmaps) we synthesize |
| 316 | + // a base ref to ensure that it's not GC'd. |
| 317 | + for (i, deployment) in sysroot.deployments().into_iter().enumerate() { |
| 318 | + let commit = deployment.csum(); |
| 319 | + if let Some(base) = get_base_commit(repo, &commit)? { |
| 320 | + repo.transaction_set_refspec(&format!("{BASE_IMAGE_PREFIX}/{i}"), Some(&base)); |
| 321 | + } |
| 322 | + } |
294 | 323 |
|
295 | | - // Then, for each deployment which is derived (e.g. has configmaps) we synthesize |
296 | | - // a base ref to ensure that it's not GC'd. |
297 | | - for (i, deployment) in sysroot.deployments().into_iter().enumerate() { |
298 | | - let commit = deployment.csum(); |
299 | | - if let Some(base) = get_base_commit(repo, &commit)? { |
300 | | - repo.transaction_set_refspec(&format!("{BASE_IMAGE_PREFIX}/{i}"), Some(&base)); |
| 324 | + let pruned = |
| 325 | + ostree_container::deploy::prune(locked_sysroot).context("Pruning images")?; |
| 326 | + if !pruned.is_empty() { |
| 327 | + let size = glib::format_size(pruned.objsize); |
| 328 | + println!( |
| 329 | + "Pruned images: {} (layers: {}, objsize: {})", |
| 330 | + pruned.n_images, pruned.n_layers, size |
| 331 | + ); |
| 332 | + } else { |
| 333 | + tracing::debug!("Nothing to prune"); |
301 | 334 | } |
302 | | - } |
303 | 335 |
|
304 | | - let pruned = ostree_container::deploy::prune(locked_sysroot).context("Pruning images")?; |
305 | | - if !pruned.is_empty() { |
306 | | - let size = glib::format_size(pruned.objsize); |
307 | | - println!( |
308 | | - "Pruned images: {} (layers: {}, objsize: {})", |
309 | | - pruned.n_images, pruned.n_layers, size |
310 | | - ); |
311 | | - } else { |
312 | | - tracing::debug!("Nothing to prune"); |
313 | | - } |
| 336 | + Ok(()) |
| 337 | + }); |
314 | 338 |
|
315 | | - Ok(()) |
316 | | - }) |
317 | | - .await |
| 339 | + // We run these in parallel mostly because we can. |
| 340 | + tokio::try_join!(repo_prune, bound_prune)?; |
| 341 | + Ok(()) |
318 | 342 | } |
319 | 343 |
|
320 | 344 | /// If commit is a bootc-derived commit (e.g. has configmaps), return its base. |
@@ -399,7 +423,7 @@ pub(crate) async fn stage( |
399 | 423 | ) |
400 | 424 | .await?; |
401 | 425 |
|
402 | | - crate::boundimage::pull_bound_images(sysroot, &deployment)?; |
| 426 | + crate::boundimage::pull_bound_images(sysroot, &deployment).await?; |
403 | 427 |
|
404 | 428 | crate::deploy::cleanup(sysroot).await?; |
405 | 429 | println!("Queued for next boot: {:#}", spec.image); |
|
0 commit comments