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 34 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
68726ee
driver(internal): use existing driver.Driver as plugin interface
unsuman May 21, 2025
ff8a943
driver(internal): registry init
unsuman May 23, 2025
0f4bf50
refactor(BaseDriver): remove driver.BaseDriver from Lima level
unsuman May 23, 2025
c01a36d
refactor(BaseDriver): remove driver.BaseDriver from driver(qemu) level
unsuman May 23, 2025
a1f79d5
refactor(BaseDriver): remove driver.BaseDriver from driver(vz) level
unsuman May 23, 2025
bff7bdc
refactor(BaseDriver): remove driver.BaseDriver from driver(wsl2) level
unsuman May 23, 2025
7fbd83a
refactor(BaseDriver): make drivers implement the driver.Driver interface
unsuman May 23, 2025
0fa3176
driver(internal): divide the driver.Driver interface and define some …
unsuman May 23, 2025
70372ca
driver(internal): change lima to support internal drivers
unsuman May 23, 2025
4c16e49
driver(internal): add blank imports and make changes to Lima for inte…
unsuman May 24, 2025
f79456a
driver(internal): add register files and make drivers compatible for …
unsuman May 24, 2025
54f1866
driver(internal): list available built-in drivers
unsuman May 24, 2025
f020bd5
driver(internal): fix CI checks and lint errors
unsuman May 25, 2025
0f42995
refactor(driver): move qemu,vz and wsl2 to pkg/driver
unsuman May 25, 2025
38dcc7e
driver(internal): refactor Snapshot to SnapshotManager in driver inte…
unsuman May 26, 2025
4630452
driver(internal): compile vz on darwin, wsl2 on windows only and impl…
unsuman May 26, 2025
dac2cff
refactor(driver): remove redundant builtins pkg
unsuman May 27, 2025
9db1cf4
driver(external): proto file init
unsuman May 27, 2025
76e73f9
driver(external): external driver manager init
unsuman May 28, 2025
b8558d0
driver(external): complete proto file for driver interface
unsuman May 28, 2025
b91f166
driver(external): finalise proto file and generate gRPC code
unsuman May 29, 2025
6c1a5cf
driver(external): implement server defination
unsuman May 29, 2025
28ce50c
driver(external): implement the grpc client and server
unsuman May 30, 2025
d7ccaf8
driver(external): remove error from the grpc response payload
unsuman May 30, 2025
ed4ecef
driver(external): add discovery of external drivers
unsuman May 30, 2025
80bf1d9
driver(internal): consolidate some functions to single GetInfo() func…
unsuman Jun 2, 2025
13db654
driver(external): consolidate some functions to single GetInfo() rpc …
unsuman Jun 2, 2025
6351822
driver(external): implement Start() & SetConfig() as server methods
unsuman Jun 2, 2025
ffa8b16
driver(external): complete client grpc implementation and one server …
unsuman Jun 3, 2025
09e50ef
driver(external): tweak some external driver manager code
unsuman Jun 3, 2025
35d47d6
driver(external): complete external driver manager and registering pr…
unsuman Jun 4, 2025
fad8a71
driver(external): some tweaks around server and client code
unsuman Jun 4, 2025
2a82bf3
driver(external): server logs to a file and fixed json marshal error …
unsuman Jun 5, 2025
1dc43c4
driver(external): implement bidirectional streaming for GuestAgentConn()
unsuman Jun 6, 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
8 changes: 4 additions & 4 deletions cmd/limactl/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import (

contfs "github.com/containerd/continuity/fs"
"github.com/docker/go-units"
"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/nativeimgutil"
"github.com/lima-vm/lima/pkg/qemu/imgutil"
"github.com/lima-vm/lima/pkg/qemuimgutil"
Copy link
Member

Choose a reason for hiding this comment

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

Eventually this should follow:

Not an urgent topic though

"github.com/lima-vm/lima/pkg/store"
"github.com/lima-vm/lima/pkg/store/filenames"
)
Expand Down Expand Up @@ -116,7 +116,7 @@ func diskCreateAction(cmd *cobra.Command, args []string) error {
if format == "raw" {
err = nativeimgutil.CreateRawDisk(dataDisk, int(diskSize))
} else {
err = imgutil.CreateDisk(dataDisk, format, int(diskSize))
err = qemuimgutil.CreateDisk(dataDisk, format, int(diskSize))
}
if err != nil {
rerr := os.RemoveAll(diskDir)
Expand Down Expand Up @@ -413,7 +413,7 @@ func diskResizeAction(cmd *cobra.Command, args []string) error {
if disk.Format == "raw" {
err = nativeimgutil.ResizeRawDisk(dataDisk, int(diskSize))
} else {
err = imgutil.ResizeDisk(dataDisk, disk.Format, int(diskSize))
err = qemuimgutil.ResizeDisk(dataDisk, disk.Format, int(diskSize))
}
if err != nil {
return fmt.Errorf("failed to resize disk %q: %w", diskName, err)
Expand Down
1 change: 1 addition & 0 deletions cmd/limactl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/spf13/cobra"

"github.com/lima-vm/lima/pkg/debugutil"
// _ "github.com/lima-vm/lima/pkg/driver/qemu" // register qemu driver for all platforms
"github.com/lima-vm/lima/pkg/fsutil"
"github.com/lima-vm/lima/pkg/osutil"
"github.com/lima-vm/lima/pkg/store/dirnames"
Expand Down
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 darwin && !no_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_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build windows && !no_wsl

// 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
Expand Up @@ -20,6 +20,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"
Expand All @@ -31,6 +32,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)
}

Expand All @@ -53,6 +55,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

Expand Down Expand Up @@ -391,6 +394,14 @@ func createStartActionCommon(cmd *cobra.Command, _ []string) (exit bool, err err
}
}
return true, nil
} else if listDrivers, err := cmd.Flags().GetBool("list-drivers"); err != nil {
return true, err
} else if listDrivers {
w := cmd.OutOrStdout()
for _, d := range registry.DefaultRegistry.List() {
_, _ = fmt.Fprintln(w, d)
}
return true, nil
}
return false, nil
}
Expand Down
143 changes: 40 additions & 103 deletions pkg/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -35,120 +31,61 @@ 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
}

func (d *BaseDriver) Register(_ 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) Unregister(_ context.Context) error {
return nil
ChangeDisplayPassword(ctx context.Context, password string) error
GetDisplayConnection(ctx context.Context) (string, error)
}

func (d *BaseDriver) ChangeDisplayPassword(_ context.Context, _ string) 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) GetDisplayConnection(_ 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) CreateSnapshot(_ context.Context, _ string) error {
return errors.New("unimplemented")
}
// 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) ApplySnapshot(_ 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, error)
}

func (d *BaseDriver) DeleteSnapshot(_ 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) ListSnapshots(_ context.Context) (string, error) {
return "", errors.New("unimplemented")
}
GetInfo() Info

func (d *BaseDriver) ForwardGuestAgent() bool {
// if driver is not providing, use host agent
return d.VSockPort == 0 && d.VirtioPort == ""
// SetConfig sets the configuration for the instance.
SetConfig(inst *store.Instance, sshLocalPort int)
}

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"`
}
67 changes: 67 additions & 0 deletions pkg/driver/external/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0

package client

import (
"context"
"io"
"math"
"net"
"time"

pb "github.com/lima-vm/lima/pkg/driver/external"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/keepalive"
)

type DriverClient struct {
Stdin io.WriteCloser
Stdout io.ReadCloser
Conn *grpc.ClientConn
DriverSvc pb.DriverClient
logger *logrus.Logger
}

func NewDriverClient(stdin io.WriteCloser, stdout io.ReadCloser, logger *logrus.Logger) (*DriverClient, error) {
pipeConn := newPipeConn(stdin, stdout)
opts := []grpc.DialOption{
grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(math.MaxInt64),
grpc.MaxCallSendMsgSize(math.MaxInt64),
),
grpc.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) {
return pipeConn, nil
}),
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: 10 * time.Second,
Timeout: 20 * time.Second,
PermitWithoutStream: true,
}),
}

// conn, err := grpc.NewClient("pipe", opts...)
// if err != nil {
// logger.Errorf("failed to create gRPC driver client connection: %v", err)
// return nil, err
// }

conn, err := grpc.Dial("pipe", opts...)
if err != nil {
logger.Errorf("failed to dial gRPC driver client connection: %v", err)
return nil, err
}

driverSvc := pb.NewDriverClient(conn)

return &DriverClient{
Stdin: stdin,
Stdout: stdout,
Conn: conn,
DriverSvc: driverSvc,
logger: logger,
}, nil
}
Loading