1use crate::common::CommonArch;
18use flowey::node::prelude::*;
19use std::collections::BTreeMap;
20use std::collections::BTreeSet;
21
22#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
29pub enum LinuxTestKernelVersion {
30 Linux6_1,
31}
32
33impl LinuxTestKernelVersion {
34 pub fn artifact_tag(self) -> &'static str {
37 match self {
38 Self::Linux6_1 => "6.1",
39 }
40 }
41}
42
43#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
45pub enum OpenvmmTestKernelFile {
46 Kernel,
48 BzImage,
50}
51
52impl OpenvmmTestKernelFile {
53 pub fn is_available_for(self, arch: CommonArch) -> bool {
55 match self {
56 Self::Kernel => true,
57 Self::BzImage => matches!(arch, CommonArch::X86_64),
58 }
59 }
60
61 pub fn filename(self, arch: CommonArch) -> &'static str {
63 match self {
64 Self::Kernel => match arch {
65 CommonArch::X86_64 => "vmlinux",
66 CommonArch::Aarch64 => "Image",
67 },
68 Self::BzImage => "bzImage",
69 }
70 }
71}
72
73pub const DEFAULT_LINUX_TEST_KERNEL_VERSION: LinuxTestKernelVersion =
76 LinuxTestKernelVersion::Linux6_1;
77
78flowey_config! {
79 pub struct Config {
81 pub version: Option<String>,
83 pub local_paths: BTreeMap<(CommonArch, LinuxTestKernelVersion), ConfigVar<PathBuf>>,
86 }
87}
88
89flowey_request! {
90 pub enum Request {
91 Get(
93 OpenvmmTestKernelFile,
94 CommonArch,
95 LinuxTestKernelVersion,
96 WriteVar<PathBuf>,
97 ),
98 }
99}
100
101new_flow_node_with_config!(struct Node);
102
103impl FlowNodeWithConfig for Node {
104 type Request = Request;
105 type Config = Config;
106
107 fn imports(ctx: &mut ImportCtx<'_>) {
108 ctx.import::<flowey_lib_common::install_dist_pkg::Node>();
109 ctx.import::<flowey_lib_common::download_gh_release::Node>();
110 }
111
112 fn emit(
113 config: Config,
114 requests: Vec<Self::Request>,
115 ctx: &mut NodeCtx<'_>,
116 ) -> anyhow::Result<()> {
117 let Config {
118 version,
119 local_paths,
120 } = config;
121 let mut deps: BTreeMap<
122 (OpenvmmTestKernelFile, CommonArch, LinuxTestKernelVersion),
123 Vec<WriteVar<PathBuf>>,
124 > = BTreeMap::new();
125
126 for req in requests {
127 match req {
128 Request::Get(file, arch, kver, var) => {
129 if !file.is_available_for(arch) {
130 anyhow::bail!(
131 "{file:?} is not available in the openvmm-test-linux archive for {arch:?}"
132 );
133 }
134 deps.entry((file, arch, kver)).or_default().push(var);
135 }
136 }
137 }
138
139 if version.is_some() && !local_paths.is_empty() {
140 anyhow::bail!("Cannot specify both Version and LocalPath requests");
141 }
142
143 if version.is_none() && local_paths.is_empty() {
144 anyhow::bail!("Must specify a Version or LocalPath request");
145 }
146
147 if deps.is_empty() {
150 return Ok(());
151 }
152
153 if !local_paths.is_empty() {
154 ctx.emit_rust_step("use local openvmm-test-linux", |ctx| {
155 let deps = deps.claim(ctx);
156 let local_paths: BTreeMap<_, _> = local_paths
157 .into_iter()
158 .map(|(key, var)| (key, var.claim(ctx)))
159 .collect();
160 move |rt| {
161 let resolved_paths: BTreeMap<(CommonArch, LinuxTestKernelVersion), PathBuf> =
162 local_paths
163 .into_iter()
164 .map(|(key, var)| (key, rt.read(var)))
165 .collect();
166
167 for ((file, arch, kver), vars) in deps {
168 let base_dir = resolved_paths.get(&(arch, kver)).ok_or_else(|| {
169 anyhow::anyhow!("No local path specified for ({:?}, {:?})", arch, kver)
170 })?;
171 let path = base_dir.join(file.filename(arch));
172 rt.write_all(vars, &path)
173 }
174
175 Ok(())
176 }
177 });
178
179 return Ok(());
180 }
181
182 let needed_archives: BTreeSet<(CommonArch, LinuxTestKernelVersion)> =
186 deps.keys().map(|(_, arch, kver)| (*arch, *kver)).collect();
187
188 let mut archives = BTreeMap::new();
189 for (arch, kver) in needed_archives {
190 let version = version.clone().expect("local requests handled above");
191 let arch_str = match arch {
192 CommonArch::X86_64 => "x86_64",
193 CommonArch::Aarch64 => "aarch64",
194 };
195 let kver_str = kver.artifact_tag();
196 let archive = ctx.reqv(|v| flowey_lib_common::download_gh_release::Request {
197 repo_owner: "microsoft".into(),
198 repo_name: "openvmm-deps".into(),
199 needs_auth: false,
200 tag: version.clone(),
201 file_name: format!("openvmm-test-linux-{kver_str}.{arch_str}.{version}.tar.gz"),
202 path: v,
203 });
204 archives.insert((arch, kver), archive);
205 }
206
207 let persistent_dir = ctx.persistent_dir();
208
209 ctx.emit_rust_step("unpack openvmm-test-linux archives", |ctx| {
210 let persistent_dir = persistent_dir.claim(ctx);
211 let archives = archives.claim(ctx);
212 let deps = deps.claim(ctx);
213 let version = version.clone().expect("local requests handled above");
214 move |rt| {
215 let persistent_dir = persistent_dir.map(|d| rt.read(d));
216
217 let mut extract_dirs = BTreeMap::new();
218 for (key, archive) in archives {
219 let file = rt.read(archive);
220 let dir = flowey_lib_common::_util::extract::extract_tar_gz_if_new(
221 rt,
222 persistent_dir.as_deref(),
223 &file,
224 &version,
225 )?;
226 extract_dirs.insert(key, dir);
227 }
228
229 for ((file, arch, kver), vars) in deps {
230 let path = extract_dirs[&(arch, kver)].join(file.filename(arch));
231 rt.write_all(vars, &path)
232 }
233
234 Ok(())
235 }
236 });
237
238 Ok(())
239 }
240}