Skip to content

Commit 3a8f108

Browse files
edigaryevfkorotkov
andauthored
Documentation (#25)
* .goreleaser.yml: automatically assign capabilities in postinstall script * Document how to install Vetu in different environments * Expand the "Usage" section a bit and document VM's location on disk * Explain the difference between {QEMU,Firecracker,Cloud Hypervisor} And document networking options. * Automatically fetch Cloud Hypervisor and Rust Hypervisor Firmware But only if Cloud Hypervisor and EDK2 firmware is not available on the system. * old-school → traditional Co-authored-by: Fedor Korotkov <[email protected]> * Highlight the features inherited from Tart * Decipher the Vetu name in the main heading Co-authored-by: Fedor Korotkov <[email protected]> * Remove bold-parts of the deciphered name as they're ineffectual * Better highlight the Tart-inherited benefits Co-authored-by: Fedor Korotkov <[email protected]> * Do not use path.Join() to join URL parts --------- Co-authored-by: Fedor Korotkov <[email protected]>
1 parent 6c13f3f commit 3a8f108

File tree

8 files changed

+304
-9
lines changed

8 files changed

+304
-9
lines changed

.goreleaser.yml

+2-3
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,8 @@ nfpms:
3434
maintainer: [email protected]
3535
description: CLI for executing Cirrus tasks locally and in any CI
3636
section: misc
37-
dependencies:
38-
- cloud-hypervisor
39-
- edk2-cloud-hypervisor
37+
scripts:
38+
postinstall: "scripts/postinstall.sh"
4039
formats:
4140
- deb
4241
- rpm

INSTALL.md

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Index
2+
3+
* [Debian-based distributions](#debian-based-distributions) (Debian, Ubuntu, etc.)
4+
* [RPM-based distributions](#rpm-based-distributions) (Fedora, CentOS, etc.)
5+
* [Prebuilt Binary](#prebuilt-binary)
6+
* [From Source](#from-source)
7+
8+
# Prerequisites
9+
10+
Make sure that your user has the `/dev/kvm` access. On most distributions, this can be accomplished by adding the current user to the `kvm` group:
11+
12+
```shell
13+
sudo gpasswd -a $USER kvm
14+
```
15+
16+
Once added to a group, you will need to re-login for the changes to take effect.
17+
18+
## Installation
19+
20+
## Debian-based distributions
21+
22+
First, make sure that you've installed the APT transport for downloading packages via HTTPS and common X.509 certificates:
23+
24+
```shell
25+
sudo apt-get update && sudo apt-get -y install apt-transport-https ca-certificates
26+
```
27+
28+
Then, add the Cirrus Labs repository:
29+
30+
```shell
31+
echo "deb [trusted=yes] https://apt.fury.io/cirruslabs/ /" | sudo tee /etc/apt/sources.list.d/cirruslabs.list
32+
```
33+
34+
Now you can update the package index files and install the Vetu:
35+
36+
```shell
37+
sudo apt-get update && sudo apt-get -y install vetu
38+
```
39+
40+
## RPM-based distributions
41+
42+
First, create a `/etc/yum.repos.d/cirruslabs.repo` file with the following contents:
43+
44+
```
45+
[cirruslabs]
46+
name=Cirrus Labs Repo
47+
baseurl=https://yum.fury.io/cirruslabs/
48+
enabled=1
49+
gpgcheck=0
50+
```
51+
52+
Now you can install the Vetu:
53+
54+
```shell
55+
sudo yum -y install vetu
56+
```
57+
58+
## Prebuilt Binary
59+
60+
Check the [releases page](https://github.com/cirruslabs/vetu/releases) for a pre-built `vetu` binary for your platform.
61+
62+
Here's a one-liner for Linux to download the latest release:
63+
64+
```bash
65+
curl -L -o vetu https://github.com/cirruslabs/vetu/releases/latest/download/vetu-linux-$(uname -m) && sudo mv vetu /usr/bin/vetu && sudo chmod +x /usr/bin/vetu && sudo setcap cap_net_raw,cap_net_admin+eip /usr/bin/vetu
66+
```
67+
68+
## From Source
69+
70+
If you have [Golang](https://golang.org/) 1.21 or newer installed, you can run:
71+
72+
```
73+
go install github.com/cirruslabs/vetu/...@latest
74+
```
75+
76+
This will build and place the `vetu` binary in `$GOPATH/bin`.
77+
78+
Vetu binary also needs some capabilities assigned to it:
79+
80+
```shell
81+
sudo setcap cap_net_raw,cap_net_admin+eip $GOPATH/bin/vetu
82+
```
83+
84+
To be able to run `vetu` command from anywhere, make sure the `$GOPATH/bin` directory is added to your `PATH`
85+
environment variable (see [article in the Go wiki](https://github.com/golang/go/wiki/SettingGOPATH) for more details).

README.md

+56-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,57 @@
1-
# vetu
1+
# **vetu** - Virtualization that is Easy To Use
22

3-
_vetu_ is virtualization toolset to effortlessly run Cloud Hypervisor-backed virtual machines on Linux hosts.
3+
_vetu_ is virtualization toolset to effortlessly run [Cloud Hypervisor](https://www.cloudhypervisor.org/)-backed virtual machines on Linux hosts.
4+
5+
We say effortlessly, because the existing virtualization solutions like the traditional [QEMU](https://www.qemu.org/) and the new-wave [Firecracker](https://firecracker-microvm.github.io/) and Cloud Hypervisor provide lots of options and require users to essentially build a tooling on top of them to be able to simply run a basic VM.
6+
7+
Vetu builds on the success of [Tart](https://tart.run/) and abstracts all these peculiarities and makes the virtualization as easy as running containers.
8+
9+
Here are just some of the cool features that Vetu inherited from Tart:
10+
11+
* Ability to easily distribute VM images by integrating with OCI-compatible container registries. Push and pull virtual machines like they are containers.
12+
* Effortless SSH'ing into VMs (see [Usage](#usage) for an example)
13+
* [Cirrus CLI](https://github.com/cirruslabs/cirrus-cli) integration
14+
15+
## Installation
16+
17+
* [Debian-based distributions](INSTALL.md#debian-based-distributions) (Debian, Ubuntu, etc.)
18+
* [RPM-based distributions](INSTALL.md#rpm-based-distributions) (Fedora, CentOS, etc.)
19+
* [Prebuilt Binary](INSTALL.md#prebuilt-binary)
20+
* [From Source](INSTALL.md#from-source)
21+
22+
## Usage
23+
24+
Try running a Vetu VM on your Linux machine with `arm64` processor:
25+
26+
```shell
27+
vetu clone ghcr.io/cirruslabs/ubuntu:latest ubuntu
28+
vetu run ubuntu
29+
```
30+
31+
The default username is `admin` and password is `admin`. The machine is only reachable from the localhost with the default configuration, and you can connect to it over SSH using the following command:
32+
33+
```shell
34+
ssh admin@$(vetu ip ubuntu)
35+
```
36+
37+
## Networking options
38+
39+
### Default (NAT)
40+
41+
The default NAT networking is powered by the [gVisor's TCP/IP stack](https://gvisor.dev/docs/user_guide/networking/) and has an advantage of requiring no configuration on the user's end.
42+
43+
The main disadvantage is the reduced speed, because all the routing and processing is also done in the software (in the Vetu process), in addition to the kernel.
44+
45+
However, this choice is still fast enough to run most of the tasks, for example, provisioning a Linux distro and installing the packages.
46+
47+
### Bridged
48+
49+
Bridged networking can be enabling by specifying `--net-bridged=BRIDGE_INTERFACE_NAME` argument to `vetu run` and has an advantage of being fast, because all the processing and routing is done in the kernel.
50+
51+
The main disadvantage is that this choice requires the system administrator to properly configure the bridge interface, IP forwarding, DHCP server (if required by the VM) and the packet filter to provide adequate network isolation.
52+
53+
## FAQ
54+
55+
### VM location on disk
56+
57+
Vetu stores all it's files in `~/.vetu/` directory. Local images that you can run are stored in `~/.vetu/vms/`. Remote images are pulled into `~/.vetu/cache/OCIs/`.
+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package binaryfetcher
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"github.com/cirruslabs/vetu/internal/homedir"
7+
"io"
8+
"net/http"
9+
"os"
10+
"path/filepath"
11+
)
12+
13+
func Fetch(ctx context.Context, downloadURL string, binaryName string, executable bool) (string, error) {
14+
// Determine the binary path
15+
binaryPath, err := binaryPath(binaryName)
16+
if err != nil {
17+
return "", err
18+
}
19+
20+
// Use the cached binary if possible
21+
if _, err := os.Stat(binaryPath); err == nil {
22+
return binaryPath, nil
23+
}
24+
25+
// Download and cache the binary if not available in the cache
26+
client := http.Client{}
27+
28+
request, err := http.NewRequestWithContext(ctx, http.MethodGet, downloadURL, nil)
29+
if err != nil {
30+
return "", err
31+
}
32+
33+
resp, err := client.Do(request)
34+
if err != nil {
35+
return "", err
36+
}
37+
defer resp.Body.Close()
38+
39+
if resp.StatusCode != http.StatusOK {
40+
return "", fmt.Errorf("failed to fetch %q binary from %s: HTTP %d",
41+
binaryName, downloadURL, resp.StatusCode)
42+
}
43+
44+
binaryFile, err := os.Create(binaryPath)
45+
if err != nil {
46+
return "", err
47+
}
48+
defer binaryFile.Close()
49+
50+
if _, err := io.Copy(binaryFile, resp.Body); err != nil {
51+
return "", err
52+
}
53+
54+
// Make the binary executable if requested
55+
if executable {
56+
if err := binaryFile.Chmod(0755); err != nil {
57+
return "", err
58+
}
59+
}
60+
61+
return binaryPath, nil
62+
}
63+
64+
func binaryPath(binaryName string) (string, error) {
65+
homeDir, err := homedir.Path()
66+
if err != nil {
67+
return "", err
68+
}
69+
70+
baseDir := filepath.Join(homeDir, "cache", "bin")
71+
72+
// Ensure that the base directory exists
73+
if err := os.MkdirAll(baseDir, 0755); err != nil {
74+
return "", err
75+
}
76+
77+
return filepath.Join(baseDir, binaryName), nil
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package firmware
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"github.com/cirruslabs/vetu/internal/binaryfetcher"
7+
"os"
8+
"runtime"
9+
)
10+
11+
const (
12+
edk2BinaryPath = "/usr/share/cloud-hypervisor/CLOUDHV_EFI.fd"
13+
baseURL = "https://github.com/cirruslabs/rust-hypervisor-firmware/releases/latest/download/"
14+
)
15+
16+
var goarchToDownloadURL = map[string]string{
17+
"amd64": baseURL + "hypervisor-fw",
18+
"arm64": baseURL + "hypervisor-fw-aarch64",
19+
}
20+
21+
func Firmware(ctx context.Context) (string, string, error) {
22+
// Always prefer the EDK2 firmware installed on the system
23+
_, err := os.Stat(edk2BinaryPath)
24+
if err == nil {
25+
return edk2BinaryPath, "EDK2 firmware", nil
26+
}
27+
28+
// Fall back to downloading the Rust Hypervisor Firmware from GitHub
29+
downloadURL, ok := goarchToDownloadURL[runtime.GOARCH]
30+
if !ok {
31+
return "", "", fmt.Errorf("no EDK2 firmware installed on the system "+
32+
"and architecture %q is not available in Rust Hypervisor Firmware's GitHub releases", runtime.GOARCH)
33+
}
34+
35+
fmt.Printf("no EDK2 firmware installed on the system, downloading Rust Hypervisor Firmware "+
36+
"from %s...\n", downloadURL)
37+
38+
binaryPath, err := binaryfetcher.Fetch(ctx, downloadURL, "hypervisor-fw", true)
39+
if err != nil {
40+
return "", "", err
41+
}
42+
43+
return binaryPath, "Rust Hypervisor Firmware", nil
44+
}

internal/externalcommand/cloudhypervisor/cloudhypervisor.go

+26-2
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,39 @@ package cloudhypervisor
22

33
import (
44
"context"
5+
"fmt"
6+
"github.com/cirruslabs/vetu/internal/binaryfetcher"
57
"os/exec"
8+
"runtime"
69
)
710

8-
const binaryName = "cloud-hypervisor"
11+
const (
12+
binaryName = "cloud-hypervisor"
13+
baseURL = "https://github.com/cloud-hypervisor/cloud-hypervisor/releases/latest/download/"
14+
)
15+
16+
var goarchToDownloadURL = map[string]string{
17+
"amd64": baseURL + "cloud-hypervisor-static",
18+
"arm64": baseURL + "cloud-hypervisor-static-aarch64",
19+
}
920

1021
func CloudHypervisor(ctx context.Context, args ...string) (*exec.Cmd, error) {
22+
// Always prefer the Cloud Hypervisor binary in PATH
1123
binaryPath, err := exec.LookPath(binaryName)
1224
if err != nil {
13-
return nil, err
25+
// Fall back to downloading the Cloud Hypervisor binary from GitHub
26+
downloadURL, ok := goarchToDownloadURL[runtime.GOARCH]
27+
if !ok {
28+
return nil, fmt.Errorf("no %q binary found in PATH and architecture %q "+
29+
"is not available in Cloud Hypervisor's GitHub releases", binaryName, runtime.GOARCH)
30+
}
31+
32+
fmt.Printf("no %q binary found in PATH, downloading it from %s...\n", binaryName, downloadURL)
33+
34+
binaryPath, err = binaryfetcher.Fetch(ctx, downloadURL, binaryName, true)
35+
if err != nil {
36+
return nil, err
37+
}
1438
}
1539

1640
return exec.CommandContext(ctx, binaryPath, args...), nil

internal/oci/pull/tart/tart.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package tart
33
import (
44
"context"
55
"fmt"
6+
"github.com/cirruslabs/vetu/internal/externalbinary/firmware"
67
"github.com/cirruslabs/vetu/internal/oci/annotations"
78
"github.com/cirruslabs/vetu/internal/oci/diskpuller"
89
"github.com/cirruslabs/vetu/internal/oci/mediatypes"
@@ -80,9 +81,14 @@ func PullVMDirectory(
8081

8182
// Copy latest Hypervisor Firmware since
8283
// Tart VM images have no separate kernel
83-
fmt.Println("copying EDK2 firmware to use as a kernel...")
84+
firmwarePath, firmwareDesc, err := firmware.Firmware(ctx)
85+
if err != nil {
86+
return err
87+
}
88+
89+
fmt.Printf("copying %s to use as a kernel...\n", firmwareDesc)
8490

85-
if err := cp.Copy("/usr/share/cloud-hypervisor/CLOUDHV_EFI.fd", vmDir.KernelPath()); err != nil {
91+
if err := cp.Copy(firmwarePath, vmDir.KernelPath()); err != nil {
8692
return err
8793
}
8894

scripts/postinstall.sh

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/sh
2+
3+
set -e
4+
5+
/sbin/setcap cap_net_raw,cap_net_admin+eip /usr/bin/vetu

0 commit comments

Comments
 (0)