Skip to content

Feature: Internal driver plugin system #3693

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions cmd/limactl/main_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build !external_vz

// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0

package main

// Import vz driver to register it in the registry on darwin.
import _ "github.com/lima-vm/lima/pkg/driver/vz"
9 changes: 9 additions & 0 deletions cmd/limactl/main_qemu.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build !external_qemu

// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0

package main

// Import qemu driver to register it in the registry on all platforms.
import _ "github.com/lima-vm/lima/pkg/driver/qemu"
9 changes: 9 additions & 0 deletions cmd/limactl/main_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build !external_wsl2

// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0

package main

// Import wsl2 driver to register it in the registry on windows.
import _ "github.com/lima-vm/lima/pkg/driver/wsl2"
79 changes: 52 additions & 27 deletions pkg/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ package driver
import (
"context"
"net"

"github.com/lima-vm/lima/pkg/store"
)

// Driver interface is used by hostagent for managing vm.
type Driver interface {
// Lifecycle defines basic lifecycle operations.
type Lifecycle interface {
// Validate returns error if the current driver isn't support for given config
Validate() error

Expand All @@ -29,43 +31,66 @@ type Driver interface {
// The second argument may contain error occurred while starting driver
Start(_ context.Context) (chan error, error)

// CanRunGUI returns bool to indicate if the hostagent need to run GUI synchronously
CanRunGUI() bool

// RunGUI is for starting GUI synchronously by hostagent. This method should be wait and return only after vm terminates
// It returns error if there are any failures
RunGUI() error

// Stop will terminate the running vm instance.
// It returns error if there are any errors during Stop
Stop(_ context.Context) error
}

// Register will add an instance to a registry.
// It returns error if there are any errors during Register
Register(_ context.Context) error
// GUI defines GUI-related operations.
type GUI interface {
// RunGUI is for starting GUI synchronously by hostagent. This method should be wait and return only after vm terminates
// It returns error if there are any failures
RunGUI() error

// Unregister will perform any cleanup related to the vm instance.
// It returns error if there are any errors during Unregister
Unregister(_ context.Context) error
ChangeDisplayPassword(ctx context.Context, password string) error
DisplayConnection(ctx context.Context) (string, error)
}

ChangeDisplayPassword(_ context.Context, password string) error
// SnapshotManager defines operations for managing snapshots.
type SnapshotManager interface {
CreateSnapshot(ctx context.Context, tag string) error
ApplySnapshot(ctx context.Context, tag string) error
DeleteSnapshot(ctx context.Context, tag string) error
ListSnapshots(ctx context.Context) (string, error)
}

GetDisplayConnection(_ context.Context) (string, error)
// Registration defines operations for registering and unregistering the driver instance.
type Registration interface {
Register(ctx context.Context) error
Unregister(ctx context.Context) error
}

CreateSnapshot(_ context.Context, tag string) error
// GuestAgent defines operations for the guest agent.
type GuestAgent interface {
// ForwardGuestAgent returns if the guest agent sock needs forwarding by host agent.
ForwardGuestAgent() bool

ApplySnapshot(_ context.Context, tag string) error
// GuestAgentConn returns the guest agent connection, or nil (if forwarded by ssh).
GuestAgentConn(_ context.Context) (net.Conn, string, error)
}

DeleteSnapshot(_ context.Context, tag string) error
// Driver interface is used by hostagent for managing vm.
type Driver interface {
Lifecycle
GUI
SnapshotManager
Registration
GuestAgent

ListSnapshots(_ context.Context) (string, error)
Info() Info

// ForwardGuestAgent returns if the guest agent sock needs forwarding by host agent.
ForwardGuestAgent() bool
// SetConfig sets the configuration for the instance.
Configure(inst *store.Instance, sshLocalPort int) *ConfiguredDriver
}

// GuestAgentConn returns the guest agent connection, or nil (if forwarded by ssh).
GuestAgentConn(_ context.Context) (net.Conn, error)
type ConfiguredDriver struct {
Driver
}

VSockPort() int
VirtioPort() string
type Info struct {
DriverName string `json:"driverName"`
CanRunGUI bool `json:"canRunGui,omitempty"`
VsockPort int `json:"vsockPort"`
VirtioPort string `json:"virtioPort"`
InstanceDir string `json:"instanceDir,omitempty"`
}
47 changes: 27 additions & 20 deletions pkg/driver/qemu/qemu_driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type LimaQemuDriver struct {

var _ driver.Driver = (*LimaQemuDriver)(nil)

func New(inst *store.Instance, sshLocalPort int) *LimaQemuDriver {
func New() *LimaQemuDriver {
// virtserialport doesn't seem to work reliably: https://github.com/lima-vm/lima/issues/2064
// but on Windows default Unix socket forwarding is not available
var virtioPort string
Expand All @@ -59,10 +59,17 @@ func New(inst *store.Instance, sshLocalPort int) *LimaQemuDriver {
virtioPort = ""
}
return &LimaQemuDriver{
Instance: inst,
vSockPort: 0,
virtioPort: virtioPort,
SSHLocalPort: sshLocalPort,
vSockPort: 0,
virtioPort: virtioPort,
}
}

func (l *LimaQemuDriver) Configure(inst *store.Instance, sshLocalPort int) *driver.ConfiguredDriver {
l.Instance = inst
l.SSHLocalPort = sshLocalPort

return &driver.ConfiguredDriver{
Driver: l,
}
}

Expand Down Expand Up @@ -238,7 +245,7 @@ func (l *LimaQemuDriver) ChangeDisplayPassword(_ context.Context, password strin
return l.changeVNCPassword(password)
}

func (l *LimaQemuDriver) GetDisplayConnection(_ context.Context) (string, error) {
func (l *LimaQemuDriver) DisplayConnection(_ context.Context) (string, error) {
return l.getVNCDisplayPort()
}

Expand Down Expand Up @@ -450,10 +457,10 @@ func (l *LimaQemuDriver) ListSnapshots(_ context.Context) (string, error) {
return List(qCfg, l.Instance.Status == store.StatusRunning)
}

func (l *LimaQemuDriver) GuestAgentConn(ctx context.Context) (net.Conn, error) {
func (l *LimaQemuDriver) GuestAgentConn(ctx context.Context) (net.Conn, string, error) {
var d net.Dialer
dialContext, err := d.DialContext(ctx, "unix", filepath.Join(l.Instance.Dir, filenames.GuestAgentSock))
return dialContext, err
return dialContext, "unix", err
}

type qArgTemplateApplier struct {
Expand Down Expand Up @@ -508,12 +515,20 @@ func (a *qArgTemplateApplier) applyTemplate(qArg string) (string, error) {
return b.String(), nil
}

func (l *LimaQemuDriver) Initialize(_ context.Context) error {
return nil
func (l *LimaQemuDriver) Info() driver.Info {
var info driver.Info
if l.Instance != nil && l.Instance.Dir != "" {
info.InstanceDir = l.Instance.Dir
}
info.DriverName = "qemu"
info.CanRunGUI = false
info.VirtioPort = l.virtioPort
info.VsockPort = l.vSockPort
return info
}

func (l *LimaQemuDriver) CanRunGUI() bool {
return false
func (l *LimaQemuDriver) Initialize(_ context.Context) error {
return nil
}

func (l *LimaQemuDriver) RunGUI() error {
Expand All @@ -532,11 +547,3 @@ func (l *LimaQemuDriver) ForwardGuestAgent() bool {
// if driver is not providing, use host agent
return l.vSockPort == 0 && l.virtioPort == ""
}

func (l *LimaQemuDriver) VSockPort() int {
return l.vSockPort
}

func (l *LimaQemuDriver) VirtioPort() string {
return l.virtioPort
}
12 changes: 12 additions & 0 deletions pkg/driver/qemu/register.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//go:build !external_qemu

// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0

package qemu

import "github.com/lima-vm/lima/pkg/registry"

func init() {
registry.Register(New())
}
5 changes: 4 additions & 1 deletion pkg/driver/vz/errors_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ package vz
import "errors"

//nolint:revive,staticcheck // false positives with proper nouns
var errRosettaUnsupported = errors.New("Rosetta is unsupported on non-ARM64 hosts")
var (
errRosettaUnsupported = errors.New("Rosetta is unsupported on non-ARM64 hosts")
errUnimplemented = errors.New("unimplemented")
)
12 changes: 12 additions & 0 deletions pkg/driver/vz/register.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//go:build darwin && !external_vz

// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0

package vz

import "github.com/lima-vm/lima/pkg/registry"

func init() {
registry.Register(New())
}
63 changes: 38 additions & 25 deletions pkg/driver/vz/vz_driver_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,19 @@ type LimaVzDriver struct {

var _ driver.Driver = (*LimaVzDriver)(nil)

func New(inst *store.Instance, sshLocalPort int) *LimaVzDriver {
func New() *LimaVzDriver {
return &LimaVzDriver{
Instance: inst,
vSockPort: 2222,
virtioPort: "",
SSHLocalPort: sshLocalPort,
vSockPort: 2222,
virtioPort: "",
}
}

func (l *LimaVzDriver) Configure(inst *store.Instance, sshLocalPort int) *driver.ConfiguredDriver {
l.Instance = inst
l.SSHLocalPort = sshLocalPort

return &driver.ConfiguredDriver{
Driver: l,
}
}

Expand Down Expand Up @@ -197,7 +204,7 @@ func (l *LimaVzDriver) Start(ctx context.Context) (chan error, error) {
return errCh, nil
}

func (l *LimaVzDriver) CanRunGUI() bool {
func (l *LimaVzDriver) canRunGUI() bool {
switch *l.Instance.Config.Video.Display {
case "vz", "default":
return true
Expand All @@ -207,7 +214,7 @@ func (l *LimaVzDriver) CanRunGUI() bool {
}

func (l *LimaVzDriver) RunGUI() error {
if l.CanRunGUI() {
if l.canRunGUI() {
return l.machine.StartGraphicApplication(1920, 1200)
}
return fmt.Errorf("RunGUI is not supported for the given driver '%s' and display '%s'", "vz", *l.Instance.Config.Video.Display)
Expand Down Expand Up @@ -243,14 +250,28 @@ func (l *LimaVzDriver) Stop(_ context.Context) error {
return errors.New("vz: CanRequestStop is not supported")
}

func (l *LimaVzDriver) GuestAgentConn(_ context.Context) (net.Conn, error) {
func (l *LimaVzDriver) GuestAgentConn(_ context.Context) (net.Conn, string, error) {
for _, socket := range l.machine.SocketDevices() {
connect, err := socket.Connect(uint32(l.vSockPort))
if err == nil && connect.SourcePort() != 0 {
return connect, nil
}
return connect, "vsock", err
}
return nil, errors.New("unable to connect to guest agent via vsock port 2222")

return nil, "", errors.New("unable to connect to guest agent via vsock port 2222")
}

func (l *LimaVzDriver) Info() driver.Info {
var info driver.Info
if l.Instance != nil {
info.CanRunGUI = l.canRunGUI()
}

info.DriverName = "vz"
info.VsockPort = l.vSockPort
info.VirtioPort = l.virtioPort
if l.Instance != nil {
info.InstanceDir = l.Instance.Dir
}
return info
}

func (l *LimaVzDriver) Register(_ context.Context) error {
Expand All @@ -265,35 +286,27 @@ func (l *LimaVzDriver) ChangeDisplayPassword(_ context.Context, _ string) error
return nil
}

func (l *LimaVzDriver) GetDisplayConnection(_ context.Context) (string, error) {
func (l *LimaVzDriver) DisplayConnection(_ context.Context) (string, error) {
return "", nil
}

func (l *LimaVzDriver) CreateSnapshot(_ context.Context, _ string) error {
return errors.New("unimplemented")
return errUnimplemented
}

func (l *LimaVzDriver) ApplySnapshot(_ context.Context, _ string) error {
return errors.New("unimplemented")
return errUnimplemented
}

func (l *LimaVzDriver) DeleteSnapshot(_ context.Context, _ string) error {
return errors.New("unimplemented")
return errUnimplemented
}

func (l *LimaVzDriver) ListSnapshots(_ context.Context) (string, error) {
return "", errors.New("unimplemented")
return "", errUnimplemented
}

func (l *LimaVzDriver) ForwardGuestAgent() bool {
// If driver is not providing, use host agent
return l.vSockPort == 0 && l.virtioPort == ""
}

func (l *LimaVzDriver) VSockPort() int {
return l.vSockPort
}

func (l *LimaVzDriver) VirtioPort() string {
return l.virtioPort
}
Loading