Skip to main content

flowey_lib_hvlite/
resolve_openvmm_test_initrd.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Download the shared test initrd from the `openvmm-deps` GitHub release,
5//! or use a local path if specified.
6//!
7//! The initrd is identical across all kernel versions and ships as its own
8//! versioned artifact (`openvmm-test-initrd.<arch>.<ver>.tar.gz`), separate
9//! from the per-kernel `openvmm-test-linux-<kver>` artifact.
10
11use crate::common::CommonArch;
12use flowey::node::prelude::*;
13use std::collections::BTreeMap;
14use std::collections::BTreeSet;
15
16flowey_config! {
17    /// Config for the resolve_openvmm_test_initrd node.
18    pub struct Config {
19        /// Specify version of the github release to pull from
20        pub version: Option<String>,
21        /// Use locally downloaded openvmm-test-initrd contents, keyed by
22        /// architecture
23        pub local_paths: BTreeMap<CommonArch, ConfigVar<PathBuf>>,
24    }
25}
26
27flowey_request! {
28    pub enum Request {
29        /// Get the path to the initrd image for a given architecture
30        Get(CommonArch, WriteVar<PathBuf>),
31    }
32}
33
34new_flow_node_with_config!(struct Node);
35
36impl FlowNodeWithConfig for Node {
37    type Request = Request;
38    type Config = Config;
39
40    fn imports(ctx: &mut ImportCtx<'_>) {
41        ctx.import::<flowey_lib_common::install_dist_pkg::Node>();
42        ctx.import::<flowey_lib_common::download_gh_release::Node>();
43    }
44
45    fn emit(
46        config: Config,
47        requests: Vec<Self::Request>,
48        ctx: &mut NodeCtx<'_>,
49    ) -> anyhow::Result<()> {
50        let Config {
51            version,
52            local_paths,
53        } = config;
54        let mut deps: BTreeMap<CommonArch, Vec<WriteVar<PathBuf>>> = BTreeMap::new();
55
56        for req in requests {
57            match req {
58                Request::Get(arch, var) => {
59                    deps.entry(arch).or_default().push(var);
60                }
61            }
62        }
63
64        if version.is_some() && !local_paths.is_empty() {
65            anyhow::bail!("Cannot specify both Version and LocalPath requests");
66        }
67
68        if version.is_none() && local_paths.is_empty() {
69            anyhow::bail!("Must specify a Version or LocalPath request");
70        }
71
72        // -- end of req processing -- //
73
74        if deps.is_empty() {
75            return Ok(());
76        }
77
78        if !local_paths.is_empty() {
79            ctx.emit_rust_step("use local openvmm-test-initrd", |ctx| {
80                let deps = deps.claim(ctx);
81                let local_paths: BTreeMap<_, _> = local_paths
82                    .into_iter()
83                    .map(|(key, var)| (key, var.claim(ctx)))
84                    .collect();
85                move |rt| {
86                    let resolved_paths: BTreeMap<CommonArch, PathBuf> = local_paths
87                        .into_iter()
88                        .map(|(key, var)| (key, rt.read(var)))
89                        .collect();
90
91                    for (arch, vars) in deps {
92                        let base_dir = resolved_paths.get(&arch).ok_or_else(|| {
93                            anyhow::anyhow!("No local path specified for {:?}", arch)
94                        })?;
95                        let path = base_dir.join("initrd");
96                        rt.write_all(vars, &path)
97                    }
98
99                    Ok(())
100                }
101            });
102
103            return Ok(());
104        }
105
106        // The openvmm-test-initrd.<arch>.<ver>.tar.gz archive contains a
107        // single `initrd` file at the archive root. Download one archive per
108        // requested architecture.
109        let needed_archives: BTreeSet<CommonArch> = deps.keys().copied().collect();
110
111        let mut archives = BTreeMap::new();
112        for arch in needed_archives {
113            let version = version.clone().expect("local requests handled above");
114            let arch_str = match arch {
115                CommonArch::X86_64 => "x86_64",
116                CommonArch::Aarch64 => "aarch64",
117            };
118            let archive = ctx.reqv(|v| flowey_lib_common::download_gh_release::Request {
119                repo_owner: "microsoft".into(),
120                repo_name: "openvmm-deps".into(),
121                needs_auth: false,
122                tag: version.clone(),
123                file_name: format!("openvmm-test-initrd.{arch_str}.{version}.tar.gz"),
124                path: v,
125            });
126            archives.insert(arch, archive);
127        }
128
129        let persistent_dir = ctx.persistent_dir();
130
131        ctx.emit_rust_step("unpack openvmm-test-initrd archives", |ctx| {
132            let persistent_dir = persistent_dir.claim(ctx);
133            let archives = archives.claim(ctx);
134            let deps = deps.claim(ctx);
135            let version = version.clone().expect("local requests handled above");
136            move |rt| {
137                let persistent_dir = persistent_dir.map(|d| rt.read(d));
138
139                let mut extract_dirs = BTreeMap::new();
140                for (arch, archive) in archives {
141                    let file = rt.read(archive);
142                    let dir = flowey_lib_common::_util::extract::extract_tar_gz_if_new(
143                        rt,
144                        persistent_dir.as_deref(),
145                        &file,
146                        &version,
147                    )?;
148                    extract_dirs.insert(arch, dir);
149                }
150
151                for (arch, vars) in deps {
152                    let path = extract_dirs[&arch].join("initrd");
153                    rt.write_all(vars, &path)
154                }
155
156                Ok(())
157            }
158        });
159
160        Ok(())
161    }
162}