bootc_lib/bootc_composefs/
repo.rs1use fn_error_context::context;
2use std::sync::Arc;
3
4use anyhow::{Context, Result};
5
6use ostree_ext::composefs::fsverity::{FsVerityHashValue, Sha512HashValue};
7use ostree_ext::composefs_boot::{BootOps, bootloader::BootEntry as ComposefsBootEntry};
8use ostree_ext::composefs_oci::{
9 image::create_filesystem as create_composefs_filesystem, pull as composefs_oci_pull,
10};
11
12use ostree_ext::container::ImageReference as OstreeExtImgRef;
13
14use cap_std_ext::cap_std::{ambient_authority, fs::Dir};
15
16use crate::install::{RootSetup, State};
17
18pub(crate) fn open_composefs_repo(rootfs_dir: &Dir) -> Result<crate::store::ComposefsRepository> {
19 crate::store::ComposefsRepository::open_path(rootfs_dir, "composefs")
20 .context("Failed to open composefs repository")
21}
22
23pub(crate) async fn initialize_composefs_repository(
24 state: &State,
25 root_setup: &RootSetup,
26) -> Result<(String, impl FsVerityHashValue)> {
27 let rootfs_dir = &root_setup.physical_root;
28
29 rootfs_dir
30 .create_dir_all("composefs")
31 .context("Creating dir composefs")?;
32
33 let repo = open_composefs_repo(rootfs_dir)?;
34
35 let OstreeExtImgRef {
36 name: image_name,
37 transport,
38 } = &state.source.imageref;
39
40 composefs_oci_pull(
42 &Arc::new(repo),
43 &format!("{transport}{image_name}"),
44 None,
45 None,
46 )
47 .await
48}
49
50pub(crate) fn get_imgref(transport: &str, image: &str) -> String {
58 let img = image.strip_prefix(":").unwrap_or(&image);
59 let transport = transport.strip_suffix(":").unwrap_or(&transport);
60
61 if transport == "registry" || transport == "docker://" {
62 format!("docker://{img}")
63 } else if transport == "docker-daemon" {
64 format!("docker-daemon:{img}")
65 } else {
66 format!("{transport}:{img}")
67 }
68}
69
70#[context("Pulling composefs repository")]
73pub(crate) async fn pull_composefs_repo(
74 transport: &String,
75 image: &String,
76) -> Result<(
77 crate::store::ComposefsRepository,
78 Vec<ComposefsBootEntry<Sha512HashValue>>,
79 Sha512HashValue,
80 crate::store::ComposefsFilesystem,
81)> {
82 let rootfs_dir = Dir::open_ambient_dir("/sysroot", ambient_authority())?;
83
84 let repo = open_composefs_repo(&rootfs_dir).context("Opening composefs repo")?;
85
86 let final_imgref = get_imgref(transport, image);
87
88 tracing::debug!("Image to pull {final_imgref}");
89
90 let (id, verity) = composefs_oci_pull(&Arc::new(repo), &final_imgref, None, None)
91 .await
92 .context("Pulling composefs repo")?;
93
94 tracing::info!("ID: {id}, Verity: {}", verity.to_hex());
95
96 let repo = open_composefs_repo(&rootfs_dir)?;
97 let mut fs: crate::store::ComposefsFilesystem =
98 create_composefs_filesystem(&repo, &id, None)
99 .context("Failed to create composefs filesystem")?;
100
101 let entries = fs.transform_for_boot(&repo)?;
102 let id = fs.commit_image(&repo, None)?;
103
104 Ok((repo, entries, id, fs))
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 const IMAGE_NAME: &str = "quay.io/example/image:latest";
112
113 #[test]
114 fn test_get_imgref_registry_transport() {
115 assert_eq!(
116 get_imgref("registry:", IMAGE_NAME),
117 format!("docker://{IMAGE_NAME}")
118 );
119 }
120
121 #[test]
122 fn test_get_imgref_containers_storage() {
123 assert_eq!(
124 get_imgref("containers-storage", IMAGE_NAME),
125 format!("containers-storage:{IMAGE_NAME}")
126 );
127
128 assert_eq!(
129 get_imgref("containers-storage:", IMAGE_NAME),
130 format!("containers-storage:{IMAGE_NAME}")
131 );
132 }
133
134 #[test]
135 fn test_get_imgref_edge_cases() {
136 assert_eq!(
137 get_imgref("registry", IMAGE_NAME),
138 format!("docker://{IMAGE_NAME}")
139 );
140 }
141
142 #[test]
143 fn test_get_imgref_docker_daemon_transport() {
144 assert_eq!(
145 get_imgref("docker-daemon", IMAGE_NAME),
146 format!("docker-daemon:{IMAGE_NAME}")
147 );
148 }
149}