mirror of https://github.com/containers/krunvm.git
Compare commits
70 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 | |
|
b1673da3a1 | |
|
fd963f6b4c | |
|
718de12e2e | |
|
f88a1dccf0 | |
|
3b3948df8b | |
|
775217ef34 | |
|
ebf3e3004f | |
|
8244dd6f30 | |
|
d7d4d6741d | |
|
38f1ea1232 | |
|
eedd4054ed | |
|
3e9a284a79 | |
|
20359b534d | |
|
8027270987 | |
|
630c475fcd | |
|
5add8c5da7 | |
|
8ba8cf23b4 | |
|
6247b3da93 | |
|
3ef17f30d4 | |
|
367898a940 | |
|
1a5597620c | |
|
75c83b923e | |
|
7300dc97b9 | |
|
a349505e54 | |
|
cd55ff6d42 | |
|
93e31a4867 | |
|
c21fcd0c51 |
|
@ -0,0 +1,42 @@
|
|||
name: Code Quality (rustfmt and clippy)
|
||||
on: [pull_request, create]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.event_name == 'pull_request'
|
||||
name: Code Quality (clippy, rustfmt)
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- stable
|
||||
target:
|
||||
- x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- name: Code checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Rust toolchain (${{ matrix.rust }})
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- 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
|
|
@ -1,4 +1,3 @@
|
|||
Cargo.lock
|
||||
/build/
|
||||
target
|
||||
*.rs.bk
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
## The krunvm Project Community Code of Conduct
|
||||
|
||||
The krunvm Project follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/master/CODE-OF-CONDUCT.md).
|
|
@ -0,0 +1,481 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
|
||||
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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
||||
|
||||
[[package]]
|
||||
name = "blake2b_simd"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"constant_time_eq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
|
||||
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",
|
||||
"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]]
|
||||
name = "confy"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2913470204e9e8498a0f31f17f90a0de801ae92c8c5ac18c49af4819e6786697"
|
||||
dependencies = [
|
||||
"directories",
|
||||
"serde",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if 1.0.0",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "directories"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "krunvm"
|
||||
version = "0.2.3"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"confy",
|
||||
"libc",
|
||||
"nix",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"text_io",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.149"
|
||||
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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"redox_syscall",
|
||||
"rust-argon2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-argon2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"blake2b_simd",
|
||||
"constant_time_eq",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.124"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f"
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.124"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"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 = "toml"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
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,15 +1,18 @@
|
|||
[package]
|
||||
name = "krunvm"
|
||||
version = "0.1.3"
|
||||
version = "0.2.3"
|
||||
authors = ["Sergio Lopez <slp@redhat.com>"]
|
||||
description = "Create microVMs from OCI images"
|
||||
repository = "https://github.com/containers/krunvm"
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.3"
|
||||
clap = {version = "4.4.6", features = ["derive"]}
|
||||
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,7 +1,6 @@
|
|||
OS = $(shell uname -s)
|
||||
KRUNVM_RELEASE = target/release/krunvm
|
||||
KRUNVM_DEBUG = target/debug/krunvm
|
||||
INIT_BINARY = init/init
|
||||
|
||||
ifeq ($(PREFIX),)
|
||||
PREFIX := /usr/local
|
||||
|
|
35
README.md
35
README.md
|
@ -1,6 +1,6 @@
|
|||
# krunvm
|
||||
|
||||
```krunvm``` is a CLI-based utility for managing lightweight VMs created from OCI images, using [libkrun](https://github.com/containers/libkrun) and [buildah](https://github.com/containers/buildah).
|
||||
```krunvm``` is a CLI-based utility for creating microVMs from OCI images, using [libkrun](https://github.com/containers/libkrun) and [buildah](https://github.com/containers/buildah).
|
||||
|
||||
## Features
|
||||
|
||||
|
@ -15,6 +15,12 @@
|
|||
|
||||
[](https://asciinema.org/a/CGtTS93VsdzWwUfkY1kqVnaik)
|
||||
|
||||
## Supported platforms
|
||||
|
||||
- Linux/KVM on x86_64.
|
||||
- Linux/KVM on AArch64.
|
||||
- macOS/Hypervisor.framework on ARM64.
|
||||
|
||||
## Installation
|
||||
|
||||
### macOS
|
||||
|
@ -27,10 +33,10 @@ brew install krunvm
|
|||
### Fedora
|
||||
|
||||
```
|
||||
dnf copr enable slp/libkrunfw
|
||||
dnf copr enable slp/libkrun
|
||||
dnf copr enable slp/krunvm
|
||||
dnf install krunvm
|
||||
dnf copr enable -y slp/libkrunfw
|
||||
dnf copr enable -y slp/libkrun
|
||||
dnf copr enable -y slp/krunvm
|
||||
dnf install -y krunvm
|
||||
```
|
||||
|
||||
### Building from sources
|
||||
|
@ -40,27 +46,10 @@ dnf install krunvm
|
|||
* Rust Toolchain
|
||||
* [libkrun](https://github.com/containers/libkrun)
|
||||
* [buildah](https://github.com/containers/buildah)
|
||||
* [asciidoctor](https://github.com/asciidoctor/asciidoctor)
|
||||
|
||||
#### Building
|
||||
|
||||
```
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
### Networking
|
||||
|
||||
#### Networking support is limited to TCP IPv4
|
||||
|
||||
The current implementation of TSI (Transparent Socket Impersonation)
|
||||
in libkrun is limited to TCP and IPv4. This is expected to improve
|
||||
soon.
|
||||
|
||||
#### Domain name resolution is broken on musl-based distributions
|
||||
|
||||
As a consequence of the previous point, libkrun-based VMs need to use
|
||||
TCP for connecting to the DNS servers. **musl libc** does not support
|
||||
domain name resolution using TCP, so on distributions based on this
|
||||
library (such as Alpine), name resolution is broken.
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
## Security and Disclosure Information Policy for the krunvm Project
|
||||
|
||||
The krunvm Project follows the [Security and Disclosure Information Policy](https://github.com/containers/common/blob/master/SECURITY.md) for the Containers Projects.
|
||||
|
56
build.rs
56
build.rs
|
@ -1,4 +1,60 @@
|
|||
use std::path::Path;
|
||||
use std::{env, fs, io, process};
|
||||
|
||||
const COMMANDS: [&str; 7] = [
|
||||
"krunvm",
|
||||
"krunvm-changevm",
|
||||
"krunvm-create",
|
||||
"krunvm-config",
|
||||
"krunvm-delete",
|
||||
"krunvm-list",
|
||||
"krunvm-start",
|
||||
];
|
||||
|
||||
fn main() {
|
||||
let outdir = match env::var_os("OUT_DIR") {
|
||||
Some(outdir) => outdir,
|
||||
None => {
|
||||
panic!("OUT_DIR environment variable not defined.");
|
||||
}
|
||||
};
|
||||
fs::create_dir_all(&outdir).unwrap();
|
||||
|
||||
for command in COMMANDS {
|
||||
if let Err(err) = generate_man_page(&outdir, command) {
|
||||
panic!("failed to generate man page: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
println!("cargo:rustc-link-search=/opt/homebrew/lib");
|
||||
}
|
||||
|
||||
fn generate_man_page<P: AsRef<Path>>(outdir: P, command: &str) -> io::Result<()> {
|
||||
// If asciidoctor isn't installed, fallback to asciidoc.
|
||||
if let Err(err) = process::Command::new("asciidoctor").output() {
|
||||
eprintln!("Error from running 'asciidoctor': {}", err);
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let outdir = outdir.as_ref();
|
||||
let outfile = outdir.join(format!("{}.1", command));
|
||||
let cwd = env::current_dir()?;
|
||||
let txt_path = cwd.join("docs").join(format!("{}.1.txt", command));
|
||||
|
||||
let result = process::Command::new("asciidoctor")
|
||||
.arg("--doctype")
|
||||
.arg("manpage")
|
||||
.arg("--backend")
|
||||
.arg("manpage")
|
||||
.arg("--out-file")
|
||||
.arg(&outfile)
|
||||
.arg(&txt_path)
|
||||
.spawn()?
|
||||
.wait()?;
|
||||
if !result.success() {
|
||||
let msg = format!("'asciidoctor' failed with exit code {:?}", result.code());
|
||||
return Err(io::Error::new(io::ErrorKind::Other, msg));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
krunvm-changevm(1)
|
||||
==================
|
||||
|
||||
NAME
|
||||
----
|
||||
krunvm-changevm - Change the configuration of a microVM
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
*krunvm changevm* [_OPTIONS_] _microVM_
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
*krunvm changevm* changes the configuration of an existing microVM.
|
||||
|
||||
When run without any _OPTIONS_, it displays the current configuration
|
||||
of the microVM.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
*--remove-ports*::
|
||||
Removes all port mappings.
|
||||
|
||||
*--remote-volumes*::
|
||||
Removes all volume mappings.
|
||||
|
||||
*--cpus* _NUM_::
|
||||
Changes the number of vCPUs that will be created for this microVM.
|
||||
|
||||
*--mem* _NUM_::
|
||||
Changes the amount of RAM, in MiB, that will be available to this
|
||||
microVM.
|
||||
+
|
||||
The memory configured for the microVM will not be reserved
|
||||
immediately. Instead, it will be provided as the guest demands it, and
|
||||
both the guest and libkrun (acting as the Virtual Machine Monitor)
|
||||
will attempt to return as many pages as possible to the host.
|
||||
|
||||
*--name* _NAME_::
|
||||
Assigns a new name to the microVM.
|
||||
|
||||
*-p, --port* _HOST_PORT:GUEST_PORT_::
|
||||
Exposes a port in the guest running in the microVM through a port in the host.
|
||||
+
|
||||
This option can be specified multiple times to expose as many guest
|
||||
ports as desired.
|
||||
|
||||
*-v, --volume* _HOST_PATH:GUEST_PATH_::
|
||||
Makes _HOST_PATH_ visible in the guest running in the microVM through _GUEST_PATH_.
|
||||
+
|
||||
This option can be specified multiple times to make more paths in the
|
||||
host visible in the guest.
|
||||
|
||||
*-w, --workdir* _GUEST_PATH_::
|
||||
Configures _GUEST_PATH_ as the working directory for the first
|
||||
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
|
||||
--------
|
||||
*krunvm(1)*, *krunvm-create(1)*
|
|
@ -0,0 +1,40 @@
|
|||
krunvm-config(1)
|
||||
================
|
||||
|
||||
NAME
|
||||
----
|
||||
krunvm-config - Configure default values
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
*krunvm config* [_OPTIONS_]
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
*krunvm config* configures the default values that will be used for
|
||||
newly created microVMs when a explicit value has not been passed to
|
||||
*krunvm-create(1)*
|
||||
|
||||
When run without any _OPTIONS_ it displays the current default values.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
*--cpus* _NUM_::
|
||||
Sets the default number of vCPUs that will be configured for newly
|
||||
created microVMs.
|
||||
|
||||
*--dns* _IP_::
|
||||
Sets the default IP that will be configured as DNS for newly created
|
||||
microVMs.
|
||||
|
||||
*--mem* _NUM_::
|
||||
Sets the default mount of RAM, in MiB, that will be configured for
|
||||
newly created microVMs.
|
||||
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
*krunvm(1)*
|
|
@ -0,0 +1,59 @@
|
|||
krunvm-create(1)
|
||||
================
|
||||
|
||||
NAME
|
||||
----
|
||||
krunvm-create - Create a new microVM from an OCI image
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
*krunvm create* [_OPTIONS_] _IMAGE_
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
*krunvm create* creates a new microVM from the OCI image specified by
|
||||
_IMAGE_. Please refer to buildah-from(1) for information about the
|
||||
format supported by the _IMAGE_ argument.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
*--cpus* _NUM_::
|
||||
The number of vCPUs that will be created for this microVM.
|
||||
|
||||
*--mem* _NUM_::
|
||||
The amount of RAM, in MiB, that will be available to this microVM.
|
||||
+
|
||||
The memory configured for the microVM will not be reserved
|
||||
immediately. Instead, it will be provided as the guest demands it, and
|
||||
both the guest and libkrun (acting as the Virtual Machine Monitor)
|
||||
will attempt to return as many pages as possible to the host.
|
||||
|
||||
*--name* _NAME_::
|
||||
The name to be assigned to this microVM.
|
||||
|
||||
*-p, --port* _HOST_PORT:GUEST_PORT_::
|
||||
Exposes a port in the guest running in the microVM through a port in the host.
|
||||
+
|
||||
This option can be specified multiple times to expose as many guest
|
||||
ports as desired.
|
||||
|
||||
*-v, --volume* _HOST_PATH:GUEST_PATH_::
|
||||
Makes _HOST_PATH_ visible in the guest running in the microVM through _GUEST_PATH_.
|
||||
+
|
||||
This option can be specified multiple times to make more paths in the
|
||||
host visible in the guest.
|
||||
|
||||
*-w, --workdir* _GUEST_PATH_::
|
||||
Configures _GUEST_PATH_ as the working directory for the first
|
||||
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
|
||||
--------
|
||||
*buildah(1)*, *buildah-from(1)*, *krunvm(1)*, *krunvm-changevm(1)*
|
|
@ -0,0 +1,22 @@
|
|||
krunvm-delete(1)
|
||||
================
|
||||
|
||||
NAME
|
||||
----
|
||||
krunvm-delete - Deletes an existing microVM
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
*krunvm delete* _microVM_
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
*krunvm delete* deletes an existing microVM configuration and requests
|
||||
to buildah(1) to unmount and remove the OCI image that was backing it.
|
||||
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
*buildah(1)*, *krunvm(1)*
|
|
@ -0,0 +1,22 @@
|
|||
krunvm-list(1)
|
||||
==============
|
||||
|
||||
NAME
|
||||
----
|
||||
krunvm-list - Lists the existing microVMs
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
*krunvm list*
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
*krunvm list* lists the microVMs created by *krunvm-create(1)* that
|
||||
have not been removed by *krunvm-delete(1)*.
|
||||
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
*krunvm(1)*, *krunvm-create(1)*, *krunvm-delete(1)*
|
|
@ -0,0 +1,41 @@
|
|||
krunvm-start(1)
|
||||
===============
|
||||
|
||||
NAME
|
||||
----
|
||||
krunvm-start - Starts an existing microVM
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
*krunvm start* [_OPTIONS_] _microVM_ [_COMMAND_] [-- ARGS]
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
*krunvm start* starts an existing microVM created by krunvm-create(1)
|
||||
and attaches stdin/stdout to its virtio-console providing a seamless
|
||||
experience for interacting with the guest running inside it.
|
||||
|
||||
_COMMAND_ is the first binary to be executed in the microVM. If it's
|
||||
not present in the command line, krunvm-start(1) lets libkrun decide
|
||||
which binary will be executed.
|
||||
|
||||
Additional arguments for _COMMAND_ can be specified in the command
|
||||
line by appending _--_ followed by _ARGS_.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
*--cpus* _NUM_::
|
||||
Override the number of vCPUs configured for this microVM.
|
||||
|
||||
*--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
|
||||
--------
|
||||
*krunvm(1)*, *krunvm-create(1)*, *krunvm-changevm(1)*
|
|
@ -0,0 +1,59 @@
|
|||
krunvm(1)
|
||||
=========
|
||||
|
||||
NAME
|
||||
----
|
||||
krunvm - Create microVMs from OCI images
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
*krunvm* [_GLOBAL_OPTIONS_] *command*
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
krunvm is a CLI utility to create, manage and start microVMs which are
|
||||
generated from OCI images, providing an interface that resembles
|
||||
operating on conventional containers.
|
||||
|
||||
krunvm uses buildah(1) to download the OCI image and mount it into a
|
||||
local directory, and libkrun to launch the microVM.
|
||||
|
||||
The local directory where the OCI image has been mounted is used as
|
||||
the root filesystem for the microVM, serviced by a virtio-fs
|
||||
device/server bundled into libkrun.
|
||||
|
||||
krunvm supports mounting additional local directories into the
|
||||
microVM and exposing ports from the guest to the host (and the
|
||||
networks connected to it).
|
||||
|
||||
Networking to the guest running in the microVM is provided by
|
||||
libkrun's TSI (Transparent Socket Impersonation), enabling a seamless
|
||||
experience that doesn't require network bridges nor other explicit
|
||||
network configuration.
|
||||
|
||||
|
||||
GLOBAL OPTIONS
|
||||
--------------
|
||||
*-v* _NUM_::
|
||||
Sets the verbosity level, from the lowest (0) to the highest (5).
|
||||
|
||||
|
||||
COMMANDS
|
||||
--------
|
||||
|===
|
||||
|Command | Description
|
||||
|
||||
|krunvm-changevm(1) | Change the configuration of a microVM
|
||||
|krunvm-config(1) | Configure global values
|
||||
|krunvm-create(1) | Create a new microVM
|
||||
|krunvm-delete(1) | Delete an existing microVM
|
||||
|krunvm-list(1) | List the existing microVMs
|
||||
|krunvm-start(1) | Start an existing microVM
|
||||
|===
|
||||
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
*buildah(1)*
|
|
@ -1,21 +1,24 @@
|
|||
// 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 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_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_exec(
|
||||
ctx: u32,
|
||||
exec_path: *const i8,
|
||||
argv: *const *const i8,
|
||||
envp: *const *const i8,
|
||||
exec_path: *const c_char,
|
||||
argv: *const *const c_char,
|
||||
envp: *const *const c_char,
|
||||
) -> i32;
|
||||
pub fn krun_set_env(ctx: u32, envp: *const *const c_char) -> 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,16 +1,86 @@
|
|||
// 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")]
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
#[cfg(target_os = "macos")]
|
||||
use std::path::Path;
|
||||
|
||||
use super::bindings;
|
||||
use super::utils::{mount_container, umount_container};
|
||||
use crate::{ArgMatches, KrunvmConfig, VmConfig};
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn map_volumes(_ctx: u32, vmcfg: &VmConfig, rootfs: &str) {
|
||||
|
@ -41,7 +111,10 @@ fn map_volumes(_ctx: u32, vmcfg: &VmConfig, rootfs: &str) {
|
|||
}
|
||||
|
||||
#[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();
|
||||
for (host_path, guest_path) in vmcfg.mapped_volumes.iter() {
|
||||
let full_guest = format!("{}{}", &rootfs, guest_path);
|
||||
|
@ -58,14 +131,20 @@ fn map_volumes(ctx: u32, vmcfg: &VmConfig, _rootfs: &str) {
|
|||
vols.push(vol.as_ptr());
|
||||
}
|
||||
vols.push(std::ptr::null());
|
||||
let ret = bindings::krun_set_mapped_volumes(ctx, vols.as_ptr());
|
||||
let ret = unsafe { bindings::krun_set_mapped_volumes(ctx, vols.as_ptr()) };
|
||||
if ret < 0 {
|
||||
println!("Error setting VM mapped volumes");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
let ctx = bindings::krun_create_ctx() as u32;
|
||||
|
@ -77,64 +156,71 @@ unsafe fn exec_vm(vmcfg: &VmConfig, rootfs: &str, cmd: &str, args: Vec<CString>)
|
|||
}
|
||||
|
||||
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 {
|
||||
println!("Error setting VM rootfs");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
|
||||
map_volumes(ctx, &vmcfg, rootfs);
|
||||
map_volumes(ctx, vmcfg, rootfs);
|
||||
|
||||
let mut ports = Vec::new();
|
||||
for (host_port, guest_port) in vmcfg.mapped_ports.iter() {
|
||||
let map = format!("{}:{}", host_port, guest_port);
|
||||
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() {
|
||||
ps.push(port.as_ptr() as *const i8);
|
||||
ps.push(port.as_ptr());
|
||||
}
|
||||
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");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
|
||||
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() as *const i8);
|
||||
let ret = bindings::krun_set_workdir(ctx, c_workdir.as_ptr());
|
||||
if ret < 0 {
|
||||
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 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 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());
|
||||
|
||||
if let Some(cmd) = cmd {
|
||||
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() as *const i8,
|
||||
argv.as_ptr() as *const *const i8,
|
||||
env.as_ptr() as *const *const i8,
|
||||
);
|
||||
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);
|
||||
if ret < 0 {
|
||||
|
@ -173,32 +259,3 @@ fn set_lock(rootfs: &str) -> 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
|
||||
);
|
||||
}
|
141
src/create.rs
141
src/create.rs
|
@ -1,141 +0,0 @@
|
|||
// Copyright 2021 Red Hat, Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::process::Command;
|
||||
|
||||
use super::utils::{mount_container, parse_mapped_ports, parse_mapped_volumes, umount_container};
|
||||
use crate::{ArgMatches, KrunvmConfig, VmConfig, APP_NAME};
|
||||
|
||||
fn fix_resolv_conf(rootfs: &str, dns: &str) -> Result<(), std::io::Error> {
|
||||
let resolvconf = format!("{}/etc/resolv.conf", rootfs);
|
||||
let mut file = 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);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
let mut args = vec!["from"];
|
||||
#[cfg(target_os = "macos")]
|
||||
let storage_root = format!("{}/root", cfg.storage_volume);
|
||||
#[cfg(target_os = "macos")]
|
||||
let storage_runroot = format!("{}/runroot", cfg.storage_volume);
|
||||
#[cfg(target_os = "macos")]
|
||||
let mut args = vec![
|
||||
"--root",
|
||||
&storage_root,
|
||||
"--runroot",
|
||||
&storage_runroot,
|
||||
"from",
|
||||
"--os",
|
||||
"linux",
|
||||
];
|
||||
|
||||
args.push(image);
|
||||
|
||||
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.to_string());
|
||||
}
|
||||
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!("Lightweight VM 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 lightweight VMs found");
|
||||
} else {
|
||||
for (_name, vm) in cfg.vmconfig_map.iter() {
|
||||
println!();
|
||||
printvm(vm);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
261
src/main.rs
261
src/main.rs
|
@ -7,19 +7,17 @@ use std::fs::File;
|
|||
#[cfg(target_os = "macos")]
|
||||
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};
|
||||
#[cfg(target_os = "macos")]
|
||||
use text_io::read;
|
||||
|
||||
#[allow(unused)]
|
||||
mod bindings;
|
||||
mod changevm;
|
||||
mod config;
|
||||
mod create;
|
||||
mod delete;
|
||||
mod list;
|
||||
mod start;
|
||||
mod commands;
|
||||
mod utils;
|
||||
|
||||
const APP_NAME: &str = "krunvm";
|
||||
|
@ -96,8 +94,8 @@ fn check_volume(cfg: &mut KrunvmConfig) {
|
|||
println!(
|
||||
"
|
||||
On macOS, krunvm requires a dedicated, case-sensitive volume.
|
||||
You can easily such volume by executing something like this on
|
||||
another terminal:
|
||||
You can easily create such volume by executing something like
|
||||
this on another terminal:
|
||||
|
||||
diskutil apfs addVolume disk3 \"Case-sensitive APFS\" krunvm
|
||||
|
||||
|
@ -140,232 +138,53 @@ volume.
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn check_unshare() {
|
||||
let uid = unsafe { libc::getuid() };
|
||||
if uid != 0 {
|
||||
if std::env::vars()
|
||||
.find(|(key, _)| key == "BUILDAH_ISOLATION")
|
||||
.is_none()
|
||||
{
|
||||
if uid != 0 && !std::env::vars().any(|(key, _)| key == "BUILDAH_ISOLATION") {
|
||||
println!("Please re-run krunvm inside a \"buildah unshare\" session");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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 mut app = App::new("krunvm")
|
||||
.version(crate_version!())
|
||||
.author("Sergio Lopez <slp@redhat.com>")
|
||||
.about("Manage lightweight VMs 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 lightweight VM")
|
||||
.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("workdir")
|
||||
.long("workdir")
|
||||
.short("w")
|
||||
.help("Working directory inside the lightweight VM")
|
||||
.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 lightweight VM")
|
||||
.takes_value(true),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
App::new("create")
|
||||
.about("Create a new lightweight VM")
|
||||
.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 lightweight VM")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("workdir")
|
||||
.long("workdir")
|
||||
.short("w")
|
||||
.help("Working directory inside the lightweight VM")
|
||||
.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 lightweight VM")
|
||||
.arg(
|
||||
Arg::with_name("NAME")
|
||||
.help("Name of the lightweight VM to be deleted")
|
||||
.required(true)
|
||||
.index(1),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
App::new("list").about("List lightweight VMs").arg(
|
||||
Arg::with_name("debug")
|
||||
.short("d")
|
||||
.help("print debug information verbosely"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
App::new("start")
|
||||
.about("Start an existing lightweight VM")
|
||||
.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 lightweight VM")
|
||||
.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();
|
||||
let cli_args = Cli::parse();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
check_volume(&mut cfg);
|
||||
#[cfg(target_os = "linux")]
|
||||
check_unshare();
|
||||
|
||||
if let Some(ref matches) = matches.subcommand_matches("changevm") {
|
||||
changevm::changevm(&mut cfg, matches);
|
||||
} else if let Some(ref matches) = matches.subcommand_matches("config") {
|
||||
config::config(&mut cfg, matches);
|
||||
} else if let Some(ref matches) = matches.subcommand_matches("create") {
|
||||
create::create(&mut cfg, matches);
|
||||
} else if let Some(ref matches) = matches.subcommand_matches("delete") {
|
||||
delete::delete(&mut cfg, matches);
|
||||
} else if let Some(ref matches) = matches.subcommand_matches("list") {
|
||||
list::list(&cfg, matches);
|
||||
} else if let Some(ref matches) = matches.subcommand_matches("start") {
|
||||
start::start(&cfg, matches);
|
||||
} else {
|
||||
app.print_long_help().unwrap();
|
||||
println!();
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
|
270
src/utils.rs
270
src/utils.rs
|
@ -4,93 +4,195 @@
|
|||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::{KrunvmConfig, VmConfig, APP_NAME};
|
||||
|
||||
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();
|
||||
pub enum BuildahCommand {
|
||||
From,
|
||||
Inspect,
|
||||
Mount,
|
||||
Unmount,
|
||||
Remove,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn get_buildah_args(_cfg: &KrunvmConfig, cmd: BuildahCommand) -> Vec<String> {
|
||||
match cmd {
|
||||
BuildahCommand::From => vec!["from".to_string()],
|
||||
BuildahCommand::Inspect => vec!["inspect".to_string()],
|
||||
BuildahCommand::Mount => vec!["mount".to_string()],
|
||||
BuildahCommand::Unmount => vec!["umount".to_string()],
|
||||
BuildahCommand::Remove => vec!["rm".to_string()],
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn get_buildah_args(cfg: &KrunvmConfig, cmd: BuildahCommand) -> Vec<String> {
|
||||
let mut hbpath = std::env::current_exe().unwrap();
|
||||
hbpath.pop();
|
||||
hbpath.pop();
|
||||
let hbpath = hbpath.as_path().display();
|
||||
let policy_json = format!("{}/etc/containers/policy.json", hbpath);
|
||||
let registries_json = format!("{}/etc/containers/registries.conf", hbpath);
|
||||
let storage_root = format!("{}/root", cfg.storage_volume);
|
||||
let storage_runroot = format!("{}/runroot", cfg.storage_volume);
|
||||
|
||||
let mut args = vec![
|
||||
"--root".to_string(),
|
||||
storage_root,
|
||||
"--runroot".to_string(),
|
||||
storage_runroot,
|
||||
];
|
||||
|
||||
match cmd {
|
||||
BuildahCommand::From => {
|
||||
args.push("--signature-policy".to_string());
|
||||
args.push(policy_json);
|
||||
args.push("--registries-conf".to_string());
|
||||
args.push(registries_json);
|
||||
|
||||
args.push("from".to_string());
|
||||
args.push("--os".to_string());
|
||||
args.push("linux".to_string());
|
||||
}
|
||||
BuildahCommand::Inspect => {
|
||||
args.push("inspect".to_string());
|
||||
}
|
||||
BuildahCommand::Mount => {
|
||||
args.push("mount".to_string());
|
||||
}
|
||||
BuildahCommand::Unmount => {
|
||||
args.push("umount".to_string());
|
||||
}
|
||||
BuildahCommand::Remove => {
|
||||
args.push("rm".to_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();
|
||||
if vtuple.len() != 2 {
|
||||
println!("Invalid value for \"port\"");
|
||||
std::process::exit(-1);
|
||||
return Err("Too many ':' separators");
|
||||
}
|
||||
let host_port: u16 = match vtuple[0].parse() {
|
||||
Ok(p) => p,
|
||||
Err(_) => {
|
||||
println!("Invalid host port");
|
||||
std::process::exit(-1);
|
||||
return Err("Invalid host port");
|
||||
}
|
||||
};
|
||||
let guest_port: u16 = match vtuple[1].parse() {
|
||||
Ok(p) => p,
|
||||
Err(_) => {
|
||||
println!("Invalid guest port");
|
||||
return Err("Invalid guest port");
|
||||
}
|
||||
};
|
||||
Ok(PortPair {
|
||||
host_port: host_port.to_string(),
|
||||
guest_port: guest_port.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[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();
|
||||
if vtuple.len() != 2 {
|
||||
return Err("Too many ':' separators");
|
||||
}
|
||||
|
||||
let host_path = Path::new(vtuple[0]);
|
||||
if !host_path.is_absolute() {
|
||||
return Err("Invalid volume, host_path is not an absolute path");
|
||||
}
|
||||
if !host_path.exists() {
|
||||
return Err("Invalid volume, host_path does not exists");
|
||||
}
|
||||
let guest_path = Path::new(vtuple[1]);
|
||||
if !guest_path.is_absolute() {
|
||||
return Err("Invalid volume, guest_path is not an absolute path");
|
||||
}
|
||||
if guest_path.components().count() != 2 {
|
||||
return Err(
|
||||
"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);
|
||||
}
|
||||
};
|
||||
|
||||
mapped_ports.insert(host_port.to_string(), guest_port.to_string());
|
||||
}
|
||||
|
||||
mapped_ports
|
||||
}
|
||||
|
||||
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 {
|
||||
println!("Invalid value for \"volume\"");
|
||||
let exit_code = output.status.code().unwrap_or(-1);
|
||||
if exit_code != 0 {
|
||||
println!("xattr returned an error: {}", exit_code);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
let host_path = Path::new(vtuple[0]);
|
||||
if !host_path.is_absolute() {
|
||||
println!("Invalid volume, host_path is not an absolute path");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
if !host_path.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() {
|
||||
println!("Invalid volume, guest_path is not an absolute path");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
if guest_path.components().count() != 2 {
|
||||
println!(
|
||||
"Invalid volume, only single direct root children are supported as guest_path"
|
||||
);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
mapped_volumes.insert(
|
||||
host_path.to_str().unwrap().to_string(),
|
||||
guest_path.to_str().unwrap().to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
mapped_volumes
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub fn mount_container(cfg: &KrunvmConfig, vmcfg: &VmConfig) -> Result<String, std::io::Error> {
|
||||
#[cfg(target_os = "macos")]
|
||||
let storage_root = format!("{}/root", cfg.storage_volume);
|
||||
#[cfg(target_os = "macos")]
|
||||
let storage_runroot = format!("{}/runroot", cfg.storage_volume);
|
||||
#[cfg(target_os = "macos")]
|
||||
let mut args = vec![
|
||||
"--root",
|
||||
&storage_root,
|
||||
"--runroot",
|
||||
&storage_runroot,
|
||||
"mount",
|
||||
];
|
||||
#[cfg(target_os = "linux")]
|
||||
let mut args = vec!["mount"];
|
||||
|
||||
args.push(&vmcfg.container);
|
||||
let mut args = get_buildah_args(cfg, BuildahCommand::Mount);
|
||||
args.push(vmcfg.container.clone());
|
||||
|
||||
let output = match Command::new("buildah")
|
||||
.args(&args)
|
||||
|
@ -102,7 +204,7 @@ pub fn mount_container(cfg: &KrunvmConfig, vmcfg: &VmConfig) -> Result<String, s
|
|||
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.to_string());
|
||||
println!("Error executing buildah: {}", err);
|
||||
}
|
||||
std::process::exit(-1);
|
||||
}
|
||||
|
@ -118,27 +220,17 @@ 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);
|
||||
|
||||
Ok(rootfs.to_string())
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub fn umount_container(cfg: &KrunvmConfig, vmcfg: &VmConfig) -> Result<(), std::io::Error> {
|
||||
#[cfg(target_os = "macos")]
|
||||
let storage_root = format!("{}/root", cfg.storage_volume);
|
||||
#[cfg(target_os = "macos")]
|
||||
let storage_runroot = format!("{}/runroot", cfg.storage_volume);
|
||||
#[cfg(target_os = "macos")]
|
||||
let mut args = vec![
|
||||
"--root",
|
||||
&storage_root,
|
||||
"--runroot",
|
||||
&storage_runroot,
|
||||
"umount",
|
||||
];
|
||||
#[cfg(target_os = "linux")]
|
||||
let mut args = vec!["umount"];
|
||||
|
||||
args.push(&vmcfg.container);
|
||||
let mut args = get_buildah_args(cfg, BuildahCommand::Unmount);
|
||||
args.push(vmcfg.container.clone());
|
||||
|
||||
let output = match Command::new("buildah")
|
||||
.args(&args)
|
||||
|
@ -150,7 +242,7 @@ pub fn umount_container(cfg: &KrunvmConfig, vmcfg: &VmConfig) -> Result<(), std:
|
|||
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.to_string());
|
||||
println!("Error executing buildah: {}", err);
|
||||
}
|
||||
std::process::exit(-1);
|
||||
}
|
||||
|
@ -170,16 +262,8 @@ pub fn umount_container(cfg: &KrunvmConfig, vmcfg: &VmConfig) -> Result<(), std:
|
|||
|
||||
#[allow(unused_variables)]
|
||||
pub fn remove_container(cfg: &KrunvmConfig, vmcfg: &VmConfig) -> Result<(), std::io::Error> {
|
||||
#[cfg(target_os = "macos")]
|
||||
let storage_root = format!("{}/root", cfg.storage_volume);
|
||||
#[cfg(target_os = "macos")]
|
||||
let storage_runroot = format!("{}/runroot", cfg.storage_volume);
|
||||
#[cfg(target_os = "macos")]
|
||||
let mut args = vec!["--root", &storage_root, "--runroot", &storage_runroot, "rm"];
|
||||
#[cfg(target_os = "linux")]
|
||||
let mut args = vec!["rm"];
|
||||
|
||||
args.push(&vmcfg.container);
|
||||
let mut args = get_buildah_args(cfg, BuildahCommand::Remove);
|
||||
args.push(vmcfg.container.clone());
|
||||
|
||||
let output = match Command::new("buildah")
|
||||
.args(&args)
|
||||
|
@ -191,7 +275,7 @@ pub fn remove_container(cfg: &KrunvmConfig, vmcfg: &VmConfig) -> Result<(), std:
|
|||
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.to_string());
|
||||
println!("Error executing buildah: {}", err);
|
||||
}
|
||||
std::process::exit(-1);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue