Skip to content
Open
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
42 changes: 40 additions & 2 deletions pkg/crc/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func getVersionGeneric(executablePath string, args ...string) (string, error) {
logging.Debugf("failed to run executable %s: %v", executablePath, err)
return "", err
}
parsedOutput := strings.Split(stdOut, ":")
parsedOutput := strings.Split(stdOut, " ")
if len(parsedOutput) < 2 {
logging.Debugf("failed to parse version information for %s: %s", executablePath, stdOut)
return "", fmt.Errorf("Unable to parse the version information of %s", executablePath)
Expand Down Expand Up @@ -144,7 +144,45 @@ func (c *Cache) cacheExecutable() error {
if err != nil {
return err
}
if c.GetExecutableName() == constants.KrunkitCommand {
err = signKrunkit(finalExecutablePath)
if err != nil {
return err
}
}
}
return nil
}
Comment on lines +147 to +155
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Platform-specific constant used in non-platform-specific file causes build failures.

The code references constants.KrunkitCommand which is only defined in constants_darwin.go. This causes build failures on Windows and Linux platforms as shown in the pipeline failures and static analysis hints.

🔎 Proposed fix

Add build tags to this file or move the krunkit-specific logic to a Darwin-only file:

Option 1: Add build tags to this section

//go:build darwin

func (c *Cache) cacheExecutable() error {
    // ... existing code ...
    if c.GetExecutableName() == constants.KrunkitCommand {
        err = signKrunkit(finalExecutablePath)
        if err != nil {
            return err
        }
    }
    return nil
}

Option 2: Move signing to a platform-specific file

Create cache_darwin.go:

//go:build darwin

package cache

func (c *Cache) platformSpecificPostCache(finalExecutablePath string) error {
    if c.GetExecutableName() == constants.KrunkitCommand {
        return signKrunkit(finalExecutablePath)
    }
    return nil
}

Then call it from the main cacheExecutable function (with a no-op implementation in cache_other.go for other platforms).

🧰 Tools
🪛 GitHub Actions: Build Windows chocolatey

[error] 147-147: undefined: constants.KrunkitCommand

🪛 GitHub Actions: Test OKD bundle

[error] 147-147: undefined: constants.KrunkitCommand

🪛 GitHub Check: build (ubuntu-latest, 1.24)

[failure] 147-147:
undefined: constants.KrunkitCommand

🪛 GitHub Check: build (windows-2022, 1.24)

[failure] 147-147:
undefined: constants.KrunkitCommand

🪛 GitHub Check: Run OKD bundle with crc (1.24)

[failure] 147-147:
undefined: constants.KrunkitCommand

🤖 Prompt for AI Agents
In pkg/crc/cache/cache.go around lines 147-155, the code references
constants.KrunkitCommand (defined only in constants_darwin.go) causing
cross-platform build failures; to fix, move the krunkit-specific signing into a
Darwin-only implementation or guard the function with a darwin build tag: create
a darwin-specific helper (e.g., cache_darwin.go) that implements signing logic
for Krunkit and expose a no-op stub for other platforms (or mark the entire
cacheExecutable function with //go:build darwin and provide an alternative/no-op
in a non-darwin file), then call the platform-specific helper from the main flow
so non-Darwin builds no longer reference the darwin-only constant.


func signKrunkit(executablePath string) error {
const krunkitEntitlements = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.hypervisor</key>
<true/>
</dict>
</plist>
`
tmpFile, err := os.CreateTemp("", "krunkit-entitlements-*.plist")
if err != nil {
return fmt.Errorf("failed to create temp entitlements file: %w", err)
}
defer os.Remove(tmpFile.Name())

if _, err := tmpFile.WriteString(krunkitEntitlements); err != nil {
tmpFile.Close()
return fmt.Errorf("failed to write entitlements: %w", err)
}
tmpFile.Close()

_, _, err = crcos.RunWithDefaultLocale("codesign", "-s", "-", "--entitlements", tmpFile.Name(), "--force", executablePath)
if err != nil {
return err
}
logging.Debugf("Signed %s", executablePath)
return nil
}
Comment on lines +157 to 187
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Code signing implementation looks correct but needs platform isolation.

The krunkit signing logic with entitlements for hypervisor access is appropriate for macOS. However, this function should be in a Darwin-specific file (e.g., cache_darwin.go) with appropriate build tags since it's only relevant on macOS.

🤖 Prompt for AI Agents
In pkg/crc/cache/cache.go lines 157-187 the macOS-only signKrunkit function must
be moved to a Darwin-specific file: create pkg/crc/cache/cache_darwin.go with
the proper build tags at the top (//go:build darwin and the legacy +build darwin
line), copy the signKrunkit implementation into that file, ensure required
imports (os, fmt, logging, crcos) are declared there, and remove the function
from cache.go so non-mac builds don’t reference macOS APIs; keep the function
signature unchanged and verify no unused imports remain in either file.


Expand Down Expand Up @@ -183,7 +221,7 @@ func (c *Cache) CheckVersion() error {
}

func isTarball(filename string) bool {
tarballExtensions := []string{".tar", ".tar.gz", ".tar.xz", ".zip", ".tar.bz2", ".crcbundle"}
tarballExtensions := []string{".tar", ".tar.gz", ".tar.xz", ".zip", ".tar.bz2", ".crcbundle", ".tgz"}
for _, extension := range tarballExtensions {
if strings.HasSuffix(strings.ToLower(filename), extension) {
return true
Expand Down
4 changes: 2 additions & 2 deletions pkg/crc/cache/cache_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import (
)

func NewVfkitCache() *Cache {
return newCache(vfkit.ExecutablePath(), vfkit.VfkitDownloadURL, vfkit.VfkitVersion, getVfkitVersion)
return newCache(vfkit.ExecutablePath(), vfkit.DownloadURL(), vfkit.Version(), getVfkitVersion)
}

func getVfkitVersion(executablePath string) (string, error) {
version, err := getVersionGeneric(executablePath, "-v")
version, err := getVersionGeneric(executablePath, "--version")
if err != nil {
return version, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/crc/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func GetDefaultBundleSignedHashURL(preset crcpreset.Preset) string {
}

func ResolveHelperPath(executableName string) string {
if version.IsInstaller() {
if version.IsInstaller() && executableName != KrunkitCommand {
return filepath.Join(version.InstallPath(), executableName)
}
return filepath.Join(CrcBinDir, executableName)
Expand Down
23 changes: 23 additions & 0 deletions pkg/crc/constants/constants_darwin.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
//go:build darwin

package constants

import (
"log"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use the project's logging package instead of the standard library.

The standard library log package is imported, but the project has its own logging package at pkg/crc/logging with a Fatalf function. Line 32 should use logging.Fatalf instead of log.Fatalf to maintain consistency with the rest of the codebase.

🔎 Proposed fix
 import (
-	"log"
 	"os"
 	"path/filepath"
+
+	"github.com/crc-org/crc/v2/pkg/crc/logging"
 )

Then update line 32:

 	default:
-		log.Fatalf("Invalid provider: %s. Choose between %s or %s", provider, VfkitCommand, KrunkitCommand)
+		logging.Fatalf("Invalid provider: %s. Choose between %s or %s", provider, VfkitCommand, KrunkitCommand)
 		return ""

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In pkg/crc/constants/constants_darwin.go around line 6 and line 32, the file
imports the standard library "log" and calls log.Fatalf; replace the import with
the project's logging package "pkg/crc/logging" and change the call to use
logging.Fatalf instead of log.Fatalf so the project-wide logger is used
consistently.

"os"
"path/filepath"
)

Expand All @@ -9,8 +13,27 @@ const (
PodmanRemoteExecutableName = "podman"
DaemonAgentLabel = "com.redhat.crc.daemon"
QemuGuestAgentPort = 1234

VfkitCommand = "vfkit"
KrunkitCommand = "krunkit"
)

func Provider() string {
provider := os.Getenv("CRC_PROVIDER")
if provider == "" {
provider = VfkitCommand
}
switch provider {
case VfkitCommand:
return VfkitCommand
case KrunkitCommand:
return KrunkitCommand
default:
log.Fatalf("Invalid provider: %s. Choose between %s or %s", provider, VfkitCommand, KrunkitCommand)
return ""
}
}

var (
TapSocketPath = filepath.Join(CrcBaseDir, "tap.sock")
DaemonHTTPSocketPath = filepath.Join(CrcBaseDir, "crc-http.sock")
Expand Down
5 changes: 5 additions & 0 deletions pkg/crc/constants/constants_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ const (
OcExecutableName = "oc"
PodmanRemoteExecutableName = "podman-remote"
TapSocketPath = ""
KrunkitCommand = ""
)

var DaemonHTTPSocketPath = filepath.Join(CrcBaseDir, "crc-http.sock")

func Provider() string {
return ""
}
5 changes: 5 additions & 0 deletions pkg/crc/constants/constants_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@ const (
DaemonHTTPNamedPipe = `\\.\pipe\crc-http`
DaemonTaskName = "crcDaemon"
AdminHelperServiceName = "crcAdminHelper"
KrunkitCommand = ""
)

func Provider() string {
return ""
}
38 changes: 34 additions & 4 deletions pkg/crc/machine/vfkit/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,50 @@ package vfkit

import (
"fmt"
"path/filepath"

"github.com/crc-org/crc/v2/pkg/crc/constants"
)

const (
VfkitVersion = "0.6.1"
vfkitCommand = "vfkit"
VfkitVersion = "0.6.1"
VfkitCommand = "vfkit"
KrunkitVersion = "1.1.1"
KrunkitCommand = "krunkit"
)

var (
VfkitDownloadURL = fmt.Sprintf("https://github.com/crc-org/vfkit/releases/download/v%s/%s", VfkitVersion, vfkitCommand)
VfkitDownloadURL = fmt.Sprintf("https://github.com/crc-org/vfkit/releases/download/v%s/%s", VfkitVersion, VfkitCommand)
VfkitEntitlementsURL = fmt.Sprintf("https://raw.githubusercontent.com/crc-org/vfkit/v%s/vf.entitlements", VfkitVersion)
KrunkitDownloadURL = fmt.Sprintf("https://github.com/containers/krunkit/releases/download/v%s/krunkit-podman-unsigned-%s.tgz", KrunkitVersion, KrunkitVersion)
)
Comment on lines +20 to 23
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -name "constants.go" -path "*/vfkit/*" -type f 2>/dev/null | head -5

Repository: crc-org/crc

Length of output: 91


🏁 Script executed:

cat -n ./pkg/crc/machine/vfkit/constants.go

Repository: crc-org/crc

Length of output: 1479


Krunkit download URL references non-existent GitHub release v1.1.1.

The URL attempts to download from https://github.com/containers/krunkit/releases/download/v1.1.1/krunkit-podman-unsigned-1.1.1.tgz, but the containers/krunkit repository on GitHub only has releases v0.1.1 and v0.1.2. Release v1.1.1 does not exist, so the download will fail with a 404 error. Update KrunkitVersion to match an actual GitHub release, or use an alternative distribution method if v1.1.1 is only available elsewhere (e.g., Homebrew).

🤖 Prompt for AI Agents
In pkg/crc/machine/vfkit/constants.go around lines 19 to 22, the
KrunkitDownloadURL is built using KrunkitVersion v1.1.1 which does not exist on
the containers/krunkit GitHub releases (only v0.1.1 and v0.1.2 exist); update
KrunkitVersion to a valid release (for example "0.1.2") or change the URL to a
supported distribution (Homebrew or alternate host), regenerate the
KrunkitDownloadURL accordingly, and add a short comment noting the chosen source
and version so future changes reference a known existing release.


func ExecutablePath() string {
return constants.ResolveHelperPath(vfkitCommand)
provider := constants.Provider()
if provider == KrunkitCommand {
return filepath.Join(constants.CrcBinDir, provider)
}
return constants.ResolveHelperPath(provider)
}

func DownloadURL() string {
switch constants.Provider() {
case VfkitCommand:
return VfkitDownloadURL
case KrunkitCommand:
return KrunkitDownloadURL
default:
return ""
}
Comment on lines +33 to +41
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Default case returns empty string silently.

When constants.Provider() returns an unexpected value, DownloadURL() returns an empty string without logging or error indication. This could cause silent failures downstream when attempting to download.

Proposed fix
 func DownloadURL() string {
 	switch constants.Provider() {
 	case VfkitCommand:
 		return VfkitDownloadURL
 	case KrunkitCommand:
 		return KrunkitDownloadURL
 	default:
+		// This should not happen as Provider() validates the value
 		return ""
 	}
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func DownloadURL() string {
switch constants.Provider() {
case VfkitCommand:
return VfkitDownloadURL
case KrunkitCommand:
return KrunkitDownloadURL
default:
return ""
}
func DownloadURL() string {
switch constants.Provider() {
case VfkitCommand:
return VfkitDownloadURL
case KrunkitCommand:
return KrunkitDownloadURL
default:
// This should not happen as Provider() validates the value
return ""
}
}
🧰 Tools
🪛 GitHub Check: build-installer (windows-2022, 1.24)

[failure] 29-29:
undefined: constants.Provider

🤖 Prompt for AI Agents
In pkg/crc/machine/vfkit/constants.go around lines 28-36, the switch default
silently returns an empty string when constants.Provider() is unexpected; update
the default branch to log or warn the unexpected provider value (including the
actual value returned by constants.Provider()) before returning the empty string
so downstream callers have diagnostic context. Add the necessary import for the
chosen logger (e.g., fmt or log) if not present and ensure the log message
clearly states it is an unexpected provider and includes the provider string.

}

func Version() string {
switch constants.Provider() {
case VfkitCommand:
return VfkitVersion
case KrunkitCommand:
return KrunkitVersion
default:
return ""
}
}
4 changes: 1 addition & 3 deletions pkg/crc/machine/vfkit/driver_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ import (
)

func CreateHost(machineConfig config.MachineConfig) *vfkit.Driver {
vfDriver := vfkit.NewDriver(machineConfig.Name, constants.MachineBaseDir)
vfDriver := vfkit.NewDriver(machineConfig.Name, constants.MachineBaseDir, VfkitCommand)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Consider using dynamic provider selection instead of hardcoded VfkitCommand.

Line 14 passes VfkitCommand directly to the driver constructor, which hardcodes the provider to vfkit. Since the PR introduces a provider abstraction via constants.Provider(), this should likely use that function instead to support both vfkit and krunkit dynamically.

🔎 Proposed fix
-	vfDriver := vfkit.NewDriver(machineConfig.Name, constants.MachineBaseDir, VfkitCommand)
+	vfDriver := vfkit.NewDriver(machineConfig.Name, constants.MachineBaseDir, constants.Provider())
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
vfDriver := vfkit.NewDriver(machineConfig.Name, constants.MachineBaseDir, VfkitCommand)
vfDriver := vfkit.NewDriver(machineConfig.Name, constants.MachineBaseDir, constants.Provider())
🤖 Prompt for AI Agents
In pkg/crc/machine/vfkit/driver_darwin.go around line 14, the driver is being
constructed with a hardcoded VfkitCommand which forces the vfkit provider;
replace that argument with the dynamic provider lookup (use constants.Provider()
or the equivalent provider value introduced in constants) so the driver is
created using the runtime-selected provider instead of the hardcoded
VfkitCommand; ensure the provider value matches the expected type of the
NewDriver constructor and update any imports or variable names accordingly.


config.InitVMDriverFromMachineConfig(machineConfig, vfDriver.VMDriver)

vfDriver.VfkitPath = ExecutablePath()

vfDriver.VirtioNet = machineConfig.NetworkMode == network.SystemNetworkingMode

vfDriver.VsockPath = constants.TapSocketPath
Expand Down
5 changes: 4 additions & 1 deletion pkg/crc/preflight/preflight_checks_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,10 @@ func killVfkitProcess() error {
func getDaemonConfig() (*launchd.AgentConfig, error) {
logFilePath := filepath.Join(constants.CrcBaseDir, ".launchd-crcd.log")

env := map[string]string{"Version": version.GetCRCVersion()}
env := map[string]string{
"Version": version.GetCRCVersion(),
"CRC_PROVIDER": constants.Provider(),
}
daemonConfig := launchd.AgentConfig{
Label: constants.DaemonAgentLabel,
ExecutablePath: constants.CrcSymlinkPath,
Expand Down
6 changes: 3 additions & 3 deletions pkg/crc/preflight/preflight_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ var vfkitPreflightChecks = []Check{
},
{
configKeySuffix: "check-vfkit-installed",
checkDescription: "Checking if vfkit is installed",
checkDescription: "Checking if " + constants.Provider() + " is installed",
check: checkVfkitInstalled,
fixDescription: "Setting up virtualization with vfkit",
fixDescription: "Setting up virtualization with " + constants.Provider(),
fix: fixVfkitInstallation,

labels: labels{Os: Darwin},
},
{
cleanupDescription: "Stopping CRC vfkit process",
cleanupDescription: "Stopping CRC " + constants.Provider() + " process",
cleanup: killVfkitProcess,
flags: CleanUpOnly,

Expand Down
19 changes: 3 additions & 16 deletions pkg/drivers/vfkit/driver_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ type Driver struct {
UnixgramSockPath string
}

func NewDriver(hostName, storePath string) *Driver {
func NewDriver(hostName, storePath, executableName string) *Driver {
// checks that vfdriver.Driver implements the libmachine.Driver interface
var _ drivers.Driver = &Driver{}
return &Driver{
Expand All @@ -69,6 +69,7 @@ func NewDriver(hostName, storePath string) *Driver {
DaemonVsockPort: constants.DaemonVsockPort,
UnixgramSockPath: constants.UnixgramSocketPath,
UnixgramMacAddress: constants.VsockMacAddress,
VfkitPath: constants.ResolveHelperPath(executableName),
}
}

Expand Down Expand Up @@ -272,20 +273,6 @@ func (d *Driver) Start() error {
}
}

// when loading a VM created by a crc version predating this commit,
// d.QemuGAVsockPort will be missing from ~/.crc/machines/crc/config.json
// In such a case, assume the VM will not support time sync
if d.QemuGAVsockPort != 0 {
timesync, err := config.TimeSyncNew(d.QemuGAVsockPort)
if err != nil {
return err
}
err = vm.AddDevice(timesync)
if err != nil {
return err
}
}

args, err := vm.ToCmdLine()
if err != nil {
return err
Expand Down Expand Up @@ -447,7 +434,7 @@ func (d *Driver) findVfkitProcess() (*process.Process, error) {
if err != nil {
return nil, err
}
if !strings.HasPrefix(name, "vfkit") {
if !strings.HasPrefix(name, constants.Provider()) {
// return InvalidExecutable error?
log.Debugf("pid %d is stale, and is being used by %s", pid, name)
return nil, nil
Expand Down
8 changes: 5 additions & 3 deletions pkg/libmachine/load_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package libmachine
import (
"encoding/json"

"github.com/crc-org/crc/v2/pkg/drivers/vfkit"
"github.com/crc-org/crc/v2/pkg/crc/constants"
vfkitDriver "github.com/crc-org/crc/v2/pkg/drivers/vfkit"
"github.com/crc-org/crc/v2/pkg/libmachine/host"
)

func (api *Client) NewHost(_ string, driverPath string, rawDriver []byte) (*host.Host, error) {
driver := vfkit.NewDriver("", "")
driver := vfkitDriver.NewDriver("", "", constants.Provider())
if err := json.Unmarshal(rawDriver, &driver); err != nil {
return nil, err
}
Expand All @@ -29,10 +30,11 @@ func (api *Client) Load(name string) (*host.Host, error) {
return nil, err
}

driver := vfkit.NewDriver("", "")
driver := vfkitDriver.NewDriver("", "", constants.Provider())
if err := json.Unmarshal(h.RawDriver, &driver); err != nil {
return nil, err
}
driver.VfkitPath = constants.ResolveHelperPath(constants.Provider())
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's find the relevant files and understand the structure
echo "=== Searching for Driver struct definition ==="
fd -e go -e json | head -20

# Find load_darwin.go and examine context around line 37
echo -e "\n=== Examining load_darwin.go ==="
find . -name "load_darwin.go" -type f

# Search for Driver struct definition
echo -e "\n=== Searching for Driver struct ==="
rg "type Driver struct" -A 20 --type go | head -50

# Search for VfkitPath usage
echo -e "\n=== Searching for VfkitPath usage ==="
rg "VfkitPath" --type go

Repository: crc-org/crc

Length of output: 5080


🏁 Script executed:

#!/bin/bash
# Let's look at the actual load_darwin.go file
cat -n pkg/libmachine/load_darwin.go

Repository: crc-org/crc

Length of output: 1390


🏁 Script executed:

#!/bin/bash
# Search for how driver configuration is saved/loaded
echo "=== Searching for JSON serialization of Driver ==="
rg "json:" --type go | rg -i "driver\|vfkit\|provider" | head -30

# Search for constants.Provider usage
echo -e "\n=== Searching for constants.Provider usage ==="
rg "constants.Provider" --type go

Repository: crc-org/crc

Length of output: 1096


🏁 Script executed:

#!/bin/bash
# Let's find the Driver struct more carefully
echo "=== Finding Driver struct in the codebase ==="
fd -e go -type f | xargs rg "type Driver struct" -l

echo -e "\n=== Full Driver struct definition ==="
fd -e go -type f | xargs grep -l "type Driver struct" | xargs cat | rg "type Driver struct" -A 50

Repository: crc-org/crc

Length of output: 665


🏁 Script executed:

#!/bin/bash
# Let's examine the vfkit driver struct with field tags
cat -n pkg/drivers/vfkit/driver_darwin.go | head -50

Repository: crc-org/crc

Length of output: 1642


🏁 Script executed:

#!/bin/bash
# Search for json tags on VfkitPath and nearby fields
rg "VfkitPath|json:" pkg/drivers/vfkit/driver_darwin.go -B 2 -A 2

Repository: crc-org/crc

Length of output: 401


🏁 Script executed:

#!/bin/bash
# Let's check the base driver struct to understand field persistence
rg "type.*VMDriver|type.*BaseDriver" vendor/github.com/crc-org/machine/drivers/ -A 30 | head -100

Repository: crc-org/crc

Length of output: 37


🏁 Script executed:

#!/bin/bash
# Check what fields are actually stored in the JSON by looking at the base driver
find . -path ./vendor -prune -o -name "driver.go" -type f -print | xargs grep -l "VMDriver\|BaseDriver" | head -5

Repository: crc-org/crc

Length of output: 146


🏁 Script executed:

#!/bin/bash
# Let's check if provider is stored anywhere in the configuration
rg "Provider\(\)" pkg/drivers/vfkit/driver_darwin.go -B 3 -A 3

# Also check if there's any provider field in the driver
rg "Provider" pkg/drivers/vfkit/driver_darwin.go

Repository: crc-org/crc

Length of output: 321


🏁 Script executed:

#!/bin/bash
# Let's see the full Driver struct definition with all fields and potential json tags
cat -n pkg/drivers/vfkit/driver_darwin.go | sed -n '42,100p'

Repository: crc-org/crc

Length of output: 2188


🏁 Script executed:

#!/bin/bash
# Check the VMDriver base class to see what fields it has and if they're serialized
cat -n vendor/github.com/crc-org/machine/drivers/base_driver.go | head -100

Repository: crc-org/crc

Length of output: 143


🏁 Script executed:

#!/bin/bash
# Let's see if VfkitPath gets serialized by checking what happens when we marshal
# and if there are any struct tags
rg "type Driver struct" -A 50 pkg/drivers/vfkit/driver_darwin.go | head -60

Repository: crc-org/crc

Length of output: 1434


🏁 Script executed:

#!/bin/bash
# Check what constants.Provider() returns and if it's configurable
cat -n pkg/crc/constants/constants.go | grep -A 20 "func Provider"

Repository: crc-org/crc

Length of output: 37


🏁 Script executed:

#!/bin/bash
# Find the constants file with Provider function
find . -path ./vendor -prune -o -name "*.go" -type f -print | xargs grep -l "func Provider" | grep -v vendor

Repository: crc-org/crc

Length of output: 174


🏁 Script executed:

#!/bin/bash
# Let's check the constants_darwin.go file for Provider function
cat -n pkg/crc/constants/constants_darwin.go | grep -A 10 "func Provider"

Repository: crc-org/crc

Length of output: 362


🏁 Script executed:

#!/bin/bash
# Check if Provider is a global function or method
rg "Provider\(\)" pkg/crc/constants/ -B 2 -A 2

Repository: crc-org/crc

Length of output: 868


🏁 Script executed:

#!/bin/bash
# Let's examine what files exist in the constants directory
ls -la pkg/crc/constants/

Repository: crc-org/crc

Length of output: 499


🏁 Script executed:

#!/bin/bash
# Now check the actual load_darwin.go again in context - see if VfkitPath is expected to be in JSON
# and look at what happens during unmarshal
cat -n pkg/libmachine/load_darwin.go

Repository: crc-org/crc

Length of output: 1390


🏁 Script executed:

#!/by
# Check what fields would be unmarshalled from JSON in the VMDriver base
find . -path ./vendor -prune -o -name "*.go" -type f -print | xargs grep -l "type VMDriver" | head -3

Repository: crc-org/crc

Length of output: 37


🏁 Script executed:

#!/bin/bash
# Let's search for where Provider is defined and what it returns
rg "^func Provider|^var Provider" --type go | grep -v vendor | head -20

Repository: crc-org/crc

Length of output: 243


🏁 Script executed:

#!/bin/bash
# Check all go files in constants package
ls -la pkg/crc/constants/*.go

Repository: crc-org/crc

Length of output: 470


🏁 Script executed:

#!/bin/bash
# Check the full Provider() function to understand its behavior
cat -n pkg/crc/constants/constants_darwin.go

Repository: crc-org/crc

Length of output: 1209


🏁 Script executed:

#!/bin/bash
# Let's verify if VfkitPath is actually used when starting the VM
# and what happens with different providers
rg "VfkitPath" pkg/drivers/vfkit/driver_darwin.go -B 5 -A 5

Repository: crc-org/crc

Length of output: 852


🏁 Script executed:

#!/bin/bash
# Check if the VM configuration persists any provider information
# or if switching providers would actually cause issues
rg "krunkit|Provider" pkg/drivers/vfkit/driver_darwin.go

Repository: crc-org/crc

Length of output: 107


🏁 Script executed:

#!/bin/bash
# Check the NewDriver function more carefully - see what executableName param does
cat -n pkg/drivers/vfkit/driver_darwin.go | sed -n '55,75p'

Repository: crc-org/crc

Length of output: 893


🏁 Script executed:

#!/bin/bash
# Let's check if there are tests or documentation about provider switching
rg "CRC_PROVIDER" --type go -B 3 -A 3 | head -50

Repository: crc-org/crc

Length of output: 437


🏁 Script executed:

#!/bin/bash
# Check what ResolveHelperPath does
rg "func ResolveHelperPath" -A 15 --type go pkg/crc/constants/

Repository: crc-org/crc

Length of output: 1021


Unconditionally overwriting VfkitPath with current provider breaks existing VMs.

When loading an existing VM, line 37 overwrites VfkitPath with the currently configured provider via constants.Provider() (which reads the CRC_PROVIDER environment variable). Since VfkitPath is serialized to JSON by default and contains the filesystem path to the hypervisor executable, switching providers causes the VM's startup to use the wrong hypervisor:

  1. VM created and stored with vfkit → VfkitPath saved pointing to vfkit executable
  2. User sets CRC_PROVIDER=krunkit and loads the VM
  3. Line 37 overwrites VfkitPath to point to krunkit executable
  4. VM startup executes with krunkit's path instead of vfkit's → failure or incorrect behavior

The VM's original provider should be recorded and validated on load, not silently switched. Either:

  • Persist the provider in the VM configuration and validate consistency
  • Skip overwriting VfkitPath if it was already restored from JSON
  • Document that switching providers requires VM recreation
🤖 Prompt for AI Agents
In pkg/libmachine/load_darwin.go around line 37, the code unconditionally sets
driver.VfkitPath = constants.ResolveHelperPath(constants.Provider()), which
overwrites the VfkitPath restored from the VM JSON and can change the hypervisor
used for existing VMs; change this to preserve an existing VfkitPath when the
driver was loaded from disk (i.e., only call ResolveHelperPath and assign
VfkitPath if driver.VfkitPath is empty), and also add a persisted Provider field
to the VM config (or use an existing field) and validate that the current
constants.Provider() matches the stored provider on load — if they differ, log a
clear error and refuse to silently switch the VfkitPath or fail fast with
guidance to recreate the VM.

h.Driver = driver
return h, nil
}
Loading