Prepare, configure, and manage Firecracker microVMs in seconds!
virtualization linux microvm firecracker

implement basic commands: up, down, status, ssh, logs

+348 -100
+101
Cargo.lock
··· 68 68 checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 69 69 70 70 [[package]] 71 + name = "bitflags" 72 + version = "2.9.1" 73 + source = "registry+https://github.com/rust-lang/crates.io-index" 74 + checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 75 + 76 + [[package]] 77 + name = "cfg-if" 78 + version = "1.0.1" 79 + source = "registry+https://github.com/rust-lang/crates.io-index" 80 + checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 81 + 82 + [[package]] 71 83 name = "clap" 72 84 version = "4.5.41" 73 85 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 101 113 checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 102 114 103 115 [[package]] 116 + name = "dirs" 117 + version = "6.0.0" 118 + source = "registry+https://github.com/rust-lang/crates.io-index" 119 + checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" 120 + dependencies = [ 121 + "dirs-sys", 122 + ] 123 + 124 + [[package]] 125 + name = "dirs-sys" 126 + version = "0.5.0" 127 + source = "registry+https://github.com/rust-lang/crates.io-index" 128 + checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" 129 + dependencies = [ 130 + "libc", 131 + "option-ext", 132 + "redox_users", 133 + "windows-sys", 134 + ] 135 + 136 + [[package]] 104 137 name = "firecracker-prepare" 105 138 version = "0.1.0" 106 139 dependencies = [ 107 140 "anyhow", 141 + "dirs", 108 142 "libc", 109 143 "owo-colors", 110 144 "regex", ··· 124 158 version = "0.1.0" 125 159 dependencies = [ 126 160 "anyhow", 161 + "dirs", 127 162 "glob", 128 163 "libc", 129 164 "num_cpus", ··· 137 172 dependencies = [ 138 173 "anyhow", 139 174 "clap", 175 + "dirs", 140 176 "firecracker-prepare", 141 177 "firecracker-process", 142 178 "firecracker-vm", 179 + "glob", 143 180 "owo-colors", 144 181 ] 145 182 146 183 [[package]] 184 + name = "getrandom" 185 + version = "0.2.16" 186 + source = "registry+https://github.com/rust-lang/crates.io-index" 187 + checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 188 + dependencies = [ 189 + "cfg-if", 190 + "libc", 191 + "wasi", 192 + ] 193 + 194 + [[package]] 147 195 name = "glob" 148 196 version = "0.3.2" 149 197 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 174 222 checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" 175 223 176 224 [[package]] 225 + name = "libredox" 226 + version = "0.1.8" 227 + source = "registry+https://github.com/rust-lang/crates.io-index" 228 + checksum = "360e552c93fa0e8152ab463bc4c4837fce76a225df11dfaeea66c313de5e61f7" 229 + dependencies = [ 230 + "bitflags", 231 + "libc", 232 + ] 233 + 234 + [[package]] 177 235 name = "memchr" 178 236 version = "2.7.5" 179 237 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 196 254 checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 197 255 198 256 [[package]] 257 + name = "option-ext" 258 + version = "0.2.0" 259 + source = "registry+https://github.com/rust-lang/crates.io-index" 260 + checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" 261 + 262 + [[package]] 199 263 name = "owo-colors" 200 264 version = "4.2.2" 201 265 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 217 281 checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 218 282 dependencies = [ 219 283 "proc-macro2", 284 + ] 285 + 286 + [[package]] 287 + name = "redox_users" 288 + version = "0.5.0" 289 + source = "registry+https://github.com/rust-lang/crates.io-index" 290 + checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" 291 + dependencies = [ 292 + "getrandom", 293 + "libredox", 294 + "thiserror", 220 295 ] 221 296 222 297 [[package]] ··· 304 379 ] 305 380 306 381 [[package]] 382 + name = "thiserror" 383 + version = "2.0.12" 384 + source = "registry+https://github.com/rust-lang/crates.io-index" 385 + checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 386 + dependencies = [ 387 + "thiserror-impl", 388 + ] 389 + 390 + [[package]] 391 + name = "thiserror-impl" 392 + version = "2.0.12" 393 + source = "registry+https://github.com/rust-lang/crates.io-index" 394 + checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 395 + dependencies = [ 396 + "proc-macro2", 397 + "quote", 398 + "syn", 399 + ] 400 + 401 + [[package]] 307 402 name = "unicode-ident" 308 403 version = "1.0.18" 309 404 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 314 409 version = "0.2.2" 315 410 source = "registry+https://github.com/rust-lang/crates.io-index" 316 411 checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 412 + 413 + [[package]] 414 + name = "wasi" 415 + version = "0.11.1+wasi-snapshot-preview1" 416 + source = "registry+https://github.com/rust-lang/crates.io-index" 417 + checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 317 418 318 419 [[package]] 319 420 name = "windows-sys"
-2
README.md
··· 4 4 5 5 `fireup` is a tool designed to simplify the process of setting up and managing Firecracker microVMs. It automates the preparation of the necessary files, including kernel images, root filesystems, and SSH keys, to quickly get you started with [Firecracker](https://firecracker-microvm.github.io/). 6 6 7 - > [!NOTE] **🐲 It is a work in progress and is not yet ready for use. 🏗️🚧** 8 - 9 7 ![Fireup Preview](./preview.png)
+1
crates/firecracker-prepare/Cargo.toml
··· 8 8 9 9 [dependencies] 10 10 anyhow = "1.0.98" 11 + dirs = "6.0.0" 11 12 libc = "0.2.174" 12 13 owo-colors = "4.2.2" 13 14 regex = "1.11.1"
+13
crates/firecracker-prepare/src/config.rs
··· 1 + use anyhow::Error; 2 + use anyhow::{anyhow, Context}; 3 + use std::fs; 4 + 5 + pub fn get_config_dir() -> Result<String, Error> { 6 + let app_dir = dirs::home_dir() 7 + .ok_or_else(|| anyhow!("Failed to get home directory"))? 8 + .join(".fireup"); 9 + fs::create_dir_all(&app_dir) 10 + .with_context(|| format!("Failed to create app directory: {}", app_dir.display()))?; 11 + 12 + Ok(app_dir.display().to_string()) 13 + }
+11 -21
crates/firecracker-prepare/src/downloader.rs
··· 1 1 use anyhow::{anyhow, Context, Result}; 2 2 use owo_colors::OwoColorize; 3 3 use regex::Regex; 4 - use std::fs; 5 4 use std::path::Path; 6 5 7 6 use crate::command::{run_command, run_command_with_stdout_inherit}; 8 7 9 8 pub fn download_files(arch: &str) -> Result<(String, String, String)> { 10 - // Get latest version 9 + let app_dir = 10 + crate::config::get_config_dir().with_context(|| "Failed to get configuration directory")?; 11 11 let output = run_command( 12 12 "curl", 13 13 &[ ··· 31 31 .last() 32 32 .ok_or_else(|| anyhow!("Failed to parse CI version"))?; 33 33 34 - // Fetch and download kernel 35 34 let kernel_prefix = format!("firecracker-ci/{}/{}/vmlinux-", ci_version, arch); 36 35 let latest_kernel_key = 37 36 get_latest_key("http://spec.ccfc.min.s3.amazonaws.com/", &kernel_prefix)?; 38 - let kernel_file = Path::new(&latest_kernel_key) 39 - .file_name() 40 - .ok_or_else(|| anyhow!("Failed to get kernel filename"))? 41 - .to_string_lossy() 42 - .to_string(); 37 + let kernel_file = format!( 38 + "{}/{}", 39 + app_dir, 40 + latest_kernel_key.split('/').last().unwrap() 41 + ); 43 42 44 - let kernel_abs = fs::canonicalize(&kernel_file) 45 - .unwrap_or_else(|_| Path::new(&kernel_file).to_path_buf()) 46 - .display() 47 - .to_string(); 48 43 download_file( 49 44 &format!( 50 45 "https://s3.amazonaws.com/spec.ccfc.min/{}", 51 46 latest_kernel_key 52 47 ), 53 - &kernel_abs, 48 + &kernel_file, 54 49 )?; 55 50 56 51 let ubuntu_prefix = format!("firecracker-ci/{}/{}/ubuntu-", ci_version, arch); ··· 76 71 .unwrap() 77 72 .to_string() 78 73 }); 79 - let ubuntu_file = format!("ubuntu-{}.squashfs.upstream", ubuntu_version); 80 - 81 - let ubuntu_abs = fs::canonicalize(&ubuntu_file) 82 - .unwrap_or_else(|_| Path::new(&ubuntu_file).to_path_buf()) 83 - .display() 84 - .to_string(); 74 + let ubuntu_file = format!("{}/ubuntu-{}.squashfs.upstream", app_dir, ubuntu_version); 85 75 download_file( 86 76 &format!( 87 77 "https://s3.amazonaws.com/spec.ccfc.min/{}", 88 78 latest_ubuntu_key 89 79 ), 90 - &ubuntu_abs, 80 + &ubuntu_file, 91 81 )?; 92 82 93 - Ok((kernel_abs, ubuntu_abs, ubuntu_version)) 83 + Ok((kernel_file, ubuntu_file, ubuntu_version)) 94 84 } 95 85 96 86 fn get_latest_key(url: &str, prefix: &str) -> Result<String> {
+13 -24
crates/firecracker-prepare/src/lib.rs
··· 1 - use anyhow::{Context, Result}; 1 + use anyhow::Result; 2 2 use owo_colors::OwoColorize; 3 - use std::fs; 4 3 5 4 pub mod command; 5 + pub mod config; 6 6 pub mod downloader; 7 7 pub mod rootfs; 8 8 pub mod ssh; ··· 14 14 println!("[+] Detected architecture: {}", arch.bright_green()); 15 15 16 16 let (kernel_file, ubuntu_file, ubuntu_version) = downloader::download_files(&arch)?; 17 - let squashfs_root_dir = "squashfs-root"; 18 17 19 - rootfs::extract_squashfs(&ubuntu_file, squashfs_root_dir)?; 18 + let app_dir = config::get_config_dir()?; 19 + let squashfs_root_dir = format!("{}/squashfs_root", app_dir); 20 + 21 + rootfs::extract_squashfs(&ubuntu_file, &squashfs_root_dir)?; 20 22 21 23 let ssh_key_name = format!("ubuntu-{}.id_rsa", ubuntu_version); 22 - ssh::generate_and_copy_ssh_key(&ssh_key_name, squashfs_root_dir)?; 24 + ssh::generate_and_copy_ssh_key(&ssh_key_name, &squashfs_root_dir)?; 23 25 24 - let ext4_file = format!("ubuntu-{}.ext4", ubuntu_version); 26 + let ext4_file = format!("{}/ubuntu-{}.ext4", app_dir, ubuntu_version); 25 27 26 - rootfs::create_ext4_filesystem(squashfs_root_dir, &ext4_file)?; 28 + rootfs::create_ext4_filesystem(&squashfs_root_dir, &ext4_file)?; 27 29 28 - let kernel_abs = fs::canonicalize(&kernel_file).with_context(|| { 29 - format!( 30 - "Failed to resolve absolute path for kernel: {}", 31 - kernel_file 32 - ) 33 - })?; 34 - let ext4_abs = fs::canonicalize(&ext4_file) 35 - .with_context(|| format!("Failed to resolve absolute path for rootfs: {}", ext4_file))?; 36 - let ssh_key_abs = fs::canonicalize(&ssh_key_name).with_context(|| { 37 - format!( 38 - "Failed to resolve absolute path for SSH key: {}", 39 - ssh_key_name 40 - ) 41 - })?; 30 + let ssh_key_file = format!("{}/{}", app_dir, ssh_key_name); 42 31 43 - println!("[✓] Kernel: {}", kernel_abs.display().bright_green()); 44 - println!("[✓] Rootfs: {}", ext4_abs.display().bright_green()); 45 - println!("[✓] SSH Key: {}", ssh_key_abs.display().bright_green()); 32 + println!("[✓] Kernel: {}", kernel_file.bright_green()); 33 + println!("[✓] Rootfs: {}", ext4_file.bright_green()); 34 + println!("[✓] SSH Key: {}", ssh_key_file.bright_green()); 46 35 47 36 Ok(()) 48 37 }
+1 -1
crates/firecracker-prepare/src/rootfs.rs
··· 5 5 pub fn extract_squashfs(squashfs_file: &str, output_dir: &str) -> Result<()> { 6 6 run_command("rm", &["-rf", output_dir], true)?; 7 7 println!("Extracting rootfs..."); 8 - run_command("unsquashfs", &[squashfs_file], false)?; 8 + run_command("unsquashfs", &["-d", output_dir, squashfs_file], false)?; 9 9 Ok(()) 10 10 } 11 11
+6 -3
crates/firecracker-prepare/src/ssh.rs
··· 3 3 use crate::command::{run_command, run_command_with_stdout_inherit}; 4 4 5 5 pub fn generate_and_copy_ssh_key(key_name: &str, squashfs_root_dir: &str) -> Result<()> { 6 - if std::path::Path::new(key_name).exists() { 6 + let app_dir = crate::config::get_config_dir()?; 7 + 8 + if std::path::Path::new(&format!("{}/{}", app_dir, key_name)).exists() { 7 9 println!( 8 10 "[!] Warning: {} already exists, skipping key generation.", 9 11 key_name 10 12 ); 11 - let pub_key_path = format!("{}.pub", key_name); 13 + let pub_key_path = format!("{}/{}.pub", app_dir, key_name); 12 14 let auth_keys_path = format!("{}/root/.ssh/authorized_keys", squashfs_root_dir); 13 15 run_command("cp", &[&pub_key_path, &auth_keys_path], true)?; 14 16 return Ok(()); 15 17 } 16 18 17 - run_command_with_stdout_inherit("ssh-keygen", &["-f", key_name, "-N", ""], false)?; 19 + let key_name = format!("{}/{}", app_dir, key_name); 20 + run_command_with_stdout_inherit("ssh-keygen", &["-f", &key_name, "-N", ""], false)?; 18 21 19 22 let pub_key_path = format!("{}.pub", key_name); 20 23 let auth_keys_path = format!("{}/root/.ssh/authorized_keys", squashfs_root_dir);
+1 -1
crates/firecracker-process/src/lib.rs
··· 21 21 } 22 22 run_command("killall", &["-s", "KILL", "firecracker"], true)?; 23 23 run_command("rm", &["-rf", FIRECRACKER_SOCKET], true)?; 24 - println!("[+] Firecracker stopped."); 24 + println!("[+] Firecracker has been stopped."); 25 25 Ok(()) 26 26 } 27 27
+2
crates/firecracker-up/Cargo.toml
··· 13 13 firecracker-process = { path = "../firecracker-process" } 14 14 clap = "4.5.41" 15 15 owo-colors = "4.2.2" 16 + glob = "0.3.2" 17 + dirs = "6.0.0"
+6
crates/firecracker-up/src/cmd/down.rs
··· 1 + use anyhow::Error; 2 + 3 + pub fn down() -> Result<(), Error> { 4 + firecracker_process::stop()?; 5 + Ok(()) 6 + }
+18
crates/firecracker-up/src/cmd/logs.rs
··· 1 + use anyhow::Error; 2 + 3 + use crate::command::run_command; 4 + 5 + pub fn logs(follow: bool) -> Result<(), Error> { 6 + let app_dir = crate::config::get_config_dir()?; 7 + let logfile = format!("{}/firecracker.log", app_dir); 8 + let logfile = logfile.as_str(); 9 + run_command( 10 + "tail", 11 + &match follow { 12 + true => vec!["-f", logfile], 13 + false => vec![logfile], 14 + }, 15 + true, 16 + )?; 17 + Ok(()) 18 + }
+5
crates/firecracker-up/src/cmd/mod.rs
··· 1 + pub mod down; 2 + pub mod logs; 3 + pub mod ssh; 4 + pub mod status; 5 + pub mod up;
+18
crates/firecracker-up/src/cmd/ssh.rs
··· 1 + use anyhow::Error; 2 + use glob::glob; 3 + 4 + use crate::{command::run_command, config::get_config_dir}; 5 + 6 + pub fn ssh() -> Result<(), Error> { 7 + let app_dir = get_config_dir()?; 8 + let private_key = glob(format!("{}/*.id_rsa", app_dir).as_str()) 9 + .map_err(|e| Error::msg(format!("Failed to find SSH key: {}", e)))? 10 + .last() 11 + .ok_or_else(|| Error::msg("No SSH key file found"))?; 12 + run_command( 13 + "ssh", 14 + &["-i", &private_key?.display().to_string(), "root@172.16.0.2"], 15 + true, 16 + )?; 17 + Ok(()) 18 + }
+18
crates/firecracker-up/src/cmd/status.rs
··· 1 + use anyhow::Error; 2 + use owo_colors::OwoColorize; 3 + 4 + pub fn status() -> Result<(), Error> { 5 + if firecracker_process::is_running() { 6 + println!( 7 + "Firecracker MicroVM is running. {}", 8 + "[✓] RUNNING".bright_green() 9 + ); 10 + return Ok(()); 11 + } 12 + 13 + println!( 14 + "Firecracker MicroVM is not running. {}", 15 + "[✗] STOPPED".bright_red() 16 + ); 17 + Ok(()) 18 + }
+41
crates/firecracker-up/src/cmd/up.rs
··· 1 + use std::thread; 2 + 3 + use anyhow::Error; 4 + use owo_colors::OwoColorize; 5 + 6 + use crate::command::run_command; 7 + 8 + pub fn up() -> Result<(), Error> { 9 + check_kvm_support()?; 10 + 11 + firecracker_process::start()?; 12 + 13 + loop { 14 + thread::sleep(std::time::Duration::from_secs(1)); 15 + if firecracker_process::is_running() { 16 + println!("[+] Firecracker is running."); 17 + break; 18 + } 19 + } 20 + 21 + firecracker_prepare::prepare()?; 22 + firecracker_vm::setup()?; 23 + Ok(()) 24 + } 25 + 26 + pub fn check_kvm_support() -> Result<(), Error> { 27 + print!("[+] Checking for kvm support... "); 28 + 29 + if !run_command("sh", &["-c", "lsmod | grep kvm"], false) 30 + .map(|output| output.status.success()) 31 + .unwrap_or(false) 32 + { 33 + return Err(anyhow::anyhow!( 34 + "KVM is not available. Please ensure KVM is enabled in your system." 35 + )); 36 + } 37 + 38 + println!("{}", "[✓] OK".bright_green()); 39 + 40 + Ok(()) 41 + }
+9 -2
crates/firecracker-up/src/command.rs
··· 1 1 use anyhow::{anyhow, Context, Result}; 2 2 use std::process::{Command, Output, Stdio}; 3 3 4 - pub fn run_command(command: &str, args: &[&str]) -> Result<Output> { 4 + pub fn run_command(command: &str, args: &[&str], with_stdin: bool) -> Result<Output> { 5 5 let mut cmd = Command::new(command); 6 6 7 + match with_stdin { 8 + true => cmd 9 + .stdin(Stdio::inherit()) 10 + .stdout(Stdio::inherit()) 11 + .stderr(Stdio::inherit()), 12 + false => cmd.stderr(Stdio::piped()), 13 + }; 14 + 7 15 let output = cmd 8 16 .args(args) 9 - .stderr(Stdio::piped()) 10 17 .output() 11 18 .with_context(|| format!("Failed to execute {}", command))?; 12 19
+13
crates/firecracker-up/src/config.rs
··· 1 + use anyhow::Error; 2 + use anyhow::{anyhow, Context}; 3 + use std::fs; 4 + 5 + pub fn get_config_dir() -> Result<String, Error> { 6 + let app_dir = dirs::home_dir() 7 + .ok_or_else(|| anyhow!("Failed to get home directory"))? 8 + .join(".fireup"); 9 + fs::create_dir_all(&app_dir) 10 + .with_context(|| format!("Failed to create app directory: {}", app_dir.display()))?; 11 + 12 + Ok(app_dir.display().to_string()) 13 + }
+46 -29
crates/firecracker-up/src/main.rs
··· 1 - use std::thread; 2 - 3 1 use anyhow::Result; 2 + use clap::{arg, Command}; 4 3 use owo_colors::OwoColorize; 5 4 6 - use crate::command::run_command; 5 + use crate::cmd::{down::down, logs::logs, ssh::ssh, status::status, up::up}; 7 6 7 + pub mod cmd; 8 8 pub mod command; 9 + pub mod config; 9 10 10 - fn main() -> Result<()> { 11 - check_kvm_support()?; 11 + fn cli() -> Command { 12 + let banner = format!( 13 + "{}", 14 + r#" 15 + _______ __ __ 16 + / ____(_)_______ / / / /___ 17 + / /_ / / ___/ _ \/ / / / __ \ 18 + / __/ / / / / __/ /_/ / /_/ / 19 + /_/ /_/_/ \___/\____/ .___/ 20 + /_/ 21 + "# 22 + .yellow() 23 + ); 12 24 13 - firecracker_process::start()?; 14 - 15 - loop { 16 - thread::sleep(std::time::Duration::from_secs(1)); 17 - if firecracker_process::is_running() { 18 - println!("[+] Firecracker is running."); 19 - break; 20 - } 21 - } 22 - 23 - firecracker_prepare::prepare()?; 24 - firecracker_vm::setup()?; 25 - Ok(()) 25 + Command::new("fireup") 26 + .version(env!("CARGO_PKG_VERSION")) 27 + .about(&banner) 28 + .subcommand(Command::new("up").about("Start Firecracker MicroVM")) 29 + .subcommand(Command::new("down").about("Stop Firecracker MicroVM")) 30 + .subcommand(Command::new("status").about("Check the status of Firecracker MicroVM")) 31 + .subcommand( 32 + Command::new("logs") 33 + .arg( 34 + arg!(--follow -f "Follow the logs") 35 + .short('f') 36 + .long("follow") 37 + .default_value("false"), 38 + ) 39 + .about("View the logs of the Firecracker MicroVM"), 40 + ) 41 + .subcommand(Command::new("ssh").about("SSH into the Firecracker MicroVM")) 26 42 } 27 43 28 - pub fn check_kvm_support() -> Result<()> { 29 - print!("[+] Checking for kvm support... "); 44 + fn main() -> Result<()> { 45 + let matches = cli().get_matches(); 30 46 31 - if !run_command("sh", &["-c", "lsmod | grep kvm"]) 32 - .map(|output| output.status.success()) 33 - .unwrap_or(false) 34 - { 35 - return Err(anyhow::anyhow!( 36 - "KVM is not available. Please ensure KVM is enabled in your system." 37 - )); 47 + match matches.subcommand() { 48 + Some(("up", _)) => up()?, 49 + Some(("down", _)) => down()?, 50 + Some(("status", _)) => status()?, 51 + Some(("logs", args)) => { 52 + let follow = args.get_one::<bool>("follow").copied().unwrap_or(false); 53 + logs(follow)?; 54 + } 55 + Some(("ssh", _)) => ssh()?, 56 + _ => up()?, 38 57 } 39 - 40 - println!("{}", "[✓] OK".bright_green()); 41 58 42 59 Ok(()) 43 60 }
+1
crates/firecracker-vm/Cargo.toml
··· 8 8 9 9 [dependencies] 10 10 anyhow = "1.0.98" 11 + dirs = "6.0.0" 11 12 glob = "0.3.2" 12 13 libc = "0.2.174" 13 14 num_cpus = "1.17.0"
+13
crates/firecracker-vm/src/config.rs
··· 1 + use anyhow::Error; 2 + use anyhow::{anyhow, Context}; 3 + use std::fs; 4 + 5 + pub fn get_config_dir() -> Result<String, Error> { 6 + let app_dir = dirs::home_dir() 7 + .ok_or_else(|| anyhow!("Failed to get home directory"))? 8 + .join(".fireup"); 9 + fs::create_dir_all(&app_dir) 10 + .with_context(|| format!("Failed to create app directory: {}", app_dir.display()))?; 11 + 12 + Ok(app_dir.display().to_string()) 13 + }
+11 -17
crates/firecracker-vm/src/lib.rs
··· 2 2 use owo_colors::OwoColorize; 3 3 use std::fs; 4 4 5 - use crate::constants::GUEST_IP; 5 + use crate::config::get_config_dir; 6 6 7 7 mod command; 8 + mod config; 8 9 mod constants; 9 10 mod firecracker; 10 11 mod guest; 11 12 mod network; 12 13 13 14 pub fn setup() -> Result<()> { 14 - let logfile = format!("{}/firecracker.log", std::env::current_dir()?.display()); 15 + let app_dir = get_config_dir().with_context(|| "Failed to get configuration directory")?; 16 + 17 + let logfile = format!("{}/firecracker.log", app_dir); 15 18 fs::File::create(&logfile) 16 19 .with_context(|| format!("Failed to create log file: {}", logfile))?; 17 20 18 - let kernel = glob::glob("vmlinux*") 21 + let kernel = glob::glob(format!("{}/vmlinux*", app_dir).as_str()) 19 22 .with_context(|| "Failed to glob kernel files")? 20 23 .last() 21 24 .ok_or_else(|| anyhow!("No kernel file found"))? ··· 30 33 .display() 31 34 .to_string(); 32 35 33 - let rootfs = glob::glob("*.ext4") 36 + let rootfs = glob::glob(format!("{}/*.ext4", app_dir).as_str()) 34 37 .with_context(|| "Failed to glob rootfs files")? 35 38 .last() 36 39 .ok_or_else(|| anyhow!("No rootfs file found"))? ··· 45 48 .display() 46 49 .to_string(); 47 50 48 - let key_name = glob::glob("*.id_rsa") 51 + let key_name = glob::glob(format!("{}/*.id_rsa", app_dir).as_str()) 49 52 .with_context(|| "Failed to glob ssh key files")? 50 53 .last() 51 54 .ok_or_else(|| anyhow!("No SSH key file found"))? ··· 65 68 firecracker::configure(&logfile, &kernel, &rootfs, &arch)?; 66 69 guest::configure_guest_network(&key_name)?; 67 70 68 - println!("[✓] MicroVM booted and network is configured."); 69 - 70 - let key_name = key_name 71 - .rsplit('/') 72 - .next() 73 - .ok_or_else(|| anyhow!("Failed to extract key name from path"))? 74 - .to_string(); 71 + println!("[✓] MicroVM booted and network is configured 🎉"); 75 72 76 - println!("SSH into the VM using:"); 77 - println!( 78 - "{}", 79 - format!("ssh -i ./{} root@{}", key_name, GUEST_IP).bright_green() 80 - ); 73 + println!("SSH into the VM using the following command:"); 74 + println!("{}", "fireup ssh".bright_green()); 81 75 82 76 Ok(()) 83 77 }
preview.png

This is a binary file and will not be displayed.