Skip to main content

mvm_core/
config.rs

1/// Default Firecracker version, overridable at build time via `MVM_FC_VERSION` env var.
2pub const FC_VERSION_DEFAULT: &str = match option_env!("MVM_FC_VERSION") {
3    Some(v) => v,
4    None => "v1.14.1",
5};
6
7pub const ARCH: &str = "aarch64";
8
9/// Normalize Firecracker version strings to a canonical form (e.g., "Firecracker v1.14.1" -> "v1.14.1").
10pub fn normalize_fc_version(raw: &str) -> String {
11    // Capture the last semantic version (v?MAJOR.MINOR[.PATCH])
12    let re = regex::Regex::new(r"(?:v)?\d+\.\d+(?:\.\d+)?").expect("valid regex");
13    let candidate = re
14        .captures_iter(raw)
15        .last()
16        .map(|c| c.get(0).unwrap().as_str().to_string())
17        .unwrap_or_else(|| FC_VERSION_DEFAULT.to_string());
18
19    if candidate.starts_with('v') {
20        candidate
21    } else {
22        format!("v{}", candidate)
23    }
24}
25
26/// Get the effective Firecracker version.
27/// Priority: runtime env `MVM_FC_VERSION` > compile-time default.
28/// The CLI `--fc-version` flag sets `MVM_FC_VERSION` before calling this.
29pub fn fc_version() -> String {
30    let raw = std::env::var("MVM_FC_VERSION").unwrap_or_else(|_| FC_VERSION_DEFAULT.to_string());
31    normalize_fc_version(&raw)
32}
33
34/// Short Firecracker version for S3 asset paths (e.g., "v1.13").
35/// Strips the patch component from the effective version.
36pub fn fc_version_short() -> String {
37    let full = fc_version();
38    let trimmed = full.trim_start_matches('v');
39    let parts = trimmed.split('.').collect::<Vec<_>>();
40    if parts.len() >= 2 {
41        format!("v{}.{}", parts[0], parts[1])
42    } else {
43        full
44    }
45}
46
47/// Root data directory for mvm dev-tool state.
48///
49/// Resolution order:
50///   1. `MVM_DATA_DIR` env var (explicit override)
51///   2. `$HOME/.mvm`
52///
53/// This is a user-owned directory — no sudo required.
54/// Fleet orchestration state (tenants, pools, instances) uses `/var/lib/mvm/`
55/// and is managed by mvmd with appropriate permissions.
56pub fn mvm_data_dir() -> String {
57    if let Ok(d) = std::env::var("MVM_DATA_DIR")
58        && !d.is_empty()
59    {
60        return d;
61    }
62    let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
63    format!("{}/.mvm", home)
64}
65
66/// Check if running in production mode (MVM_PRODUCTION=1).
67pub fn is_production_mode() -> bool {
68    std::env::var("MVM_PRODUCTION")
69        .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
70        .unwrap_or(false)
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn test_not_production_by_default() {
79        let _ = is_production_mode();
80    }
81
82    #[test]
83    fn test_fc_version_default() {
84        // Without runtime env override, should return the compiled-in default
85        let v = fc_version();
86        assert!(v.starts_with('v'), "FC version should start with 'v'");
87        assert!(v.contains('.'), "FC version should contain a dot");
88    }
89
90    #[test]
91    fn test_fc_version_short() {
92        let short = fc_version_short();
93        assert!(short.starts_with('v'));
94        // Should have exactly one dot (major.minor)
95        assert_eq!(short.matches('.').count(), 1);
96    }
97
98    #[test]
99    fn normalize_firecracker_banner() {
100        let raw = "Firecracker v1.14.1";
101        assert_eq!(normalize_fc_version(raw), "v1.14.1");
102    }
103
104    #[test]
105    fn normalize_with_leading_v() {
106        let raw = "v1.14.1";
107        assert_eq!(normalize_fc_version(raw), "v1.14.1");
108    }
109
110    #[test]
111    fn normalize_without_v() {
112        let raw = "1.14.1";
113        assert_eq!(normalize_fc_version(raw), "v1.14.1");
114    }
115
116    #[test]
117    fn normalize_minor_only() {
118        let raw = "Firecracker v1.14";
119        assert_eq!(normalize_fc_version(raw), "v1.14");
120        // short should remain the same when no patch component
121        assert_eq!(fc_version_short_from(raw), "v1.14");
122    }
123
124    #[test]
125    fn normalize_garbage_falls_back() {
126        let raw = "nonsense";
127        assert_eq!(normalize_fc_version(raw), FC_VERSION_DEFAULT);
128    }
129
130    // Helper to test short derivation with a temp env override.
131    fn fc_version_short_from(raw: &str) -> String {
132        // Env mutation is unsafe in Rust 2024; limit scope to this helper.
133        unsafe { std::env::set_var("MVM_FC_VERSION", raw) };
134        let short = fc_version_short();
135        unsafe { std::env::remove_var("MVM_FC_VERSION") };
136        short
137    }
138}