Skip to content

Commit 9d820ff

Browse files
committed
support for libkrun using krunkit
Signed-off-by: Ansuman Sahoo <anshumansahoo500@gmail.com>
1 parent 9d5f469 commit 9d820ff

File tree

3 files changed

+344
-0
lines changed

3 files changed

+344
-0
lines changed

cmd/lima-driver-krunkit/main.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package main
5+
6+
import (
7+
"context"
8+
9+
"github.com/lima-vm/lima/v2/pkg/driver/external/server"
10+
"github.com/lima-vm/lima/v2/pkg/driver/krunkit"
11+
)
12+
13+
// To be used as an external driver for Lima.
14+
func main() {
15+
server.Serve(context.Background(), krunkit.New())
16+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package krunkit
5+
6+
import (
7+
"context"
8+
"errors"
9+
"fmt"
10+
"os"
11+
"os/exec"
12+
"path/filepath"
13+
"strconv"
14+
15+
"github.com/docker/go-units"
16+
"github.com/lima-vm/lima/v2/pkg/imgutil/proxyimgutil"
17+
"github.com/lima-vm/lima/v2/pkg/iso9660util"
18+
"github.com/lima-vm/lima/v2/pkg/limatype"
19+
"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
20+
"github.com/lima-vm/lima/v2/pkg/limayaml"
21+
"github.com/lima-vm/lima/v2/pkg/networks"
22+
)
23+
24+
const (
25+
logLevelInfo = "3"
26+
KrunEfi = "krun-efi" // efi variable store
27+
)
28+
29+
// Cmdline constructs the command line arguments for krunkit based on the instance configuration.
30+
func Cmdline(inst *limatype.Instance) (*exec.Cmd, error) {
31+
var args = []string{
32+
"--memory", strconv.Itoa(2048),
33+
"--cpus", fmt.Sprintf("%d", *inst.Config.CPUs),
34+
"--device", fmt.Sprintf("virtio-serial,logFilePath=%s", filepath.Join(inst.Dir, filenames.SerialLog)),
35+
"--krun-log-level", logLevelInfo,
36+
"--restful-uri", "none://",
37+
38+
// First virtio-blk device is the boot disk
39+
"--device", fmt.Sprintf("virtio-blk,path=%s,format=raw", filepath.Join(inst.Dir, filenames.DiffDisk)),
40+
"--device", fmt.Sprintf("virtio-blk,path=%s", filepath.Join(inst.Dir, filenames.CIDataISO)),
41+
}
42+
43+
// TODO: socket_vmnet and ssh not working
44+
networkArgs, err := buildNetworkArgs(inst)
45+
if err != nil {
46+
return nil, fmt.Errorf("failed to build network arguments: %w", err)
47+
}
48+
args = append(args, networkArgs...)
49+
50+
return exec.CommandContext(context.Background(), "krunkit", args...), nil
51+
}
52+
53+
func buildNetworkArgs(inst *limatype.Instance) ([]string, error) {
54+
var args []string
55+
nwCfg, err := networks.LoadConfig()
56+
if err != nil {
57+
return nil, err
58+
}
59+
socketVMNetOk, err := nwCfg.IsDaemonInstalled(networks.SocketVMNet)
60+
if err != nil {
61+
return nil, err
62+
}
63+
if socketVMNetOk {
64+
sock, err := networks.Sock(networks.ModeShared)
65+
if err != nil {
66+
return nil, err
67+
}
68+
networkArg := fmt.Sprintf("virtio-net,type=unixstream,path=%s,mac=%s",
69+
sock,
70+
limayaml.MACAddress(inst.Dir),
71+
)
72+
args = append(args, "--device", networkArg)
73+
74+
return args, nil
75+
}
76+
77+
return args, errors.New("socket_vmnet is not installed")
78+
}
79+
80+
func EnsureDisk(ctx context.Context, inst *limatype.Instance) error {
81+
diffDisk := filepath.Join(inst.Dir, filenames.DiffDisk)
82+
if _, err := os.Stat(diffDisk); err == nil || !errors.Is(err, os.ErrNotExist) {
83+
// disk is already ensured
84+
return err
85+
}
86+
87+
diskUtil := proxyimgutil.NewDiskUtil(ctx)
88+
89+
baseDisk := filepath.Join(inst.Dir, filenames.BaseDisk)
90+
91+
diskSize, _ := units.RAMInBytes(*inst.Config.Disk)
92+
if diskSize == 0 {
93+
return nil
94+
}
95+
isBaseDiskISO, err := iso9660util.IsISO9660(baseDisk)
96+
if err != nil {
97+
return err
98+
}
99+
if isBaseDiskISO {
100+
// Create an empty data volume (sparse)
101+
diffDiskF, err := os.Create(diffDisk)
102+
if err != nil {
103+
return err
104+
}
105+
106+
err = diskUtil.MakeSparse(ctx, diffDiskF, 0)
107+
if err != nil {
108+
diffDiskF.Close()
109+
return fmt.Errorf("failed to create sparse diff disk %q: %w", diffDisk, err)
110+
}
111+
return diffDiskF.Close()
112+
}
113+
114+
// Krunkit also supports qcow2 disks but raw is faster to create and use.
115+
if err = diskUtil.ConvertToRaw(ctx, baseDisk, diffDisk, &diskSize, false); err != nil {
116+
return fmt.Errorf("failed to convert %q to a raw disk %q: %w", baseDisk, diffDisk, err)
117+
}
118+
return err
119+
}
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package krunkit
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"net"
10+
"os"
11+
"os/exec"
12+
"path/filepath"
13+
"syscall"
14+
"time"
15+
16+
"github.com/sirupsen/logrus"
17+
18+
"github.com/lima-vm/lima/v2/pkg/driver"
19+
"github.com/lima-vm/lima/v2/pkg/executil"
20+
"github.com/lima-vm/lima/v2/pkg/limatype"
21+
"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
22+
"github.com/lima-vm/lima/v2/pkg/ptr"
23+
)
24+
25+
type LimaKrunDriver struct {
26+
Instance *limatype.Instance
27+
SSHLocalPort int
28+
29+
krunkitCmd *exec.Cmd
30+
krunkitWaitCh chan error
31+
}
32+
33+
var _ driver.Driver = (*LimaKrunDriver)(nil)
34+
35+
func New() *LimaKrunDriver {
36+
return &LimaKrunDriver{}
37+
}
38+
39+
func (l *LimaKrunDriver) Configure(inst *limatype.Instance) *driver.ConfiguredDriver {
40+
l.Instance = inst
41+
l.SSHLocalPort = inst.SSHLocalPort
42+
43+
return &driver.ConfiguredDriver{
44+
Driver: l,
45+
}
46+
}
47+
48+
func (l *LimaKrunDriver) CreateDisk(ctx context.Context) error {
49+
return EnsureDisk(ctx, l.Instance)
50+
}
51+
52+
func (l *LimaKrunDriver) Start(ctx context.Context) (chan error, error) {
53+
krunCmd, err := Cmdline(l.Instance)
54+
if err != nil {
55+
return nil, fmt.Errorf("failed to construct krunkit command line: %w", err)
56+
}
57+
krunCmd.SysProcAttr = executil.BackgroundSysProcAttr
58+
59+
logPath := filepath.Join(l.Instance.Dir, "krun.log")
60+
logfile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
61+
if err != nil {
62+
return nil, fmt.Errorf("failed to open krunkit logfile: %w", err)
63+
}
64+
krunCmd.Stderr = logfile
65+
66+
logrus.Infof("Starting krun VM (hint: to watch the progress, see %q)", logPath)
67+
logrus.Debugf("krunCmd.Args: %v", krunCmd.Args)
68+
69+
if err := krunCmd.Start(); err != nil {
70+
logfile.Close()
71+
return nil, err
72+
}
73+
74+
pidPath := filepath.Join(l.Instance.Dir, filenames.PIDFile(*l.Instance.Config.VMType))
75+
if err := os.WriteFile(pidPath, []byte(fmt.Sprintf("%d\n", krunCmd.Process.Pid)), 0644); err != nil {
76+
logrus.WithError(err).Warn("Failed to write PID file")
77+
}
78+
79+
l.krunkitCmd = krunCmd
80+
l.krunkitWaitCh = make(chan error, 1)
81+
go func() {
82+
defer func() {
83+
logfile.Close()
84+
os.RemoveAll(pidPath)
85+
close(l.krunkitWaitCh)
86+
}()
87+
l.krunkitWaitCh <- krunCmd.Wait()
88+
}()
89+
90+
return l.krunkitWaitCh, nil
91+
}
92+
93+
func (l *LimaKrunDriver) Stop(ctx context.Context) error {
94+
if l.krunkitCmd == nil {
95+
return nil
96+
}
97+
98+
if err := l.krunkitCmd.Process.Signal(syscall.SIGTERM); err != nil {
99+
logrus.WithError(err).Warn("Failed to send interrupt signal")
100+
}
101+
102+
timeout := time.After(30 * time.Second)
103+
select {
104+
case <-l.krunkitWaitCh:
105+
return nil
106+
case <-timeout:
107+
if err := l.krunkitCmd.Process.Kill(); err != nil {
108+
return err
109+
}
110+
111+
<-l.krunkitWaitCh
112+
return nil
113+
}
114+
}
115+
116+
func (l *LimaKrunDriver) Delete(ctx context.Context) error {
117+
return nil
118+
}
119+
120+
func (l *LimaKrunDriver) InspectStatus(ctx context.Context, inst *limatype.Instance) string {
121+
return ""
122+
}
123+
124+
func (l *LimaKrunDriver) RunGUI() error {
125+
return nil
126+
}
127+
128+
func (l *LimaKrunDriver) ChangeDisplayPassword(ctx context.Context, password string) error {
129+
return fmt.Errorf("display password change not supported by krun driver")
130+
}
131+
132+
func (l *LimaKrunDriver) DisplayConnection(ctx context.Context) (string, error) {
133+
return "", fmt.Errorf("display connection not supported by krun driver")
134+
}
135+
136+
func (l *LimaKrunDriver) CreateSnapshot(ctx context.Context, tag string) error {
137+
return fmt.Errorf("snapshots not supported by krun driver")
138+
}
139+
140+
func (l *LimaKrunDriver) ApplySnapshot(ctx context.Context, tag string) error {
141+
return fmt.Errorf("snapshots not supported by krun driver")
142+
}
143+
144+
func (l *LimaKrunDriver) DeleteSnapshot(ctx context.Context, tag string) error {
145+
return fmt.Errorf("snapshots not supported by krun driver")
146+
}
147+
148+
func (l *LimaKrunDriver) ListSnapshots(ctx context.Context) (string, error) {
149+
return "", fmt.Errorf("snapshots not supported by krun driver")
150+
}
151+
152+
func (l *LimaKrunDriver) Register(ctx context.Context) error {
153+
return nil
154+
}
155+
156+
func (l *LimaKrunDriver) Unregister(ctx context.Context) error {
157+
return nil
158+
}
159+
160+
func (l *LimaKrunDriver) ForwardGuestAgent() bool {
161+
return true
162+
}
163+
164+
func (l *LimaKrunDriver) GuestAgentConn(ctx context.Context) (net.Conn, string, error) {
165+
return nil, "", fmt.Errorf("guest agent connection not implemented for krun driver")
166+
}
167+
168+
func (l *LimaKrunDriver) Validate(ctx context.Context) error {
169+
return nil
170+
}
171+
172+
func (l *LimaKrunDriver) FillConfig(ctx context.Context, cfg *limatype.LimaYAML, filePath string) error {
173+
if cfg.MountType == nil {
174+
cfg.MountType = ptr.Of(limatype.VIRTIOFS)
175+
} else {
176+
*cfg.MountType = limatype.VIRTIOFS
177+
}
178+
179+
cfg.VMType = ptr.Of("krunkit")
180+
181+
return nil
182+
}
183+
184+
func (l *LimaKrunDriver) BootScripts() (map[string][]byte, error) {
185+
return nil, nil
186+
}
187+
188+
func (l *LimaKrunDriver) Create(ctx context.Context) error {
189+
return nil
190+
}
191+
192+
func (l *LimaKrunDriver) Info() driver.Info {
193+
var info driver.Info
194+
info.Name = "krunkit"
195+
if l.Instance != nil && l.Instance.Dir != "" {
196+
info.InstanceDir = l.Instance.Dir
197+
}
198+
199+
info.Features = driver.DriverFeatures{
200+
DynamicSSHAddress: false,
201+
SkipSocketForwarding: false,
202+
CanRunGUI: false,
203+
}
204+
return info
205+
}
206+
207+
func (l *LimaKrunDriver) SSHAddress(ctx context.Context) (string, error) {
208+
return "127.0.0.1", nil
209+
}

0 commit comments

Comments
 (0)