From 5fc42aa4dfde8608320e06192a7d5ec428834bb5 Mon Sep 17 00:00:00 2001 From: Matej Hrica Date: Wed, 8 Nov 2023 11:42:06 +0100 Subject: [PATCH] Add support for starting VMs with passt network This required adding another field to the config. This is done by migrating the old config to a newer version. We are backwards compatible on configuration but not forwards compatible (older versions of krunvm will not be able to use the config from this version) If we want forward compatibility, I feel like we need to ditch the confy crate. Signed-off-by: Matej Hrica --- Cargo.toml | 2 +- docs/krunvm-changevm.1.txt | 2 + docs/krunvm-config.1.txt | 3 + docs/krunvm-create.1.txt | 2 + docs/krunvm.1.txt | 11 ++- src/bindings.rs | 1 + src/commands/changevm.rs | 13 +++- src/commands/config.rs | 17 ++++- src/commands/create.rs | 10 ++- src/commands/delete.rs | 5 +- src/commands/list.rs | 3 +- src/commands/start.rs | 85 +++++++++++++++++++++-- src/config/migrate.rs | 138 +++++++++++++++++++++++++++++++++++++ src/config/mod.rs | 20 ++++++ src/config/v1.rs | 37 ++++++++++ src/config/v2.rs | 98 ++++++++++++++++++++++++++ src/main.rs | 42 ++--------- src/utils.rs | 3 +- 18 files changed, 433 insertions(+), 59 deletions(-) create mode 100644 src/config/migrate.rs create mode 100644 src/config/mod.rs create mode 100644 src/config/v1.rs create mode 100644 src/config/v2.rs diff --git a/Cargo.toml b/Cargo.toml index dec77b6..80451e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,4 +15,4 @@ libc = "0.2.82" serde = "1.0.120" serde_derive = "1.0.120" text_io = "0.1.8" -nix = {version = "0.27.1", features = ["socket", "fs"]} +nix = {version = "0.27.1", features = ["socket", "fs"]} \ No newline at end of file diff --git a/docs/krunvm-changevm.1.txt b/docs/krunvm-changevm.1.txt index 4f14e90..e00838d 100644 --- a/docs/krunvm-changevm.1.txt +++ b/docs/krunvm-changevm.1.txt @@ -61,6 +61,8 @@ host visible in the guest. An empty string ("") tells krunvm to not set a working directory explicitly, letting libkrun decide which one should be set. +*--net* _NETWORK_MODE_:: + Configures the network connection mode. Supported modes are either PASST or TSI. SEE ALSO -------- diff --git a/docs/krunvm-config.1.txt b/docs/krunvm-config.1.txt index 168ed91..ae3b416 100644 --- a/docs/krunvm-config.1.txt +++ b/docs/krunvm-config.1.txt @@ -34,6 +34,9 @@ OPTIONS Sets the default mount of RAM, in MiB, that will be configured for newly created microVMs. +*--net* _NETWORK_MODE_:: + Sets the default network connection mode, that will be configured for + newly created microVMs. Supported modes are PASST or TSI. SEE ALSO -------- diff --git a/docs/krunvm-create.1.txt b/docs/krunvm-create.1.txt index d63f3f8..488bcb4 100644 --- a/docs/krunvm-create.1.txt +++ b/docs/krunvm-create.1.txt @@ -53,6 +53,8 @@ host visible in the guest. An empty string ("") tells krunvm to not set a working directory explicitly, letting libkrun decide which one should be set. +*--net* _NETWORK_MODE_:: + Set the network connection mode. Supported modes are either PASST or TSI. SEE ALSO -------- diff --git a/docs/krunvm.1.txt b/docs/krunvm.1.txt index 8a956e8..e04ea82 100644 --- a/docs/krunvm.1.txt +++ b/docs/krunvm.1.txt @@ -29,10 +29,15 @@ microVM and exposing ports from the guest to the host (and the networks connected to it). Networking to the guest running in the microVM is provided by -libkrun's TSI (Transparent Socket Impersonation), enabling a seamless -experience that doesn't require network bridges nor other explicit -network configuration. +either libkrun's TSI (Transparent Socket Impersonation) or PASST. +TSI enables a seamless experience that doesn't require network bridges nor other explicit +network configuration. It only supports impersonating AF_INET SOCK_DGRAM and SOCK_STREAM sockets. +This implies it's not possible to communicate outside the VM with raw sockets. + +PASST uses virtio-net guest device and sends all traffic to a passt subprocess. +Support of network protocols is therefore dependent on what passt supports. +Note that currently you need to run a DHCP client in the guest to get an IP address. GLOBAL OPTIONS -------------- diff --git a/src/bindings.rs b/src/bindings.rs index 41dc52c..b8676e3 100644 --- a/src/bindings.rs +++ b/src/bindings.rs @@ -13,6 +13,7 @@ extern "C" { pub fn krun_set_mapped_volumes(ctx: u32, mapped_volumes: *const *const c_char) -> i32; pub fn krun_set_port_map(ctx: u32, port_map: *const *const c_char) -> i32; pub fn krun_set_workdir(ctx: u32, workdir_path: *const c_char) -> i32; + pub fn krun_set_passt_fd(ctx: u32, fd: c_int) -> i32; pub fn krun_set_exec( ctx: u32, exec_path: *const c_char, diff --git a/src/commands/changevm.rs b/src/commands/changevm.rs index 5cd365b..9b7e9d7 100644 --- a/src/commands/changevm.rs +++ b/src/commands/changevm.rs @@ -4,8 +4,8 @@ use clap::Args; use std::collections::HashMap; +use crate::config::{KrunvmConfig, NetworkMode}; use crate::utils::{path_pairs_to_hash_map, port_pairs_to_hash_map, PathPair, PortPair}; -use crate::{KrunvmConfig, APP_NAME}; use super::list::printvm; @@ -46,6 +46,10 @@ pub struct ChangeVmCmd { /// Port(s) in format "host_port:guest_port" to be exposed to the host #[arg(long = "port")] ports: Vec, + + /// Set the network connection mode for the microVM + #[arg(long)] + net: Option, } impl ChangeVmCmd { @@ -130,12 +134,17 @@ impl ChangeVmCmd { cfg_changed = true; } + if let Some(network_mode) = self.net { + vmcfg.network_mode = network_mode; + cfg_changed = true; + } + println!(); printvm(vmcfg); println!(); if cfg_changed { - confy::store(APP_NAME, &cfg).unwrap(); + crate::config::save(cfg).unwrap(); } } } diff --git a/src/commands/config.rs b/src/commands/config.rs index ab09d34..1e3b372 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -1,7 +1,7 @@ // Copyright 2021 Red Hat, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::{KrunvmConfig, APP_NAME}; +use crate::config::{KrunvmConfig, NetworkMode}; use clap::Args; /// Configure global values @@ -18,6 +18,10 @@ pub struct ConfigCmd { /// DNS server to use in the microVM #[arg(long)] dns: Option, + + /// Default network connection mode to use + #[arg(long)] + net: Option, } impl ConfigCmd { @@ -47,11 +51,18 @@ impl ConfigCmd { cfg_changed = true; } + if let Some(network_mode) = self.net { + if network_mode != cfg.default_network_mode { + cfg.default_network_mode = network_mode; + cfg_changed = true; + } + } + if cfg_changed { - confy::store(APP_NAME, &cfg).unwrap(); + crate::config::save(cfg).unwrap(); } - println!("Global configuration:"); + println!("Global config:"); println!( "Default number of CPUs for newly created VMs: {}", cfg.default_cpus diff --git a/src/commands/create.rs b/src/commands/create.rs index 4040983..60d7ae0 100644 --- a/src/commands/create.rs +++ b/src/commands/create.rs @@ -1,6 +1,8 @@ // Copyright 2021 Red Hat, Inc. // SPDX-License-Identifier: Apache-2.0 +use crate::config::{KrunvmConfig, NetworkMode, VmConfig}; +use crate::APP_NAME; use clap::Args; use std::fs; use std::io::Write; @@ -12,8 +14,6 @@ use crate::utils::{ get_buildah_args, mount_container, path_pairs_to_hash_map, port_pairs_to_hash_map, umount_container, BuildahCommand, PathPair, PortPair, }; -use crate::{KrunvmConfig, VmConfig, APP_NAME}; - #[cfg(target_os = "macos")] const KRUNVM_ROSETTA_FILE: &str = ".krunvm-rosetta"; @@ -51,6 +51,10 @@ pub struct CreateCmd { #[arg(long = "port")] ports: Vec, + /// Network connection mode to use + #[arg(long)] + net: Option, + /// Create a x86_64 microVM even on an Aarch64 host #[arg(short, long)] #[cfg(target_os = "macos")] @@ -68,6 +72,7 @@ impl CreateCmd { let mapped_ports = port_pairs_to_hash_map(self.ports); let image = self.image; let name = self.name; + let network_mode = self.net.unwrap_or_else(|| cfg.default_network_mode.clone()); if let Some(ref name) = name { if cfg.vmconfig_map.contains_key(name) { @@ -160,6 +165,7 @@ https://threedots.ovh/blog/2022/06/quick-look-at-rosetta-on-linux/ workdir: workdir.to_string(), mapped_volumes, mapped_ports, + network_mode, }; let rootfs = mount_container(cfg, &vmcfg).unwrap(); diff --git a/src/commands/delete.rs b/src/commands/delete.rs index 2f4c54a..d027a92 100644 --- a/src/commands/delete.rs +++ b/src/commands/delete.rs @@ -1,7 +1,8 @@ // Copyright 2021 Red Hat, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::{KrunvmConfig, APP_NAME}; +use crate::config; +use crate::config::KrunvmConfig; use clap::Args; use crate::utils::{remove_container, umount_container}; @@ -26,6 +27,6 @@ impl DeleteCmd { umount_container(cfg, &vmcfg).unwrap(); remove_container(cfg, &vmcfg).unwrap(); - confy::store(APP_NAME, &cfg).unwrap(); + config::save(cfg).unwrap() } } diff --git a/src/commands/list.rs b/src/commands/list.rs index 791943c..7ca03cf 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -1,7 +1,7 @@ // Copyright 2021 Red Hat, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::{KrunvmConfig, VmConfig}; +use crate::config::{KrunvmConfig, VmConfig}; use clap::Args; /// List microVMs @@ -33,6 +33,7 @@ pub fn printvm(vm: &VmConfig) { println!(" DNS server: {}", vm.dns); println!(" Buildah container: {}", vm.container); println!(" Workdir: {}", vm.workdir); + println!(" Network mode: {:?}", vm.network_mode); println!(" Mapped volumes: {:?}", vm.mapped_volumes); println!(" Mapped ports: {:?}", vm.mapped_ports); } diff --git a/src/commands/start.rs b/src/commands/start.rs index 2d67423..11641a5 100644 --- a/src/commands/start.rs +++ b/src/commands/start.rs @@ -2,18 +2,29 @@ // SPDX-License-Identifier: Apache-2.0 use clap::Args; -use libc::c_char; +use libc::{c_char, c_int}; +use nix::errno::Errno; +use nix::sys::socket::{socketpair, AddressFamily, SockFlag, SockType}; +use std::collections::HashMap; use std::ffi::CString; + use std::fs::File; #[cfg(target_os = "linux")] use std::io::{Error, ErrorKind}; + +use std::os::fd::{IntoRawFd, OwnedFd}; use std::os::unix::io::AsRawFd; + #[cfg(target_os = "macos")] use std::path::Path; +use std::process::Stdio; + +use nix::fcntl::{fcntl, FcntlArg, FdFlag}; use crate::bindings; +use crate::bindings::krun_set_passt_fd; +use crate::config::{KrunvmConfig, NetworkMode, VmConfig}; use crate::utils::{mount_container, umount_container}; -use crate::{KrunvmConfig, VmConfig}; #[derive(Args, Debug)] /// Start an existing microVM @@ -36,6 +47,49 @@ pub struct StartCmd { mem: Option, // TODO: implement or remove this } +fn start_passt(mapped_ports: &HashMap) -> Result { + let (passt_fd, krun_fd) = socketpair( + AddressFamily::Unix, + SockType::Stream, + None, + SockFlag::empty(), + ) + .map_err(|e| { + eprint!("Failed to create socket pair for passt: {e}"); + })?; + + if let Err(e) = fcntl(krun_fd.as_raw_fd(), FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)) { + eprint!("Failed to set FD_CLOEXEC: {e}"); + } + + let mut cmd = std::process::Command::new("passt"); + cmd.arg("-q") + .arg("-f") + .arg("-F") + .arg(passt_fd.as_raw_fd().to_string()); + + if !mapped_ports.is_empty() { + let comma_separated_ports = mapped_ports + .iter() + .map(|(host_port, guest_port)| format!("{}:{}", host_port, guest_port)) + .collect::>() + .join(","); + + cmd.arg("-t").arg(comma_separated_ports); + } + + cmd.stdout(Stdio::null()) + .stderr(Stdio::null()) + .stdin(Stdio::null()); + + if let Err(e) = cmd.spawn() { + eprintln!("Failed to start passt: {e}"); + return Err(()); + } + + Ok(krun_fd) +} + impl StartCmd { pub fn run(self, cfg: &KrunvmConfig) { let vmcfg = match cfg.vmconfig_map.get(&self.name) { @@ -152,10 +206,29 @@ unsafe fn exec_vm(vmcfg: &VmConfig, rootfs: &str, cmd: Option<&str>, args: Vec { + let ret = bindings::krun_set_port_map(ctx, ps.as_ptr()); + if ret < 0 { + println!("Error setting VM port map"); + std::process::exit(-1); + } + } + NetworkMode::Passt => { + let Ok(passt_fd) = start_passt(&vmcfg.mapped_ports) else { + std::process::exit(-1); + }; + let ret = krun_set_passt_fd(ctx, passt_fd.into_raw_fd() as c_int); + if ret < 0 { + let errno = Errno::from_i32(-ret); + if errno == Errno::ENOTSUP { + println!("Failed to set passt fd: your libkrun build does not support virtio-net/passt mode."); + } else { + println!("Failed to set passt fd: {}", errno); + } + std::process::exit(-1); + } + } } if !vmcfg.workdir.is_empty() { diff --git a/src/config/migrate.rs b/src/config/migrate.rs new file mode 100644 index 0000000..f3d8d8b --- /dev/null +++ b/src/config/migrate.rs @@ -0,0 +1,138 @@ +use crate::config::{v1, v2}; +use confy::ConfyError; + +pub fn migrate_and_load_impl( + load_v2: impl FnOnce() -> Result, + load_v1: impl FnOnce() -> Result, + save_v2: impl FnOnce(&v2::KrunvmConfig) -> Result<(), ()>, +) -> Result { + fn check_version(got: u8, expected: u8) -> Result<(), ()> { + if expected != got { + eprintln!( + "Invalid config version number {} expected {}", + got, expected + ); + Err(()) + } else { + Ok(()) + } + } + + let v2_load_err = match load_v2() { + Ok(conf) => { + check_version(conf.version, 2)?; + return Ok(conf); + } + Err(e) => e, + }; + + let v1_load_err = match load_v1() { + Ok(cfg) => { + check_version(cfg.version, 1)?; + let v2_config = cfg.into(); + save_v2(&v2_config)?; + return Ok(v2_config); + } + Err(e) => e, + }; + + eprintln!("Failed to load config: "); + eprintln!("Tried to load as as v2 config, got error: {v2_load_err}"); + eprintln!("Tried to load as as v1 config, got error: {v1_load_err}"); + Err(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::config::NetworkMode; + use std::collections::HashMap; + + #[test] + fn load_without_migrate() { + let cfg = v2::KrunvmConfig { + default_dns: "8.8.8.8".into(), + ..v2::KrunvmConfig::default() + }; + + let returned_cfg = migrate_and_load_impl( + || Ok(cfg.clone()), + || panic!("Loading v1 should not be attempted"), + |_| panic!("Migration should not occur"), + ) + .unwrap(); + assert_eq!(returned_cfg, cfg); + } + + #[test] + fn load_migrating_to_v2() { + let v1_vms = [( + "fedora".to_string(), + v1::VmConfig { + name: "fedora".to_string(), + mapped_ports: Default::default(), + cpus: 2, + dns: "1.1.1.1".to_string(), + mapped_volumes: Default::default(), + workdir: "/".to_string(), + container: "fedora".to_string(), + mem: 8192, + }, + )]; + + let v1_cfg = v1::KrunvmConfig { + default_dns: "8.8.8.8".into(), + vmconfig_map: HashMap::from(v1_vms), + ..v1::KrunvmConfig::default() + }; + + let result_v2_vms = [( + "fedora".to_string(), + v2::VmConfig { + name: "fedora".to_string(), + mapped_ports: Default::default(), + cpus: 2, + dns: "1.1.1.1".to_string(), + mapped_volumes: Default::default(), + workdir: "/".to_string(), + container: "fedora".to_string(), + mem: 8192, + network_mode: NetworkMode::Tsi, + }, + )]; + + let result_v2_cfg = v2::KrunvmConfig { + default_dns: "8.8.8.8".into(), + vmconfig_map: HashMap::from(result_v2_vms), + default_network_mode: NetworkMode::Tsi, + ..v2::KrunvmConfig::default() + }; + + let mut load_v2_called = false; + let mut load_v1_called = false; + let mut save_called = false; + + let returned_cfg = migrate_and_load_impl( + || { + load_v2_called = true; + Err(ConfyError::BadConfigDirectoryStr) + }, + || { + load_v1_called = true; + Ok(v1_cfg) + }, + |migrated| { + save_called = true; + assert_eq!(migrated, &result_v2_cfg); + Ok(()) + }, + ) + .unwrap(); + + assert!(load_v2_called, "Load v2 must be called"); + assert!(load_v1_called, "Load v1 must be called"); + assert!(save_called, "Save must be called"); + + assert_eq!(returned_cfg, result_v2_cfg); + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..f50873c --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,20 @@ +mod migrate; +mod v1; +mod v2; + +use crate::APP_NAME; + +use crate::config::migrate::migrate_and_load_impl; +pub use v2::{KrunvmConfig, NetworkMode, VmConfig}; + +pub fn save(cfg: &KrunvmConfig) -> Result<(), ()> { + confy::store(APP_NAME, cfg).map_err(|e| eprintln!("Failed to load config: {e}")) +} + +pub fn load() -> Result { + migrate_and_load_impl( + || confy::load::(APP_NAME), + || confy::load::(APP_NAME), + save, + ) +} diff --git a/src/config/v1.rs b/src/config/v1.rs new file mode 100644 index 0000000..877f034 --- /dev/null +++ b/src/config/v1.rs @@ -0,0 +1,37 @@ +use serde_derive::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Debug, Serialize, Deserialize)] +pub struct KrunvmConfig { + pub version: u8, + pub default_cpus: u32, + pub default_mem: u32, + pub default_dns: String, + pub storage_volume: String, + pub vmconfig_map: HashMap, +} + +impl Default for KrunvmConfig { + fn default() -> KrunvmConfig { + KrunvmConfig { + version: 1, + default_cpus: 2, + default_mem: 1024, + default_dns: "1.1.1.1".to_string(), + storage_volume: String::new(), + vmconfig_map: HashMap::new(), + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct VmConfig { + pub name: String, + pub cpus: u32, + pub mem: u32, + pub container: String, + pub workdir: String, + pub dns: String, + pub mapped_volumes: HashMap, + pub mapped_ports: HashMap, +} diff --git a/src/config/v2.rs b/src/config/v2.rs new file mode 100644 index 0000000..c48e3d7 --- /dev/null +++ b/src/config/v2.rs @@ -0,0 +1,98 @@ +use crate::config::v1; + +use serde_derive::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::str::FromStr; + +#[derive(Clone, Serialize, Deserialize, Debug, Default, Eq, PartialEq)] +pub enum NetworkMode { + #[default] + Tsi, + Passt, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub struct KrunvmConfig { + pub version: u8, + pub default_cpus: u32, + pub default_mem: u32, + pub default_dns: String, + pub default_network_mode: NetworkMode, + pub storage_volume: String, + pub vmconfig_map: HashMap, +} + +impl Default for KrunvmConfig { + fn default() -> KrunvmConfig { + KrunvmConfig { + version: 2, + default_cpus: 2, + default_mem: 1024, + default_dns: "1.1.1.1".to_string(), + default_network_mode: NetworkMode::default(), + storage_volume: String::new(), + vmconfig_map: HashMap::new(), + } + } +} + +impl From for KrunvmConfig { + fn from(old: v1::KrunvmConfig) -> Self { + KrunvmConfig { + version: 2, + default_cpus: old.default_cpus, + default_mem: old.default_mem, + default_dns: old.default_dns, + default_network_mode: NetworkMode::default(), + storage_volume: old.storage_volume, + vmconfig_map: old + .vmconfig_map + .into_iter() + .map(|(key, value)| (key, value.into())) + .collect(), + } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct VmConfig { + pub name: String, + pub cpus: u32, + pub mem: u32, + pub container: String, + pub workdir: String, + pub dns: String, + pub network_mode: NetworkMode, + pub mapped_volumes: HashMap, + pub mapped_ports: HashMap, +} + +impl From for VmConfig { + fn from(old: v1::VmConfig) -> Self { + VmConfig { + name: old.name, + cpus: old.cpus, + mem: old.mem, + container: old.container, + workdir: old.workdir, + dns: old.dns, + mapped_volumes: old.mapped_volumes, + mapped_ports: old.mapped_ports, + network_mode: NetworkMode::default(), + } + } +} + +impl FromStr for NetworkMode { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + if s.eq_ignore_ascii_case("tsi") { + Ok(NetworkMode::Tsi) + } else if s.eq_ignore_ascii_case("passt") { + Ok(NetworkMode::Passt) + } else { + Err("Invalid network mode") + } + } +} diff --git a/src/main.rs b/src/main.rs index 0531136..c35d619 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,60 +1,26 @@ // Copyright 2021 Red Hat, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::collections::HashMap; #[cfg(target_os = "macos")] use std::fs::File; #[cfg(target_os = "macos")] use std::io::{self, Read, Write}; use crate::commands::{ChangeVmCmd, ConfigCmd, CreateCmd, DeleteCmd, ListCmd, StartCmd}; +#[cfg(target_os = "macos")] +use crate::config::KrunvmConfig; use clap::{Parser, Subcommand}; -use serde_derive::{Deserialize, Serialize}; #[cfg(target_os = "macos")] use text_io::read; #[allow(unused)] mod bindings; mod commands; +mod config; mod utils; const APP_NAME: &str = "krunvm"; -#[derive(Default, Debug, Serialize, Deserialize)] -pub struct VmConfig { - name: String, - cpus: u32, - mem: u32, - container: String, - workdir: String, - dns: String, - mapped_volumes: HashMap, - mapped_ports: HashMap, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct KrunvmConfig { - version: u8, - default_cpus: u32, - default_mem: u32, - default_dns: String, - storage_volume: String, - vmconfig_map: HashMap, -} - -impl Default for KrunvmConfig { - fn default() -> KrunvmConfig { - KrunvmConfig { - version: 1, - default_cpus: 2, - default_mem: 1024, - default_dns: "1.1.1.1".to_string(), - storage_volume: String::new(), - vmconfig_map: HashMap::new(), - } - } -} - #[cfg(target_os = "macos")] fn check_case_sensitivity(volume: &str) -> Result { let first_path = format!("{}/krunvm_test", volume); @@ -167,7 +133,7 @@ enum Command { } fn main() { - let mut cfg: KrunvmConfig = confy::load(APP_NAME).unwrap(); + let mut cfg = config::load().unwrap(); let cli_args = Cli::parse(); #[cfg(target_os = "macos")] diff --git a/src/utils.rs b/src/utils.rs index 8b3d5b2..003f621 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,12 +1,13 @@ // Copyright 2021 Red Hat, Inc. // SPDX-License-Identifier: Apache-2.0 +use crate::APP_NAME; use std::collections::HashMap; use std::path::Path; use std::process::Command; use std::str::FromStr; -use crate::{KrunvmConfig, VmConfig, APP_NAME}; +use crate::config::{KrunvmConfig, VmConfig}; pub enum BuildahCommand { From,