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)]
25pub enum LinuxTestKernelVersion {
26 Linux6_1,
27 Linux6_18,
28 KvmCcaDev,
36}
37
38impl LinuxTestKernelVersion {
39 pub fn artifact_tag(self) -> &'static str {
42 match self {
43 Self::Linux6_1 => "6.1",
44 Self::Linux6_18 => "6.18",
45 Self::KvmCcaDev => "kvm-cca-dev",
46 }
47 }
48
49 pub fn is_available_for(self, arch: CommonArch) -> bool {
53 match self {
54 Self::Linux6_1 | Self::Linux6_18 => true,
55 Self::KvmCcaDev => matches!(arch, CommonArch::Aarch64),
56 }
57 }
58}
59
60#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
62pub enum OpenvmmTestKernelFile {
63 Kernel,
65 BzImage,
67}
68
69impl OpenvmmTestKernelFile {
70 pub fn is_available_for(self, arch: CommonArch) -> bool {
72 match self {
73 Self::Kernel => true,
74 Self::BzImage => matches!(arch, CommonArch::X86_64),
75 }
76 }
77
78 pub fn filename(self, arch: CommonArch) -> &'static str {
80 match self {
81 Self::Kernel => match arch {
82 CommonArch::X86_64 => "vmlinux",
83 CommonArch::Aarch64 => "Image",
84 },
85 Self::BzImage => "bzImage",
86 }
87 }
88}
89
90pub const DEFAULT_LINUX_TEST_KERNEL_VERSION: LinuxTestKernelVersion =
93 LinuxTestKernelVersion::Linux6_18;
94
95pub const INCUBATOR_LINUX_TEST_KERNEL_VERSION: LinuxTestKernelVersion =
102 LinuxTestKernelVersion::KvmCcaDev;
103
104flowey_config! {
105 pub struct Config {
107 pub version: Option<String>,
109 pub local_paths: BTreeMap<(CommonArch, LinuxTestKernelVersion), ConfigVar<PathBuf>>,
112 }
113}
114
115flowey_request! {
116 pub enum Request {
117 Get(
119 OpenvmmTestKernelFile,
120 CommonArch,
121 LinuxTestKernelVersion,
122 WriteVar<PathBuf>,
123 ),
124 }
125}
126
127new_flow_node_with_config!(struct Node);
128
129impl FlowNodeWithConfig for Node {
130 type Request = Request;
131 type Config = Config;
132
133 fn imports(ctx: &mut ImportCtx<'_>) {
134 ctx.import::<flowey_lib_common::install_dist_pkg::Node>();
135 ctx.import::<flowey_lib_common::download_gh_release::Node>();
136 }
137
138 fn emit(
139 config: Config,
140 requests: Vec<Self::Request>,
141 ctx: &mut NodeCtx<'_>,
142 ) -> anyhow::Result<()> {
143 let Config {
144 version,
145 local_paths,
146 } = config;
147 let mut deps: BTreeMap<
148 (OpenvmmTestKernelFile, CommonArch, LinuxTestKernelVersion),
149 Vec<WriteVar<PathBuf>>,
150 > = BTreeMap::new();
151
152 for req in requests {
153 match req {
154 Request::Get(file, arch, kver, var) => {
155 if !kver.is_available_for(arch) {
156 anyhow::bail!(
157 "test kernel {:?} is not published for {arch:?}",
158 kver.artifact_tag()
159 );
160 }
161 if !file.is_available_for(arch) {
162 anyhow::bail!(
163 "{file:?} is not available in the openvmm-test-linux archive for {arch:?}"
164 );
165 }
166 deps.entry((file, arch, kver)).or_default().push(var);
167 }
168 }
169 }
170
171 if version.is_some() && !local_paths.is_empty() {
172 anyhow::bail!("Cannot specify both Version and LocalPath requests");
173 }
174
175 if version.is_none() && local_paths.is_empty() {
176 anyhow::bail!("Must specify a Version or LocalPath request");
177 }
178
179 if deps.is_empty() {
182 return Ok(());
183 }
184
185 if !local_paths.is_empty() {
186 ctx.emit_rust_step("use local openvmm-test-linux", |ctx| {
187 let deps = deps.claim(ctx);
188 let local_paths: BTreeMap<_, _> = local_paths
189 .into_iter()
190 .map(|(key, var)| (key, var.claim(ctx)))
191 .collect();
192 move |rt| {
193 let resolved_paths: BTreeMap<(CommonArch, LinuxTestKernelVersion), PathBuf> =
194 local_paths
195 .into_iter()
196 .map(|(key, var)| (key, rt.read(var)))
197 .collect();
198
199 for ((file, arch, kver), vars) in deps {
200 let base_dir = resolved_paths.get(&(arch, kver)).ok_or_else(|| {
201 anyhow::anyhow!("No local path specified for ({:?}, {:?})", arch, kver)
202 })?;
203 let path = base_dir.join(file.filename(arch));
204 rt.write_all(vars, &path)
205 }
206
207 Ok(())
208 }
209 });
210
211 return Ok(());
212 }
213
214 let needed_archives: BTreeSet<(CommonArch, LinuxTestKernelVersion)> =
218 deps.keys().map(|(_, arch, kver)| (*arch, *kver)).collect();
219
220 let mut archives = BTreeMap::new();
221 for (arch, kver) in needed_archives {
222 let version = version.clone().expect("local requests handled above");
223 let arch_str = match arch {
224 CommonArch::X86_64 => "x86_64",
225 CommonArch::Aarch64 => "aarch64",
226 };
227 let kver_str = kver.artifact_tag();
228 let archive = ctx.reqv(|v| flowey_lib_common::download_gh_release::Request {
229 repo_owner: "microsoft".into(),
230 repo_name: "openvmm-deps".into(),
231 needs_auth: false,
232 tag: version.clone(),
233 file_name: format!("openvmm-test-linux-{kver_str}.{arch_str}.{version}.tar.gz"),
234 path: v,
235 });
236 archives.insert((arch, kver), archive);
237 }
238
239 let persistent_dir = ctx.persistent_dir();
240
241 ctx.emit_rust_step("unpack openvmm-test-linux archives", |ctx| {
242 let persistent_dir = persistent_dir.claim(ctx);
243 let archives = archives.claim(ctx);
244 let deps = deps.claim(ctx);
245 let version = version.clone().expect("local requests handled above");
246 move |rt| {
247 let persistent_dir = persistent_dir.map(|d| rt.read(d));
248
249 let mut extract_dirs = BTreeMap::new();
250 for (key, archive) in archives {
251 let file = rt.read(archive);
252 let dir = flowey_lib_common::_util::extract::extract_tar_gz_if_new(
253 rt,
254 persistent_dir.as_deref(),
255 &file,
256 &version,
257 )?;
258 extract_dirs.insert(key, dir);
259 }
260
261 for ((file, arch, kver), vars) in deps {
262 let path = extract_dirs[&(arch, kver)].join(file.filename(arch));
263 rt.write_all(vars, &path)
264 }
265
266 Ok(())
267 }
268 });
269
270 Ok(())
271 }
272}