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