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 checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 69 70 [[package]] 71 name = "clap" 72 version = "4.5.41" 73 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 101 checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 102 103 [[package]] 104 name = "firecracker-prepare" 105 version = "0.1.0" 106 dependencies = [ 107 "anyhow", 108 "libc", 109 "owo-colors", 110 "regex", ··· 124 version = "0.1.0" 125 dependencies = [ 126 "anyhow", 127 "glob", 128 "libc", 129 "num_cpus", ··· 137 dependencies = [ 138 "anyhow", 139 "clap", 140 "firecracker-prepare", 141 "firecracker-process", 142 "firecracker-vm", 143 "owo-colors", 144 ] 145 146 [[package]] 147 name = "glob" 148 version = "0.3.2" 149 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 174 checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" 175 176 [[package]] 177 name = "memchr" 178 version = "2.7.5" 179 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 196 checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 197 198 [[package]] 199 name = "owo-colors" 200 version = "4.2.2" 201 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 217 checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 218 dependencies = [ 219 "proc-macro2", 220 ] 221 222 [[package]] ··· 304 ] 305 306 [[package]] 307 name = "unicode-ident" 308 version = "1.0.18" 309 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 314 version = "0.2.2" 315 source = "registry+https://github.com/rust-lang/crates.io-index" 316 checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 317 318 [[package]] 319 name = "windows-sys"
··· 68 checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 69 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]] 83 name = "clap" 84 version = "4.5.41" 85 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 113 checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 114 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]] 137 name = "firecracker-prepare" 138 version = "0.1.0" 139 dependencies = [ 140 "anyhow", 141 + "dirs", 142 "libc", 143 "owo-colors", 144 "regex", ··· 158 version = "0.1.0" 159 dependencies = [ 160 "anyhow", 161 + "dirs", 162 "glob", 163 "libc", 164 "num_cpus", ··· 172 dependencies = [ 173 "anyhow", 174 "clap", 175 + "dirs", 176 "firecracker-prepare", 177 "firecracker-process", 178 "firecracker-vm", 179 + "glob", 180 "owo-colors", 181 ] 182 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]] 195 name = "glob" 196 version = "0.3.2" 197 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 222 checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" 223 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]] 235 name = "memchr" 236 version = "2.7.5" 237 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 254 checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 255 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]] 263 name = "owo-colors" 264 version = "4.2.2" 265 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 281 checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 282 dependencies = [ 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", 295 ] 296 297 [[package]] ··· 379 ] 380 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]] 402 name = "unicode-ident" 403 version = "1.0.18" 404 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 409 version = "0.2.2" 410 source = "registry+https://github.com/rust-lang/crates.io-index" 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" 418 419 [[package]] 420 name = "windows-sys"
-2
README.md
··· 4 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 7 - > [!NOTE] **🐲 It is a work in progress and is not yet ready for use. 🏗️🚧** 8 - 9 ![Fireup Preview](./preview.png)
··· 4 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 7 ![Fireup Preview](./preview.png)
+1
crates/firecracker-prepare/Cargo.toml
··· 8 9 [dependencies] 10 anyhow = "1.0.98" 11 libc = "0.2.174" 12 owo-colors = "4.2.2" 13 regex = "1.11.1"
··· 8 9 [dependencies] 10 anyhow = "1.0.98" 11 + dirs = "6.0.0" 12 libc = "0.2.174" 13 owo-colors = "4.2.2" 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 use anyhow::{anyhow, Context, Result}; 2 use owo_colors::OwoColorize; 3 use regex::Regex; 4 - use std::fs; 5 use std::path::Path; 6 7 use crate::command::{run_command, run_command_with_stdout_inherit}; 8 9 pub fn download_files(arch: &str) -> Result<(String, String, String)> { 10 - // Get latest version 11 let output = run_command( 12 "curl", 13 &[ ··· 31 .last() 32 .ok_or_else(|| anyhow!("Failed to parse CI version"))?; 33 34 - // Fetch and download kernel 35 let kernel_prefix = format!("firecracker-ci/{}/{}/vmlinux-", ci_version, arch); 36 let latest_kernel_key = 37 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(); 43 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 download_file( 49 &format!( 50 "https://s3.amazonaws.com/spec.ccfc.min/{}", 51 latest_kernel_key 52 ), 53 - &kernel_abs, 54 )?; 55 56 let ubuntu_prefix = format!("firecracker-ci/{}/{}/ubuntu-", ci_version, arch); ··· 76 .unwrap() 77 .to_string() 78 }); 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(); 85 download_file( 86 &format!( 87 "https://s3.amazonaws.com/spec.ccfc.min/{}", 88 latest_ubuntu_key 89 ), 90 - &ubuntu_abs, 91 )?; 92 93 - Ok((kernel_abs, ubuntu_abs, ubuntu_version)) 94 } 95 96 fn get_latest_key(url: &str, prefix: &str) -> Result<String> {
··· 1 use anyhow::{anyhow, Context, Result}; 2 use owo_colors::OwoColorize; 3 use regex::Regex; 4 use std::path::Path; 5 6 use crate::command::{run_command, run_command_with_stdout_inherit}; 7 8 pub fn download_files(arch: &str) -> Result<(String, String, String)> { 9 + let app_dir = 10 + crate::config::get_config_dir().with_context(|| "Failed to get configuration directory")?; 11 let output = run_command( 12 "curl", 13 &[ ··· 31 .last() 32 .ok_or_else(|| anyhow!("Failed to parse CI version"))?; 33 34 let kernel_prefix = format!("firecracker-ci/{}/{}/vmlinux-", ci_version, arch); 35 let latest_kernel_key = 36 get_latest_key("http://spec.ccfc.min.s3.amazonaws.com/", &kernel_prefix)?; 37 + let kernel_file = format!( 38 + "{}/{}", 39 + app_dir, 40 + latest_kernel_key.split('/').last().unwrap() 41 + ); 42 43 download_file( 44 &format!( 45 "https://s3.amazonaws.com/spec.ccfc.min/{}", 46 latest_kernel_key 47 ), 48 + &kernel_file, 49 )?; 50 51 let ubuntu_prefix = format!("firecracker-ci/{}/{}/ubuntu-", ci_version, arch); ··· 71 .unwrap() 72 .to_string() 73 }); 74 + let ubuntu_file = format!("{}/ubuntu-{}.squashfs.upstream", app_dir, ubuntu_version); 75 download_file( 76 &format!( 77 "https://s3.amazonaws.com/spec.ccfc.min/{}", 78 latest_ubuntu_key 79 ), 80 + &ubuntu_file, 81 )?; 82 83 + Ok((kernel_file, ubuntu_file, ubuntu_version)) 84 } 85 86 fn get_latest_key(url: &str, prefix: &str) -> Result<String> {
+13 -24
crates/firecracker-prepare/src/lib.rs
··· 1 - use anyhow::{Context, Result}; 2 use owo_colors::OwoColorize; 3 - use std::fs; 4 5 pub mod command; 6 pub mod downloader; 7 pub mod rootfs; 8 pub mod ssh; ··· 14 println!("[+] Detected architecture: {}", arch.bright_green()); 15 16 let (kernel_file, ubuntu_file, ubuntu_version) = downloader::download_files(&arch)?; 17 - let squashfs_root_dir = "squashfs-root"; 18 19 - rootfs::extract_squashfs(&ubuntu_file, squashfs_root_dir)?; 20 21 let ssh_key_name = format!("ubuntu-{}.id_rsa", ubuntu_version); 22 - ssh::generate_and_copy_ssh_key(&ssh_key_name, squashfs_root_dir)?; 23 24 - let ext4_file = format!("ubuntu-{}.ext4", ubuntu_version); 25 26 - rootfs::create_ext4_filesystem(squashfs_root_dir, &ext4_file)?; 27 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 - })?; 42 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()); 46 47 Ok(()) 48 }
··· 1 + use anyhow::Result; 2 use owo_colors::OwoColorize; 3 4 pub mod command; 5 + pub mod config; 6 pub mod downloader; 7 pub mod rootfs; 8 pub mod ssh; ··· 14 println!("[+] Detected architecture: {}", arch.bright_green()); 15 16 let (kernel_file, ubuntu_file, ubuntu_version) = downloader::download_files(&arch)?; 17 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)?; 22 23 let ssh_key_name = format!("ubuntu-{}.id_rsa", ubuntu_version); 24 + ssh::generate_and_copy_ssh_key(&ssh_key_name, &squashfs_root_dir)?; 25 26 + let ext4_file = format!("{}/ubuntu-{}.ext4", app_dir, ubuntu_version); 27 28 + rootfs::create_ext4_filesystem(&squashfs_root_dir, &ext4_file)?; 29 30 + let ssh_key_file = format!("{}/{}", app_dir, ssh_key_name); 31 32 + println!("[✓] Kernel: {}", kernel_file.bright_green()); 33 + println!("[✓] Rootfs: {}", ext4_file.bright_green()); 34 + println!("[✓] SSH Key: {}", ssh_key_file.bright_green()); 35 36 Ok(()) 37 }
+1 -1
crates/firecracker-prepare/src/rootfs.rs
··· 5 pub fn extract_squashfs(squashfs_file: &str, output_dir: &str) -> Result<()> { 6 run_command("rm", &["-rf", output_dir], true)?; 7 println!("Extracting rootfs..."); 8 - run_command("unsquashfs", &[squashfs_file], false)?; 9 Ok(()) 10 } 11
··· 5 pub fn extract_squashfs(squashfs_file: &str, output_dir: &str) -> Result<()> { 6 run_command("rm", &["-rf", output_dir], true)?; 7 println!("Extracting rootfs..."); 8 + run_command("unsquashfs", &["-d", output_dir, squashfs_file], false)?; 9 Ok(()) 10 } 11
+6 -3
crates/firecracker-prepare/src/ssh.rs
··· 3 use crate::command::{run_command, run_command_with_stdout_inherit}; 4 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() { 7 println!( 8 "[!] Warning: {} already exists, skipping key generation.", 9 key_name 10 ); 11 - let pub_key_path = format!("{}.pub", key_name); 12 let auth_keys_path = format!("{}/root/.ssh/authorized_keys", squashfs_root_dir); 13 run_command("cp", &[&pub_key_path, &auth_keys_path], true)?; 14 return Ok(()); 15 } 16 17 - run_command_with_stdout_inherit("ssh-keygen", &["-f", key_name, "-N", ""], false)?; 18 19 let pub_key_path = format!("{}.pub", key_name); 20 let auth_keys_path = format!("{}/root/.ssh/authorized_keys", squashfs_root_dir);
··· 3 use crate::command::{run_command, run_command_with_stdout_inherit}; 4 5 pub fn generate_and_copy_ssh_key(key_name: &str, squashfs_root_dir: &str) -> Result<()> { 6 + let app_dir = crate::config::get_config_dir()?; 7 + 8 + if std::path::Path::new(&format!("{}/{}", app_dir, key_name)).exists() { 9 println!( 10 "[!] Warning: {} already exists, skipping key generation.", 11 key_name 12 ); 13 + let pub_key_path = format!("{}/{}.pub", app_dir, key_name); 14 let auth_keys_path = format!("{}/root/.ssh/authorized_keys", squashfs_root_dir); 15 run_command("cp", &[&pub_key_path, &auth_keys_path], true)?; 16 return Ok(()); 17 } 18 19 + let key_name = format!("{}/{}", app_dir, key_name); 20 + run_command_with_stdout_inherit("ssh-keygen", &["-f", &key_name, "-N", ""], false)?; 21 22 let pub_key_path = format!("{}.pub", key_name); 23 let auth_keys_path = format!("{}/root/.ssh/authorized_keys", squashfs_root_dir);
+1 -1
crates/firecracker-process/src/lib.rs
··· 21 } 22 run_command("killall", &["-s", "KILL", "firecracker"], true)?; 23 run_command("rm", &["-rf", FIRECRACKER_SOCKET], true)?; 24 - println!("[+] Firecracker stopped."); 25 Ok(()) 26 } 27
··· 21 } 22 run_command("killall", &["-s", "KILL", "firecracker"], true)?; 23 run_command("rm", &["-rf", FIRECRACKER_SOCKET], true)?; 24 + println!("[+] Firecracker has been stopped."); 25 Ok(()) 26 } 27
+2
crates/firecracker-up/Cargo.toml
··· 13 firecracker-process = { path = "../firecracker-process" } 14 clap = "4.5.41" 15 owo-colors = "4.2.2"
··· 13 firecracker-process = { path = "../firecracker-process" } 14 clap = "4.5.41" 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 use anyhow::{anyhow, Context, Result}; 2 use std::process::{Command, Output, Stdio}; 3 4 - pub fn run_command(command: &str, args: &[&str]) -> Result<Output> { 5 let mut cmd = Command::new(command); 6 7 let output = cmd 8 .args(args) 9 - .stderr(Stdio::piped()) 10 .output() 11 .with_context(|| format!("Failed to execute {}", command))?; 12
··· 1 use anyhow::{anyhow, Context, Result}; 2 use std::process::{Command, Output, Stdio}; 3 4 + pub fn run_command(command: &str, args: &[&str], with_stdin: bool) -> Result<Output> { 5 let mut cmd = Command::new(command); 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 + 15 let output = cmd 16 .args(args) 17 .output() 18 .with_context(|| format!("Failed to execute {}", command))?; 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 use anyhow::Result; 4 use owo_colors::OwoColorize; 5 6 - use crate::command::run_command; 7 8 pub mod command; 9 10 - fn main() -> Result<()> { 11 - check_kvm_support()?; 12 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(()) 26 } 27 28 - pub fn check_kvm_support() -> Result<()> { 29 - print!("[+] Checking for kvm support... "); 30 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 - )); 38 } 39 - 40 - println!("{}", "[✓] OK".bright_green()); 41 42 Ok(()) 43 }
··· 1 use anyhow::Result; 2 + use clap::{arg, Command}; 3 use owo_colors::OwoColorize; 4 5 + use crate::cmd::{down::down, logs::logs, ssh::ssh, status::status, up::up}; 6 7 + pub mod cmd; 8 pub mod command; 9 + pub mod config; 10 11 + fn cli() -> Command { 12 + let banner = format!( 13 + "{}", 14 + r#" 15 + _______ __ __ 16 + / ____(_)_______ / / / /___ 17 + / /_ / / ___/ _ \/ / / / __ \ 18 + / __/ / / / / __/ /_/ / /_/ / 19 + /_/ /_/_/ \___/\____/ .___/ 20 + /_/ 21 + "# 22 + .yellow() 23 + ); 24 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")) 42 } 43 44 + fn main() -> Result<()> { 45 + let matches = cli().get_matches(); 46 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()?, 57 } 58 59 Ok(()) 60 }
+1
crates/firecracker-vm/Cargo.toml
··· 8 9 [dependencies] 10 anyhow = "1.0.98" 11 glob = "0.3.2" 12 libc = "0.2.174" 13 num_cpus = "1.17.0"
··· 8 9 [dependencies] 10 anyhow = "1.0.98" 11 + dirs = "6.0.0" 12 glob = "0.3.2" 13 libc = "0.2.174" 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 use owo_colors::OwoColorize; 3 use std::fs; 4 5 - use crate::constants::GUEST_IP; 6 7 mod command; 8 mod constants; 9 mod firecracker; 10 mod guest; 11 mod network; 12 13 pub fn setup() -> Result<()> { 14 - let logfile = format!("{}/firecracker.log", std::env::current_dir()?.display()); 15 fs::File::create(&logfile) 16 .with_context(|| format!("Failed to create log file: {}", logfile))?; 17 18 - let kernel = glob::glob("vmlinux*") 19 .with_context(|| "Failed to glob kernel files")? 20 .last() 21 .ok_or_else(|| anyhow!("No kernel file found"))? ··· 30 .display() 31 .to_string(); 32 33 - let rootfs = glob::glob("*.ext4") 34 .with_context(|| "Failed to glob rootfs files")? 35 .last() 36 .ok_or_else(|| anyhow!("No rootfs file found"))? ··· 45 .display() 46 .to_string(); 47 48 - let key_name = glob::glob("*.id_rsa") 49 .with_context(|| "Failed to glob ssh key files")? 50 .last() 51 .ok_or_else(|| anyhow!("No SSH key file found"))? ··· 65 firecracker::configure(&logfile, &kernel, &rootfs, &arch)?; 66 guest::configure_guest_network(&key_name)?; 67 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(); 75 76 - println!("SSH into the VM using:"); 77 - println!( 78 - "{}", 79 - format!("ssh -i ./{} root@{}", key_name, GUEST_IP).bright_green() 80 - ); 81 82 Ok(()) 83 }
··· 2 use owo_colors::OwoColorize; 3 use std::fs; 4 5 + use crate::config::get_config_dir; 6 7 mod command; 8 + mod config; 9 mod constants; 10 mod firecracker; 11 mod guest; 12 mod network; 13 14 pub fn setup() -> Result<()> { 15 + let app_dir = get_config_dir().with_context(|| "Failed to get configuration directory")?; 16 + 17 + let logfile = format!("{}/firecracker.log", app_dir); 18 fs::File::create(&logfile) 19 .with_context(|| format!("Failed to create log file: {}", logfile))?; 20 21 + let kernel = glob::glob(format!("{}/vmlinux*", app_dir).as_str()) 22 .with_context(|| "Failed to glob kernel files")? 23 .last() 24 .ok_or_else(|| anyhow!("No kernel file found"))? ··· 33 .display() 34 .to_string(); 35 36 + let rootfs = glob::glob(format!("{}/*.ext4", app_dir).as_str()) 37 .with_context(|| "Failed to glob rootfs files")? 38 .last() 39 .ok_or_else(|| anyhow!("No rootfs file found"))? ··· 48 .display() 49 .to_string(); 50 51 + let key_name = glob::glob(format!("{}/*.id_rsa", app_dir).as_str()) 52 .with_context(|| "Failed to glob ssh key files")? 53 .last() 54 .ok_or_else(|| anyhow!("No SSH key file found"))? ··· 68 firecracker::configure(&logfile, &kernel, &rootfs, &arch)?; 69 guest::configure_guest_network(&key_name)?; 70 71 + println!("[✓] MicroVM booted and network is configured 🎉"); 72 73 + println!("SSH into the VM using the following command:"); 74 + println!("{}", "fireup ssh".bright_green()); 75 76 Ok(()) 77 }
preview.png

This is a binary file and will not be displayed.