Skip to content
Draft
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
16 changes: 16 additions & 0 deletions cmd/lima-driver-krunkit/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0

package main

import (
"context"

"github.com/lima-vm/lima/v2/pkg/driver/external/server"
"github.com/lima-vm/lima/v2/pkg/driver/krunkit"

Check failure on line 10 in cmd/lima-driver-krunkit/main.go

View workflow job for this annotation

GitHub Actions / Vulncheck

could not import github.com/lima-vm/lima/v2/pkg/driver/krunkit (invalid package name: "")
Copy link
Member

Choose a reason for hiding this comment

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

This file needs to be called "main_darwin.go" (not main.go), since krunkit is only implemented for darwin

)

// To be used as an external driver for Lima.
func main() {
server.Serve(context.Background(), krunkit.New())
}
210 changes: 210 additions & 0 deletions pkg/driver/krunkit/krunkit_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0

package krunkit

import (
"context"
"errors"
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"strconv"

"github.com/docker/go-units"

Check failure on line 16 in pkg/driver/krunkit/krunkit_darwin.go

View workflow job for this annotation

GitHub Actions / Lint Go (macos-26)

File is not properly formatted (gci)
"github.com/lima-vm/lima/v2/pkg/driver/vz"
"github.com/lima-vm/lima/v2/pkg/imgutil/proxyimgutil"
"github.com/lima-vm/lima/v2/pkg/iso9660util"
"github.com/lima-vm/lima/v2/pkg/limatype"
"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
"github.com/lima-vm/lima/v2/pkg/limayaml"
"github.com/lima-vm/lima/v2/pkg/networks"
"github.com/lima-vm/lima/v2/pkg/networks/usernet"
"github.com/sirupsen/logrus"
)

const logLevelInfo = "3"

// Cmdline constructs the command line arguments for krunkit based on the instance configuration.
func Cmdline(inst *limatype.Instance) (*exec.Cmd, error) {
var args = []string{

Check failure on line 32 in pkg/driver/krunkit/krunkit_darwin.go

View workflow job for this annotation

GitHub Actions / Lint Go (macos-26)

File is not properly formatted (gofumpt)
"--memory", strconv.Itoa(2048),
"--cpus", fmt.Sprintf("%d", *inst.Config.CPUs),
"--device", fmt.Sprintf("virtio-serial,logFilePath=%s", filepath.Join(inst.Dir, filenames.SerialLog)),
"--krun-log-level", logLevelInfo,
"--restful-uri", "none://",

// First virtio-blk device is the boot disk
"--device", fmt.Sprintf("virtio-blk,path=%s,format=raw", filepath.Join(inst.Dir, filenames.DiffDisk)),
Copy link
Member

Choose a reason for hiding this comment

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

Worth a comment that this is the boot disk. The order of the device matters.

"--device", fmt.Sprintf("virtio-blk,path=%s", filepath.Join(inst.Dir, filenames.CIDataISO)),
}

networkArgs, err := buildNetworkArgs(inst)
if err != nil {
return nil, fmt.Errorf("failed to build network arguments: %w", err)
}

if *inst.Config.MountType == limatype.VIRTIOFS {
for i, mount := range inst.Config.Mounts {
logrus.Infof("Mount: %+v", mount)
if _, err := os.Stat(mount.Location); errors.Is(err, os.ErrNotExist) {
if err := os.MkdirAll(mount.Location, 0o750); err != nil {
return nil, err
}
}
tag := fmt.Sprintf("mount%d", i)
mountArg := fmt.Sprintf("virtio-fs,sharedDir=%s,mountTag=%s", mount.Location, tag)
args = append(args, "--device", mountArg)
}
}

args = append(args, networkArgs...)
cmd := exec.CommandContext(context.Background(), "krunkit", args...)

return cmd, nil
}

func buildNetworkArgs(inst *limatype.Instance) ([]string, error) {
var args []string

// Configure default usernetwork with limayaml.MACAddress(inst.Dir) for eth0 interface
firstUsernetIndex := limayaml.FirstUsernetIndex(inst.Config)
if firstUsernetIndex == -1 {
// slirp network using gvisor netstack
krunkitSock, err := usernet.SockWithDirectory(inst.Dir, "", usernet.FDSock)
if err != nil {
return nil, err
}
client, err := vz.PassFDToUnix(krunkitSock)
if err != nil {
return nil, err
}

args = append(args, "--device", fmt.Sprintf("virtio-net,type=unixgram,fd=%d,mac=%s", client.Fd(), limayaml.MACAddress(inst.Dir)))
}

for _, nw := range inst.Networks {
var sock string
var mac string
if nw.Lima != "" {
nwCfg, err := networks.LoadConfig()
if err != nil {
return nil, err
}
switch nw.Lima {
case networks.ModeUserV2:
sock, err = usernet.Sock(nw.Lima, usernet.QEMUSock)
if err != nil {
return nil, err
}
mac = limayaml.MACAddress(inst.Dir)
case networks.ModeShared, networks.ModeBridged:
socketVMNetInstalled, err := nwCfg.IsDaemonInstalled(networks.SocketVMNet)
if err != nil {
return nil, err
}
if !socketVMNetInstalled {
return nil, errors.New("socket_vmnet is not installed")
}
sock, err = networks.Sock(nw.Lima)
if err != nil {
return nil, err
}
mac = nw.MACAddress
default:
return nil, fmt.Errorf("invalid network spec %+v", nw)
}
} else if nw.Socket != "" {
sock = nw.Socket
mac = nw.MACAddress
} else {
return nil, fmt.Errorf("invalid network spec %+v", nw)
}

device := fmt.Sprintf("virtio-net,type=unixstream,path=%s,mac=%s", sock, mac)
args = append(args, "--device", device)
}

if len(args) == 0 {
return args, fmt.Errorf("no socket_vment networks defined")

Check failure on line 131 in pkg/driver/krunkit/krunkit_darwin.go

View workflow job for this annotation

GitHub Actions / Lint Go (macos-26)

unnecessary-format: unnecessary use of formatting function "fmt.Errorf", you can replace it with "errors.New" (revive)
}

return args, nil
}

func EnsureDisk(ctx context.Context, inst *limatype.Instance) error {
diffDisk := filepath.Join(inst.Dir, filenames.DiffDisk)
if _, err := os.Stat(diffDisk); err == nil || !errors.Is(err, os.ErrNotExist) {
// disk is already ensured
return err
}

diskUtil := proxyimgutil.NewDiskUtil(ctx)

baseDisk := filepath.Join(inst.Dir, filenames.BaseDisk)

diskSize, _ := units.RAMInBytes(*inst.Config.Disk)
if diskSize == 0 {
return nil
}
isBaseDiskISO, err := iso9660util.IsISO9660(baseDisk)
if err != nil {
return err
}
if isBaseDiskISO {
// Create an empty data volume (sparse)
diffDiskF, err := os.Create(diffDisk)
if err != nil {
return err
}

err = diskUtil.MakeSparse(ctx, diffDiskF, 0)
if err != nil {
diffDiskF.Close()
return fmt.Errorf("failed to create sparse diff disk %q: %w", diffDisk, err)
}
return diffDiskF.Close()
}

// Krunkit also supports qcow2 disks but raw is faster to create and use.
if err = diskUtil.ConvertToRaw(ctx, baseDisk, diffDisk, &diskSize, false); err != nil {
return fmt.Errorf("failed to convert %q to a raw disk %q: %w", baseDisk, diffDisk, err)
}
Copy link
Member

Choose a reason for hiding this comment

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

krunkit support qcow2 disks but using raw disk is likely to perform better. We can add a comment here.

return err
}

func startUsernet(ctx context.Context, inst *limatype.Instance) (*usernet.Client, context.CancelFunc, error) {
if firstUsernetIndex := limayaml.FirstUsernetIndex(inst.Config); firstUsernetIndex != -1 {
return usernet.NewClientByName(inst.Config.Networks[firstUsernetIndex].Lima), nil, nil
}
// Start a in-process gvisor-tap-vsock
endpointSock, err := usernet.SockWithDirectory(inst.Dir, "", usernet.EndpointSock)
if err != nil {
return nil, nil, err
}
krunkitSock, err := usernet.SockWithDirectory(inst.Dir, "", usernet.FDSock)
if err != nil {
return nil, nil, err
}
os.RemoveAll(endpointSock)
os.RemoveAll(krunkitSock)
ctx, cancel := context.WithCancel(ctx)
err = usernet.StartGVisorNetstack(ctx, &usernet.GVisorNetstackOpts{
MTU: 1500,
Endpoint: endpointSock,
FdSocket: krunkitSock,
Async: true,
DefaultLeases: map[string]string{
networks.SlirpIPAddress: limayaml.MACAddress(inst.Dir),
},
Subnet: networks.SlirpNetwork,
})
if err != nil {
defer cancel()
return nil, nil, err
}
subnetIP, _, err := net.ParseCIDR(networks.SlirpNetwork)
return usernet.NewClient(endpointSock, subnetIP), cancel, err
}
Loading
Loading