Skip to content

Commit 3ff1a15

Browse files
authored
Merge pull request #285 from AkihiroSuda/auto-assign-ssh-localport
Assign `ssh.localPort` automatically to an available port
2 parents 8091d05 + 0675508 commit 3ff1a15

File tree

31 files changed

+352
-104
lines changed

31 files changed

+352
-104
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ jobs:
9090
integration:
9191
name: Integration tests
9292
runs-on: macos-11
93-
timeout-minutes: 60
93+
timeout-minutes: 120
9494
strategy:
9595
matrix:
9696
# GHA macOS is slow and flaky, so we only test a few YAMLS here.
@@ -165,7 +165,7 @@ jobs:
165165
- name: Test
166166
uses: nick-invision/retry@v2
167167
with:
168-
timeout_minutes: 15
168+
timeout_minutes: 30
169169
retry_on: error
170170
max_attempts: 3
171171
command: ./hack/test-example.sh examples/${{ matrix.example }}

cmd/limactl/hostagent.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@ import (
44
"errors"
55
"fmt"
66
"io"
7+
"net"
8+
"net/http"
79
"os"
810
"os/signal"
911
"strconv"
1012

13+
"github.com/gorilla/mux"
1114
"github.com/lima-vm/lima/pkg/hostagent"
15+
"github.com/lima-vm/lima/pkg/hostagent/api/server"
16+
"github.com/sirupsen/logrus"
1217
"github.com/spf13/cobra"
1318
)
1419

@@ -21,6 +26,7 @@ func newHostagentCommand() *cobra.Command {
2126
Hidden: true,
2227
}
2328
hostagentCommand.Flags().StringP("pidfile", "p", "", "write pid to file")
29+
hostagentCommand.Flags().String("socket", "", "hostagent socket")
2430
return hostagentCommand
2531
}
2632

@@ -38,6 +44,13 @@ func hostagentAction(cmd *cobra.Command, args []string) error {
3844
}
3945
defer os.RemoveAll(pidfile)
4046
}
47+
socket, err := cmd.Flags().GetString("socket")
48+
if err != nil {
49+
return err
50+
}
51+
if socket == "" {
52+
return fmt.Errorf("socket must be specified (limactl version mismatch?)")
53+
}
4154

4255
instName := args[0]
4356

@@ -51,6 +64,28 @@ func hostagentAction(cmd *cobra.Command, args []string) error {
5164
if err != nil {
5265
return err
5366
}
67+
68+
backend := &server.Backend{
69+
Agent: ha,
70+
}
71+
r := mux.NewRouter()
72+
server.AddRoutes(r, backend)
73+
srv := &http.Server{Handler: r}
74+
err = os.RemoveAll(socket)
75+
if err != nil {
76+
return err
77+
}
78+
l, err := net.Listen("unix", socket)
79+
if err != nil {
80+
return err
81+
}
82+
go func() {
83+
defer os.RemoveAll(socket)
84+
defer srv.Close()
85+
if serveErr := srv.Serve(l); serveErr != nil {
86+
logrus.WithError(serveErr).Warn("hostagent API server exited with an error")
87+
}
88+
}()
5489
return ha.Run(cmd.Context())
5590
}
5691

docs/internal.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ Guest agent:
5050

5151
Host agent:
5252
- `ha.pid`: hostagent PID
53+
- `ha.sock`: hostagent REST API
5354
- `ha.stdout.log`: hostagent stdout (JSON lines, see `pkg/hostagent/events.Event`)
5455
- `ha.stderr.log`: hostagent stderr (human-readable messages)
5556

examples/alpine.yaml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# This example requires Lima v0.7.0 or later.
12
images:
23
- location: https://github.com/lima-vm/alpine-lima/releases/download/v0.1.8/alpine-lima-std-3.13.5-x86_64.iso
34
arch: "x86_64"
@@ -9,11 +10,6 @@ mounts:
910
- location: "/tmp/lima"
1011
writable: true
1112

12-
ssh:
13-
# localPort is changed from 60022 to avoid conflicting with the default.
14-
# (TODO: assign localPort automatically)
15-
localPort: 60020
16-
1713
firmware:
1814
legacyBIOS: true
1915

examples/archlinux.yaml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# This example requires Lima v0.5.0 or later
1+
# This example requires Lima v0.7.0 or later
22
arch: "x86_64"
33
images:
44
# NOTE: the image is periodically rotated, if you face 404, see https://mirror.pkgbuild.com/images/ to find the latest image.
@@ -10,9 +10,5 @@ mounts:
1010
writable: false
1111
- location: "/tmp/lima"
1212
writable: true
13-
ssh:
14-
# localPort is changed from 60022 to avoid conflicting with the default.
15-
# (TODO: assign localPort automatically)
16-
localPort: 60050
1713
firmware:
1814
legacyBIOS: true

examples/debian.yaml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# This example requires Lima v0.7.0 or later
12
images:
23
- location: "https://cloud.debian.org/images/cloud/bullseye/daily/20211005-786/debian-11-generic-amd64-daily-20211005-786.qcow2"
34
arch: "x86_64"
@@ -10,7 +11,3 @@ mounts:
1011
writable: false
1112
- location: "/tmp/lima"
1213
writable: true
13-
ssh:
14-
# localPort is changed from 60022 to avoid conflicting with the default.
15-
# (TODO: assign localPort automatically)
16-
localPort: 60030

examples/fedora.yaml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# This example requires Lima v0.7.0 or later.
12
arch: "x86_64"
23
images:
34
- location: "https://download.fedoraproject.org/pub/fedora/linux/releases/34/Cloud/x86_64/images/Fedora-Cloud-Base-34-1.2.x86_64.qcow2"
@@ -8,10 +9,5 @@ mounts:
89
writable: false
910
- location: "/tmp/lima"
1011
writable: true
11-
ssh:
12-
# localPort is changed from 60022 to avoid conflicting with the default.
13-
# (TODO: assign localPort automatically)
14-
localPort: 60024
15-
1612
firmware:
1713
legacyBIOS: true

examples/k3s.yaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
# $ kubectl get no
99
# NAME STATUS ROLES AGE VERSION
1010
# lima-k3s Ready control-plane,master 69s v1.21.1+k3s1
11+
#
12+
# This example requires Lima v0.7.0 or later.
1113

1214
images:
1315
# Hint: run `limactl prune` to invalidate the "current" cache
@@ -19,9 +21,6 @@ images:
1921
# Mounts are disabled in this example, but can be enabled optionally.
2022
mounts: []
2123

22-
ssh:
23-
localPort: 60022
24-
2524
# containerd is managed by k3s, not by Lima, so the values are set to false here.
2625
containerd:
2726
system: false

examples/opensuse.yaml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# This example requires Lima v0.5.0 or later
1+
# This example requires Lima v0.7.0 or later
22
images:
33
# Hint: run `limactl prune` to invalidate the "Current" cache
44
- location: https://download.opensuse.org/distribution/leap/15.3/appliances/openSUSE-Leap-15.3-JeOS.x86_64-15.3-OpenStack-Cloud-Current.qcow2
@@ -9,7 +9,3 @@ mounts:
99
writable: false
1010
- location: "/tmp/lima"
1111
writable: true
12-
ssh:
13-
# localPort is changed from 60022 to avoid conflicting with the default.
14-
# (TODO: assign localPort automatically)
15-
localPort: 60044

examples/ubuntu.yaml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# This example requires Lima v0.7.0 or later.
12
images:
23
# Hint: run `limactl prune` to invalidate the "current" cache
34
- location: "https://cloud-images.ubuntu.com/hirsute/current/hirsute-server-cloudimg-amd64.img"
@@ -9,7 +10,3 @@ mounts:
910
writable: false
1011
- location: "/tmp/lima"
1112
writable: true
12-
ssh:
13-
# localPort is changed from 60022 to avoid conflicting with the default.
14-
# (TODO: assign localPort automatically)
15-
localPort: 60023

examples/vmnet.yaml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@ mounts:
1212
writable: false
1313
- location: "/tmp/lima"
1414
writable: true
15-
ssh:
16-
# localPort is changed from 60022 to avoid conflicting with the default.
17-
# (TODO: assign localPort automatically)
18-
localPort: 60105
19-
2015
networks:
2116
# The instance can get routable IP addresses from the vmnet framework using
2217
# https://github.com/lima-vm/vde_vmnet. Available networks are defined in

pkg/cidata/cidata.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func setupEnv(y *limayaml.LimaYAML) (map[string]string, error) {
6565
return env, nil
6666
}
6767

68-
func GenerateISO9660(instDir, name string, y *limayaml.LimaYAML) error {
68+
func GenerateISO9660(instDir, name string, y *limayaml.LimaYAML, udpDNSLocalPort int) error {
6969
if err := limayaml.Validate(*y, false); err != nil {
7070
return err
7171
}
@@ -78,13 +78,13 @@ func GenerateISO9660(instDir, name string, y *limayaml.LimaYAML) error {
7878
return err
7979
}
8080
args := TemplateArgs{
81-
Name: name,
82-
User: u.Username,
83-
UID: uid,
84-
Containerd: Containerd{System: *y.Containerd.System, User: *y.Containerd.User},
85-
SlirpNICName: qemu.SlirpNICName,
86-
SlirpGateway: qemu.SlirpGateway,
87-
SlirpDNS: qemu.SlirpDNS,
81+
Name: name,
82+
User: u.Username,
83+
UID: uid,
84+
Containerd: Containerd{System: *y.Containerd.System, User: *y.Containerd.User},
85+
SlirpNICName: qemu.SlirpNICName,
86+
SlirpGateway: qemu.SlirpGateway,
87+
SlirpDNS: qemu.SlirpDNS,
8888
}
8989

9090
// change instance id on every boot so network config will be processed again
@@ -120,7 +120,7 @@ func GenerateISO9660(instDir, name string, y *limayaml.LimaYAML) error {
120120
return err
121121
}
122122
if *y.UseHostResolver {
123-
args.UDPDNSLocalPort = y.SSH.LocalPort
123+
args.UDPDNSLocalPort = udpDNSLocalPort
124124
args.DNSAddresses = append(args.DNSAddresses, qemu.SlirpDNS)
125125
} else if len(y.DNS) > 0 {
126126
for _, addr := range y.DNS {

pkg/guestagent/api/api.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@ import (
66
"time"
77
)
88

9-
// ErrorJSON is returned with "application/json" content type and non-2XX status code
10-
type ErrorJSON struct {
11-
Message string `json:"message"`
12-
}
13-
149
var (
1510
IPv4loopback1 = net.IPv4(127, 0, 0, 1)
1611
)

pkg/guestagent/api/server/server.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/gorilla/mux"
99
"github.com/lima-vm/lima/pkg/guestagent"
1010
"github.com/lima-vm/lima/pkg/guestagent/api"
11+
"github.com/lima-vm/lima/pkg/httputil"
1112
"github.com/sirupsen/logrus"
1213
)
1314

@@ -18,8 +19,9 @@ type Backend struct {
1819
func (b *Backend) onError(w http.ResponseWriter, r *http.Request, err error, ec int) {
1920
w.WriteHeader(ec)
2021
w.Header().Set("Content-Type", "application/json")
21-
// it is safe to return the err to the client, because the client is reliable
22-
e := api.ErrorJSON{
22+
// err may potentially contain credential info (in a future version),
23+
// but it is safe to return the err to the client, because we do not expose the socket to the internet
24+
e := httputil.ErrorJSON{
2325
Message: err.Error(),
2426
}
2527
_ = json.NewEncoder(w).Encode(e)

pkg/hostagent/api/api.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package api
2+
3+
type Info struct {
4+
SSHLocalPort int `json:"sshLocalPort,omitempty"`
5+
}

pkg/hostagent/api/client/client.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package client
2+
3+
// Forked from https://github.com/rootless-containers/rootlesskit/blob/v0.14.2/pkg/api/client/client.go
4+
// Apache License 2.0
5+
6+
import (
7+
"context"
8+
"encoding/json"
9+
"fmt"
10+
"net/http"
11+
12+
"github.com/lima-vm/lima/pkg/hostagent/api"
13+
"github.com/lima-vm/lima/pkg/httpclientutil"
14+
)
15+
16+
type HostAgentClient interface {
17+
HTTPClient() *http.Client
18+
Info(context.Context) (*api.Info, error)
19+
}
20+
21+
// NewHostAgentClient creates a client.
22+
// socketPath is a path to the UNIX socket, without unix:// prefix.
23+
func NewHostAgentClient(socketPath string) (HostAgentClient, error) {
24+
hc, err := httpclientutil.NewHTTPClientWithSocketPath(socketPath)
25+
if err != nil {
26+
return nil, err
27+
}
28+
return NewHostAgentClientWithHTTPClient(hc), nil
29+
}
30+
31+
func NewHostAgentClientWithHTTPClient(hc *http.Client) HostAgentClient {
32+
return &client{
33+
Client: hc,
34+
version: "v1",
35+
dummyHost: "lima-hostagent",
36+
}
37+
}
38+
39+
type client struct {
40+
*http.Client
41+
// version is always "v1"
42+
// TODO(AkihiroSuda): negotiate the version
43+
version string
44+
dummyHost string
45+
}
46+
47+
func (c *client) HTTPClient() *http.Client {
48+
return c.Client
49+
}
50+
51+
func (c *client) Info(ctx context.Context) (*api.Info, error) {
52+
u := fmt.Sprintf("http://%s/%s/info", c.dummyHost, c.version)
53+
resp, err := httpclientutil.Get(ctx, c.HTTPClient(), u)
54+
if err != nil {
55+
return nil, err
56+
}
57+
defer resp.Body.Close()
58+
var info api.Info
59+
dec := json.NewDecoder(resp.Body)
60+
if err := dec.Decode(&info); err != nil {
61+
return nil, err
62+
}
63+
return &info, nil
64+
}

0 commit comments

Comments
 (0)