Path: blob/master/pkg/driver/krunkit/krunkit_driver_darwin_arm64.go
2639 views
// SPDX-FileCopyrightText: Copyright The Lima Authors1// SPDX-License-Identifier: Apache-2.023package krunkit45import (6"context"7"embed"8"errors"9"fmt"10"net"11"os"12"os/exec"13"path/filepath"14"strings"15"syscall"16"time"1718"github.com/coreos/go-semver/semver"19"github.com/lima-vm/go-qcow2reader/image/raw"20"github.com/sirupsen/logrus"2122"github.com/lima-vm/lima/v2/pkg/driver"23"github.com/lima-vm/lima/v2/pkg/driverutil"24"github.com/lima-vm/lima/v2/pkg/executil"25"github.com/lima-vm/lima/v2/pkg/limatype"26"github.com/lima-vm/lima/v2/pkg/limatype/filenames"27"github.com/lima-vm/lima/v2/pkg/limayaml"28"github.com/lima-vm/lima/v2/pkg/networks/usernet"29"github.com/lima-vm/lima/v2/pkg/osutil"30"github.com/lima-vm/lima/v2/pkg/ptr"31)3233type LimaKrunkitDriver struct {34Instance *limatype.Instance35SSHLocalPort int3637usernetClient *usernet.Client38stopUsernet context.CancelFunc39krunkitCmd *exec.Cmd40krunkitWaitCh chan error41}4243var (44_ driver.Driver = (*LimaKrunkitDriver)(nil)45vmType limatype.VMType = "krunkit"46)4748func New() *LimaKrunkitDriver {49return &LimaKrunkitDriver{}50}5152func (l *LimaKrunkitDriver) Configure(inst *limatype.Instance) *driver.ConfiguredDriver {53l.Instance = inst54l.SSHLocalPort = inst.SSHLocalPort5556return &driver.ConfiguredDriver{57Driver: l,58}59}6061func (l *LimaKrunkitDriver) CreateDisk(ctx context.Context) error {62// Krunkit also supports qcow2 disks but raw is faster to create and use.63return driverutil.EnsureDisk(ctx, l.Instance.Dir, *l.Instance.Config.Disk, raw.Type)64}6566func (l *LimaKrunkitDriver) Start(ctx context.Context) (chan error, error) {67if l.Instance.Config.SSH.OverVsock != nil && *l.Instance.Config.SSH.OverVsock {68logrus.Warn(".ssh.overVsock is not implemented yet for krunkit driver")69}7071var err error72l.usernetClient, l.stopUsernet, err = startUsernet(ctx, l.Instance)73if err != nil {74return nil, fmt.Errorf("failed to start usernet: %w", err)75}7677krunkitCmd, err := Cmdline(l.Instance)78if err != nil {79return nil, fmt.Errorf("failed to construct krunkit command line: %w", err)80}81// Detach krunkit process from parent Lima process82krunkitCmd.SysProcAttr = executil.BackgroundSysProcAttr8384logPath := filepath.Join(l.Instance.Dir, "krunkit.log")85logfile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)86if err != nil {87return nil, fmt.Errorf("failed to open krunkit logfile: %w", err)88}89krunkitCmd.Stderr = logfile9091logrus.Infof("Starting krun VM (hint: to watch the progress, see %q)", logPath)92logrus.Infof("krunkitCmd.Args: %v", krunkitCmd.Args)9394if err := krunkitCmd.Start(); err != nil {95logfile.Close()96return nil, errors.New("failed to start krunkitCmd")97}9899pidPath := filepath.Join(l.Instance.Dir, filenames.PIDFile(*l.Instance.Config.VMType))100if err := os.WriteFile(pidPath, fmt.Appendf(nil, "%d\n", krunkitCmd.Process.Pid), 0o644); err != nil {101logrus.WithError(err).Warn("Failed to write PID file")102}103104l.krunkitCmd = krunkitCmd105l.krunkitWaitCh = make(chan error, 1)106go func() {107defer func() {108logfile.Close()109os.RemoveAll(pidPath)110close(l.krunkitWaitCh)111}()112l.krunkitWaitCh <- krunkitCmd.Wait()113}()114115err = l.usernetClient.ConfigureDriver(ctx, l.Instance, l.SSHLocalPort)116if err != nil {117l.krunkitWaitCh <- fmt.Errorf("failed to configure usernet: %w", err)118}119120return l.krunkitWaitCh, nil121}122123func (l *LimaKrunkitDriver) Stop(_ context.Context) error {124if l.krunkitCmd == nil {125return nil126}127128if err := l.krunkitCmd.Process.Signal(syscall.SIGTERM); err != nil {129logrus.WithError(err).Warn("Failed to send interrupt signal")130}131132go func() {133if l.usernetClient != nil {134_ = l.usernetClient.UnExposeSSH(l.Instance.SSHLocalPort)135}136if l.stopUsernet != nil {137l.stopUsernet()138}139}()140141timeout := time.After(30 * time.Second)142select {143case <-l.krunkitWaitCh:144return nil145case <-timeout:146if err := l.krunkitCmd.Process.Kill(); err != nil {147return err148}149150<-l.krunkitWaitCh151return nil152}153}154155func (l *LimaKrunkitDriver) Validate(_ context.Context) error {156return validateConfig(l.Instance.Config)157}158159func validateConfig(cfg *limatype.LimaYAML) error {160if cfg == nil {161return errors.New("configuration is nil")162}163macOSProductVersion, err := osutil.ProductVersion()164if err != nil {165return err166}167if macOSProductVersion.LessThan(*semver.New("13.0.0")) {168return errors.New("krunkit driver requires macOS 13 or higher to run")169}170if cfg.Arch != nil && !limayaml.IsNativeArch(*cfg.Arch) {171return fmt.Errorf("unsupported arch: %q (krunkit requires native arch)", *cfg.Arch)172}173if _, err := exec.LookPath(vmType); err != nil {174return errors.New("krunkit CLI not found in PATH. Install it via:\nbrew tap slp/krunkit\nbrew install krunkit")175}176177if cfg.MountType != nil && (*cfg.MountType != limatype.VIRTIOFS && *cfg.MountType != limatype.REVSSHFS) {178return fmt.Errorf("field `mountType` must be %q or %q for krunkit driver, got %q", limatype.VIRTIOFS, limatype.REVSSHFS, *cfg.MountType)179}180181return nil182}183184func isFedoraConfigured(cfg *limatype.LimaYAML) bool {185for _, b := range cfg.Base {186if strings.Contains(strings.ToLower(b.URL), "fedora") {187return true188}189}190for _, img := range cfg.Images {191if strings.Contains(strings.ToLower(img.Location), "fedora") {192return true193}194}195return false196}197198func (l *LimaKrunkitDriver) FillConfig(_ context.Context, cfg *limatype.LimaYAML, _ string) error {199if cfg.MountType == nil {200cfg.MountType = ptr.Of(limatype.VIRTIOFS)201} else {202*cfg.MountType = limatype.VIRTIOFS203}204205if cfg.Arch == nil {206cfg.Arch = ptr.Of(limatype.AARCH64)207} else {208*cfg.Arch = limatype.AARCH64209}210211cfg.VMType = ptr.Of(vmType)212213return validateConfig(cfg)214}215216//go:embed boot/*.sh217var bootFS embed.FS218219func (l *LimaKrunkitDriver) BootScripts() (map[string][]byte, error) {220scripts := make(map[string][]byte)221222entries, err := bootFS.ReadDir("boot")223if err == nil && !isFedoraConfigured(l.Instance.Config) {224for _, entry := range entries {225if entry.IsDir() {226continue227}228229content, err := bootFS.ReadFile("boot/" + entry.Name())230if err != nil {231return nil, err232}233234scripts[entry.Name()] = content235}236}237238// Disabled by krunkit driver for Fedora to make boot time faster239if isFedoraConfigured(l.Instance.Config) {240scripts["00-reboot-if-required.sh"] = []byte(`#!/bin/sh241set -eu242exit 0243`)244}245246return scripts, nil247}248249func (l *LimaKrunkitDriver) Create(_ context.Context) error {250return nil251}252253func (l *LimaKrunkitDriver) Info() driver.Info {254var info driver.Info255info.Name = vmType256if l.Instance != nil && l.Instance.Dir != "" {257info.InstanceDir = l.Instance.Dir258}259260info.Features = driver.DriverFeatures{261DynamicSSHAddress: false,262SkipSocketForwarding: false,263CanRunGUI: false,264}265return info266}267268func (l *LimaKrunkitDriver) SSHAddress(_ context.Context) (string, error) {269return "127.0.0.1", nil270}271272func (l *LimaKrunkitDriver) ForwardGuestAgent() bool {273return true274}275276func (l *LimaKrunkitDriver) Delete(_ context.Context) error {277return nil278}279280func (l *LimaKrunkitDriver) InspectStatus(_ context.Context, _ *limatype.Instance) string {281return ""282}283284func (l *LimaKrunkitDriver) RunGUI() error {285return nil286}287288func (l *LimaKrunkitDriver) ChangeDisplayPassword(_ context.Context, _ string) error {289return errUnimplemented290}291292func (l *LimaKrunkitDriver) DisplayConnection(_ context.Context) (string, error) {293return "", errUnimplemented294}295296func (l *LimaKrunkitDriver) CreateSnapshot(_ context.Context, _ string) error {297return errUnimplemented298}299300func (l *LimaKrunkitDriver) ApplySnapshot(_ context.Context, _ string) error {301return errUnimplemented302}303304func (l *LimaKrunkitDriver) DeleteSnapshot(_ context.Context, _ string) error {305return errUnimplemented306}307308func (l *LimaKrunkitDriver) ListSnapshots(_ context.Context) (string, error) {309return "", errUnimplemented310}311312func (l *LimaKrunkitDriver) Register(_ context.Context) error {313return nil314}315316func (l *LimaKrunkitDriver) Unregister(_ context.Context) error {317return nil318}319320func (l *LimaKrunkitDriver) GuestAgentConn(_ context.Context) (net.Conn, string, error) {321return nil, "unix", nil322}323324func (l *LimaKrunkitDriver) AdditionalSetupForSSH(_ context.Context) error {325return nil326}327328329