mirror of https://github.com/containers/krunvm.git
Compare commits
No commits in common. "main" and "v0.2.1" have entirely different histories.
|
@ -26,17 +26,8 @@ jobs:
|
|||
- name: Install asciidoctor
|
||||
run: sudo apt-get install -y asciidoctor
|
||||
|
||||
- name: Install additional Rust rust targets
|
||||
run: rustup target add aarch64-unknown-linux-gnu aarch64-apple-darwin
|
||||
|
||||
- name: Formatting (rustfmt)
|
||||
run: cargo fmt -- --check
|
||||
|
||||
- name: Clippy x86_64-unknown-linux-gnu (all features)
|
||||
run: cargo clippy --all-features --target x86_64-unknown-linux-gnu
|
||||
|
||||
- name: Clippy aarch64-unknown-linux-gnu (all features)
|
||||
run: cargo clippy --all-features --target aarch64-unknown-linux-gnu
|
||||
|
||||
- name: Clippy aarch64-apple-darwin (all features)
|
||||
run: cargo clippy --all-features --target aarch64-apple-darwin
|
||||
- name: Clippy (all features)
|
||||
run: cargo clippy --all-targets --all-features
|
||||
|
|
|
@ -3,51 +3,12 @@
|
|||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.4"
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -62,6 +23,17 @@ version = "0.5.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
|
@ -76,9 +48,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
|||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.0"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "blake2b_simd"
|
||||
|
@ -105,50 +77,19 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.6"
|
||||
version = "2.33.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
|
||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "confy"
|
||||
version = "0.4.0"
|
||||
|
@ -210,19 +151,21 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
name = "hermit-abi"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "krunvm"
|
||||
version = "0.2.3"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"confy",
|
||||
"libc",
|
||||
"nix",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"text_io",
|
||||
|
@ -236,45 +179,24 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.149"
|
||||
version = "0.2.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
checksum = "ba4aede83fc3617411dc6993bc8c70919750c1c257c6ca6a502aed6e0e2394ae"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.69"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -322,14 +244,14 @@ checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.64",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
|
@ -342,23 +264,21 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "text_io"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cb170b4f47dc48835fbc56259c12d8963e542b05a24be2e3a1f5a6c320fd2d4"
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.8"
|
||||
|
@ -369,10 +289,10 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
|
@ -381,10 +301,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
|
@ -413,69 +333,3 @@ name = "winapi-x86_64-pc-windows-gnu"
|
|||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "krunvm"
|
||||
version = "0.2.3"
|
||||
version = "0.2.1"
|
||||
authors = ["Sergio Lopez <slp@redhat.com>"]
|
||||
description = "Create microVMs from OCI images"
|
||||
repository = "https://github.com/containers/krunvm"
|
||||
|
@ -9,10 +9,9 @@ edition = "2018"
|
|||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = {version = "4.4.6", features = ["derive"]}
|
||||
clap = "2.33.3"
|
||||
confy = "0.4.0"
|
||||
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"]}
|
||||
|
|
1
Makefile
1
Makefile
|
@ -1,6 +1,7 @@
|
|||
OS = $(shell uname -s)
|
||||
KRUNVM_RELEASE = target/release/krunvm
|
||||
KRUNVM_DEBUG = target/debug/krunvm
|
||||
INIT_BINARY = init/init
|
||||
|
||||
ifeq ($(PREFIX),)
|
||||
PREFIX := /usr/local
|
||||
|
|
|
@ -46,7 +46,6 @@ dnf install -y krunvm
|
|||
* Rust Toolchain
|
||||
* [libkrun](https://github.com/containers/libkrun)
|
||||
* [buildah](https://github.com/containers/buildah)
|
||||
* [asciidoctor](https://github.com/asciidoctor/asciidoctor)
|
||||
|
||||
#### Building
|
||||
|
||||
|
|
|
@ -33,8 +33,6 @@ OPTIONS
|
|||
*--mem* _NUM_::
|
||||
Override amount of RAM, in MiB, configured for this microVM.
|
||||
|
||||
*--env* _KEY=VALUE_::
|
||||
Set environment variable to be passed to the microVM.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
// Copyright 2021 Red Hat, Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use libc::{c_char, c_int};
|
||||
|
||||
#[link(name = "krun")]
|
||||
extern "C" {
|
||||
pub fn krun_set_log_level(level: u32) -> i32;
|
||||
pub fn krun_create_ctx() -> i32;
|
||||
pub fn krun_free_ctx(ctx: u32) -> i32;
|
||||
pub fn krun_set_vm_config(ctx: u32, num_vcpus: u8, ram_mib: u32) -> i32;
|
||||
pub fn krun_set_root(ctx: u32, root_path: *const c_char) -> i32;
|
||||
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_root(ctx: u32, root_path: *const i8) -> i32;
|
||||
pub fn krun_set_mapped_volumes(ctx: u32, mapped_volumes: *const *const i8) -> i32;
|
||||
pub fn krun_set_port_map(ctx: u32, port_map: *const *const i8) -> i32;
|
||||
pub fn krun_set_workdir(ctx: u32, workdir_path: *const i8) -> i32;
|
||||
pub fn krun_set_exec(
|
||||
ctx: u32,
|
||||
exec_path: *const c_char,
|
||||
argv: *const *const c_char,
|
||||
envp: *const *const c_char,
|
||||
exec_path: *const i8,
|
||||
argv: *const *const i8,
|
||||
envp: *const *const i8,
|
||||
) -> i32;
|
||||
pub fn krun_set_env(ctx: u32, envp: *const *const c_char) -> i32;
|
||||
pub fn krun_set_env(ctx: u32, envp: *const *const i8) -> i32;
|
||||
pub fn krun_start_enter(ctx: u32) -> i32;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
// Copyright 2021 Red Hat, Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{ArgMatches, KrunvmConfig, APP_NAME};
|
||||
|
||||
use super::list::printvm;
|
||||
use super::utils::{parse_mapped_ports, parse_mapped_volumes};
|
||||
|
||||
pub fn changevm(cfg: &mut KrunvmConfig, matches: &ArgMatches) {
|
||||
let mut cfg_changed = false;
|
||||
|
||||
let name = matches.value_of("NAME").unwrap();
|
||||
|
||||
let mut vmcfg = if let Some(new_name) = matches.value_of("new-name") {
|
||||
if cfg.vmconfig_map.contains_key(new_name) {
|
||||
println!("A VM with name {} already exists", new_name);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
|
||||
let mut vmcfg = match cfg.vmconfig_map.remove(name) {
|
||||
None => {
|
||||
println!("No VM found with name {}", name);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
Some(vmcfg) => vmcfg,
|
||||
};
|
||||
|
||||
cfg_changed = true;
|
||||
let name = new_name.to_string();
|
||||
vmcfg.name = name.clone();
|
||||
cfg.vmconfig_map.insert(name.clone(), vmcfg);
|
||||
cfg.vmconfig_map.get_mut(&name).unwrap()
|
||||
} else {
|
||||
match cfg.vmconfig_map.get_mut(name) {
|
||||
None => {
|
||||
println!("No VM found with name {}", name);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
Some(vmcfg) => vmcfg,
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(cpus_str) = matches.value_of("cpus") {
|
||||
match cpus_str.parse::<u32>() {
|
||||
Err(_) => println!("Invalid value for \"cpus\""),
|
||||
Ok(cpus) => {
|
||||
if cpus > 8 {
|
||||
println!("Error: the maximum number of CPUs supported is 8");
|
||||
} else {
|
||||
vmcfg.cpus = cpus;
|
||||
cfg_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mem_str) = matches.value_of("mem") {
|
||||
match mem_str.parse::<u32>() {
|
||||
Err(_) => println!("Invalid value for \"mem\""),
|
||||
Ok(mem) => {
|
||||
if mem > 16384 {
|
||||
println!("Error: the maximum amount of RAM supported is 16384 MiB");
|
||||
} else {
|
||||
vmcfg.mem = mem;
|
||||
cfg_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if matches.is_present("remove-volumes") {
|
||||
vmcfg.mapped_volumes = HashMap::new();
|
||||
cfg_changed = true;
|
||||
} else {
|
||||
let volume_matches = if matches.is_present("volume") {
|
||||
matches.values_of("volume").unwrap().collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let mapped_volumes = parse_mapped_volumes(volume_matches);
|
||||
|
||||
if !mapped_volumes.is_empty() {
|
||||
vmcfg.mapped_volumes = mapped_volumes;
|
||||
cfg_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if matches.is_present("remove-ports") {
|
||||
vmcfg.mapped_ports = HashMap::new();
|
||||
cfg_changed = true;
|
||||
} else {
|
||||
let port_matches = if matches.is_present("port") {
|
||||
matches.values_of("port").unwrap().collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let mapped_ports = parse_mapped_ports(port_matches);
|
||||
|
||||
if !mapped_ports.is_empty() {
|
||||
vmcfg.mapped_ports = mapped_ports;
|
||||
cfg_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(workdir) = matches.value_of("workdir") {
|
||||
vmcfg.workdir = workdir.to_string();
|
||||
cfg_changed = true;
|
||||
}
|
||||
|
||||
println!();
|
||||
printvm(vmcfg);
|
||||
println!();
|
||||
|
||||
if cfg_changed {
|
||||
confy::store(APP_NAME, &cfg).unwrap();
|
||||
}
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
// Copyright 2021 Red Hat, Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Args;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::utils::{path_pairs_to_hash_map, port_pairs_to_hash_map, PathPair, PortPair};
|
||||
use crate::{KrunvmConfig, APP_NAME};
|
||||
|
||||
use super::list::printvm;
|
||||
|
||||
/// Change the configuration of a microVM
|
||||
#[derive(Args, Debug)]
|
||||
pub struct ChangeVmCmd {
|
||||
/// Name of the VM to be modified
|
||||
name: String,
|
||||
|
||||
/// Assign a new name to the VM
|
||||
#[arg(long)]
|
||||
new_name: Option<String>,
|
||||
|
||||
/// Number of vCPUs
|
||||
#[arg(long)]
|
||||
cpus: Option<u32>,
|
||||
|
||||
/// Amount of RAM in MiB
|
||||
#[arg(long)]
|
||||
mem: Option<u32>,
|
||||
|
||||
/// Working directory inside the microVM
|
||||
#[arg(short, long)]
|
||||
workdir: Option<String>,
|
||||
|
||||
/// Remove all volume mappings
|
||||
#[arg(long)]
|
||||
remove_volumes: bool,
|
||||
|
||||
/// Volume(s) in form "host_path:guest_path" to be exposed to the guest
|
||||
#[arg(short, long = "volume")]
|
||||
volumes: Vec<PathPair>,
|
||||
|
||||
/// Remove all port mappings
|
||||
#[arg(long)]
|
||||
remove_ports: bool,
|
||||
|
||||
/// Port(s) in format "host_port:guest_port" to be exposed to the host
|
||||
#[arg(long = "port")]
|
||||
ports: Vec<PortPair>,
|
||||
}
|
||||
|
||||
impl ChangeVmCmd {
|
||||
pub fn run(self, cfg: &mut KrunvmConfig) {
|
||||
let mut cfg_changed = false;
|
||||
|
||||
let vmcfg = if let Some(new_name) = &self.new_name {
|
||||
if cfg.vmconfig_map.contains_key(new_name) {
|
||||
println!("A VM with name {} already exists", new_name);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
|
||||
let mut vmcfg = match cfg.vmconfig_map.remove(&self.name) {
|
||||
None => {
|
||||
println!("No VM found with name {}", &self.name);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
Some(vmcfg) => vmcfg,
|
||||
};
|
||||
|
||||
cfg_changed = true;
|
||||
let name = new_name.to_string();
|
||||
vmcfg.name = name.clone();
|
||||
cfg.vmconfig_map.insert(name.clone(), vmcfg);
|
||||
cfg.vmconfig_map.get_mut(&name).unwrap()
|
||||
} else {
|
||||
match cfg.vmconfig_map.get_mut(&self.name) {
|
||||
None => {
|
||||
println!("No VM found with name {}", self.name);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
Some(vmcfg) => vmcfg,
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(cpus) = self.cpus {
|
||||
if cpus > 8 {
|
||||
println!("Error: the maximum number of CPUs supported is 8");
|
||||
} else {
|
||||
vmcfg.cpus = cpus;
|
||||
cfg_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mem) = self.mem {
|
||||
if mem > 16384 {
|
||||
println!("Error: the maximum amount of RAM supported is 16384 MiB");
|
||||
} else {
|
||||
vmcfg.mem = mem;
|
||||
cfg_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if self.remove_volumes {
|
||||
vmcfg.mapped_volumes = HashMap::new();
|
||||
cfg_changed = true;
|
||||
} else {
|
||||
let mapped_volumes = path_pairs_to_hash_map(self.volumes);
|
||||
|
||||
if !mapped_volumes.is_empty() {
|
||||
vmcfg.mapped_volumes = mapped_volumes;
|
||||
cfg_changed = true;
|
||||
}
|
||||
}
|
||||
// TODO: don't just silently ignore --volume args when --remove_volumes is specified
|
||||
|
||||
if self.remove_ports {
|
||||
vmcfg.mapped_ports = HashMap::new();
|
||||
cfg_changed = true;
|
||||
} else {
|
||||
let mapped_ports = port_pairs_to_hash_map(self.ports);
|
||||
|
||||
if !mapped_ports.is_empty() {
|
||||
vmcfg.mapped_ports = mapped_ports;
|
||||
cfg_changed = true;
|
||||
}
|
||||
}
|
||||
// TODO: don't just silently ignore --port args when --remove_ports is specified
|
||||
|
||||
if let Some(workdir) = self.workdir {
|
||||
vmcfg.workdir = workdir.to_string();
|
||||
cfg_changed = true;
|
||||
}
|
||||
|
||||
println!();
|
||||
printvm(vmcfg);
|
||||
println!();
|
||||
|
||||
if cfg_changed {
|
||||
confy::store(APP_NAME, &cfg).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
// Copyright 2021 Red Hat, Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{KrunvmConfig, APP_NAME};
|
||||
use clap::Args;
|
||||
|
||||
/// Configure global values
|
||||
#[derive(Args, Debug)]
|
||||
pub struct ConfigCmd {
|
||||
// Default number of vCPUs for newly created VMs
|
||||
#[arg(long)]
|
||||
cpus: Option<u32>,
|
||||
|
||||
///Default amount of RAM in MiB for newly created VMs
|
||||
#[arg(long)]
|
||||
mem: Option<u32>,
|
||||
|
||||
/// DNS server to use in the microVM
|
||||
#[arg(long)]
|
||||
dns: Option<String>,
|
||||
}
|
||||
|
||||
impl ConfigCmd {
|
||||
pub fn run(self, cfg: &mut KrunvmConfig) {
|
||||
let mut cfg_changed = false;
|
||||
|
||||
if let Some(cpus) = self.cpus {
|
||||
if cpus > 8 {
|
||||
println!("Error: the maximum number of CPUs supported is 8");
|
||||
} else {
|
||||
cfg.default_cpus = cpus;
|
||||
cfg_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mem) = self.mem {
|
||||
if mem > 16384 {
|
||||
println!("Error: the maximum amount of RAM supported is 16384 MiB");
|
||||
} else {
|
||||
cfg.default_mem = mem;
|
||||
cfg_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(dns) = self.dns {
|
||||
cfg.default_dns = dns;
|
||||
cfg_changed = true;
|
||||
}
|
||||
|
||||
if cfg_changed {
|
||||
confy::store(APP_NAME, &cfg).unwrap();
|
||||
}
|
||||
|
||||
println!("Global configuration:");
|
||||
println!(
|
||||
"Default number of CPUs for newly created VMs: {}",
|
||||
cfg.default_cpus
|
||||
);
|
||||
println!(
|
||||
"Default amount of RAM (MiB) for newly created VMs: {}",
|
||||
cfg.default_mem
|
||||
);
|
||||
println!(
|
||||
"Default DNS server for newly created VMs: {}",
|
||||
cfg.default_dns
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,233 +0,0 @@
|
|||
// Copyright 2021 Red Hat, Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Args;
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
#[cfg(target_os = "macos")]
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
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";
|
||||
|
||||
/// Create a new microVM
|
||||
#[derive(Args, Debug)]
|
||||
pub struct CreateCmd {
|
||||
/// OCI image to use as template
|
||||
image: String,
|
||||
|
||||
/// Assign a name to the VM
|
||||
#[arg(long)]
|
||||
name: Option<String>,
|
||||
|
||||
/// Number of vCPUs
|
||||
#[arg(long)]
|
||||
cpus: Option<u32>,
|
||||
|
||||
/// Amount of RAM in MiB
|
||||
#[arg(long)]
|
||||
mem: Option<u32>,
|
||||
|
||||
/// DNS server to use in the microVM
|
||||
#[arg(long)]
|
||||
dns: Option<String>,
|
||||
|
||||
/// Working directory inside the microVM
|
||||
#[arg(short, long, default_value = "")]
|
||||
workdir: String,
|
||||
|
||||
/// Volume(s) in form "host_path:guest_path" to be exposed to the guest
|
||||
#[arg(short, long = "volume")]
|
||||
volumes: Vec<PathPair>,
|
||||
|
||||
/// Port(s) in format "host_port:guest_port" to be exposed to the host
|
||||
#[arg(long = "port")]
|
||||
ports: Vec<PortPair>,
|
||||
|
||||
/// Create a x86_64 microVM even on an Aarch64 host
|
||||
#[arg(short, long)]
|
||||
#[cfg(target_os = "macos")]
|
||||
x86: bool,
|
||||
}
|
||||
|
||||
impl CreateCmd {
|
||||
pub fn run(self, cfg: &mut KrunvmConfig) {
|
||||
#[allow(unused_mut)]
|
||||
let mut cpus = self.cpus.unwrap_or(cfg.default_cpus);
|
||||
let mem = self.mem.unwrap_or(cfg.default_mem);
|
||||
let dns = self.dns.unwrap_or_else(|| cfg.default_dns.clone());
|
||||
let workdir = self.workdir;
|
||||
let mapped_volumes = path_pairs_to_hash_map(self.volumes);
|
||||
let mapped_ports = port_pairs_to_hash_map(self.ports);
|
||||
let image = self.image;
|
||||
let name = self.name;
|
||||
|
||||
if let Some(ref name) = name {
|
||||
if name.is_empty() {
|
||||
println!("Invalid name for VM");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
if cfg.vmconfig_map.contains_key(name) {
|
||||
println!("A VM with this name already exists");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
let mut args = get_buildah_args(cfg, BuildahCommand::From);
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
let force_x86 = self.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")
|
||||
.args(&args)
|
||||
.stderr(std::process::Stdio::inherit())
|
||||
.output()
|
||||
{
|
||||
Ok(output) => output,
|
||||
Err(err) => {
|
||||
if err.kind() == std::io::ErrorKind::NotFound {
|
||||
println!("{} requires buildah to manage the OCI images, and it wasn't found on this system.", APP_NAME);
|
||||
} else {
|
||||
println!("Error executing buildah: {}", err);
|
||||
}
|
||||
std::process::exit(-1);
|
||||
}
|
||||
};
|
||||
|
||||
let exit_code = output.status.code().unwrap_or(-1);
|
||||
if exit_code != 0 {
|
||||
println!(
|
||||
"buildah returned an error: {}",
|
||||
std::str::from_utf8(&output.stdout).unwrap()
|
||||
);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
|
||||
let container = std::str::from_utf8(&output.stdout).unwrap().trim();
|
||||
let name = if let Some(name) = name {
|
||||
name.to_string()
|
||||
} else {
|
||||
container.to_string()
|
||||
};
|
||||
let vmcfg = VmConfig {
|
||||
name: name.clone(),
|
||||
cpus,
|
||||
mem,
|
||||
dns: dns.to_string(),
|
||||
container: container.to_string(),
|
||||
workdir: workdir.to_string(),
|
||||
mapped_volumes,
|
||||
mapped_ports,
|
||||
};
|
||||
|
||||
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);
|
||||
confy::store(APP_NAME, cfg).unwrap();
|
||||
|
||||
println!("microVM created with name: {}", name);
|
||||
}
|
||||
}
|
||||
|
||||
fn fix_resolv_conf(rootfs: &str, dns: &str) -> Result<(), std::io::Error> {
|
||||
let resolvconf_dir = format!("{}/etc/", rootfs);
|
||||
fs::create_dir_all(resolvconf_dir)?;
|
||||
let resolvconf = format!("{}/etc/resolv.conf", rootfs);
|
||||
let mut file = fs::File::create(resolvconf)?;
|
||||
file.write_all(b"options use-vc\nnameserver ")?;
|
||||
file.write_all(dns.as_bytes())?;
|
||||
file.write_all(b"\n")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn export_container_config(
|
||||
cfg: &KrunvmConfig,
|
||||
rootfs: &str,
|
||||
image: &str,
|
||||
) -> Result<(), std::io::Error> {
|
||||
let mut args = get_buildah_args(cfg, BuildahCommand::Inspect);
|
||||
args.push(image.to_string());
|
||||
|
||||
let output = match Command::new("buildah")
|
||||
.args(&args)
|
||||
.stderr(std::process::Stdio::inherit())
|
||||
.output()
|
||||
{
|
||||
Ok(output) => output,
|
||||
Err(err) => {
|
||||
if err.kind() == std::io::ErrorKind::NotFound {
|
||||
println!("{} requires buildah to manage the OCI images, and it wasn't found on this system.", APP_NAME);
|
||||
} else {
|
||||
println!("Error executing buildah: {}", err);
|
||||
}
|
||||
std::process::exit(-1);
|
||||
}
|
||||
};
|
||||
|
||||
let exit_code = output.status.code().unwrap_or(-1);
|
||||
if exit_code != 0 {
|
||||
println!(
|
||||
"buildah returned an error: {}",
|
||||
std::str::from_utf8(&output.stdout).unwrap()
|
||||
);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
|
||||
let mut file = fs::File::create(format!("{}/.krun_config.json", rootfs))?;
|
||||
file.write_all(&output.stdout)?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright 2021 Red Hat, Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{KrunvmConfig, APP_NAME};
|
||||
use clap::Args;
|
||||
|
||||
use crate::utils::{remove_container, umount_container};
|
||||
|
||||
/// Delete an existing microVM
|
||||
#[derive(Args, Debug)]
|
||||
pub struct DeleteCmd {
|
||||
/// Name of the microVM to be deleted
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl DeleteCmd {
|
||||
pub fn run(self, cfg: &mut KrunvmConfig) {
|
||||
let vmcfg = match cfg.vmconfig_map.remove(&self.name) {
|
||||
None => {
|
||||
println!("No VM found with that name");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
Some(vmcfg) => vmcfg,
|
||||
};
|
||||
|
||||
umount_container(cfg, &vmcfg).unwrap();
|
||||
remove_container(cfg, &vmcfg).unwrap();
|
||||
|
||||
confy::store(APP_NAME, &cfg).unwrap();
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
// Copyright 2021 Red Hat, Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
use crate::{
|
||||
utils::{get_buildah_args, BuildahCommand},
|
||||
KrunvmConfig,
|
||||
};
|
||||
use clap::Args;
|
||||
|
||||
/// Run `buildah inspect` on an existing microVM
|
||||
#[derive(Args, Debug)]
|
||||
pub struct InspectCmd {
|
||||
/// Name of the microVM to be inspected
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl InspectCmd {
|
||||
pub fn run(self, cfg: &mut KrunvmConfig) {
|
||||
let vmcfg = match cfg.vmconfig_map.get(&self.name) {
|
||||
None => {
|
||||
println!("No VM found with that name");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
Some(vmcfg) => vmcfg,
|
||||
};
|
||||
|
||||
let mut args = get_buildah_args(cfg, BuildahCommand::Inspect);
|
||||
args.push(vmcfg.container.clone());
|
||||
|
||||
let output = Command::new("buildah")
|
||||
.args(&args)
|
||||
.stderr(std::process::Stdio::inherit())
|
||||
.output();
|
||||
|
||||
if output.is_err() {
|
||||
println!("Failed to inspect VM");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let output = match String::from_utf8(output.unwrap().stdout) {
|
||||
Err(err) => {
|
||||
println!("Failed to parse `buildah inspect` output: #{err}.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
Ok(output) => output,
|
||||
};
|
||||
|
||||
println!("{output}");
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
// Copyright 2021 Red Hat, Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{KrunvmConfig, VmConfig};
|
||||
use clap::Args;
|
||||
|
||||
/// List microVMs
|
||||
#[derive(Args, Debug)]
|
||||
pub struct ListCmd {
|
||||
/// Print debug information verbosely
|
||||
#[arg(short)]
|
||||
pub debug: bool, //TODO: implement or remove this
|
||||
}
|
||||
|
||||
impl ListCmd {
|
||||
pub fn run(self, cfg: &KrunvmConfig) {
|
||||
if cfg.vmconfig_map.is_empty() {
|
||||
println!("No microVMs found");
|
||||
} else {
|
||||
for (_name, vm) in cfg.vmconfig_map.iter() {
|
||||
println!();
|
||||
printvm(vm);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn printvm(vm: &VmConfig) {
|
||||
println!("{}", vm.name);
|
||||
println!(" CPUs: {}", vm.cpus);
|
||||
println!(" RAM (MiB): {}", vm.mem);
|
||||
println!(" DNS server: {}", vm.dns);
|
||||
println!(" Buildah container: {}", vm.container);
|
||||
println!(" Workdir: {}", vm.workdir);
|
||||
println!(" Mapped volumes: {:?}", vm.mapped_volumes);
|
||||
println!(" Mapped ports: {:?}", vm.mapped_ports);
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
mod changevm;
|
||||
mod config;
|
||||
mod create;
|
||||
mod delete;
|
||||
mod inspect;
|
||||
mod list;
|
||||
mod start;
|
||||
|
||||
pub use changevm::ChangeVmCmd;
|
||||
pub use config::ConfigCmd;
|
||||
pub use create::CreateCmd;
|
||||
pub use delete::DeleteCmd;
|
||||
pub use inspect::InspectCmd;
|
||||
pub use list::ListCmd;
|
||||
pub use start::StartCmd;
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2021 Red Hat, Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{ArgMatches, KrunvmConfig, APP_NAME};
|
||||
|
||||
pub fn config(cfg: &mut KrunvmConfig, matches: &ArgMatches) {
|
||||
let mut cfg_changed = false;
|
||||
|
||||
if let Some(cpus_str) = matches.value_of("cpus") {
|
||||
match cpus_str.parse::<u32>() {
|
||||
Err(_) => println!("Invalid value for \"cpus\""),
|
||||
Ok(cpus) => {
|
||||
if cpus > 8 {
|
||||
println!("Error: the maximum number of CPUs supported is 8");
|
||||
} else {
|
||||
cfg.default_cpus = cpus;
|
||||
cfg_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mem_str) = matches.value_of("mem") {
|
||||
match mem_str.parse::<u32>() {
|
||||
Err(_) => println!("Invalid value for \"mem\""),
|
||||
Ok(mem) => {
|
||||
if mem > 16384 {
|
||||
println!("Error: the maximum amount of RAM supported is 16384 MiB");
|
||||
} else {
|
||||
cfg.default_mem = mem;
|
||||
cfg_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(dns) = matches.value_of("dns") {
|
||||
cfg.default_dns = dns.to_string();
|
||||
cfg_changed = true;
|
||||
}
|
||||
|
||||
if cfg_changed {
|
||||
confy::store(APP_NAME, &cfg).unwrap();
|
||||
}
|
||||
|
||||
println!("Global configuration:");
|
||||
println!(
|
||||
"Default number of CPUs for newly created VMs: {}",
|
||||
cfg.default_cpus
|
||||
);
|
||||
println!(
|
||||
"Default amount of RAM (MiB) for newly created VMs: {}",
|
||||
cfg.default_mem
|
||||
);
|
||||
println!(
|
||||
"Default DNS server for newly created VMs: {}",
|
||||
cfg.default_dns
|
||||
);
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
// Copyright 2021 Red Hat, Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::process::Command;
|
||||
|
||||
use super::utils::{
|
||||
get_buildah_args, mount_container, parse_mapped_ports, parse_mapped_volumes, umount_container,
|
||||
BuildahCommand,
|
||||
};
|
||||
use crate::{ArgMatches, KrunvmConfig, VmConfig, APP_NAME};
|
||||
|
||||
fn fix_resolv_conf(rootfs: &str, dns: &str) -> Result<(), std::io::Error> {
|
||||
let resolvconf_dir = format!("{}/etc/", rootfs);
|
||||
fs::create_dir_all(resolvconf_dir)?;
|
||||
let resolvconf = format!("{}/etc/resolv.conf", rootfs);
|
||||
let mut file = fs::File::create(resolvconf)?;
|
||||
file.write_all(b"options use-vc\nnameserver ")?;
|
||||
file.write_all(dns.as_bytes())?;
|
||||
file.write_all(b"\n")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn export_container_config(
|
||||
cfg: &KrunvmConfig,
|
||||
rootfs: &str,
|
||||
image: &str,
|
||||
) -> Result<(), std::io::Error> {
|
||||
let mut args = get_buildah_args(cfg, BuildahCommand::Inspect);
|
||||
args.push(image.to_string());
|
||||
|
||||
let output = match Command::new("buildah")
|
||||
.args(&args)
|
||||
.stderr(std::process::Stdio::inherit())
|
||||
.output()
|
||||
{
|
||||
Ok(output) => output,
|
||||
Err(err) => {
|
||||
if err.kind() == std::io::ErrorKind::NotFound {
|
||||
println!("{} requires buildah to manage the OCI images, and it wasn't found on this system.", APP_NAME);
|
||||
} else {
|
||||
println!("Error executing buildah: {}", err);
|
||||
}
|
||||
std::process::exit(-1);
|
||||
}
|
||||
};
|
||||
|
||||
let exit_code = output.status.code().unwrap_or(-1);
|
||||
if exit_code != 0 {
|
||||
println!(
|
||||
"buildah returned an error: {}",
|
||||
std::str::from_utf8(&output.stdout).unwrap()
|
||||
);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
|
||||
let mut file = fs::File::create(format!("{}/.krun_config.json", rootfs))?;
|
||||
file.write_all(&output.stdout)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create(cfg: &mut KrunvmConfig, matches: &ArgMatches) {
|
||||
let cpus = match matches.value_of("cpus") {
|
||||
Some(c) => match c.parse::<u32>() {
|
||||
Err(_) => {
|
||||
println!("Invalid value for \"cpus\"");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
Ok(cpus) => cpus,
|
||||
},
|
||||
None => cfg.default_cpus,
|
||||
};
|
||||
let mem = match matches.value_of("mem") {
|
||||
Some(m) => match m.parse::<u32>() {
|
||||
Err(_) => {
|
||||
println!("Invalid value for \"mem\"");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
Ok(mem) => mem,
|
||||
},
|
||||
None => cfg.default_mem,
|
||||
};
|
||||
let dns = match matches.value_of("dns") {
|
||||
Some(d) => d,
|
||||
None => &cfg.default_dns,
|
||||
};
|
||||
|
||||
let workdir = matches.value_of("workdir").unwrap();
|
||||
|
||||
let volume_matches = if matches.is_present("volume") {
|
||||
matches.values_of("volume").unwrap().collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let mapped_volumes = parse_mapped_volumes(volume_matches);
|
||||
|
||||
let port_matches = if matches.is_present("port") {
|
||||
matches.values_of("port").unwrap().collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let mapped_ports = parse_mapped_ports(port_matches);
|
||||
|
||||
let image = matches.value_of("IMAGE").unwrap();
|
||||
|
||||
let name = matches.value_of("name");
|
||||
if let Some(name) = name {
|
||||
if cfg.vmconfig_map.contains_key(name) {
|
||||
println!("A VM with this name already exists");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
let mut args = get_buildah_args(cfg, BuildahCommand::From);
|
||||
args.push(image.to_string());
|
||||
|
||||
let output = match Command::new("buildah")
|
||||
.args(&args)
|
||||
.stderr(std::process::Stdio::inherit())
|
||||
.output()
|
||||
{
|
||||
Ok(output) => output,
|
||||
Err(err) => {
|
||||
if err.kind() == std::io::ErrorKind::NotFound {
|
||||
println!("{} requires buildah to manage the OCI images, and it wasn't found on this system.", APP_NAME);
|
||||
} else {
|
||||
println!("Error executing buildah: {}", err);
|
||||
}
|
||||
std::process::exit(-1);
|
||||
}
|
||||
};
|
||||
|
||||
let exit_code = output.status.code().unwrap_or(-1);
|
||||
if exit_code != 0 {
|
||||
println!(
|
||||
"buildah returned an error: {}",
|
||||
std::str::from_utf8(&output.stdout).unwrap()
|
||||
);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
|
||||
let container = std::str::from_utf8(&output.stdout).unwrap().trim();
|
||||
let name = if let Some(name) = name {
|
||||
name.to_string()
|
||||
} else {
|
||||
container.to_string()
|
||||
};
|
||||
let vmcfg = VmConfig {
|
||||
name: name.clone(),
|
||||
cpus,
|
||||
mem,
|
||||
dns: dns.to_string(),
|
||||
container: container.to_string(),
|
||||
workdir: workdir.to_string(),
|
||||
mapped_volumes,
|
||||
mapped_ports,
|
||||
};
|
||||
|
||||
let rootfs = mount_container(cfg, &vmcfg).unwrap();
|
||||
export_container_config(cfg, &rootfs, image).unwrap();
|
||||
fix_resolv_conf(&rootfs, dns).unwrap();
|
||||
umount_container(cfg, &vmcfg).unwrap();
|
||||
|
||||
cfg.vmconfig_map.insert(name.clone(), vmcfg);
|
||||
confy::store(APP_NAME, cfg).unwrap();
|
||||
|
||||
println!("microVM created with name: {}", name);
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2021 Red Hat, Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{ArgMatches, KrunvmConfig, APP_NAME};
|
||||
|
||||
use super::utils::{remove_container, umount_container};
|
||||
|
||||
pub fn delete(cfg: &mut KrunvmConfig, matches: &ArgMatches) {
|
||||
let name = matches.value_of("NAME").unwrap();
|
||||
|
||||
let vmcfg = match cfg.vmconfig_map.remove(name) {
|
||||
None => {
|
||||
println!("No VM found with that name");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
Some(vmcfg) => vmcfg,
|
||||
};
|
||||
|
||||
umount_container(cfg, &vmcfg).unwrap();
|
||||
remove_container(cfg, &vmcfg).unwrap();
|
||||
|
||||
confy::store(APP_NAME, &cfg).unwrap();
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2021 Red Hat, Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{ArgMatches, KrunvmConfig, VmConfig};
|
||||
|
||||
pub fn printvm(vm: &VmConfig) {
|
||||
println!("{}", vm.name);
|
||||
println!(" CPUs: {}", vm.cpus);
|
||||
println!(" RAM (MiB): {}", vm.mem);
|
||||
println!(" DNS server: {}", vm.dns);
|
||||
println!(" Buildah container: {}", vm.container);
|
||||
println!(" Workdir: {}", vm.workdir);
|
||||
println!(" Mapped volumes: {:?}", vm.mapped_volumes);
|
||||
println!(" Mapped ports: {:?}", vm.mapped_ports);
|
||||
}
|
||||
|
||||
pub fn list(cfg: &KrunvmConfig, _matches: &ArgMatches) {
|
||||
if cfg.vmconfig_map.is_empty() {
|
||||
println!("No microVMs found");
|
||||
} else {
|
||||
for (_name, vm) in cfg.vmconfig_map.iter() {
|
||||
println!();
|
||||
printvm(vm);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
252
src/main.rs
252
src/main.rs
|
@ -7,17 +7,19 @@ use std::fs::File;
|
|||
#[cfg(target_os = "macos")]
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use crate::commands::{
|
||||
ChangeVmCmd, ConfigCmd, CreateCmd, DeleteCmd, InspectCmd, ListCmd, StartCmd,
|
||||
};
|
||||
use clap::{Parser, Subcommand};
|
||||
use clap::{crate_version, App, Arg, ArgMatches};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
#[cfg(target_os = "macos")]
|
||||
use text_io::read;
|
||||
|
||||
#[allow(unused)]
|
||||
mod bindings;
|
||||
mod commands;
|
||||
mod changevm;
|
||||
mod config;
|
||||
mod create;
|
||||
mod delete;
|
||||
mod list;
|
||||
mod start;
|
||||
mod utils;
|
||||
|
||||
const APP_NAME: &str = "krunvm";
|
||||
|
@ -147,44 +149,222 @@ fn check_unshare() {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about)]
|
||||
struct Cli {
|
||||
/// Sets the level of verbosity
|
||||
#[arg(short)]
|
||||
verbosity: Option<u8>, //TODO: implement or remove this
|
||||
#[command(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
enum Command {
|
||||
Start(StartCmd),
|
||||
Create(CreateCmd),
|
||||
Inspect(InspectCmd),
|
||||
List(ListCmd),
|
||||
Delete(DeleteCmd),
|
||||
#[command(name = "changevm")]
|
||||
ChangeVm(ChangeVmCmd),
|
||||
Config(ConfigCmd),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut cfg: KrunvmConfig = confy::load(APP_NAME).unwrap();
|
||||
let cli_args = Cli::parse();
|
||||
|
||||
let mut app = App::new("krunvm")
|
||||
.version(crate_version!())
|
||||
.author("Sergio Lopez <slp@redhat.com>")
|
||||
.about("Manage microVMs created from OCI images")
|
||||
.arg(
|
||||
Arg::with_name("v")
|
||||
.short("v")
|
||||
.multiple(true)
|
||||
.help("Sets the level of verbosity"),
|
||||
)
|
||||
.subcommand(
|
||||
App::new("changevm")
|
||||
.about("Change the configuration of a 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("workdir")
|
||||
.long("workdir")
|
||||
.short("w")
|
||||
.help("Working directory inside the microVM")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("remove-volumes")
|
||||
.long("remove-volumes")
|
||||
.help("Remove all volume mappings"),
|
||||
)
|
||||
.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),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("remove-ports")
|
||||
.long("remove-ports")
|
||||
.help("Remove all port mappings"),
|
||||
)
|
||||
.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),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("new-name")
|
||||
.long("name")
|
||||
.help("Assign a new name to the VM")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("NAME")
|
||||
.help("Name of the VM to be modified")
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
App::new("config")
|
||||
.about("Configure global values")
|
||||
.arg(
|
||||
Arg::with_name("cpus")
|
||||
.long("cpus")
|
||||
.help("Default number of vCPUs for newly created VMs")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("mem")
|
||||
.long("mem")
|
||||
.help("Default amount of RAM in MiB for newly created VMs")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("dns")
|
||||
.long("dns")
|
||||
.help("DNS server to use in the microVM")
|
||||
.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),
|
||||
)
|
||||
.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),
|
||||
)
|
||||
.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")
|
||||
.help("Name of the microVM to be deleted")
|
||||
.required(true)
|
||||
.index(1),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
App::new("list").about("List microVMs").arg(
|
||||
Arg::with_name("debug")
|
||||
.short("d")
|
||||
.help("print debug information verbosely"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
App::new("start")
|
||||
.about("Start an existing microVM")
|
||||
.arg(Arg::with_name("cpus").long("cpus").help("Number of vCPUs"))
|
||||
.arg(
|
||||
Arg::with_name("mem")
|
||||
.long("mem")
|
||||
.help("Amount of RAM in MiB"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("NAME")
|
||||
.help("Name of the microVM")
|
||||
.required(true)
|
||||
.index(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("COMMAND")
|
||||
.help("Command to run inside the VM")
|
||||
.index(2),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("ARGS")
|
||||
.help("Arguments to be passed to the command executed in the VM")
|
||||
.multiple(true)
|
||||
.last(true),
|
||||
),
|
||||
);
|
||||
|
||||
let matches = app.clone().get_matches();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
check_volume(&mut cfg);
|
||||
#[cfg(target_os = "linux")]
|
||||
check_unshare();
|
||||
|
||||
match cli_args.command {
|
||||
Command::Inspect(cmd) => cmd.run(&mut cfg),
|
||||
Command::Start(cmd) => cmd.run(&cfg),
|
||||
Command::Create(cmd) => cmd.run(&mut cfg),
|
||||
Command::List(cmd) => cmd.run(&cfg),
|
||||
Command::Delete(cmd) => cmd.run(&mut cfg),
|
||||
Command::ChangeVm(cmd) => cmd.run(&mut cfg),
|
||||
Command::Config(cmd) => cmd.run(&mut cfg),
|
||||
if let Some(matches) = matches.subcommand_matches("changevm") {
|
||||
changevm::changevm(&mut cfg, matches);
|
||||
} else if let Some(matches) = matches.subcommand_matches("config") {
|
||||
config::config(&mut cfg, matches);
|
||||
} else if let Some(matches) = matches.subcommand_matches("create") {
|
||||
create::create(&mut cfg, matches);
|
||||
} else if let Some(matches) = matches.subcommand_matches("delete") {
|
||||
delete::delete(&mut cfg, matches);
|
||||
} else if let Some(matches) = matches.subcommand_matches("list") {
|
||||
list::list(&cfg, matches);
|
||||
} else if let Some(matches) = matches.subcommand_matches("start") {
|
||||
start::start(&cfg, matches);
|
||||
} else {
|
||||
app.print_long_help().unwrap();
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright 2021 Red Hat, Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Args;
|
||||
use libc::c_char;
|
||||
use std::ffi::CString;
|
||||
use std::fs::File;
|
||||
#[cfg(target_os = "linux")]
|
||||
|
@ -11,76 +9,9 @@ use std::os::unix::io::AsRawFd;
|
|||
#[cfg(target_os = "macos")]
|
||||
use std::path::Path;
|
||||
|
||||
use crate::bindings;
|
||||
use crate::utils::{mount_container, umount_container};
|
||||
use crate::{KrunvmConfig, VmConfig};
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
/// Start an existing microVM
|
||||
pub struct StartCmd {
|
||||
/// Name of the microVM
|
||||
name: String,
|
||||
|
||||
/// Command to run inside the VM
|
||||
command: Option<String>,
|
||||
|
||||
/// Arguments to be passed to the command executed in the VM
|
||||
args: Vec<String>,
|
||||
|
||||
/// Number of vCPUs
|
||||
#[arg(long)]
|
||||
cpus: Option<u8>, // TODO: implement or remove this
|
||||
|
||||
/// Amount of RAM in MiB
|
||||
#[arg(long)]
|
||||
mem: Option<usize>, // TODO: implement or remove this
|
||||
|
||||
/// env(s) in format "key=value" to be exposed to the VM
|
||||
#[arg(long = "env")]
|
||||
envs: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl StartCmd {
|
||||
pub fn run(self, cfg: &KrunvmConfig) {
|
||||
let vmcfg = match cfg.vmconfig_map.get(&self.name) {
|
||||
None => {
|
||||
println!("No VM found with name {}", self.name);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
Some(vmcfg) => vmcfg,
|
||||
};
|
||||
|
||||
umount_container(cfg, vmcfg).expect("Error unmounting container");
|
||||
let rootfs = mount_container(cfg, vmcfg).expect("Error mounting container");
|
||||
|
||||
let vm_args: Vec<CString> = if self.command.is_some() {
|
||||
self.args
|
||||
.into_iter()
|
||||
.map(|val| CString::new(val).unwrap())
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let env_pairs: Vec<CString> = if self.envs.is_some() {
|
||||
self.envs
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|val| CString::new(val).unwrap())
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
set_rlimits();
|
||||
|
||||
let _file = set_lock(&rootfs);
|
||||
|
||||
unsafe { exec_vm(vmcfg, &rootfs, self.command.as_deref(), vm_args, env_pairs) };
|
||||
|
||||
umount_container(cfg, vmcfg).expect("Error unmounting container");
|
||||
}
|
||||
}
|
||||
use super::bindings;
|
||||
use super::utils::{mount_container, umount_container};
|
||||
use crate::{ArgMatches, KrunvmConfig, VmConfig};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn map_volumes(_ctx: u32, vmcfg: &VmConfig, rootfs: &str) {
|
||||
|
@ -112,9 +43,6 @@ fn map_volumes(_ctx: u32, vmcfg: &VmConfig, rootfs: &str) {
|
|||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn map_volumes(ctx: u32, vmcfg: &VmConfig, rootfs: &str) {
|
||||
if vmcfg.mapped_volumes.is_empty() {
|
||||
return;
|
||||
}
|
||||
let mut volumes = Vec::new();
|
||||
for (host_path, guest_path) in vmcfg.mapped_volumes.iter() {
|
||||
let full_guest = format!("{}{}", &rootfs, guest_path);
|
||||
|
@ -138,13 +66,7 @@ fn map_volumes(ctx: u32, vmcfg: &VmConfig, rootfs: &str) {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe fn exec_vm(
|
||||
vmcfg: &VmConfig,
|
||||
rootfs: &str,
|
||||
cmd: Option<&str>,
|
||||
args: Vec<CString>,
|
||||
env_pairs: Vec<CString>,
|
||||
) {
|
||||
unsafe fn exec_vm(vmcfg: &VmConfig, rootfs: &str, cmd: Option<&str>, args: Vec<CString>) {
|
||||
//bindings::krun_set_log_level(9);
|
||||
|
||||
let ctx = bindings::krun_create_ctx() as u32;
|
||||
|
@ -156,7 +78,7 @@ unsafe fn exec_vm(
|
|||
}
|
||||
|
||||
let c_rootfs = CString::new(rootfs).unwrap();
|
||||
let ret = bindings::krun_set_root(ctx, c_rootfs.as_ptr());
|
||||
let ret = bindings::krun_set_root(ctx, c_rootfs.as_ptr() as *const i8);
|
||||
if ret < 0 {
|
||||
println!("Error setting VM rootfs");
|
||||
std::process::exit(-1);
|
||||
|
@ -169,12 +91,11 @@ unsafe fn exec_vm(
|
|||
let map = format!("{}:{}", host_port, guest_port);
|
||||
ports.push(CString::new(map).unwrap());
|
||||
}
|
||||
let mut ps: Vec<*const c_char> = Vec::new();
|
||||
let mut ps: Vec<*const i8> = Vec::new();
|
||||
for port in ports.iter() {
|
||||
ps.push(port.as_ptr());
|
||||
ps.push(port.as_ptr() as *const i8);
|
||||
}
|
||||
ps.push(std::ptr::null());
|
||||
|
||||
let ret = bindings::krun_set_port_map(ctx, ps.as_ptr());
|
||||
if ret < 0 {
|
||||
println!("Error setting VM port map");
|
||||
|
@ -183,7 +104,7 @@ unsafe fn exec_vm(
|
|||
|
||||
if !vmcfg.workdir.is_empty() {
|
||||
let c_workdir = CString::new(vmcfg.workdir.clone()).unwrap();
|
||||
let ret = bindings::krun_set_workdir(ctx, c_workdir.as_ptr());
|
||||
let ret = bindings::krun_set_workdir(ctx, c_workdir.as_ptr() as *const i8);
|
||||
if ret < 0 {
|
||||
println!("Error setting VM workdir");
|
||||
std::process::exit(-1);
|
||||
|
@ -192,30 +113,32 @@ unsafe fn exec_vm(
|
|||
|
||||
let hostname = CString::new(format!("HOSTNAME={}", vmcfg.name)).unwrap();
|
||||
let home = CString::new("HOME=/root").unwrap();
|
||||
|
||||
let mut env: Vec<*const c_char> = Vec::new();
|
||||
env.push(hostname.as_ptr());
|
||||
env.push(home.as_ptr());
|
||||
for value in env_pairs.iter() {
|
||||
env.push(value.as_ptr());
|
||||
}
|
||||
env.push(std::ptr::null());
|
||||
let env: [*const i8; 3] = [
|
||||
hostname.as_ptr() as *const i8,
|
||||
home.as_ptr() as *const i8,
|
||||
std::ptr::null(),
|
||||
];
|
||||
|
||||
if let Some(cmd) = cmd {
|
||||
let mut argv: Vec<*const c_char> = Vec::new();
|
||||
let mut argv: Vec<*const i8> = Vec::new();
|
||||
for a in args.iter() {
|
||||
argv.push(a.as_ptr());
|
||||
argv.push(a.as_ptr() as *const i8);
|
||||
}
|
||||
argv.push(std::ptr::null());
|
||||
|
||||
let c_cmd = CString::new(cmd).unwrap();
|
||||
let ret = bindings::krun_set_exec(ctx, c_cmd.as_ptr(), argv.as_ptr(), env.as_ptr());
|
||||
let ret = bindings::krun_set_exec(
|
||||
ctx,
|
||||
c_cmd.as_ptr() as *const i8,
|
||||
argv.as_ptr() as *const *const i8,
|
||||
env.as_ptr() as *const *const i8,
|
||||
);
|
||||
if ret < 0 {
|
||||
println!("Error setting VM config");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
} else {
|
||||
let ret = bindings::krun_set_env(ctx, env.as_ptr());
|
||||
let ret = bindings::krun_set_env(ctx, env.as_ptr() as *const *const i8);
|
||||
if ret < 0 {
|
||||
println!("Error setting VM environment variables");
|
||||
std::process::exit(-1);
|
||||
|
@ -259,3 +182,36 @@ fn set_lock(rootfs: &str) -> File {
|
|||
|
||||
file
|
||||
}
|
||||
|
||||
pub fn start(cfg: &KrunvmConfig, matches: &ArgMatches) {
|
||||
let cmd = matches.value_of("COMMAND");
|
||||
let name = matches.value_of("NAME").unwrap();
|
||||
|
||||
let vmcfg = match cfg.vmconfig_map.get(name) {
|
||||
None => {
|
||||
println!("No VM found with name {}", name);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
Some(vmcfg) => vmcfg,
|
||||
};
|
||||
|
||||
umount_container(cfg, vmcfg).expect("Error unmounting container");
|
||||
let rootfs = mount_container(cfg, vmcfg).expect("Error mounting container");
|
||||
|
||||
let args: Vec<CString> = if cmd.is_some() {
|
||||
match matches.values_of("ARGS") {
|
||||
Some(a) => a.map(|val| CString::new(val).unwrap()).collect(),
|
||||
None => Vec::new(),
|
||||
}
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
set_rlimits();
|
||||
|
||||
let _file = set_lock(&rootfs);
|
||||
|
||||
unsafe { exec_vm(vmcfg, &rootfs, cmd, args) };
|
||||
|
||||
umount_container(cfg, vmcfg).expect("Error unmounting container");
|
||||
}
|
96
src/utils.rs
96
src/utils.rs
|
@ -4,7 +4,6 @@
|
|||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::{KrunvmConfig, VmConfig, APP_NAME};
|
||||
|
||||
|
@ -72,93 +71,70 @@ pub fn get_buildah_args(cfg: &KrunvmConfig, cmd: BuildahCommand) -> Vec<String>
|
|||
args
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PortPair {
|
||||
pub host_port: String,
|
||||
pub guest_port: String,
|
||||
}
|
||||
|
||||
pub fn port_pairs_to_hash_map(
|
||||
port_pairs: impl IntoIterator<Item = PortPair>,
|
||||
) -> HashMap<String, String> {
|
||||
port_pairs
|
||||
.into_iter()
|
||||
.map(|pair: PortPair| (pair.host_port, pair.guest_port))
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl FromStr for PortPair {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
let vtuple: Vec<&str> = input.split(':').collect();
|
||||
pub fn parse_mapped_ports(port_matches: Vec<&str>) -> HashMap<String, String> {
|
||||
let mut mapped_ports = HashMap::new();
|
||||
for port in port_matches.iter() {
|
||||
let vtuple: Vec<&str> = port.split(':').collect();
|
||||
if vtuple.len() != 2 {
|
||||
return Err("Too many ':' separators");
|
||||
println!("Invalid value for \"port\"");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
let host_port: u16 = match vtuple[0].parse() {
|
||||
Ok(p) => p,
|
||||
Err(_) => {
|
||||
return Err("Invalid host port");
|
||||
println!("Invalid host port");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
};
|
||||
let guest_port: u16 = match vtuple[1].parse() {
|
||||
Ok(p) => p,
|
||||
Err(_) => {
|
||||
return Err("Invalid guest port");
|
||||
println!("Invalid guest port");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
};
|
||||
Ok(PortPair {
|
||||
host_port: host_port.to_string(),
|
||||
guest_port: guest_port.to_string(),
|
||||
})
|
||||
|
||||
mapped_ports.insert(host_port.to_string(), guest_port.to_string());
|
||||
}
|
||||
|
||||
mapped_ports
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PathPair {
|
||||
pub host_path: String,
|
||||
pub guest_path: String,
|
||||
}
|
||||
|
||||
pub fn path_pairs_to_hash_map(
|
||||
volume_pairs: impl IntoIterator<Item = PathPair>,
|
||||
) -> HashMap<String, String> {
|
||||
volume_pairs
|
||||
.into_iter()
|
||||
.map(|pair: PathPair| (pair.host_path, pair.guest_path))
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl FromStr for PathPair {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
let vtuple: Vec<&str> = input.split(':').collect();
|
||||
pub fn parse_mapped_volumes(volume_matches: Vec<&str>) -> HashMap<String, String> {
|
||||
let mut mapped_volumes = HashMap::new();
|
||||
for volume in volume_matches.iter() {
|
||||
let vtuple: Vec<&str> = volume.split(':').collect();
|
||||
if vtuple.len() != 2 {
|
||||
return Err("Too many ':' separators");
|
||||
println!("Invalid value for \"volume\"");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
|
||||
let host_path = Path::new(vtuple[0]);
|
||||
if !host_path.is_absolute() {
|
||||
return Err("Invalid volume, host_path is not an absolute path");
|
||||
println!("Invalid volume, host_path is not an absolute path");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
if !host_path.exists() {
|
||||
return Err("Invalid volume, host_path does not exists");
|
||||
println!("Invalid volume, host_path does not exists");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
let guest_path = Path::new(vtuple[1]);
|
||||
if !guest_path.is_absolute() {
|
||||
return Err("Invalid volume, guest_path is not an absolute path");
|
||||
println!("Invalid volume, guest_path is not an absolute path");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
if guest_path.components().count() != 2 {
|
||||
return Err(
|
||||
"Invalid volume, only single direct root children are supported as guest_path",
|
||||
println!(
|
||||
"Invalid volume, only single direct root children are supported as guest_path"
|
||||
);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
Ok(Self {
|
||||
host_path: vtuple[0].to_string(),
|
||||
guest_path: vtuple[1].to_string(),
|
||||
})
|
||||
mapped_volumes.insert(
|
||||
host_path.to_str().unwrap().to_string(),
|
||||
guest_path.to_str().unwrap().to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
mapped_volumes
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
|
@ -222,7 +198,7 @@ pub fn mount_container(cfg: &KrunvmConfig, vmcfg: &VmConfig) -> Result<String, s
|
|||
let rootfs = std::str::from_utf8(&output.stdout).unwrap().trim();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fix_root_mode(rootfs);
|
||||
fix_root_mode(&rootfs);
|
||||
|
||||
Ok(rootfs.to_string())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue