Skip to main content

snix_build/oci/
bundle.rs

1//! Module to create an OCI runtime bundle for a given [BuildRequest].
2use std::{
3    ffi::OsStr,
4    fs,
5    io::Write,
6    path::{Component, Path, PathBuf},
7};
8
9use super::scratch_name;
10use crate::buildservice::BuildRequest;
11use anyhow::Context;
12use tracing::{debug, instrument};
13
14/// Produce an OCI bundle in a given path.
15/// Check [super::spec::make_spec] for a description about the paths produced.
16#[instrument(err)]
17pub(crate) fn make_bundle(
18    request: &BuildRequest,
19    runtime_spec: &oci_spec::runtime::Spec,
20    path: &Path,
21) -> anyhow::Result<()> {
22    fs::create_dir_all(path).context("failed to create bundle path")?;
23
24    let spec_json = serde_json::to_string(runtime_spec).context("failed to render spec to json")?;
25    fs::write(path.join("config.json"), spec_json).context("failed to write config.json")?;
26
27    fs::create_dir_all(path.join("inputs")).context("failed to create inputs dir")?;
28
29    let root_path = path.join("root");
30
31    fs::create_dir_all(&root_path).context("failed to create root path dir")?;
32    fs::create_dir_all(root_path.join("etc")).context("failed to create root/etc dir")?;
33
34    // TODO: populate /etc/{group,passwd}. It's a mess?
35
36    let scratch_root = path.join("scratch");
37    fs::create_dir_all(&scratch_root).context("failed to create scratch/ dir")?;
38
39    // for each scratch path, calculate its name inside scratch, and ensure the
40    // directory exists.
41    for p in request.scratch_paths.iter() {
42        let scratch_path = scratch_root.join(scratch_name(p));
43        debug!(scratch_path=?scratch_path, path=?p, "about to create scratch dir");
44        fs::create_dir_all(scratch_path.clone()).context("Unable to create scratch dir")?;
45
46        // TODO(#152): this is a hack, in the general case we may not have the "build" directory and additional files
47        // may not have /build prefix. But in practice today snix_build.rs is the only user of the builder and
48        // it always sets up a /build scratch and populates all additional_files with the /build prefix.
49        // For now this unblocks builds, but worth improving in the future.
50        if p == Path::new("build") {
51            for file in request.additional_files.iter() {
52                if file.path.components().count() < 2
53                    || file.path.components().next() != Some(Component::Normal(OsStr::new("build")))
54                {
55                    Err(std::io::Error::other(
56                        "Additional files must start with build/",
57                    ))?
58                }
59
60                // remove build/ prefix
61                let p = file.path.components().skip(1).collect::<PathBuf>();
62                if let Some(parent) = p.parent() {
63                    fs::create_dir_all(scratch_path.clone().join(parent))
64                        .context("Failed to create dir for additional file")?;
65                }
66                let p = scratch_path.join(p);
67                let mut out = std::fs::File::create(p).context("could not create file")?;
68                out.write_all(&file.contents)?;
69            }
70        }
71    }
72
73    Ok(())
74}