bootc_lib/install/
aleph.rs

1use anyhow::{Context as _, Result};
2use canon_json::CanonJsonSerialize as _;
3use cap_std_ext::{cap_std::fs::Dir, dirext::CapStdExtDirExt as _};
4use fn_error_context::context;
5use ostree_ext::{container as ostree_container, oci_spec};
6use serde::Serialize;
7
8use super::SELinuxFinalState;
9
10/// Path to initially deployed version information
11pub(crate) const BOOTC_ALEPH_PATH: &str = ".bootc-aleph.json";
12
13/// The "aleph" version information is injected into /root/.bootc-aleph.json
14/// and contains the image ID that was initially used to install.  This can
15/// be used to trace things like the specific version of `mkfs.ext4` or
16/// kernel version that was used.
17#[derive(Debug, Serialize)]
18pub(crate) struct InstallAleph {
19    /// Digested pull spec for installed image
20    pub(crate) image: String,
21    /// The version number
22    pub(crate) version: Option<String>,
23    /// The timestamp
24    pub(crate) timestamp: Option<chrono::DateTime<chrono::Utc>>,
25    /// The `uname -r` of the kernel doing the installation
26    pub(crate) kernel: String,
27    /// The state of SELinux at install time
28    pub(crate) selinux: String,
29}
30
31impl InstallAleph {
32    #[context("Creating aleph data")]
33    pub(crate) fn new(
34        src_imageref: &ostree_container::OstreeImageReference,
35        imgstate: &ostree_container::store::LayeredImageState,
36        selinux_state: &SELinuxFinalState,
37    ) -> Result<Self> {
38        let uname = rustix::system::uname();
39        let labels = crate::status::labels_of_config(&imgstate.configuration);
40        let timestamp = labels
41            .and_then(|l| {
42                l.get(oci_spec::image::ANNOTATION_CREATED)
43                    .map(|s| s.as_str())
44            })
45            .and_then(bootc_utils::try_deserialize_timestamp);
46        let r = InstallAleph {
47            image: src_imageref.imgref.name.clone(),
48            version: imgstate.version().as_ref().map(|s| s.to_string()),
49            timestamp,
50            kernel: uname.release().to_str()?.to_string(),
51            selinux: selinux_state.to_aleph().to_string(),
52        };
53        Ok(r)
54    }
55
56    /// Serialize to a file in the target root.
57    pub(crate) fn write_to(&self, root: &Dir) -> Result<()> {
58        root.atomic_replace_with(BOOTC_ALEPH_PATH, |f| {
59            anyhow::Ok(self.to_canon_json_writer(f)?)
60        })
61        .context("Writing aleph version")
62    }
63}