Skip to content

[WIP]: Create a Plugin System for Lima #3573

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

Draft
wants to merge 49 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
f30235a
driver(internal): use existing driver.Driver as plugin interface
unsuman May 21, 2025
c9d0f99
driver(internal): registry init
unsuman May 23, 2025
fda3db4
refactor(BaseDriver): remove driver.BaseDriver from Lima level
unsuman May 23, 2025
bf9d0cb
refactor(BaseDriver): remove driver.BaseDriver from driver(qemu) level
unsuman May 23, 2025
91814ea
refactor(BaseDriver): remove driver.BaseDriver from driver(vz) level
unsuman May 23, 2025
c1a08a1
refactor(BaseDriver): remove driver.BaseDriver from driver(wsl2) level
unsuman May 23, 2025
57a2080
refactor(BaseDriver): make drivers implement the driver.Driver interface
unsuman May 23, 2025
82955c1
driver(internal): divide the driver.Driver interface and define some …
unsuman May 23, 2025
b1f6a2a
driver(internal): change lima to support internal drivers
unsuman May 23, 2025
96e6a2a
driver(internal): add blank imports and make changes to Lima for inte…
unsuman May 24, 2025
f02c49b
driver(internal): add register files and make drivers compatible for …
unsuman May 24, 2025
535a080
driver(internal): list available built-in drivers
unsuman May 24, 2025
bb413ed
driver(internal): fix CI checks and lint errors
unsuman May 25, 2025
457ddea
refactor(driver): move qemu,vz and wsl2 to pkg/driver
unsuman May 25, 2025
13840ac
driver(internal): refactor Snapshot to SnapshotManager in driver inte…
unsuman May 26, 2025
dd276d3
driver(internal): compile vz on darwin, wsl2 on windows only and impl…
unsuman May 26, 2025
a20c982
refactor(driver): remove redundant builtins pkg
unsuman May 27, 2025
c56295c
driver(external): proto file init
unsuman May 27, 2025
439802d
driver(external): external driver manager init
unsuman May 28, 2025
252c024
driver(external): complete proto file for driver interface
unsuman May 28, 2025
9a9387a
driver(external): finalise proto file and generate gRPC code
unsuman May 29, 2025
0f46872
driver(external): implement server defination
unsuman May 29, 2025
5979bf3
driver(external): implement the grpc client and server
unsuman May 30, 2025
637e2b6
driver(external): remove error from the grpc response payload
unsuman May 30, 2025
e01f06d
driver(external): add discovery of external drivers
unsuman May 30, 2025
fbf9753
driver(internal): consolidate some functions to single GetInfo() func…
unsuman Jun 2, 2025
f01df5e
driver(external): consolidate some functions to single GetInfo() rpc …
unsuman Jun 2, 2025
9b22819
driver(external): implement Start() & SetConfig() as server methods
unsuman Jun 2, 2025
e2e0fd7
driver(external): complete client grpc implementation and one server …
unsuman Jun 3, 2025
43a1305
driver(external): tweak some external driver manager code
unsuman Jun 3, 2025
6d652e0
driver(external): complete external driver manager and registering pr…
unsuman Jun 4, 2025
0e0c207
driver(external): some tweaks around server and client code
unsuman Jun 4, 2025
e324e7b
driver(external): server logs to a file and fixed json marshal error …
unsuman Jun 5, 2025
62754a4
driver(external): implement bidirectional streaming for GuestAgentConn()
unsuman Jun 6, 2025
9abdd10
driver(external): manage external driver lifecycle
unsuman Jun 9, 2025
eefabda
driver(external): revamp grpchijack and GuestAgentConn()
unsuman Jun 10, 2025
a4d2fcd
driver(external): fix a nil pointer deref
unsuman Jun 11, 2025
cc54d5c
driver(external): create driver logs in instance directory
unsuman Jun 11, 2025
aab0162
driver(external): proxy vsock to ga.sock for vz
unsuman Jun 13, 2025
d13a7de
driver(external): change grpc transport to unix sockets
unsuman Jun 17, 2025
cd92ddb
driver(external): resolve GuestAgentConn() issue by proxying via new …
unsuman Jun 18, 2025
b690b21
driver(external): move proxy conn code to driver level
unsuman Jun 19, 2025
ebbfcbd
driver(external): clean code and fix driver discovery in libexec
unsuman Jun 19, 2025
963f97c
driver(external): tweak some code and move the conenction proxy to se…
unsuman Jun 20, 2025
f2cd7f0
driver(registry): prioritize internal drivers
unsuman Jun 23, 2025
c8b4113
driver(build): update Makefile to build drivers as internal or external
unsuman Jun 24, 2025
53e7cd1
driver(registry): avoid parenthesized info while listing drivers
unsuman Jun 25, 2025
bcb71ca
refactor(image-downloader): move code to limactl from vz & qemu
unsuman Jun 26, 2025
ca8867e
driver(build): limactl should not depend on additional-drivers target
unsuman Jun 26, 2025
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
30 changes: 28 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -241,18 +241,44 @@ endif
# calls the native resolver library and not the simplistic version in the Go library.
ENVS__output/bin/limactl$(exe) = CGO_ENABLED=1 GOOS="$(GOOS)" GOARCH="$(GOARCH)" CC="$(CC)"

LIMACTL_DRIVER_TAGS :=
ifneq (,$(findstring vz,$(ADDITIONAL_DRIVERS)))
LIMACTL_DRIVER_TAGS += external_vz
endif
ifneq (,$(findstring qemu,$(ADDITIONAL_DRIVERS)))
LIMACTL_DRIVER_TAGS += external_qemu
endif
ifneq (,$(findstring wsl2,$(ADDITIONAL_DRIVERS)))
LIMACTL_DRIVER_TAGS += external_wsl2
endif

GO_BUILDTAGS ?=
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
GO_BUILDTAGS ?=
GO_BUILD_TAGS ?=

GO_BUILDTAGS_LIMACTL := $(strip $(GO_BUILDTAGS) $(LIMACTL_DRIVER_TAGS))

_output/bin/limactl$(exe): $(LIMACTL_DEPS) $$(call force_build,$$@)
# If the previous cross-compilation was for GOOS=windows, limactl.exe might still be present.
ifneq ($(GOOS),windows) #
@rm -rf _output/bin/limactl.exe
else
@rm -rf _output/bin/limactl
endif
$(ENVS_$@) $(GO_BUILD) -o $@ ./cmd/limactl
$(ENVS_$@) $(GO_BUILD) -tags '$(GO_BUILDTAGS_LIMACTL)' -o $@ ./cmd/limactl
ifeq ($(GOOS),darwin)
codesign -f -v --entitlements vz.entitlements -s - $@
endif

DRIVER_INSTALL_DIR := _output/libexec/lima

.PHONY: additional-drivers
additional-drivers:
@mkdir -p $(DRIVER_INSTALL_DIR)
@for drv in $(ADDITIONAL_DRIVERS); do \
echo "Building $$drv as external"; \
$(GO_BUILD) -o $(DRIVER_INSTALL_DIR)/lima-driver-$$drv ./cmd/lima-driver-$$drv; \
if [ "$$drv" = "vz" ]; then \
codesign -f -v --entitlements vz.entitlements -s - $(DRIVER_INSTALL_DIR)/lima-driver-vz; \
fi; \
done

LIMA_CMDS = $(sort lima lima$(bat)) # $(sort ...) deduplicates the list
LIMA_DEPS = $(addprefix _output/bin/,$(LIMA_CMDS))
lima: $(LIMA_DEPS)
14 changes: 14 additions & 0 deletions cmd/lima-driver-qemu/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0

package main

import (
"github.com/lima-vm/lima/pkg/driver/external/server"
"github.com/lima-vm/lima/pkg/driver/qemu"
)

// To be used as an external driver for Lima.
func main() {
server.Serve(qemu.New())
}
16 changes: 16 additions & 0 deletions cmd/lima-driver-vz/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//go:build darwin

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

package main

import (
"github.com/lima-vm/lima/pkg/driver/external/server"
"github.com/lima-vm/lima/pkg/driver/vz"
)

// To be used as an external driver for Lima.
func main() {
server.Serve(vz.New())
}
16 changes: 16 additions & 0 deletions cmd/lima-driver-wsl2/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//go:build windows

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

package main

import (
"github.com/lima-vm/lima/pkg/driver/external/server"
"github.com/lima-vm/lima/pkg/driver/wsl2"
)

// To be used as an external driver for Lima.
func main() {
server.Serve(wsl2.New())
}
2 changes: 1 addition & 1 deletion cmd/limactl/disk.go
Original file line number Diff line number Diff line change
@@ -13,11 +13,11 @@
"text/tabwriter"

contfs "github.com/containerd/continuity/fs"
"github.com/docker/go-units"

Check failure on line 16 in cmd/limactl/disk.go

GitHub Actions / Lint Go (ubuntu-24.04)

File is not properly formatted (gci)

Check failure on line 16 in cmd/limactl/disk.go

GitHub Actions / Lint Go (macos-15)

File is not properly formatted (gci)
"github.com/lima-vm/go-qcow2reader"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"

"github.com/lima-vm/go-qcow2reader"
"github.com/lima-vm/lima/pkg/imgutil/proxyimgutil"
"github.com/lima-vm/lima/pkg/store"
"github.com/lima-vm/lima/pkg/store/filenames"
3 changes: 3 additions & 0 deletions cmd/limactl/main.go
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
"github.com/spf13/cobra"

"github.com/lima-vm/lima/pkg/debugutil"
"github.com/lima-vm/lima/pkg/driver/external/server"
"github.com/lima-vm/lima/pkg/fsutil"
"github.com/lima-vm/lima/pkg/osutil"
"github.com/lima-vm/lima/pkg/store/dirnames"
@@ -43,6 +44,8 @@
handleExitCoder(err)
logrus.Fatal(err)
}

defer server.StopAllExternalDrivers()

Check failure on line 48 in cmd/limactl/main.go

GitHub Actions / Lint Go (ubuntu-24.04)

unnecessaryDefer: defer server.StopAllExternalDrivers() is placed just before return (gocritic)

Check failure on line 48 in cmd/limactl/main.go

GitHub Actions / Lint Go (macos-15)

unnecessaryDefer: defer server.StopAllExternalDrivers() is placed just before return (gocritic)
}

func newApp() *cobra.Command {
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"
11 changes: 11 additions & 0 deletions cmd/limactl/start.go
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@ import (
"github.com/lima-vm/lima/pkg/limatmpl"
"github.com/lima-vm/lima/pkg/limayaml"
networks "github.com/lima-vm/lima/pkg/networks/reconcile"
"github.com/lima-vm/lima/pkg/registry"
"github.com/lima-vm/lima/pkg/store"
"github.com/lima-vm/lima/pkg/store/filenames"
"github.com/lima-vm/lima/pkg/templatestore"
@@ -32,6 +33,7 @@ func registerCreateFlags(cmd *cobra.Command, commentPrefix string) {
flags := cmd.Flags()
flags.String("name", "", commentPrefix+"Override the instance name")
flags.Bool("list-templates", false, commentPrefix+"List available templates and exit")
flags.Bool("list-drivers", false, commentPrefix+"List available drivers and exit")
editflags.RegisterCreate(cmd, commentPrefix)
}

@@ -54,6 +56,7 @@ $ limactl create --set='.cpus = 2 | .memory = "2GiB"'
To see the template list:
$ limactl create --list-templates


To create an instance "default" from a local file:
$ limactl create --name=default /usr/local/share/lima/templates/fedora.yaml

@@ -393,6 +396,14 @@ func createStartActionCommon(cmd *cobra.Command, _ []string) (exit bool, err err
_, _ = fmt.Fprintln(w, f.Name)
}
return true, nil
} else if listDrivers, err := cmd.Flags().GetBool("list-drivers"); err != nil {
return true, err
} else if listDrivers {
w := cmd.OutOrStdout()
for k := range registry.List() {
_, _ = fmt.Fprintln(w, k)
}
return true, nil
}
return false, nil
}
144 changes: 43 additions & 101 deletions pkg/driver/driver.go
Original file line number Diff line number Diff line change
@@ -5,17 +5,13 @@ package driver

import (
"context"
"errors"
"net"

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

// Driver interface is used by hostagent for managing vm.
//
// This interface is extended by BaseDriver which provides default implementation.
// All other driver definition must extend BaseDriver.
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

@@ -35,120 +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

// 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(_ context.Context, password string) error

GetDisplayConnection(_ context.Context) (string, error)

CreateSnapshot(_ context.Context, tag string) error

ApplySnapshot(_ context.Context, tag string) error

DeleteSnapshot(_ context.Context, tag string) error

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

// ForwardGuestAgent returns if the guest agent sock needs forwarding by host agent.
ForwardGuestAgent() bool

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

type BaseDriver struct {
Instance *store.Instance

SSHLocalPort int
VSockPort int
VirtioPort string
}

var _ Driver = (*BaseDriver)(nil)

func (d *BaseDriver) Validate() error {
return nil
}

func (d *BaseDriver) Initialize(_ context.Context) error {
return nil
}

func (d *BaseDriver) CreateDisk(_ context.Context) error {
return nil
}

func (d *BaseDriver) Start(_ context.Context) (chan error, error) {
return nil, nil
}

func (d *BaseDriver) CanRunGUI() bool {
return false
}

func (d *BaseDriver) RunGUI() error {
return nil
}

func (d *BaseDriver) Stop(_ context.Context) error {
return nil
}
// 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

func (d *BaseDriver) Register(_ context.Context) error {
return nil
ChangeDisplayPassword(ctx context.Context, password string) error
DisplayConnection(ctx context.Context) (string, error)
}

func (d *BaseDriver) Unregister(_ context.Context) error {
return nil
// 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)
}

func (d *BaseDriver) ChangeDisplayPassword(_ context.Context, _ string) error {
return nil
// Registration defines operations for registering and unregistering the driver instance.
type Registration interface {
Register(ctx context.Context) error
Unregister(ctx context.Context) error
}

func (d *BaseDriver) GetDisplayConnection(_ context.Context) (string, error) {
return "", nil
}
// GuestAgent defines operations for the guest agent.
type GuestAgent interface {
// ForwardGuestAgent returns if the guest agent sock needs forwarding by host agent.
ForwardGuestAgent() bool

func (d *BaseDriver) CreateSnapshot(_ context.Context, _ string) error {
return errors.New("unimplemented")
// GuestAgentConn returns the guest agent connection, or nil (if forwarded by ssh).
GuestAgentConn(_ context.Context) (net.Conn, string, error)
}

func (d *BaseDriver) ApplySnapshot(_ context.Context, _ string) error {
return errors.New("unimplemented")
}
// Driver interface is used by hostagent for managing vm.
type Driver interface {
Lifecycle
GUI
SnapshotManager
Registration
GuestAgent

func (d *BaseDriver) DeleteSnapshot(_ context.Context, _ string) error {
return errors.New("unimplemented")
}
Info() Info

func (d *BaseDriver) ListSnapshots(_ context.Context) (string, error) {
return "", errors.New("unimplemented")
// SetConfig sets the configuration for the instance.
Configure(inst *store.Instance, sshLocalPort int) *ConfiguredDriver
}

func (d *BaseDriver) ForwardGuestAgent() bool {
// if driver is not providing, use host agent
return d.VSockPort == 0 && d.VirtioPort == ""
type ConfiguredDriver struct {
Driver
}

func (d *BaseDriver) GuestAgentConn(_ context.Context) (net.Conn, error) {
// use the unix socket forwarded by host agent
return nil, nil
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"`
}
Loading