mirror of https://github.com/containers/krunvm.git
Compare commits
43 Commits
Author | SHA1 | Date |
---|---|---|
|
21b9cb4760 | |
|
28ad60fb7b | |
|
2d9e1d6c6d | |
|
713d418480 | |
|
765614c783 | |
|
212e203f88 | |
|
b001dca718 | |
|
57cf8e61b6 | |
|
a2b60aee8b | |
|
5494d84a66 | |
|
27a6dfb4f4 | |
|
90a8299743 | |
|
5f8693c693 | |
|
e467ff9746 | |
|
cbf34eb064 | |
|
38e1e5cffc | |
|
ced22ac583 | |
|
6b6ea50458 | |
|
101a402871 | |
|
acf6f437a1 | |
|
647ec27c24 | |
|
b5c38223f8 | |
|
59e3704368 | |
|
7a8a1c6d5e | |
|
a0e2da39b0 | |
|
12dac810ec | |
|
242a740f77 | |
|
26f5a589da | |
|
e67d0ea065 | |
|
f9f4d4f5cc | |
|
396f85c186 | |
|
ba9e585085 | |
|
7459968d8b | |
|
9985683205 | |
|
a99b7b9e00 | |
|
81444b574e | |
|
a497bb78fa | |
|
3b5baaddec | |
|
1f7a7d1dda | |
|
1992d63183 | |
|
023c665e1b | |
|
2285182aa2 | |
|
9be5aedc6b |
|
@ -26,8 +26,17 @@ jobs:
|
||||||
- name: Install asciidoctor
|
- name: Install asciidoctor
|
||||||
run: sudo apt-get install -y 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)
|
- name: Formatting (rustfmt)
|
||||||
run: cargo fmt -- --check
|
run: cargo fmt -- --check
|
||||||
|
|
||||||
- name: Clippy (all features)
|
- name: Clippy x86_64-unknown-linux-gnu (all features)
|
||||||
run: cargo clippy --all-targets --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
|
||||||
|
|
|
@ -3,12 +3,51 @@
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "anstream"
|
||||||
version = "0.11.0"
|
version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi",
|
"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",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -23,17 +62,6 @@ version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
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]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -48,9 +76,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.2.1"
|
version = "2.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blake2b_simd"
|
name = "blake2b_simd"
|
||||||
|
@ -77,19 +105,50 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "2.33.3"
|
version = "4.4.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"clap_builder",
|
||||||
"atty",
|
"clap_derive",
|
||||||
"bitflags",
|
|
||||||
"strsim",
|
|
||||||
"textwrap",
|
|
||||||
"unicode-width",
|
|
||||||
"vec_map",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "confy"
|
name = "confy"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -151,21 +210,19 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "heck"
|
||||||
version = "0.1.18"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "krunvm"
|
name = "krunvm"
|
||||||
version = "0.1.6"
|
version = "0.2.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"confy",
|
"confy",
|
||||||
"libc",
|
"libc",
|
||||||
|
"nix",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"text_io",
|
"text_io",
|
||||||
|
@ -179,24 +236,45 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.90"
|
version = "0.2.149"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba4aede83fc3617411dc6993bc8c70919750c1c257c6ca6a502aed6e0e2394ae"
|
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",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.24"
|
version = "1.0.69"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.9"
|
version = "1.0.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
@ -244,14 +322,14 @@ checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 1.0.64",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.8.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
|
@ -264,21 +342,23 @@ dependencies = [
|
||||||
"unicode-xid",
|
"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]]
|
[[package]]
|
||||||
name = "text_io"
|
name = "text_io"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6cb170b4f47dc48835fbc56259c12d8963e542b05a24be2e3a1f5a6c320fd2d4"
|
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]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.5.8"
|
version = "0.5.8"
|
||||||
|
@ -289,10 +369,10 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-ident"
|
||||||
version = "0.1.8"
|
version = "1.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
|
@ -301,10 +381,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vec_map"
|
name = "utf8parse"
|
||||||
version = "0.8.2"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
|
@ -333,3 +413,69 @@ name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
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]
|
[package]
|
||||||
name = "krunvm"
|
name = "krunvm"
|
||||||
version = "0.1.6"
|
version = "0.2.3"
|
||||||
authors = ["Sergio Lopez <slp@redhat.com>"]
|
authors = ["Sergio Lopez <slp@redhat.com>"]
|
||||||
description = "Create microVMs from OCI images"
|
description = "Create microVMs from OCI images"
|
||||||
repository = "https://github.com/containers/krunvm"
|
repository = "https://github.com/containers/krunvm"
|
||||||
|
@ -9,9 +9,10 @@ edition = "2018"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33.3"
|
clap = {version = "4.4.6", features = ["derive"]}
|
||||||
confy = "0.4.0"
|
confy = "0.4.0"
|
||||||
libc = "0.2.82"
|
libc = "0.2.82"
|
||||||
serde = "1.0.120"
|
serde = "1.0.120"
|
||||||
serde_derive = "1.0.120"
|
serde_derive = "1.0.120"
|
||||||
text_io = "0.1.8"
|
text_io = "0.1.8"
|
||||||
|
nix = {version = "0.27.1", features = ["socket", "fs"]}
|
||||||
|
|
1
Makefile
1
Makefile
|
@ -1,7 +1,6 @@
|
||||||
OS = $(shell uname -s)
|
OS = $(shell uname -s)
|
||||||
KRUNVM_RELEASE = target/release/krunvm
|
KRUNVM_RELEASE = target/release/krunvm
|
||||||
KRUNVM_DEBUG = target/debug/krunvm
|
KRUNVM_DEBUG = target/debug/krunvm
|
||||||
INIT_BINARY = init/init
|
|
||||||
|
|
||||||
ifeq ($(PREFIX),)
|
ifeq ($(PREFIX),)
|
||||||
PREFIX := /usr/local
|
PREFIX := /usr/local
|
||||||
|
|
|
@ -46,6 +46,7 @@ dnf install -y krunvm
|
||||||
* Rust Toolchain
|
* Rust Toolchain
|
||||||
* [libkrun](https://github.com/containers/libkrun)
|
* [libkrun](https://github.com/containers/libkrun)
|
||||||
* [buildah](https://github.com/containers/buildah)
|
* [buildah](https://github.com/containers/buildah)
|
||||||
|
* [asciidoctor](https://github.com/asciidoctor/asciidoctor)
|
||||||
|
|
||||||
#### Building
|
#### Building
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,9 @@ host visible in the guest.
|
||||||
*-w, --workdir* _GUEST_PATH_::
|
*-w, --workdir* _GUEST_PATH_::
|
||||||
Configures _GUEST_PATH_ as the working directory for the first
|
Configures _GUEST_PATH_ as the working directory for the first
|
||||||
binary executed in the microVM.
|
binary executed in the microVM.
|
||||||
|
+
|
||||||
|
An empty string ("") tells krunvm to not set a working directory
|
||||||
|
explicitly, letting libkrun decide which one should be set.
|
||||||
|
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
|
|
|
@ -49,6 +49,9 @@ host visible in the guest.
|
||||||
*-w, --workdir* _GUEST_PATH_::
|
*-w, --workdir* _GUEST_PATH_::
|
||||||
Configures _GUEST_PATH_ as the working directory for the first
|
Configures _GUEST_PATH_ as the working directory for the first
|
||||||
binary executed in the microVM.
|
binary executed in the microVM.
|
||||||
|
+
|
||||||
|
An empty string ("") tells krunvm to not set a working directory
|
||||||
|
explicitly, letting libkrun decide which one should be set.
|
||||||
|
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
|
|
|
@ -18,8 +18,8 @@ and attaches stdin/stdout to its virtio-console providing a seamless
|
||||||
experience for interacting with the guest running inside it.
|
experience for interacting with the guest running inside it.
|
||||||
|
|
||||||
_COMMAND_ is the first binary to be executed in the microVM. If it's
|
_COMMAND_ is the first binary to be executed in the microVM. If it's
|
||||||
not present in the command line, krunvm-start(1) defaults to executing
|
not present in the command line, krunvm-start(1) lets libkrun decide
|
||||||
_/bin/sh_.
|
which binary will be executed.
|
||||||
|
|
||||||
Additional arguments for _COMMAND_ can be specified in the command
|
Additional arguments for _COMMAND_ can be specified in the command
|
||||||
line by appending _--_ followed by _ARGS_.
|
line by appending _--_ followed by _ARGS_.
|
||||||
|
@ -33,6 +33,8 @@ OPTIONS
|
||||||
*--mem* _NUM_::
|
*--mem* _NUM_::
|
||||||
Override amount of RAM, in MiB, configured for this microVM.
|
Override amount of RAM, in MiB, configured for this microVM.
|
||||||
|
|
||||||
|
*--env* _KEY=VALUE_::
|
||||||
|
Set environment variable to be passed to the microVM.
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
|
|
|
@ -1,21 +1,24 @@
|
||||||
// Copyright 2021 Red Hat, Inc.
|
// Copyright 2021 Red Hat, Inc.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use libc::{c_char, c_int};
|
||||||
|
|
||||||
#[link(name = "krun")]
|
#[link(name = "krun")]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn krun_set_log_level(level: u32) -> i32;
|
pub fn krun_set_log_level(level: u32) -> i32;
|
||||||
pub fn krun_create_ctx() -> i32;
|
pub fn krun_create_ctx() -> i32;
|
||||||
pub fn krun_free_ctx(ctx: u32) -> 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_vm_config(ctx: u32, num_vcpus: u8, ram_mib: u32) -> i32;
|
||||||
pub fn krun_set_root(ctx: u32, root_path: *const i8) -> 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 i8) -> 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 i8) -> 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 i8) -> i32;
|
pub fn krun_set_workdir(ctx: u32, workdir_path: *const c_char) -> i32;
|
||||||
pub fn krun_set_exec(
|
pub fn krun_set_exec(
|
||||||
ctx: u32,
|
ctx: u32,
|
||||||
exec_path: *const i8,
|
exec_path: *const c_char,
|
||||||
argv: *const *const i8,
|
argv: *const *const c_char,
|
||||||
envp: *const *const i8,
|
envp: *const *const c_char,
|
||||||
) -> i32;
|
) -> i32;
|
||||||
|
pub fn krun_set_env(ctx: u32, envp: *const *const c_char) -> i32;
|
||||||
pub fn krun_start_enter(ctx: u32) -> i32;
|
pub fn krun_start_enter(ctx: u32) -> i32;
|
||||||
}
|
}
|
||||||
|
|
119
src/changevm.rs
119
src/changevm.rs
|
@ -1,119 +0,0 @@
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
// 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,233 @@
|
||||||
|
// 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(())
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
// 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}");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
// 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);
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
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;
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright 2021 Red Hat, Inc.
|
// Copyright 2021 Red Hat, Inc.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use clap::Args;
|
||||||
|
use libc::c_char;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
@ -9,9 +11,76 @@ use std::os::unix::io::AsRawFd;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use super::bindings;
|
use crate::bindings;
|
||||||
use super::utils::{mount_container, umount_container};
|
use crate::utils::{mount_container, umount_container};
|
||||||
use crate::{ArgMatches, KrunvmConfig, VmConfig};
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
fn map_volumes(_ctx: u32, vmcfg: &VmConfig, rootfs: &str) {
|
fn map_volumes(_ctx: u32, vmcfg: &VmConfig, rootfs: &str) {
|
||||||
|
@ -43,6 +112,9 @@ fn map_volumes(_ctx: u32, vmcfg: &VmConfig, rootfs: &str) {
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
fn map_volumes(ctx: u32, vmcfg: &VmConfig, rootfs: &str) {
|
fn map_volumes(ctx: u32, vmcfg: &VmConfig, rootfs: &str) {
|
||||||
|
if vmcfg.mapped_volumes.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let mut volumes = Vec::new();
|
let mut volumes = Vec::new();
|
||||||
for (host_path, guest_path) in vmcfg.mapped_volumes.iter() {
|
for (host_path, guest_path) in vmcfg.mapped_volumes.iter() {
|
||||||
let full_guest = format!("{}{}", &rootfs, guest_path);
|
let full_guest = format!("{}{}", &rootfs, guest_path);
|
||||||
|
@ -66,7 +138,13 @@ fn map_volumes(ctx: u32, vmcfg: &VmConfig, rootfs: &str) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn exec_vm(vmcfg: &VmConfig, rootfs: &str, cmd: &str, args: Vec<CString>) {
|
unsafe fn exec_vm(
|
||||||
|
vmcfg: &VmConfig,
|
||||||
|
rootfs: &str,
|
||||||
|
cmd: Option<&str>,
|
||||||
|
args: Vec<CString>,
|
||||||
|
env_pairs: Vec<CString>,
|
||||||
|
) {
|
||||||
//bindings::krun_set_log_level(9);
|
//bindings::krun_set_log_level(9);
|
||||||
|
|
||||||
let ctx = bindings::krun_create_ctx() as u32;
|
let ctx = bindings::krun_create_ctx() as u32;
|
||||||
|
@ -78,7 +156,7 @@ unsafe fn exec_vm(vmcfg: &VmConfig, rootfs: &str, cmd: &str, args: Vec<CString>)
|
||||||
}
|
}
|
||||||
|
|
||||||
let c_rootfs = CString::new(rootfs).unwrap();
|
let c_rootfs = CString::new(rootfs).unwrap();
|
||||||
let ret = bindings::krun_set_root(ctx, c_rootfs.as_ptr() as *const i8);
|
let ret = bindings::krun_set_root(ctx, c_rootfs.as_ptr());
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
println!("Error setting VM rootfs");
|
println!("Error setting VM rootfs");
|
||||||
std::process::exit(-1);
|
std::process::exit(-1);
|
||||||
|
@ -91,50 +169,57 @@ unsafe fn exec_vm(vmcfg: &VmConfig, rootfs: &str, cmd: &str, args: Vec<CString>)
|
||||||
let map = format!("{}:{}", host_port, guest_port);
|
let map = format!("{}:{}", host_port, guest_port);
|
||||||
ports.push(CString::new(map).unwrap());
|
ports.push(CString::new(map).unwrap());
|
||||||
}
|
}
|
||||||
let mut ps: Vec<*const i8> = Vec::new();
|
let mut ps: Vec<*const c_char> = Vec::new();
|
||||||
for port in ports.iter() {
|
for port in ports.iter() {
|
||||||
ps.push(port.as_ptr() as *const i8);
|
ps.push(port.as_ptr());
|
||||||
}
|
}
|
||||||
ps.push(std::ptr::null());
|
ps.push(std::ptr::null());
|
||||||
|
|
||||||
let ret = bindings::krun_set_port_map(ctx, ps.as_ptr());
|
let ret = bindings::krun_set_port_map(ctx, ps.as_ptr());
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
println!("Error setting VM port map");
|
println!("Error setting VM port map");
|
||||||
std::process::exit(-1);
|
std::process::exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let c_workdir = CString::new(vmcfg.workdir.clone()).unwrap();
|
if !vmcfg.workdir.is_empty() {
|
||||||
let ret = bindings::krun_set_workdir(ctx, c_workdir.as_ptr() as *const i8);
|
let c_workdir = CString::new(vmcfg.workdir.clone()).unwrap();
|
||||||
if ret < 0 {
|
let ret = bindings::krun_set_workdir(ctx, c_workdir.as_ptr());
|
||||||
println!("Error setting VM workdir");
|
if ret < 0 {
|
||||||
std::process::exit(-1);
|
println!("Error setting VM workdir");
|
||||||
|
std::process::exit(-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut argv: Vec<*const i8> = Vec::new();
|
|
||||||
for a in args.iter() {
|
|
||||||
argv.push(a.as_ptr() as *const i8);
|
|
||||||
}
|
|
||||||
argv.push(std::ptr::null());
|
|
||||||
|
|
||||||
let hostname = CString::new(format!("HOSTNAME={}", vmcfg.name)).unwrap();
|
let hostname = CString::new(format!("HOSTNAME={}", vmcfg.name)).unwrap();
|
||||||
let home = CString::new("HOME=/root").unwrap();
|
let home = CString::new("HOME=/root").unwrap();
|
||||||
let path = CString::new("PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin").unwrap();
|
|
||||||
let env: [*const i8; 4] = [
|
|
||||||
hostname.as_ptr() as *const i8,
|
|
||||||
home.as_ptr() as *const i8,
|
|
||||||
path.as_ptr() as *const i8,
|
|
||||||
std::ptr::null(),
|
|
||||||
];
|
|
||||||
|
|
||||||
let c_cmd = CString::new(cmd).unwrap();
|
let mut env: Vec<*const c_char> = Vec::new();
|
||||||
let ret = bindings::krun_set_exec(
|
env.push(hostname.as_ptr());
|
||||||
ctx,
|
env.push(home.as_ptr());
|
||||||
c_cmd.as_ptr() as *const i8,
|
for value in env_pairs.iter() {
|
||||||
argv.as_ptr() as *const *const i8,
|
env.push(value.as_ptr());
|
||||||
env.as_ptr() as *const *const i8,
|
}
|
||||||
);
|
env.push(std::ptr::null());
|
||||||
if ret < 0 {
|
|
||||||
println!("Error setting VM config");
|
if let Some(cmd) = cmd {
|
||||||
std::process::exit(-1);
|
let mut argv: Vec<*const c_char> = Vec::new();
|
||||||
|
for a in args.iter() {
|
||||||
|
argv.push(a.as_ptr());
|
||||||
|
}
|
||||||
|
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());
|
||||||
|
if ret < 0 {
|
||||||
|
println!("Error setting VM config");
|
||||||
|
std::process::exit(-1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let ret = bindings::krun_set_env(ctx, env.as_ptr());
|
||||||
|
if ret < 0 {
|
||||||
|
println!("Error setting VM environment variables");
|
||||||
|
std::process::exit(-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret = bindings::krun_start_enter(ctx);
|
let ret = bindings::krun_start_enter(ctx);
|
||||||
|
@ -174,32 +259,3 @@ fn set_lock(rootfs: &str) -> File {
|
||||||
|
|
||||||
file
|
file
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(cfg: &KrunvmConfig, matches: &ArgMatches) {
|
|
||||||
let cmd = matches.value_of("COMMAND").unwrap();
|
|
||||||
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> = match matches.values_of("ARGS") {
|
|
||||||
Some(a) => a.map(|val| CString::new(val).unwrap()).collect(),
|
|
||||||
None => Vec::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
set_rlimits();
|
|
||||||
|
|
||||||
let _file = set_lock(&rootfs);
|
|
||||||
|
|
||||||
unsafe { exec_vm(vmcfg, &rootfs, cmd, args) };
|
|
||||||
|
|
||||||
umount_container(cfg, vmcfg).expect("Error unmounting container");
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
// 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
|
|
||||||
);
|
|
||||||
}
|
|
130
src/create.rs
130
src/create.rs
|
@ -1,130 +0,0 @@
|
||||||
// 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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
// 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();
|
|
||||||
}
|
|
27
src/list.rs
27
src/list.rs
|
@ -1,27 +0,0 @@
|
||||||
// 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!();
|
|
||||||
}
|
|
||||||
}
|
|
253
src/main.rs
253
src/main.rs
|
@ -7,19 +7,17 @@ use std::fs::File;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
|
|
||||||
use clap::{crate_version, App, Arg, ArgMatches};
|
use crate::commands::{
|
||||||
|
ChangeVmCmd, ConfigCmd, CreateCmd, DeleteCmd, InspectCmd, ListCmd, StartCmd,
|
||||||
|
};
|
||||||
|
use clap::{Parser, Subcommand};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use text_io::read;
|
use text_io::read;
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
mod bindings;
|
mod bindings;
|
||||||
mod changevm;
|
mod commands;
|
||||||
mod config;
|
|
||||||
mod create;
|
|
||||||
mod delete;
|
|
||||||
mod list;
|
|
||||||
mod start;
|
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
const APP_NAME: &str = "krunvm";
|
const APP_NAME: &str = "krunvm";
|
||||||
|
@ -149,223 +147,44 @@ 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() {
|
fn main() {
|
||||||
let mut cfg: KrunvmConfig = confy::load(APP_NAME).unwrap();
|
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("/root"),
|
|
||||||
)
|
|
||||||
.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)
|
|
||||||
.default_value("/bin/sh"),
|
|
||||||
)
|
|
||||||
.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")]
|
#[cfg(target_os = "macos")]
|
||||||
check_volume(&mut cfg);
|
check_volume(&mut cfg);
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
check_unshare();
|
check_unshare();
|
||||||
|
|
||||||
if let Some(matches) = matches.subcommand_matches("changevm") {
|
match cli_args.command {
|
||||||
changevm::changevm(&mut cfg, matches);
|
Command::Inspect(cmd) => cmd.run(&mut cfg),
|
||||||
} else if let Some(matches) = matches.subcommand_matches("config") {
|
Command::Start(cmd) => cmd.run(&cfg),
|
||||||
config::config(&mut cfg, matches);
|
Command::Create(cmd) => cmd.run(&mut cfg),
|
||||||
} else if let Some(matches) = matches.subcommand_matches("create") {
|
Command::List(cmd) => cmd.run(&cfg),
|
||||||
create::create(&mut cfg, matches);
|
Command::Delete(cmd) => cmd.run(&mut cfg),
|
||||||
} else if let Some(matches) = matches.subcommand_matches("delete") {
|
Command::ChangeVm(cmd) => cmd.run(&mut cfg),
|
||||||
delete::delete(&mut cfg, matches);
|
Command::Config(cmd) => cmd.run(&mut cfg),
|
||||||
} 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!();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
129
src/utils.rs
129
src/utils.rs
|
@ -4,11 +4,13 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::{KrunvmConfig, VmConfig, APP_NAME};
|
use crate::{KrunvmConfig, VmConfig, APP_NAME};
|
||||||
|
|
||||||
pub enum BuildahCommand {
|
pub enum BuildahCommand {
|
||||||
From,
|
From,
|
||||||
|
Inspect,
|
||||||
Mount,
|
Mount,
|
||||||
Unmount,
|
Unmount,
|
||||||
Remove,
|
Remove,
|
||||||
|
@ -18,6 +20,7 @@ pub enum BuildahCommand {
|
||||||
pub fn get_buildah_args(_cfg: &KrunvmConfig, cmd: BuildahCommand) -> Vec<String> {
|
pub fn get_buildah_args(_cfg: &KrunvmConfig, cmd: BuildahCommand) -> Vec<String> {
|
||||||
match cmd {
|
match cmd {
|
||||||
BuildahCommand::From => vec!["from".to_string()],
|
BuildahCommand::From => vec!["from".to_string()],
|
||||||
|
BuildahCommand::Inspect => vec!["inspect".to_string()],
|
||||||
BuildahCommand::Mount => vec!["mount".to_string()],
|
BuildahCommand::Mount => vec!["mount".to_string()],
|
||||||
BuildahCommand::Unmount => vec!["umount".to_string()],
|
BuildahCommand::Unmount => vec!["umount".to_string()],
|
||||||
BuildahCommand::Remove => vec!["rm".to_string()],
|
BuildahCommand::Remove => vec!["rm".to_string()],
|
||||||
|
@ -53,6 +56,9 @@ pub fn get_buildah_args(cfg: &KrunvmConfig, cmd: BuildahCommand) -> Vec<String>
|
||||||
args.push("--os".to_string());
|
args.push("--os".to_string());
|
||||||
args.push("linux".to_string());
|
args.push("linux".to_string());
|
||||||
}
|
}
|
||||||
|
BuildahCommand::Inspect => {
|
||||||
|
args.push("inspect".to_string());
|
||||||
|
}
|
||||||
BuildahCommand::Mount => {
|
BuildahCommand::Mount => {
|
||||||
args.push("mount".to_string());
|
args.push("mount".to_string());
|
||||||
}
|
}
|
||||||
|
@ -66,70 +72,121 @@ pub fn get_buildah_args(cfg: &KrunvmConfig, cmd: BuildahCommand) -> Vec<String>
|
||||||
args
|
args
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_mapped_ports(port_matches: Vec<&str>) -> HashMap<String, String> {
|
#[derive(Debug, Clone)]
|
||||||
let mut mapped_ports = HashMap::new();
|
pub struct PortPair {
|
||||||
for port in port_matches.iter() {
|
pub host_port: String,
|
||||||
let vtuple: Vec<&str> = port.split(':').collect();
|
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();
|
||||||
if vtuple.len() != 2 {
|
if vtuple.len() != 2 {
|
||||||
println!("Invalid value for \"port\"");
|
return Err("Too many ':' separators");
|
||||||
std::process::exit(-1);
|
|
||||||
}
|
}
|
||||||
let host_port: u16 = match vtuple[0].parse() {
|
let host_port: u16 = match vtuple[0].parse() {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("Invalid host port");
|
return Err("Invalid host port");
|
||||||
std::process::exit(-1);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let guest_port: u16 = match vtuple[1].parse() {
|
let guest_port: u16 = match vtuple[1].parse() {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("Invalid guest port");
|
return Err("Invalid guest port");
|
||||||
std::process::exit(-1);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Ok(PortPair {
|
||||||
mapped_ports.insert(host_port.to_string(), guest_port.to_string());
|
host_port: host_port.to_string(),
|
||||||
|
guest_port: guest_port.to_string(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
mapped_ports
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_mapped_volumes(volume_matches: Vec<&str>) -> HashMap<String, String> {
|
#[derive(Debug, Clone)]
|
||||||
let mut mapped_volumes = HashMap::new();
|
pub struct PathPair {
|
||||||
for volume in volume_matches.iter() {
|
pub host_path: String,
|
||||||
let vtuple: Vec<&str> = volume.split(':').collect();
|
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();
|
||||||
if vtuple.len() != 2 {
|
if vtuple.len() != 2 {
|
||||||
println!("Invalid value for \"volume\"");
|
return Err("Too many ':' separators");
|
||||||
std::process::exit(-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let host_path = Path::new(vtuple[0]);
|
let host_path = Path::new(vtuple[0]);
|
||||||
if !host_path.is_absolute() {
|
if !host_path.is_absolute() {
|
||||||
println!("Invalid volume, host_path is not an absolute path");
|
return Err("Invalid volume, host_path is not an absolute path");
|
||||||
std::process::exit(-1);
|
|
||||||
}
|
}
|
||||||
if !host_path.exists() {
|
if !host_path.exists() {
|
||||||
println!("Invalid volume, host_path does not exists");
|
return Err("Invalid volume, host_path does not exists");
|
||||||
std::process::exit(-1);
|
|
||||||
}
|
}
|
||||||
let guest_path = Path::new(vtuple[1]);
|
let guest_path = Path::new(vtuple[1]);
|
||||||
if !guest_path.is_absolute() {
|
if !guest_path.is_absolute() {
|
||||||
println!("Invalid volume, guest_path is not an absolute path");
|
return Err("Invalid volume, guest_path is not an absolute path");
|
||||||
std::process::exit(-1);
|
|
||||||
}
|
}
|
||||||
if guest_path.components().count() != 2 {
|
if guest_path.components().count() != 2 {
|
||||||
println!(
|
return Err(
|
||||||
"Invalid volume, only single direct root children are supported as guest_path"
|
"Invalid volume, only single direct root children are supported as guest_path",
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
host_path: vtuple[0].to_string(),
|
||||||
|
guest_path: vtuple[1].to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
fn fix_root_mode(rootfs: &str) {
|
||||||
|
let mut args = vec!["-w", "user.containers.override_stat", "0:0:0555"];
|
||||||
|
args.push(rootfs);
|
||||||
|
|
||||||
|
let output = match Command::new("xattr")
|
||||||
|
.args(&args)
|
||||||
|
.stderr(std::process::Stdio::inherit())
|
||||||
|
.output()
|
||||||
|
{
|
||||||
|
Ok(output) => output,
|
||||||
|
Err(err) => {
|
||||||
|
if err.kind() == std::io::ErrorKind::NotFound {
|
||||||
|
println!("{} requires xattr to manage the OCI images, and it wasn't found on this system.", APP_NAME);
|
||||||
|
} else {
|
||||||
|
println!("Error executing xattr: {}", err);
|
||||||
|
}
|
||||||
std::process::exit(-1);
|
std::process::exit(-1);
|
||||||
}
|
}
|
||||||
mapped_volumes.insert(
|
};
|
||||||
host_path.to_str().unwrap().to_string(),
|
|
||||||
guest_path.to_str().unwrap().to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
mapped_volumes
|
let exit_code = output.status.code().unwrap_or(-1);
|
||||||
|
if exit_code != 0 {
|
||||||
|
println!("xattr returned an error: {}", exit_code);
|
||||||
|
std::process::exit(-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
|
@ -163,6 +220,10 @@ pub fn mount_container(cfg: &KrunvmConfig, vmcfg: &VmConfig) -> Result<String, s
|
||||||
}
|
}
|
||||||
|
|
||||||
let rootfs = std::str::from_utf8(&output.stdout).unwrap().trim();
|
let rootfs = std::str::from_utf8(&output.stdout).unwrap().trim();
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
fix_root_mode(rootfs);
|
||||||
|
|
||||||
Ok(rootfs.to_string())
|
Ok(rootfs.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue