use crate::app_config::AppConfig; use crate::constants; use std::process::Command; /// VM Cloud Init configuration /// /// RedHat documentation: https://docs.redhat.com/fr/documentation/red_hat_enterprise_linux/9/html/configuring_and_managing_cloud-init_for_rhel_9/configuring-cloud-init_cloud-content /// cloud-localds source code: https://github.com/canonical/cloud-utils/blob/main/bin/cloud-localds #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Default)] pub struct CloudInitConfig { attach_config: bool, /// Main user data user_data: String, /// Instance ID, set in metadata file #[serde(skip_serializing_if = "Option::is_none")] instance_id: Option, /// Local hostname, set in metadata file #[serde(skip_serializing_if = "Option::is_none")] local_hostname: Option, /// Network configuration #[serde(skip_serializing_if = "Option::is_none")] network_configuration: Option, } impl CloudInitConfig { /// Generate disk image for nocloud usage pub fn generate_nocloud_disk(&self) -> anyhow::Result> { let temp_path = tempfile::tempdir_in(&AppConfig::get().temp_dir)?; let mut cmd = Command::new(constants::PROGRAM_CLOUD_LOCALDS); // ISO destination path let temp_iso = temp_path.path().join("disk.iso"); cmd.arg(&temp_iso); // Process network configuration if let Some(net_conf) = &self.network_configuration { let net_conf_path = temp_path.path().join("network"); std::fs::write(&net_conf_path, net_conf)?; cmd.arg("--network-config").arg(&net_conf_path); } // Process user data let user_data_path = temp_path.path().join("user-data"); std::fs::write(&user_data_path, &self.user_data)?; cmd.arg(user_data_path); // Process metadata let mut metadatas = vec![]; if let Some(inst_id) = &self.instance_id { metadatas.push(format!("instance-id: {}", inst_id)); } if let Some(local_hostname) = &self.local_hostname { metadatas.push(format!("local-hostname: {}", local_hostname)); } let meta_data_path = temp_path.path().join("meta-data"); std::fs::write(&meta_data_path, metadatas.join("\n"))?; cmd.arg(meta_data_path); // Execute command let output = cmd.output()?; if !output.status.success() { anyhow::bail!( "{} exited with status {}!\nStdout: {}\nStderr: {}", constants::PROGRAM_CLOUD_LOCALDS, output.status, String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ); } // Read generated ISO file Ok(std::fs::read(temp_iso)?) } }