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

implement 'init' subcommand and support for 'fire.toml' config file

+265 -8
+2 -1
.gitignore
··· 1 - target/ 1 + target/ 2 + fire.toml
+124 -1
Cargo.lock
··· 134 134 ] 135 135 136 136 [[package]] 137 + name = "equivalent" 138 + version = "1.0.2" 139 + source = "registry+https://github.com/rust-lang/crates.io-index" 140 + checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 141 + 142 + [[package]] 143 + name = "fire-config" 144 + version = "0.1.0" 145 + dependencies = [ 146 + "anyhow", 147 + "firecracker-prepare", 148 + "num_cpus", 149 + "owo-colors", 150 + "serde", 151 + "serde_yml", 152 + "toml", 153 + ] 154 + 155 + [[package]] 137 156 name = "firecracker-prepare" 138 157 version = "0.1.0" 139 158 dependencies = [ ··· 142 161 "libc", 143 162 "owo-colors", 144 163 "regex", 164 + "serde", 145 165 ] 146 166 147 167 [[package]] ··· 159 179 dependencies = [ 160 180 "anyhow", 161 181 "dirs", 182 + "fire-config", 162 183 "firecracker-prepare", 163 184 "glob", 164 185 "libc", ··· 169 190 170 191 [[package]] 171 192 name = "fireup" 172 - version = "0.2.0" 193 + version = "0.3.0" 173 194 dependencies = [ 174 195 "anyhow", 175 196 "clap", 176 197 "dirs", 198 + "fire-config", 177 199 "firecracker-prepare", 178 200 "firecracker-process", 179 201 "firecracker-vm", ··· 200 222 checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" 201 223 202 224 [[package]] 225 + name = "hashbrown" 226 + version = "0.15.4" 227 + source = "registry+https://github.com/rust-lang/crates.io-index" 228 + checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" 229 + 230 + [[package]] 203 231 name = "hermit-abi" 204 232 version = "0.5.2" 205 233 source = "registry+https://github.com/rust-lang/crates.io-index" 206 234 checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" 207 235 208 236 [[package]] 237 + name = "indexmap" 238 + version = "2.10.0" 239 + source = "registry+https://github.com/rust-lang/crates.io-index" 240 + checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" 241 + dependencies = [ 242 + "equivalent", 243 + "hashbrown", 244 + ] 245 + 246 + [[package]] 209 247 name = "is_terminal_polyfill" 210 248 version = "1.70.1" 211 249 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 231 269 dependencies = [ 232 270 "bitflags", 233 271 "libc", 272 + ] 273 + 274 + [[package]] 275 + name = "libyml" 276 + version = "0.0.5" 277 + source = "registry+https://github.com/rust-lang/crates.io-index" 278 + checksum = "3302702afa434ffa30847a83305f0a69d6abd74293b6554c18ec85c7ef30c980" 279 + dependencies = [ 280 + "anyhow", 281 + "version_check", 234 282 ] 235 283 236 284 [[package]] ··· 364 412 ] 365 413 366 414 [[package]] 415 + name = "serde_spanned" 416 + version = "1.0.0" 417 + source = "registry+https://github.com/rust-lang/crates.io-index" 418 + checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" 419 + dependencies = [ 420 + "serde", 421 + ] 422 + 423 + [[package]] 424 + name = "serde_yml" 425 + version = "0.0.12" 426 + source = "registry+https://github.com/rust-lang/crates.io-index" 427 + checksum = "59e2dd588bf1597a252c3b920e0143eb99b0f76e4e082f4c92ce34fbc9e71ddd" 428 + dependencies = [ 429 + "indexmap", 430 + "itoa", 431 + "libyml", 432 + "memchr", 433 + "ryu", 434 + "serde", 435 + "version_check", 436 + ] 437 + 438 + [[package]] 367 439 name = "strsim" 368 440 version = "0.11.1" 369 441 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 401 473 ] 402 474 403 475 [[package]] 476 + name = "toml" 477 + version = "0.9.4" 478 + source = "registry+https://github.com/rust-lang/crates.io-index" 479 + checksum = "41ae868b5a0f67631c14589f7e250c1ea2c574ee5ba21c6c8dd4b1485705a5a1" 480 + dependencies = [ 481 + "indexmap", 482 + "serde", 483 + "serde_spanned", 484 + "toml_datetime", 485 + "toml_parser", 486 + "toml_writer", 487 + "winnow", 488 + ] 489 + 490 + [[package]] 491 + name = "toml_datetime" 492 + version = "0.7.0" 493 + source = "registry+https://github.com/rust-lang/crates.io-index" 494 + checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" 495 + dependencies = [ 496 + "serde", 497 + ] 498 + 499 + [[package]] 500 + name = "toml_parser" 501 + version = "1.0.1" 502 + source = "registry+https://github.com/rust-lang/crates.io-index" 503 + checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" 504 + dependencies = [ 505 + "winnow", 506 + ] 507 + 508 + [[package]] 509 + name = "toml_writer" 510 + version = "1.0.2" 511 + source = "registry+https://github.com/rust-lang/crates.io-index" 512 + checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" 513 + 514 + [[package]] 404 515 name = "unicode-ident" 405 516 version = "1.0.18" 406 517 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 411 522 version = "0.2.2" 412 523 source = "registry+https://github.com/rust-lang/crates.io-index" 413 524 checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 525 + 526 + [[package]] 527 + name = "version_check" 528 + version = "0.9.5" 529 + source = "registry+https://github.com/rust-lang/crates.io-index" 530 + checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 414 531 415 532 [[package]] 416 533 name = "wasi" ··· 490 607 version = "0.52.6" 491 608 source = "registry+https://github.com/rust-lang/crates.io-index" 492 609 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 610 + 611 + [[package]] 612 + name = "winnow" 613 + version = "0.7.12" 614 + source = "registry+https://github.com/rust-lang/crates.io-index" 615 + checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"
+1
README.md
··· 32 32 ``` 33 33 34 34 ## Subcommands 35 + - `init`: Initializes a new configuration file `fire.toml` in the current directory. 35 36 - `up`: Starts the Firecracker microVM, preparing assets and configuring the network if needed. 36 37 - `down`: Stops the running Firecracker microVM. 37 38 - `status`: Checks the status of the Firecracker microVM (running, stopped, or errored).
+16
crates/fire-config/Cargo.toml
··· 1 + [package] 2 + name = "fire-config" 3 + version = "0.1.0" 4 + authors.workspace = true 5 + edition.workspace = true 6 + license.workspace = true 7 + repository.workspace = true 8 + 9 + [dependencies] 10 + serde = { version = "1.0.219", features = ["derive", "serde_derive"] } 11 + serde_yml = "0.0.12" 12 + toml = "0.9.4" 13 + firecracker-prepare = { path = "../firecracker-prepare" } 14 + num_cpus = "1.17.0" 15 + anyhow = "1.0.98" 16 + owo-colors = "4.2.2"
+68
crates/fire-config/src/lib.rs
··· 1 + use std::{path::Path, process}; 2 + 3 + use anyhow::Error; 4 + use firecracker_prepare::Distro; 5 + use owo_colors::OwoColorize; 6 + use serde::{Deserialize, Serialize}; 7 + 8 + #[derive(Debug, Serialize, Deserialize)] 9 + pub struct Vm { 10 + pub vcpu: Option<u16>, 11 + pub memory: Option<u16>, 12 + pub vmlinux: Option<String>, 13 + pub rootfs: Option<String>, 14 + pub boot_args: Option<String>, 15 + } 16 + 17 + #[derive(Debug, Serialize, Deserialize)] 18 + pub struct FireConfig { 19 + pub distro: Distro, 20 + pub vm: Vm, 21 + } 22 + 23 + impl Default for FireConfig { 24 + fn default() -> Self { 25 + FireConfig { 26 + distro: Distro::Ubuntu, 27 + vm: Vm { 28 + vcpu: Some(num_cpus::get() as u16), 29 + memory: Some(512), 30 + vmlinux: None, 31 + rootfs: None, 32 + boot_args: None, 33 + }, 34 + } 35 + } 36 + } 37 + 38 + pub fn init_config() -> Result<(), Error> { 39 + if Path::new("fire.toml").exists() { 40 + println!( 41 + "Configuration file {} already exists, would you like to overwrite it? (y/n)", 42 + "'fire.toml'".cyan() 43 + ); 44 + let mut input = String::new(); 45 + std::io::stdin().read_line(&mut input)?; 46 + if input.trim().to_lowercase() != "y" { 47 + process::exit(1); 48 + } 49 + } 50 + let config_path = Path::new("fire.toml"); 51 + let config = FireConfig::default(); 52 + let toml_content = toml::to_string(&config)?; 53 + std::fs::write(config_path, toml_content)?; 54 + Ok(()) 55 + } 56 + 57 + pub fn read_config() -> Result<FireConfig, Error> { 58 + let config_path = Path::new("fire.toml"); 59 + if !config_path.exists() { 60 + return Err(Error::new(std::io::Error::new( 61 + std::io::ErrorKind::NotFound, 62 + "Configuration file not found", 63 + ))); 64 + } 65 + let content = std::fs::read_to_string(config_path)?; 66 + let config: FireConfig = toml::from_str(&content)?; 67 + Ok(config) 68 + }
+1
crates/firecracker-prepare/Cargo.toml
··· 12 12 libc = "0.2.174" 13 13 owo-colors = "4.2.2" 14 14 regex = "1.11.1" 15 + serde = { version = "1.0.219", features = ["serde_derive", "derive"] }
+2 -1
crates/firecracker-prepare/src/lib.rs
··· 2 2 3 3 use anyhow::Result; 4 4 use owo_colors::OwoColorize; 5 + use serde::{Deserialize, Serialize}; 5 6 6 7 use crate::command::{run_command, run_command_with_stdout_inherit}; 7 8 ··· 13 14 14 15 pub const GUEST_IP: &str = "172.16.0.2"; 15 16 16 - #[derive(Clone, Copy, PartialEq)] 17 + #[derive(Clone, Copy, PartialEq, Serialize, Deserialize, Debug)] 17 18 pub enum Distro { 18 19 Debian, 19 20 Alpine,
+2 -1
crates/firecracker-up/Cargo.toml
··· 1 1 [package] 2 2 name = "fireup" 3 - version = "0.2.0" 3 + version = "0.3.0" 4 4 authors.workspace = true 5 5 edition.workspace = true 6 6 license.workspace = true ··· 11 11 firecracker-vm = { path = "../firecracker-vm" } 12 12 firecracker-prepare = { path = "../firecracker-prepare" } 13 13 firecracker-process = { path = "../firecracker-process" } 14 + fire-config = { path = "../fire-config" } 14 15 clap = "4.5.41" 15 16 owo-colors = "4.2.2" 16 17 glob = "0.3.2"
+13
crates/firecracker-up/src/cmd/init.rs
··· 1 + use anyhow::Error; 2 + use fire_config::init_config; 3 + use owo_colors::OwoColorize; 4 + 5 + pub fn init() -> Result<(), Error> { 6 + init_config()?; 7 + println!( 8 + "[+] Firecracker MicroVM configuration initialized successfully: {} created 🎉", 9 + "`fire.toml`".cyan() 10 + ); 11 + println!("[✓] Start your MicroVM by running: {}", "fireup".green()); 12 + Ok(()) 13 + }
+1
crates/firecracker-up/src/cmd/mod.rs
··· 1 1 pub mod down; 2 + pub mod init; 2 3 pub mod logs; 3 4 pub mod reset; 4 5 pub mod ssh;
+6
crates/firecracker-up/src/cmd/up.rs
··· 1 1 use std::thread; 2 2 3 3 use anyhow::Error; 4 + use fire_config::read_config; 4 5 use firecracker_vm::types::VmOptions; 5 6 use owo_colors::OwoColorize; 6 7 ··· 8 9 9 10 pub fn up(options: VmOptions) -> Result<(), Error> { 10 11 check_kvm_support()?; 12 + 13 + let options = match read_config() { 14 + Ok(config) => VmOptions::from(config), 15 + Err(_) => options.clone(), 16 + }; 11 17 12 18 firecracker_process::start()?; 13 19
+7 -1
crates/firecracker-up/src/main.rs
··· 3 3 use firecracker_vm::types::VmOptions; 4 4 use owo_colors::OwoColorize; 5 5 6 - use crate::cmd::{down::down, logs::logs, reset::reset, ssh::ssh, status::status, up::up}; 6 + use crate::cmd::{ 7 + down::down, init::init, logs::logs, reset::reset, ssh::ssh, status::status, up::up, 8 + }; 7 9 8 10 pub mod cmd; 9 11 pub mod command; ··· 26 28 Command::new("fireup") 27 29 .version(env!("CARGO_PKG_VERSION")) 28 30 .about(&banner) 31 + .subcommand( 32 + Command::new("init").about("Create a new Firecracker MicroVM configuration: fire.toml"), 33 + ) 29 34 .subcommand( 30 35 Command::new("up") 31 36 .arg(arg!(--debian "Prepare Debian MicroVM").default_value("false")) ··· 78 83 let matches = cli().get_matches(); 79 84 80 85 match matches.subcommand() { 86 + Some(("init", _)) => init()?, 81 87 Some(("up", args)) => { 82 88 let vcpu = matches 83 89 .get_one::<String>("vcpu")
+1
crates/firecracker-vm/Cargo.toml
··· 8 8 9 9 [dependencies] 10 10 firecracker-prepare = { path = "../firecracker-prepare" } 11 + fire-config = { path = "../fire-config" } 11 12 anyhow = "1.0.98" 12 13 dirs = "6.0.0" 13 14 glob = "0.3.2"
+18
crates/firecracker-vm/src/types.rs
··· 1 + use fire_config::FireConfig; 1 2 use firecracker_prepare::Distro; 2 3 3 4 #[derive(Default, Clone)] ··· 11 12 pub vmlinux: Option<String>, 12 13 pub rootfs: Option<String>, 13 14 pub bootargs: Option<String>, 15 + } 16 + 17 + impl From<FireConfig> for VmOptions { 18 + fn from(config: FireConfig) -> Self { 19 + let vm = config.vm; 20 + VmOptions { 21 + debian: Some(config.distro == Distro::Debian), 22 + alpine: Some(config.distro == Distro::Alpine), 23 + ubuntu: Some(config.distro == Distro::Ubuntu), 24 + nixos: Some(config.distro == Distro::NixOS), 25 + vcpu: vm.vcpu.unwrap_or(num_cpus::get() as u16), 26 + memory: vm.memory.unwrap_or(512), 27 + vmlinux: vm.vmlinux, 28 + rootfs: vm.rootfs, 29 + bootargs: vm.boot_args, 30 + } 31 + } 14 32 } 15 33 16 34 impl Into<Distro> for VmOptions {
+1 -1
dist/debian/amd64/DEBIAN/control
··· 1 1 Package: fireup 2 - Version: 0.2.0 2 + Version: 0.3.0 3 3 Section: utils 4 4 Priority: optional 5 5 Architecture: amd64
+1 -1
dist/debian/arm64/DEBIAN/control
··· 1 1 Package: fireup 2 - Version: 0.2.0 2 + Version: 0.3.0 3 3 Section: utils 4 4 Priority: optional 5 5 Architecture: arm64
+1 -1
flake.nix
··· 40 40 inherit src; 41 41 42 42 pname = "fireup"; 43 - version = "0.2.0"; 43 + version = "0.3.0"; 44 44 cargoExtraArgs = "--package=fireup"; 45 45 46 46 buildInputs = [