diff --git a/Cargo.lock b/Cargo.lock index 76aec36..509f15c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -161,7 +161,7 @@ dependencies = [ [[package]] name = "krunvm" -version = "0.2.2" +version = "0.2.3" dependencies = [ "clap", "confy", diff --git a/Cargo.toml b/Cargo.toml index 6d0765b..a345f02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "krunvm" -version = "0.2.2" +version = "0.2.3" authors = ["Sergio Lopez "] description = "Create microVMs from OCI images" repository = "https://github.com/containers/krunvm" diff --git a/src/create.rs b/src/create.rs index a3c3dbd..8c71a1f 100644 --- a/src/create.rs +++ b/src/create.rs @@ -3,6 +3,8 @@ use std::fs; use std::io::Write; +#[cfg(target_os = "macos")] +use std::path::Path; use std::process::Command; use super::utils::{ @@ -11,6 +13,9 @@ use super::utils::{ }; use crate::{ArgMatches, KrunvmConfig, VmConfig, APP_NAME}; +#[cfg(target_os = "macos")] +const KRUNVM_ROSETTA_FILE: &str = ".krunvm-rosetta"; + fn fix_resolv_conf(rootfs: &str, dns: &str) -> Result<(), std::io::Error> { let resolvconf_dir = format!("{}/etc/", rootfs); fs::create_dir_all(resolvconf_dir)?; @@ -62,7 +67,7 @@ fn export_container_config( } pub fn create(cfg: &mut KrunvmConfig, matches: &ArgMatches) { - let cpus = match matches.value_of("cpus") { + let mut cpus = match matches.value_of("cpus") { Some(c) => match c.parse::() { Err(_) => { println!("Invalid value for \"cpus\""); @@ -114,6 +119,47 @@ pub fn create(cfg: &mut KrunvmConfig, matches: &ArgMatches) { } let mut args = get_buildah_args(cfg, BuildahCommand::From); + + #[cfg(target_os = "macos")] + let force_x86 = matches.is_present("x86"); + + #[cfg(target_os = "macos")] + if force_x86 { + let home = match std::env::var("HOME") { + Err(e) => { + println!("Error reading \"HOME\" enviroment variable: {}", e); + std::process::exit(-1); + } + Ok(home) => home, + }; + + let path = format!("{}/{}", home, KRUNVM_ROSETTA_FILE); + if !Path::new(&path).is_file() { + println!( + " +To use Rosetta for Linux you need to create the file... + +{} + +...with the contents that the \"rosetta\" binary expects to be served from +its specific ioctl. + +For more information, please refer to this post: +https://threedots.ovh/blog/2022/06/quick-look-at-rosetta-on-linux/ +", + path + ); + std::process::exit(-1); + } + + if cpus != 1 { + println!("x86 microVMs on Aarch64 are restricted to 1 CPU"); + cpus = 1; + } + args.push("--arch".to_string()); + args.push("x86_64".to_string()); + } + args.push(image.to_string()); let output = match Command::new("buildah") @@ -161,6 +207,10 @@ pub fn create(cfg: &mut KrunvmConfig, matches: &ArgMatches) { let rootfs = mount_container(cfg, &vmcfg).unwrap(); export_container_config(cfg, &rootfs, image).unwrap(); fix_resolv_conf(&rootfs, dns).unwrap(); + #[cfg(target_os = "macos")] + if force_x86 { + _ = fs::create_dir(format!("{}/.rosetta", rootfs)); + } umount_container(cfg, &vmcfg).unwrap(); cfg.vmconfig_map.insert(name.clone(), vmcfg); diff --git a/src/main.rs b/src/main.rs index 58d0428..4fa6a84 100644 --- a/src/main.rs +++ b/src/main.rs @@ -246,65 +246,6 @@ fn main() { .takes_value(true), ), ) - .subcommand( - App::new("create") - .about("Create a new microVM") - .arg( - Arg::with_name("cpus") - .long("cpus") - .help("Number of vCPUs") - .takes_value(true), - ) - .arg( - Arg::with_name("mem") - .long("mem") - .help("Amount of RAM in MiB") - .takes_value(true), - ) - .arg( - Arg::with_name("dns") - .long("dns") - .help("DNS server to use in the microVM") - .takes_value(true), - ) - .arg( - Arg::with_name("workdir") - .long("workdir") - .short("w") - .help("Working directory inside the microVM") - .takes_value(true) - .default_value(""), - ) - .arg( - Arg::with_name("volume") - .long("volume") - .short("v") - .help("Volume in form \"host_path:guest_path\" to be exposed to the guest") - .takes_value(true) - .multiple(true) - .number_of_values(1), - ) - .arg( - Arg::with_name("port") - .long("port") - .short("p") - .help("Port in format \"host_port:guest_port\" to be exposed to the host") - .takes_value(true) - .multiple(true) - .number_of_values(1), - ) - .arg( - Arg::with_name("name") - .long("name") - .help("Assign a name to the VM") - .takes_value(true), - ) - .arg( - Arg::with_name("IMAGE") - .help("OCI image to use as template") - .required(true), - ), - ) .subcommand( App::new("delete").about("Delete an existing microVM").arg( Arg::with_name("NAME") @@ -348,6 +289,75 @@ fn main() { ), ); + let mut create = App::new("create") + .about("Create a new microVM") + .arg( + Arg::with_name("cpus") + .long("cpus") + .help("Number of vCPUs") + .takes_value(true), + ) + .arg( + Arg::with_name("mem") + .long("mem") + .help("Amount of RAM in MiB") + .takes_value(true), + ) + .arg( + Arg::with_name("dns") + .long("dns") + .help("DNS server to use in the microVM") + .takes_value(true), + ) + .arg( + Arg::with_name("workdir") + .long("workdir") + .short("w") + .help("Working directory inside the microVM") + .takes_value(true) + .default_value(""), + ) + .arg( + Arg::with_name("volume") + .long("volume") + .short("v") + .help("Volume in form \"host_path:guest_path\" to be exposed to the guest") + .takes_value(true) + .multiple(true) + .number_of_values(1), + ) + .arg( + Arg::with_name("port") + .long("port") + .short("p") + .help("Port in format \"host_port:guest_port\" to be exposed to the host") + .takes_value(true) + .multiple(true) + .number_of_values(1), + ) + .arg( + Arg::with_name("name") + .long("name") + .help("Assign a name to the VM") + .takes_value(true), + ) + .arg( + Arg::with_name("IMAGE") + .help("OCI image to use as template") + .required(true), + ); + + if cfg!(target_os = "macos") { + create = create.arg( + Arg::with_name("x86") + .long("x86") + .short("x") + .help("Create a x86_64 microVM even on an Aarch64 host"), + ); + } + + app = app.subcommand(create); + let matches = app.clone().get_matches(); #[cfg(target_os = "macos")]