Skip to content

Commit

Permalink
Merge pull request #44 from AkihiroSuda/dev
Browse files Browse the repository at this point in the history
refactor
  • Loading branch information
AkihiroSuda authored Jun 11, 2021
2 parents 8db2baa + 7843fe2 commit 4ccd409
Show file tree
Hide file tree
Showing 12 changed files with 213 additions and 144 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ jobs:
Terminal 1:
\`\`\`console
[macOS]$ limactl start
...
INFO[0029] READY. Run `lima` to open the shell.
\`\`\`
Terminal 2:
Expand Down
141 changes: 90 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[[⬇️ **Download]**](https://github.com/AkihiroSuda/lima/releases)
[[📖**Getting started]**](#getting-started)
[[**FAQs & Troubleshooting]**](#faqs--troubleshooting)

# Lima: Linux virtual machines (on macOS, in most cases)

Lima launches Linux virtual machines with automatic file sharing, port forwarding, and [containerd](https://containerd.io).
Expand All @@ -17,9 +21,9 @@ It may work on NetBSD and Windows hosts as well.

✅ ARM on Intel

✅ ARM on ARM (untested)
✅ ARM on ARM

✅ Intel on ARM (untested)
✅ Intel on ARM

✅ Ubuntu guest

Expand Down Expand Up @@ -77,49 +81,12 @@ For the usage of containerd and nerdctl (contaiNERD ctl), visit https://github.c
### Requirements (Intel Mac)
- QEMU v6.0.0 or later (`brew install qemu`)


<details>
<summary>
Signing the binary (not needed for recent version of QEMU and macOS, in most cases)
</summary>

<p>

If you have installed QEMU v6.0.0 or later on macOS 11, your binary should have been already automatically signed to enable HVF acceleration.

However, if you see `HV_ERROR`, you might need to sign the binary manually.

```bash
cat >entitlements.xml <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.hypervisor</key>
<true/>
</dict>
</plist>
EOF

codesign -s - --entitlements entitlements.xml --force /usr/local/bin/qemu-system-x86_64
```

Note: **Only** on macOS versions **before** 10.15.7 you might need to add this entitlement in addition:

```
<key>com.apple.vm.hypervisor</key>
<true/>
```

</p>
</details>

### Requirements (ARM Mac)

- QEMU with `--accel=hvf` support, see https://gist.github.com/citruz/9896cd6fb63288ac95f81716756cb9aa
- QEMU with `--accel=hvf` support, see https://gist.github.com/nrjdalal/e70249bb5d2e9d844cc203fd11f74c55

> **NOTE**
> Lima is not tested on ARM Mac.
> Lima is not regularly tested on ARM Mac.
### Install

Expand All @@ -129,6 +96,22 @@ and extract it under `/usr/local` (or somewhere else).
To install from the source, run `make && make install`.

### Usage

Terminal 1:
```console
[macOS]$ limactl start
...
INFO[0029] READY. Run `lima` to open the shell.
```

Terminal 2:
```console
[macOS]$ lima uname
Linux
```

Detailed usage:

- Run `limactl start <INSTANCE>` to start the Linux instance.
The default instance name is "default".
Lima automatically opens an editor (`vi`) for reviewing and modifying the configuration.
Expand Down Expand Up @@ -156,7 +139,7 @@ Especially, the following data might be easily lost:

### Configuration

See [`./pkg/limayaml/default.TEMPLATE.yaml`](./pkg/limayaml/default.TEMPLATE.yaml).
See [`./pkg/limayaml/default.yaml`](./pkg/limayaml/default.yaml).

The current default spec:
- OS: Ubuntu 21.04 (Hirsute Hippo)
Expand All @@ -183,16 +166,41 @@ The current default spec:

### Help wanted
:pray:
- Test on ARM Mac
- [Test on ARM Mac](https://github.com/AkihiroSuda/lima/issues/42)
- Performance optimization
- Homebrew
- [Homebrew](https://github.com/AkihiroSuda/lima/issues/37)
- More guest distros
- Windows hosts
- GUI with system tray icon (Qt or Electron, for portability)
- VirtFS to replace the current reverse sshfs (work has to be done on QEMU repo)
- [VirtFS to replace the current reverse sshfs (work has to be done on QEMU repo)](https://github.com/NixOS/nixpkgs/pull/122420)
- [vsock](https://github.com/apple/darwin-xnu/blob/xnu-7195.81.3/bsd/man/man4/vsock.4) to replace SSH (work has to be done on QEMU repo)

## FAQs & Troubleshooting
<!-- doctoc: https://github.com/thlorenz/doctoc -->

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
### Generic

- [Generic](#generic)
- ["What's my login password?"](#whats-my-login-password)
- ["Does Lima work on ARM Mac?"](#does-lima-work-on-arm-mac)
- ["Can I run non-Ubuntu guests?"](#can-i-run-non-ubuntu-guests)
- ["Can I run other container engines such as Podman?"](#can-i-run-other-container-engines-such-as-podman)
- ["Can I run Lima with a remote Linux machine?"](#can-i-run-lima-with-a-remote-linux-machine)
- ["Advantages compared to Docker for Mac?"](#advantages-compared-to-docker-for-mac)
- [QEMU](#qemu)
- ["QEMU crashes with `HV_ERROR`"](#qemu-crashes-with-hv_error)
- ["QEMU is slow"](#qemu-is-slow)
- [error "killed -9"](#error-killed--9)
- [SSH](#ssh)
- ["Port forwarding does not work"](#port-forwarding-does-not-work)
- [error "field SSHPubKeys must be set"](#error-field-sshpubkeys-must-be-set)
- [error "hostkeys_foreach failed: No such file or directory"](#error-hostkeys_foreach-failed-no-such-file-or-directory)
- [error "failed to execute script ssh: [...] Permission denied (publickey)"](#error-failed-to-execute-script-ssh--permission-denied-publickey)
- ["Hints for debugging other problems?"](#hints-for-debugging-other-problems)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->
### Generic
#### "What's my login password?"
Password is disabled and locked by default.
Expand All @@ -201,7 +209,7 @@ You have to use `limactl shell bash` (or `lima bash`) to open a shell.
Alternatively, you may also directly ssh into the guest: `ssh -p 60022 -o NoHostAuthenticationForLocalhost=yes 127.0.0.1`.

#### "Does Lima work on ARM Mac?"
Yes, it should work, but not tested on ARM.
Yes, it should work, but not regularly tested on ARM.

#### "Can I run non-Ubuntu guests?"
Fedora is also known to work, see [`./examples/fedora.yaml`](./examples/fedora.yaml).
Expand All @@ -211,7 +219,6 @@ An image has to satisfy the following requirements:
- systemd
- cloud-init
- The following binaries to be preinstalled:
- `curl`
- `sudo`
- The following binaries to be preinstalled, or installable via the package manager:
- `sshfs`
Expand All @@ -230,17 +237,49 @@ the predecessor or Lima, provides similar features for remote Linux machines.
e.g., run `sshocker -v /Users/foo:/home/foo/mnt -p 8080:80 <USER>@<HOST>` to expose `/Users/foo` to the remote machine as `/home/foo/mnt`,
and forward `localhost:8080` to the port 80 of the remote machine.

#### "Advantages compared to Docker for Mac?"
Lima is free software (Apache License 2.0), while Docker for Mac is not.
Their [EULA](https://www.docker.com/legal/docker-software-end-user-license-agreement) even prohibits disclosure of benchmarking result.

On the other hand, [Moby](https://github.com/moby/moby), aka Docker for Linux, is free software, but Moby/Docker lacks several novel features of containerd, such as:
- [On-demand image pulling (aka lazy-pulling, eStargz)](https://github.com/containerd/nerdctl/blob/master/docs/stargz.md)
- [Running an encrypted container](https://github.com/containerd/nerdctl/blob/master/docs/ocicrypt.md)
- Importing and exporting [local OCI archives](https://github.com/opencontainers/image-spec/blob/master/image-layout.md)

### QEMU
#### "QEMU crashes with `HV_ERROR`"
You have to add `com.apple.security.hypervisor` entitlement to `qemu-system-x86_64` binary.
See [Getting started](#getting-started).
If you have installed QEMU v6.0.0 or later on macOS 11 via homebrew, your QEMU binary should have been already automatically signed to enable HVF acceleration.

However, if you see `HV_ERROR`, you might need to sign the binary manually.

```bash
cat >entitlements.xml <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.hypervisor</key>
<true/>
</dict>
</plist>
EOF

codesign -s - --entitlements entitlements.xml --force /usr/local/bin/qemu-system-x86_64
```

Note: **Only** on macOS versions **before** 10.15.7 you might need to add this entitlement in addition:

```
<key>com.apple.vm.hypervisor</key>
<true/>
```

#### "QEMU is slow"
- Make sure that HVF is enabled with `com.apple.security.hypervisor` entitlement. See [Getting started](#getting-started).
- Make sure that HVF is enabled with `com.apple.security.hypervisor` entitlement. See ["QEMU crashes with `HV_ERROR`"](#qemu-crashes-with-hv_error).
- Emulating non-native machines (ARM-on-Intel, Intel-on-ARM) is slow by design.

#### error "killed -9"
- make sure qemu is codesigned, see [Getting started](#getting-started).
- make sure qemu is codesigned, See ["QEMU crashes with `HV_ERROR`"](#qemu-crashes-with-hv_error).
- if you are on macOS 10.15.7 or 11.0 or later make sure the entitlement `com.apple.vm.hypervisor` is **not** added. It only works on older macOS versions. You can clear the codesigning with `codesign --remove-signature /usr/local/bin/qemu-system-x86_64` and [start over](#getting-started).


Expand Down
5 changes: 2 additions & 3 deletions cmd/lima-guestagent/daemon_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ import (
)

var daemonCommand = &cli.Command{
Name: "daemon",
Usage: "run the daemon",
ArgsUsage: "[SOCKET]",
Name: "daemon",
Usage: "run the daemon",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "socket",
Expand Down
2 changes: 1 addition & 1 deletion docs/internal.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ An instance directory contains the following files:
- `cidata.iso`: cloud-init ISO9660 image. (`user-data`, `meta-data`, `lima-guestagent.Linux-<ARCH>`)
- `basedisk`: the base image
- `diffdisk`: the diff image (QCOW2)
- `qemu-pid`: PID of the QEMU
- `qemu.pid`: PID of the QEMU
- `ssh.sock`: SSH control master socket
- `ga.sock`: Forwarded to `/run/user/$UID/lima-guestagent.sock`
- `serial.log`: QEMU serial log, for debugging
Expand Down
1 change: 0 additions & 1 deletion examples/default.TEMPLATE.yaml

This file was deleted.

1 change: 1 addition & 0 deletions examples/default.yaml
90 changes: 5 additions & 85 deletions pkg/guestagent/api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,10 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"os"

"github.com/AkihiroSuda/lima/pkg/guestagent/api"
"github.com/pkg/errors"
"github.com/AkihiroSuda/lima/pkg/httpclientutil"
)

type GuestAgentClient interface {
Expand All @@ -26,17 +22,10 @@ type GuestAgentClient interface {
// NewGuestAgentClient creates a client.
// socketPath is a path to the UNIX socket, without unix:// prefix.
func NewGuestAgentClient(socketPath string) (GuestAgentClient, error) {
if _, err := os.Stat(socketPath); err != nil {
hc, err := httpclientutil.NewHTTPClientWithSocketPath(socketPath)
if err != nil {
return nil, err
}
hc := &http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
var d net.Dialer
return d.DialContext(ctx, "unix", socketPath)
},
},
}
return NewGuestAgentClientWithHTTPClient(hc), nil
}

Expand All @@ -62,19 +51,11 @@ func (c *client) HTTPClient() *http.Client {

func (c *client) Info(ctx context.Context) (*api.Info, error) {
u := fmt.Sprintf("http://%s/%s/info", c.dummyHost, c.version)
req, err := http.NewRequest("GET", u, nil)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
resp, err := c.HTTPClient().Do(req)
resp, err := httpclientutil.Get(ctx, c.HTTPClient(), u)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if err := successful(resp); err != nil {
return nil, err
}
var info api.Info
dec := json.NewDecoder(resp.Body)
if err := dec.Decode(&info); err != nil {
Expand All @@ -85,19 +66,11 @@ func (c *client) Info(ctx context.Context) (*api.Info, error) {

func (c *client) Events(ctx context.Context, onEvent func(api.Event)) error {
u := fmt.Sprintf("http://%s/%s/events", c.dummyHost, c.version)
req, err := http.NewRequest("GET", u, nil)
if err != nil {
return err
}
req = req.WithContext(ctx)
resp, err := c.HTTPClient().Do(req)
resp, err := httpclientutil.Get(ctx, c.HTTPClient(), u)
if err != nil {
return err
}
defer resp.Body.Close()
if err := successful(resp); err != nil {
return err
}
var ev api.Event
dec := json.NewDecoder(resp.Body)
for {
Expand All @@ -107,56 +80,3 @@ func (c *client) Events(ctx context.Context, onEvent func(api.Event)) error {
onEvent(ev)
}
}

func readAtMost(r io.Reader, maxBytes int) ([]byte, error) {
lr := &io.LimitedReader{
R: r,
N: int64(maxBytes),
}
b, err := ioutil.ReadAll(lr)
if err != nil {
return b, err
}
if lr.N == 0 {
return b, errors.Errorf("expected at most %d bytes, got more", maxBytes)
}
return b, nil
}

// HTTPStatusErrorBodyMaxLength specifies the maximum length of HTTPStatusError.Body
const HTTPStatusErrorBodyMaxLength = 64 * 1024

// HTTPStatusError is created from non-2XX HTTP response
type HTTPStatusError struct {
// StatusCode is non-2XX status code
StatusCode int
// Body is at most HTTPStatusErrorBodyMaxLength
Body string
}

// Error implements error.
// If e.Body is a marshalled string of api.ErrorJSON, Error returns ErrorJSON.Message .
// Otherwise Error returns a human-readable string that contains e.StatusCode and e.Body.
func (e *HTTPStatusError) Error() string {
if e.Body != "" && len(e.Body) < HTTPStatusErrorBodyMaxLength {
var ej api.ErrorJSON
if json.Unmarshal([]byte(e.Body), &ej) == nil {
return ej.Message
}
}
return fmt.Sprintf("unexpected HTTP status %s, body=%q", http.StatusText(e.StatusCode), e.Body)
}

func successful(resp *http.Response) error {
if resp == nil {
return errors.New("nil response")
}
if resp.StatusCode/100 != 2 {
b, _ := readAtMost(resp.Body, HTTPStatusErrorBodyMaxLength)
return &HTTPStatusError{
StatusCode: resp.StatusCode,
Body: string(b),
}
}
return nil
}
Loading

0 comments on commit 4ccd409

Please sign in to comment.