Compare commits

...

70 Commits
v0.1.3 ... main

Author SHA1 Message Date
Sergio López 21b9cb4760
Merge pull request #68 from stephen-fox/macos-only-map-volumes-if-required
start: Only map volumes if required on macOS
2025-06-16 16:07:10 +01:00
stephen-fox 28ad60fb7b start: Only map volumes if required on macOS
In issue #67, it was reported that VM creation on macOS always fails
with the message: "Error setting VM mapped volumes". [^1] It appears
krun always calls `krun_set_mapped_volumes` even if the user has not
configured any volumes. That krun library function was disabled in
Feb 2024 by commit 186083df9. [^2] Based on the commit message, it
sounds like virtio-fs devices will be the preferred way to implement
mapped volumes going forward.

For now, we can at least avoid calling the deprecated function if
the user has not configured any mapped volumes. That will obviously
not fix the krun feature, but it makes krunvm usable on macOS.

References

1. https://github.com/containers/krunvm/issues/67
2. libkrun commit 186083df9faf704e2b431d90b1ed147363aca004

Signed-off-by: Stephen Fox <stephen.j.fox.jr@gmail.com>
2025-06-13 21:00:53 +00:00
Sergio López 2d9e1d6c6d
Merge pull request #66 from KevSlashNull/inspect
Add inspect command
2025-01-08 17:20:15 +01:00
Kev 🐶 713d418480 Add inspect command
This adds a `krunvm inspect` command that runs `buildah inspect` under
the hood, so users can find information about the VM, such as
the image metadata (e.g. image name and digest) or mount point.

Signed-off-by: Kev <kevslashnull@gmail.com>
2025-01-08 17:06:56 +01:00
Sergio López 765614c783
Merge pull request #58 from EduM22/feat/set-env
Pass environment variable(s) to microVM
2024-06-10 10:38:06 +02:00
Sergio López 212e203f88
Merge pull request #64 from slp/forbid-empty-names
create: forbid empty names
2024-05-30 19:06:09 +02:00
Sergio Lopez b001dca718 create: forbid empty names
Fixes #63

Signed-off-by: Sergio Lopez <slp@redhat.com>
2024-05-30 19:03:00 +02:00
EduM22 57cf8e61b6 add docs + change variable name
Signed-off-by: EduM22 <38257387+EduM22@users.noreply.github.com>
2024-02-06 20:09:29 +01:00
EduM22 a2b60aee8b set env in VM from start args
Signed-off-by: EduM22 <38257387+EduM22@users.noreply.github.com>
2024-02-05 18:13:59 +01:00
Sergio López 5494d84a66
Merge pull request #52 from mtjhrc/refactor-clap
Refactor argument parsing using newer clap derive
2023-11-29 18:35:51 +01:00
Sergio López 27a6dfb4f4
Merge pull request #55 from mtjhrc/fix-aarch64
Fix aarch64 i8/u8 mismatch and improve pipeline
2023-11-29 18:28:27 +01:00
Matej Hrica 90a8299743 Move commands to a module
Signed-off-by: Matej Hrica <mhrica@redhat.com>
2023-11-02 19:36:02 +01:00
Matej Hrica 5f8693c693 Instead of a free function for each command make it a method
You could have a trait and uniform interface for the run method,
but it isn't necessary for now. It also allows passing more arguments
from main.

Signed-off-by: Matej Hrica <mhrica@redhat.com>
2023-11-02 19:35:50 +01:00
Matej Hrica e467ff9746 Refactor argument parsing using new clap 4.x version
Signed-off-by: Matej Hrica <mhrica@redhat.com>
2023-11-02 19:35:50 +01:00
Matej Hrica cbf34eb064 Resolve clippy warning for macOS target
Signed-off-by: Matej Hrica <mhrica@redhat.com>
2023-11-02 12:42:33 +01:00
Matej Hrica 38e1e5cffc Fix compilation for macOS target
We need #[allow(unused_mut)], to disable warnings on other targets.

Signed-off-by: Matej Hrica <mhrica@redhat.com>
2023-11-02 12:42:33 +01:00
Matej Hrica ced22ac583 Run cargo clippy more times with each supported platform separately
This seems to catch more problems, such as the u8/i8 mismatch on
aarch64 platforms.

Signed-off-by: Matej Hrica <mhrica@redhat.com>
2023-11-02 12:42:31 +01:00
Matej Hrica 6b6ea50458 Fix compilation on aarch64 due to i8/u8 mismatch
Signed-off-by: Matej Hrica <mhrica@redhat.com>
2023-11-02 11:33:52 +01:00
Sergio López 101a402871
Merge pull request #47 from mtjhrc/resolve-warnings
Resolve clippy warnings and use correct types in bindings
2023-10-25 12:16:39 +02:00
Matej Hrica acf6f437a1 Resolve clippy warnings and use correct types in bindings.
bindings were using i8 instead of c_char for C string pointers, but
the types are defined as c_char in libkrun.

There were a lot of unecessary casts from i8 to c_char according to clippy
because on x86_64 they are the same type.
(The casts were necessary on ARM, because c_char is unsigned on ARM).

Signed-off-by: Matej Hrica <mhrica@redhat.com>
2023-10-10 12:06:09 +02:00
Sergio López 647ec27c24
Merge pull request #46 from mtjhrc/add-dependancy-to-readme
README: Add asciidoctor dependency to building from source instructions
2023-10-09 11:24:22 +02:00
Sergio López b5c38223f8
Merge pull request #45 from mtjhrc/unnecessary-var-makefile
Makefile: Remove INIT_BINARY because there is no such thing
2023-10-09 11:12:06 +02:00
Matej Hrica 59e3704368 Resolve warnings about unnecessary mut
Signed-off-by: Matej Hrica <mhrica@redhat.com>
2023-10-04 11:51:30 +02:00
Matej Hrica 7a8a1c6d5e README: Add asciidoctor dependency to building from source instructions
Signed-off-by: Matej Hrica <mhrica@redhat.com>
2023-10-04 10:54:39 +02:00
Matej Hrica a0e2da39b0 Makefile: Remove INIT_BINARY because there is no such thing
Signed-off-by: Matej Hrica <mhrica@redhat.com>
2023-10-04 10:38:21 +02:00
Sergio Lopez 12dac810ec
Merge pull request #36 from slp/macos-rosetta
macos: add support for running x86 microVMs on ARM64
2022-11-07 16:27:45 +01:00
Sergio Lopez 242a740f77 Bump version to v0.2.3
This version add support for running x86 microVMs on macOS ARM64
(M1/M2) by using Rosetta for Linux. Bump release number to v0.2.3

Signed-off-by: Sergio Lopez <slp@sinrega.org>
2022-11-07 11:34:35 +01:00
Sergio Lopez 26f5a589da macos: add support for creating x86 microVMs
Add support for creating x86 microVM on macOS ARM64 (M1/M2) by using
Rosetta for Linux.

Signed-off-by: Sergio Lopez <slp@sinrega.org>
2022-11-07 11:32:38 +01:00
Sergio Lopez e67d0ea065
Merge pull request #33 from slp/fix-multiple-args
Restrict the number of values of "-p" and "-v"
2022-08-16 18:03:53 +02:00
Sergio Lopez f9f4d4f5cc Bump version to v0.2.2
This version just contains a minor fix in the way arguments are
parsed, bump release number to v0.2.2

Signed-off-by: Sergio Lopez <slp@redhat.com>
2022-08-16 17:54:57 +02:00
Sergio Lopez 396f85c186 Restrict the number of values of "-p" and "-v"
With clap, "multiple(true)" implies that an argument may appear more
than once, but also that each time can declare multiple values. This
behavior clobbers the positional argument IMAGE.

To prevent this, declare a limit of 1 value per occurrence.

Fixes: #32

Signed-off-by: Sergio Lopez <slp@redhat.com>
2022-08-16 17:44:05 +02:00
Sergio Lopez ba9e585085
Merge pull request #28 from slp/fix-root
Use xattr to fix container image root ownership and mode
2022-08-04 23:00:52 +02:00
Sergio Lopez 7459968d8b Bump version to v0.2.1
This version just contains a minor fix for macOS, bump release number
to v0.2.1

Signed-off-by: Sergio Lopez <slp@sinrega.org>
2022-08-04 22:56:58 +02:00
Sergio Lopez 9985683205 macos: fix root ownership and mode
On macOS, buildah leaves the container image root with the uid and the
permissions set to 0700. But, for maximum compatibility with Linux
apps, we need it to be owned by root:root with mode 0555.

Use "xattr" to fix the ownership and mode after mounting the container
image.

Signed-off-by: Sergio Lopez <slp@sinrega.org>
2022-08-04 22:56:19 +02:00
Sergio Lopez a99b7b9e00
Merge pull request #27 from slp/use-set-env
Use krun_set_env to set env vars when no command is configured
2022-08-04 21:18:43 +02:00
Sergio Lopez 81444b574e Bump version to v0.2.0
This version includes changes in the behavior, bump minor number to
v0.2.0.

Signed-off-by: Sergio Lopez <slp@redhat.com>
2022-08-04 21:16:21 +02:00
Sergio Lopez a497bb78fa Avoid overriding PATH
Since libkrun's init/init.c is able to read the environment variables
for the config file, avoid setting it through the library, as it would
override the value.

Signed-off-by: Sergio Lopez <slp@sinrega.org>
2022-08-04 19:57:31 +02:00
Sergio Lopez 3b5baaddec Use krun_set_env to set env vars
If we're not setting a command line with krun_set_exec(), use the
newly introduced krun_set_env() to set some basic environment
variables.

Signed-off-by: Sergio Lopez <slp@redhat.com>
2022-08-04 19:10:37 +02:00
Sergio Lopez 1f7a7d1dda
Merge pull request #26 from slp/fix-macos-bcmd
macos: Add missing BuildahCommand
2022-08-04 17:51:22 +02:00
Sergio Lopez 1992d63183 macos: Add missing BuildahCommand
Commit "2285182aa254df8af86fca8f3e6e46a9c657418c" introduced
BuildahCommand::Inspect without adding the appropiate case for the
match in the macOS-specific code. Fix it now.

(It'd be nice having a macOS/ARM64 CI runner)

Fixes: #24
Signed-off-by: Sergio Lopez <slp@sinrega.org>
2022-08-04 17:47:51 +02:00
Sergio Lopez 023c665e1b
Merge pull request #18 from slp/empty_cmd_workdir
Allow reading Cmd and WorkingDir from the OCI image config
2022-08-04 15:07:12 +02:00
Sergio Lopez 2285182aa2 Export container config on microVM creation
libkrun's init/init.c is now able to read the workload parameters from
an OCI config file. Use "buildah inspect" to obtain this configuration
and write it to the expected location ("/.krun_config.json").

Signed-off-by: Sergio Lopez <slp@redhat.com>
2022-08-04 15:02:38 +02:00
Sergio Lopez 9be5aedc6b Remove command and workdir defaults
libkrun's init is now able to read the workload parameters from a
per-container configuration file ("/krun_config.json"). Since the
configuration set via the library takes preference over the settings
in the configuration file, avoid configuring a initial binary nor a
workdir unless the user has explicitly requested them.

In case the configuration file is missing, libkrun's init is able to
fall back to some reasonable defaults.

Signed-off-by: Sergio Lopez <slp@redhat.com>
2022-08-04 15:02:00 +02:00
Sergio Lopez b1673da3a1
Merge pull request #10 from TomSweeneyRedHat/dev/tsweeney/secfix
Rename Security.md to SECURITY.md
2022-07-05 13:41:50 +02:00
Sergio Lopez fd963f6b4c
Merge pull request #15 from slp/release-v0.1.6
Release v0.1.6
2022-07-05 13:41:27 +02:00
Sergio Lopez 718de12e2e Bump version to 0.1.6
Version 0.1.6 adds manual pages and replaces the uses of "lightweight
VM" with "microVM".

Signed-off-by: Sergio Lopez <slp@redhat.com>
2022-07-05 13:38:54 +02:00
Sergio Lopez f88a1dccf0 Cargo: add description and repository
Add the "description" and "repository" fields in preparation for
publishing the create on crates.io

Signed-off-by: Sergio Lopez <slp@redhat.com>
2022-07-05 13:38:29 +02:00
Sergio Lopez 3b3948df8b ci: Add a step to install asciidoctor
As asciidoctor is now a buildtime dependency, add a step in our CI to
install it.

Signed-off-by: Sergio Lopez <slp@redhat.com>
2022-07-05 13:36:13 +02:00
Sergio Lopez 775217ef34 Fix some clippy warnings
Fix some warnings raised by newer of clippy.

Signed-off-by: Sergio Lopez <slp@redhat.com>
2022-07-05 13:25:15 +02:00
Sergio Lopez ebf3e3004f Replace "lightweight VM" with "microVM"
Since "microVM" has become the most popular term for this kind of VMs,
use it here to give users a better idea of what krunvm does.

Signed-off-by: Sergio Lopez <slp@redhat.com>
2022-07-05 13:12:26 +02:00
Sergio Lopez 8244dd6f30 Add manual pages
Add a manual page for krunvm, and another one for each command. The
pages are in AsciiDoc format and are transformed into man pages by
build.rs running "asciidoctor", which is now a build dependency too.

Signed-off-by: Sergio Lopez <slp@redhat.com>
2022-07-05 13:12:21 +02:00
Sergio Lopez d7d4d6741d
README.md: Replace lightweight VMs with microVMs
Replace "lightweight VMs" with "microVMs", as it has become the most popular term for referring to this kind of VMs.
2022-07-05 10:36:28 +02:00
Sergio Lopez 38f1ea1232
README.md: remove "Limitations" section
Those limitations are no longer applicable since libkrun 0.2.0/libkrunfw 2.0.0
2022-05-30 19:42:49 +02:00
Sergio Lopez eedd4054ed Bump version to 0.1.5
v0.1.5 includes a fix for running buildah on macOS.

Signed-off-by: Sergio Lopez <slp@sinrega.org>
2022-04-29 20:53:37 +02:00
Sergio Lopez 3e9a284a79 Refactor buildah args generation
Refactor buildah args generation and, on macOS, add arguments for
specifying the location of policy.json and registries.conf

Signed-off-by: Sergio Lopez <slp@sinrega.org>
2022-04-29 20:47:04 +02:00
TomSweeneyRedHat 20359b534d Rename Security.md to SECURITY.md
A minor tweak to rename Security.md to SECURITY.md

Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>
2021-04-21 15:20:10 -04:00
Sergio Lopez 8027270987
Merge pull request #9 from TomSweeneyRedHat/dev/tsweeney/moredocs
Add Security and C-O-C docs
2021-04-21 10:20:58 +02:00
TomSweeneyRedHat 630c475fcd Add Security and C-O-C docs
Adds the SECURITY.md and CODE-OF-CONDUCT.md templates that point
to the main one in containers common.  If you do not like these,
please supply your own.

Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>
2021-04-20 09:27:45 -04:00
Sergio Lopez 5add8c5da7
Merge pull request #8 from humancalico/lock
chore: add Cargo.lock file
2021-03-22 17:39:36 +01:00
Akshat Agarwal 8ba8cf23b4
chore: add Cargo.lock file
Signed-off-by: Akshat Agarwal <humancalico@disroot.org>
2021-03-20 00:07:36 +05:30
Sergio Lopez 6247b3da93
Merge pull request #5 from nickgerace/main
Create directory leading up to resolv.conf
2021-02-23 17:20:40 +01:00
Nick Gerace 3ef17f30d4 Create directory leading up to resolv.conf
Create directory leading up to resolv.conf if it does not exist.

Signed-off-by: Nick Gerace <nickagerace@gmail.com>
2021-02-20 12:37:25 -05:00
Sergio Lopez 367898a940 Bump version to 0.1.4
v0.1.4 includes fixes for changevm, for building on macOS, and
some texts.

Signed-off-by: Sergio Lopez <slp@redhat.com>
2021-02-02 18:44:53 +01:00
Sergio Lopez 1a5597620c main: Fix APFS volume help message
Fix APFS volume help message that was missing a "create".

Reported-by: David Sastre <david.sastre@redhat.com>
Signed-off-by: Sergio Lopez <slp@redhat.com>
2021-02-02 17:36:33 +01:00
Sergio Lopez 75c83b923e changevm: Fix "cpus" and "mem" arguments
The "cpus" and "mem" arguments of "changevm" were missing the
"takes_value" property. Fix that.

Signed-off-by: Sergio Lopez <slp@redhat.com>
2021-02-02 17:33:59 +01:00
Sergio Lopez 7300dc97b9 ci: Add code quality checks for PRs
Add a GitHub Action to check the code quality of PRs using 'rustfmt'
and 'clippy'.

Signed-off-by: Sergio Lopez <slp@redhat.com>
2021-02-01 14:08:11 +01:00
Sergio Lopez a349505e54 Fix some cargo and clippy warnings
We really need to set up a CI for this, like the one we already have
for libkrun. At least, for building under Linux (not sure if we can
get a M1-based CI somewhere).

Signed-off-by: Sergio Lopez <slp@sinrega.org>
2021-01-30 09:35:12 +01:00
Sergio Lopez cd55ff6d42
Merge pull request #1 from dims/fix-build-on-macos-m1
fix compile failures on macos m1
2021-01-30 09:31:17 +01:00
Davanum Srinivas 93e31a4867
fix compile failures on macos m1
```
error[E0425]: cannot find value `rootfs` in this scope
  --> src/start.rs:47:43
   |
47 |         let full_guest = format!("{}{}", &rootfs, guest_path);
   |                                           ^^^^^^ help: a local variable with a similar name exists: `_rootfs`

error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
  --> src/start.rs:61:15
   |
61 |     let ret = bindings::krun_set_mapped_volumes(ctx, vols.as_ptr());
   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
   |
   = note: consult the function's documentation for information on how to avoid undefined behavior

```

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
2021-01-29 21:01:19 -05:00
Sergio Lopez c21fcd0c51 README.md: Add "Supported platforms"
Add "Supported platforms" section and "-y" to each of the "dnf"
commands.

Signed-off-by: Sergio Lopez <slp@redhat.com>
2021-01-28 10:25:35 +01:00
32 changed files with 1830 additions and 772 deletions

42
.github/workflows/code_quality.yml vendored Normal file
View File

@ -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
.gitignore vendored
View File

@ -1,4 +1,3 @@
Cargo.lock
/build/
target
*.rs.bk

3
CODE-OF-CONDUCT.md Normal file
View File

@ -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).

481
Cargo.lock generated Normal file
View File

@ -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"

View File

@ -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"]}

View File

@ -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

View File

@ -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 @@
[![asciicast](https://asciinema.org/a/CGtTS93VsdzWwUfkY1kqVnaik.svg)](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.

4
SECURITY.md Normal file
View File

@ -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.

View File

@ -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(())
}

View File

@ -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)*

40
docs/krunvm-config.1.txt Normal file
View File

@ -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)*

59
docs/krunvm-create.1.txt Normal file
View File

@ -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)*

22
docs/krunvm-delete.1.txt Normal file
View File

@ -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)*

22
docs/krunvm-list.1.txt Normal file
View File

@ -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)*

41
docs/krunvm-start.1.txt Normal file
View File

@ -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)*

59
docs/krunvm.1.txt Normal file
View File

@ -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)*

View File

@ -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;
}

View File

@ -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();
}
}

141
src/commands/changevm.rs Normal file
View File

@ -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();
}
}
}

68
src/commands/config.rs Normal file
View File

@ -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
);
}
}

233
src/commands/create.rs Normal file
View File

@ -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(())
}

31
src/commands/delete.rs Normal file
View File

@ -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();
}
}

52
src/commands/inspect.rs Normal file
View File

@ -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}");
}
}

38
src/commands/list.rs Normal file
View File

@ -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);
}

15
src/commands/mod.rs Normal file
View File

@ -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;

View File

@ -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");
}

View File

@ -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
);
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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!();
}
}

View File

@ -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),
}
}

View File

@ -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);
}