Skip to content

Commit 87bc141

Browse files
committed
Docker Container external driver
A new Linux driver similar to the WSL2 driver for Windows, creating virtual machines from container images (as rootfs). Signed-off-by: Anders F Björklund <[email protected]>
1 parent 33feb79 commit 87bc141

File tree

16 files changed

+645
-9
lines changed

16 files changed

+645
-9
lines changed

cmd/lima-driver-dc/main_linux.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package main
5+
6+
import (
7+
"github.com/lima-vm/lima/v2/pkg/driver/dc"
8+
"github.com/lima-vm/lima/v2/pkg/driver/external/server"
9+
)
10+
11+
// To be used as an external driver for Lima.
12+
func main() {
13+
server.Serve(dc.New())
14+
}

pkg/cidata/cidata.TEMPLATE.d/boot/02-wsl2-ac-setup.sh renamed to pkg/cidata/cidata.TEMPLATE.d/boot/02-container-setup.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
# SPDX-License-Identifier: Apache-2.0
55

66
# This script replaces the cloud-init functionality of creating a user and setting its SSH keys
7-
# when using a WSL2 or AC VM.
8-
[ "$LIMA_CIDATA_VMTYPE" = "wsl2" ] || [ "$LIMA_CIDATA_VMTYPE" = "ac" ] || exit 0
7+
# when using a WSL2 or AC/DC VM.
8+
[ "$LIMA_CIDATA_VMTYPE" = "wsl2" ] || [ "$LIMA_CIDATA_VMTYPE" = "ac" ] || [ "$LIMA_CIDATA_VMTYPE" = "dc" ] || exit 0
99

1010
# create user
1111
# shellcheck disable=SC2153

pkg/cidata/cidata.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ func GenerateISO9660(instDir, name string, instConfig *limayaml.LimaYAML, udpDNS
432432
})
433433
}
434434

435-
if args.VMType == limayaml.WSL2 || args.VMType == limayaml.AC {
435+
if args.VMType == limayaml.WSL2 || args.VMType == limayaml.AC || args.VMType == limayaml.DC {
436436
layout = append(layout, iso9660util.Entry{
437437
Path: "ssh_authorized_keys",
438438
Reader: strings.NewReader(strings.Join(args.SSHPubKeys, "\n")),

pkg/driver/dc/dc_driver_linux.go

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package dc
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"net"
10+
"regexp"
11+
12+
"github.com/sirupsen/logrus"
13+
14+
"github.com/lima-vm/lima/v2/pkg/driver"
15+
"github.com/lima-vm/lima/v2/pkg/limayaml"
16+
"github.com/lima-vm/lima/v2/pkg/reflectutil"
17+
"github.com/lima-vm/lima/v2/pkg/store"
18+
)
19+
20+
var knownYamlProperties = []string{
21+
"Arch",
22+
"Containerd",
23+
"CopyToHost",
24+
"CPUType",
25+
"Disk",
26+
"DNS",
27+
"Env",
28+
"HostResolver",
29+
"Images",
30+
"Message",
31+
"Mounts",
32+
"MountType",
33+
"Param",
34+
"Plain",
35+
"PortForwards",
36+
"Probes",
37+
"PropagateProxyEnv",
38+
"Provision",
39+
"SSH",
40+
"VMType",
41+
}
42+
43+
const Enabled = true
44+
45+
type LimaDcDriver struct {
46+
Instance *store.Instance
47+
48+
SSHLocalPort int
49+
vSockPort int
50+
virtioPort string
51+
}
52+
53+
var _ driver.Driver = (*LimaDcDriver)(nil)
54+
55+
func New() *LimaDcDriver {
56+
return &LimaDcDriver{
57+
vSockPort: 0,
58+
virtioPort: "",
59+
}
60+
}
61+
62+
func (l *LimaDcDriver) Configure(inst *store.Instance) *driver.ConfiguredDriver {
63+
l.Instance = inst
64+
l.SSHLocalPort = inst.SSHLocalPort
65+
66+
return &driver.ConfiguredDriver{
67+
Driver: l,
68+
}
69+
}
70+
71+
func (l *LimaDcDriver) Validate() error {
72+
if *l.Instance.Config.MountType != limayaml.REVSSHFS {
73+
return fmt.Errorf("field `mountType` must be %q for DC driver, got %q", limayaml.REVSSHFS, *l.Instance.Config.MountType)
74+
}
75+
// TODO: revise this list for DC
76+
if unknown := reflectutil.UnknownNonEmptyFields(l.Instance.Config, knownYamlProperties...); len(unknown) > 0 {
77+
logrus.Warnf("Ignoring: vmType %s: %+v", *l.Instance.Config.VMType, unknown)
78+
}
79+
80+
if !limayaml.IsNativeArch(*l.Instance.Config.Arch) {
81+
return fmt.Errorf("unsupported arch: %q", *l.Instance.Config.Arch)
82+
}
83+
84+
// TODO: real filetype checks
85+
tarFileRegex := regexp.MustCompile(`.*tar\.*`)
86+
for i, image := range l.Instance.Config.Images {
87+
if unknown := reflectutil.UnknownNonEmptyFields(image, "File"); len(unknown) > 0 {
88+
logrus.Warnf("Ignoring: vmType %s: images[%d]: %+v", *l.Instance.Config.VMType, i, unknown)
89+
}
90+
match := tarFileRegex.MatchString(image.Location)
91+
if image.Arch == *l.Instance.Config.Arch && !match {
92+
return fmt.Errorf("unsupported image type for vmType: %s, tarball root file system required: %q", *l.Instance.Config.VMType, image.Location)
93+
}
94+
}
95+
96+
for i, mount := range l.Instance.Config.Mounts {
97+
if unknown := reflectutil.UnknownNonEmptyFields(mount); len(unknown) > 0 {
98+
logrus.Warnf("Ignoring: vmType %s: mounts[%d]: %+v", *l.Instance.Config.VMType, i, unknown)
99+
}
100+
}
101+
102+
for i, network := range l.Instance.Config.Networks {
103+
if unknown := reflectutil.UnknownNonEmptyFields(network); len(unknown) > 0 {
104+
logrus.Warnf("Ignoring: vmType %s: networks[%d]: %+v", *l.Instance.Config.VMType, i, unknown)
105+
}
106+
}
107+
108+
audioDevice := *l.Instance.Config.Audio.Device
109+
if audioDevice != "" {
110+
logrus.Warnf("Ignoring: vmType %s: `audio.device`: %+v", *l.Instance.Config.VMType, audioDevice)
111+
}
112+
113+
return nil
114+
}
115+
116+
func (l *LimaDcDriver) Start(ctx context.Context) (chan error, error) {
117+
logrus.Infof("Starting DC VM")
118+
status, err := store.GetDcStatus(l.Instance.Name)
119+
if err != nil {
120+
return nil, err
121+
}
122+
123+
distroName := "lima-" + l.Instance.Name
124+
125+
if status == store.StatusUninitialized {
126+
if err := EnsureFs(ctx, l.Instance); err != nil {
127+
return nil, err
128+
}
129+
if err := initVM(ctx, l.Instance.Dir, distroName); err != nil {
130+
return nil, err
131+
}
132+
cpus := l.Instance.CPUs
133+
memory := int(l.Instance.Memory >> 20) // MiB
134+
if err := registerVM(ctx, distroName, cpus, memory); err != nil {
135+
return nil, err
136+
}
137+
}
138+
139+
errCh := make(chan error)
140+
141+
if err := startVM(ctx, distroName); err != nil {
142+
return nil, err
143+
}
144+
145+
if err := provisionVM(
146+
ctx,
147+
l.Instance.Dir,
148+
l.Instance.Name,
149+
distroName,
150+
errCh,
151+
); err != nil {
152+
return nil, err
153+
}
154+
155+
return errCh, err
156+
}
157+
158+
func (l *LimaDcDriver) canRunGUI() bool {
159+
return false
160+
}
161+
162+
func (l *LimaDcDriver) RunGUI() error {
163+
return fmt.Errorf("RunGUI is not supported for the given driver '%s' and display '%s'", "dc", *l.Instance.Config.Video.Display)
164+
}
165+
166+
func (l *LimaDcDriver) Stop(ctx context.Context) error {
167+
logrus.Info("Shutting down DC VM")
168+
distroName := "lima-" + l.Instance.Name
169+
return stopVM(ctx, distroName)
170+
}
171+
172+
func (l *LimaDcDriver) Unregister(ctx context.Context) error {
173+
distroName := "lima-" + l.Instance.Name
174+
status, err := store.GetDcStatus(l.Instance.Name)
175+
if err != nil {
176+
return err
177+
}
178+
switch status {
179+
case store.StatusRunning, store.StatusStopped, store.StatusBroken, store.StatusInstalling:
180+
return unregisterVM(ctx, distroName)
181+
}
182+
183+
logrus.Info("VM not registered, skipping unregistration")
184+
return nil
185+
}
186+
187+
// GuestAgentConn returns the guest agent connection, or nil (if forwarded by ssh).
188+
func (l *LimaDcDriver) GuestAgentConn(_ context.Context) (net.Conn, string, error) {
189+
return nil, "", nil
190+
}
191+
192+
func (l *LimaDcDriver) Info() driver.Info {
193+
var info driver.Info
194+
if l.Instance != nil {
195+
info.InstanceDir = l.Instance.Dir
196+
}
197+
info.DriverName = "dc"
198+
info.CanRunGUI = l.canRunGUI()
199+
info.VirtioPort = l.virtioPort
200+
info.VsockPort = l.vSockPort
201+
return info
202+
}
203+
204+
func (l *LimaDcDriver) Initialize(_ context.Context) error {
205+
return nil
206+
}
207+
208+
func (l *LimaDcDriver) CreateDisk(_ context.Context) error {
209+
return nil
210+
}
211+
212+
func (l *LimaDcDriver) Register(_ context.Context) error {
213+
return nil
214+
}
215+
216+
func (l *LimaDcDriver) ChangeDisplayPassword(_ context.Context, _ string) error {
217+
return nil
218+
}
219+
220+
func (l *LimaDcDriver) DisplayConnection(_ context.Context) (string, error) {
221+
return "", nil
222+
}
223+
224+
func (l *LimaDcDriver) CreateSnapshot(_ context.Context, _ string) error {
225+
return errUnimplemented
226+
}
227+
228+
func (l *LimaDcDriver) ApplySnapshot(_ context.Context, _ string) error {
229+
return errUnimplemented
230+
}
231+
232+
func (l *LimaDcDriver) DeleteSnapshot(_ context.Context, _ string) error {
233+
return errUnimplemented
234+
}
235+
236+
func (l *LimaDcDriver) ListSnapshots(_ context.Context) (string, error) {
237+
return "", errUnimplemented
238+
}
239+
240+
func (l *LimaDcDriver) ForwardGuestAgent() bool {
241+
// If driver is not providing, use host agent
242+
return l.vSockPort == 0 && l.virtioPort == ""
243+
}

pkg/driver/dc/errors_linux.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package dc
5+
6+
import "errors"
7+
8+
var errUnimplemented = errors.New("unimplemented")

pkg/driver/dc/fs.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package dc
5+
6+
import (
7+
"context"
8+
"errors"
9+
"os"
10+
"path/filepath"
11+
12+
"github.com/sirupsen/logrus"
13+
14+
"github.com/lima-vm/lima/v2/pkg/fileutils"
15+
"github.com/lima-vm/lima/v2/pkg/store"
16+
"github.com/lima-vm/lima/v2/pkg/store/filenames"
17+
)
18+
19+
// EnsureFs downloads the root fs.
20+
func EnsureFs(ctx context.Context, inst *store.Instance) error {
21+
baseDisk := filepath.Join(inst.Dir, filenames.BaseDisk)
22+
if _, err := os.Stat(baseDisk); errors.Is(err, os.ErrNotExist) {
23+
var ensuredBaseDisk bool
24+
errs := make([]error, len(inst.Config.Images))
25+
for i, f := range inst.Config.Images {
26+
if _, err := fileutils.DownloadFile(ctx, baseDisk, f.File, true, "the image", *inst.Config.Arch); err != nil {
27+
errs[i] = err
28+
continue
29+
}
30+
ensuredBaseDisk = true
31+
break
32+
}
33+
if !ensuredBaseDisk {
34+
return fileutils.Errors(errs)
35+
}
36+
}
37+
logrus.Info("Download succeeded")
38+
39+
return nil
40+
}

pkg/driver/dc/lima-init.TEMPLATE

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/bash
2+
set -eu
3+
export LOG_FILE=/var/log/lima-init.log
4+
exec > >(tee $LOG_FILE) 2>&1
5+
export LIMA_CIDATA_MNT="{{.CIDataPath}}"
6+
exec "$LIMA_CIDATA_MNT/boot.sh"

0 commit comments

Comments
 (0)