Skip to content

add validation for nestedVirtualization, rosetta, and mountType #3127

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
44 changes: 44 additions & 0 deletions pkg/limayaml/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,23 @@ func Validate(y *LimaYAML, warn bool) error {
warnExperimental(y)
}

if y.Rosetta.Enabled != nil && *y.Rosetta.Enabled {
if *y.VMType != VZ {
return fmt.Errorf("field `rosetta.enabled` can only be enabled for VMType %q; got %q", VZ, *y.VMType)
}
if !IsNativeArch(AARCH64) {
return fmt.Errorf("field `rosetta.enabled` can only be enabled on aarch64; got %q", *y.Arch)
}
}

if y.NestedVirtualization != nil && *y.NestedVirtualization && *y.VMType != VZ {
return fmt.Errorf("field `nestedVirtualization` can only be enabled for VMType %q; got %q", VZ, *y.VMType)
}

if err := validateMountType(y); err != nil {
return err
}

// Validate Param settings
// Names must start with a letter, followed by any number of letters, digits, or underscores
validParamName := regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_]*$`)
Expand Down Expand Up @@ -432,6 +449,30 @@ func validateFileObject(f File, fieldName string) error {
return errs
}

func validateMountType(y *LimaYAML) error {
validMountTypes := map[string]bool{
REVSSHFS: true,
NINEP: true,
VIRTIOFS: true,
WSLMount: true,
}

if !validMountTypes[*y.MountType] {
return fmt.Errorf("field `mountType` must be: %q, %q, %q, %q; got %q", REVSSHFS, NINEP, VIRTIOFS, WSLMount, *y.MountType)
}

// On Windows, only WSL and reverse-sshfs mount types are valid
if runtime.GOOS == "windows" && *y.MountType != WSLMount && *y.MountType != REVSSHFS {
return fmt.Errorf("field `mountType` on Windows must be %q or %q; got %q", WSLMount, REVSSHFS, *y.MountType)
}

if *y.MountType == VIRTIOFS && runtime.GOOS == "darwin" && *y.VMType != VZ {
return fmt.Errorf("field `mountType` %q on macOS requires vmType %q; got %q", *y.MountType, VZ, *y.VMType)
}

return nil
}

func validateNetwork(y *LimaYAML) error {
var errs error
interfaceName := make(map[string]int)
Expand Down Expand Up @@ -490,6 +531,9 @@ func validateNetwork(y *LimaYAML) error {
errs = errors.Join(errs, fmt.Errorf("field `%s.macAddress` must be a 48 bit (6 bytes) MAC address; actual length of %q is %d bytes", field, nw.MACAddress, len(hw)))
}
}
if nw.VZNAT != nil && *nw.VZNAT && *y.VMType != VZ {
return fmt.Errorf("field `%s.vzNAT` requires vmType %q; got %q", field, VZ, *y.VMType)
}
// FillDefault() will make sure that nw.Interface is not the empty string
if len(nw.Interface) >= 16 {
errs = errors.Join(errs, fmt.Errorf("field `%s.interface` must be less than 16 bytes, but is %d bytes: %q", field, len(nw.Interface), nw.Interface))
Expand Down
166 changes: 166 additions & 0 deletions pkg/limayaml/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package limayaml

import (
"errors"
"runtime"
"testing"

"gotest.tools/v3/assert"
Expand Down Expand Up @@ -388,3 +389,168 @@ func TestValidateAgainstLatestConfig(t *testing.T) {
})
}
}

func TestValidateRosetta(t *testing.T) {
if runtime.GOOS != "darwin" {
t.Skip("Skipping Rosetta validation test on non-macOS platform")
}

images := `images: [{"location": "/"}]`

nilData := ``
y, err := Load([]byte(nilData+"\n"+images), "lima.yaml")
assert.NilError(t, err)

err = Validate(y, false)
assert.NilError(t, err)

invalidRosetta := `rosetta:
enabled: true
vmType: "qemu"
` + images
y, err = Load([]byte(invalidRosetta), "lima.yaml")
assert.NilError(t, err)

err = Validate(y, false)
if IsNativeArch(AARCH64) {
assert.Error(t, err, "field `rosetta.enabled` can only be enabled for VMType \"vz\"; got \"qemu\"")
} else {
assert.NilError(t, err)
}

validRosetta := `rosetta:
enabled: true
vmType: "vz"
` + images
y, err = Load([]byte(validRosetta), "lima.yaml")
assert.NilError(t, err)

err = Validate(y, false)
assert.NilError(t, err)

rosettaDisabled := `rosetta:
enabled: false
vmType: "qemu"
` + images
y, err = Load([]byte(rosettaDisabled), "lima.yaml")
assert.NilError(t, err)

err = Validate(y, false)
assert.NilError(t, err)
}

func TestValidateNestedVirtualization(t *testing.T) {
if runtime.GOOS != "darwin" {
t.Skip("Skipping nested virtualization validation test on non-macOS platform")
}

images := `images: [{"location": "/"}]`

validYAML := `
nestedVirtualization: true
vmType: vz
` + images

y, err := Load([]byte(validYAML), "lima.yaml")
assert.NilError(t, err)

err = Validate(y, false)
assert.NilError(t, err)

invalidYAML := `
nestedVirtualization: true
vmType: qemu
` + images

y, err = Load([]byte(invalidYAML), "lima.yaml")
assert.NilError(t, err)

err = Validate(y, false)
assert.Error(t, err, "field `nestedVirtualization` can only be enabled for VMType \"vz\"; got \"qemu\"")
}

func TestValidateMountTypeOS(t *testing.T) {
images := `images: [{"location": "/"}]`

nilMountConf := ``
y, err := Load([]byte(nilMountConf+"\n"+images), "lima.yaml")
assert.NilError(t, err)

err = Validate(y, false)
assert.NilError(t, err)

inValidMountTypeLinux := `
mountType: "random"
`
y, err = Load([]byte(inValidMountTypeLinux+"\n"+images), "lima.yaml")
assert.NilError(t, err)

err = Validate(y, true)
assert.Error(t, err, "field `mountType` must be: \"reverse-sshfs\", \"9p\", \"virtiofs\", \"wsl2\"; got \"random\"")

// Skip macOS-specific tests on non-macOS platforms
if runtime.GOOS != "darwin" {
return
}

validMountTypeLinux := `
mountType: "virtiofs"
`
y, err = Load([]byte(validMountTypeLinux+"\n"+images), "lima.yaml")
assert.NilError(t, err)

err = Validate(y, true)
if IsNativeArch(AARCH64) {
assert.Error(t, err, "field `mountType` \"virtiofs\" on macOS requires vmType \"vz\"; got \"qemu\"")
} else {
assert.NilError(t, err)
}

validMountTypeMac := `
mountType: "virtiofs"
vmType: "vz"
`
y, err = Load([]byte(validMountTypeMac+"\n"+images), "lima.yaml")
assert.NilError(t, err)

err = Validate(y, false)
assert.NilError(t, err)

invalidMountTypeMac := `
mountType: "virtiofs"
vmType: "qemu"
`
y, err = Load([]byte(invalidMountTypeMac+"\n"+images), "lima.yaml")
assert.NilError(t, err)

err = Validate(y, false)
assert.Error(t, err, "field `mountType` \"virtiofs\" on macOS requires vmType \"vz\"; got \"qemu\"")
}

func TestValidateWindowsMountType(t *testing.T) {
if runtime.GOOS != "windows" {
t.Skip("Skipping Windows-specific mount type validation test on non-Windows platform")
}

images := `images: [{"location": "/"}]`

invalidMountTypes := []string{"9p", "virtiofs"}
for _, mountType := range invalidMountTypes {
invalidMountTypeWindows := `mountType: "` + mountType + `"`
y, err := Load([]byte(invalidMountTypeWindows+"\n"+images), "lima.yaml")
assert.NilError(t, err)

err = Validate(y, false)
assert.Error(t, err, `field `+"`mountType`"+` on Windows must be "wsl2" or "reverse-sshfs"; got "`+mountType+`"`)
}

validMountTypes := []string{"wsl2", "reverse-sshfs"}
for _, mountType := range validMountTypes {
validMountTypeWindows := `mountType: "` + mountType + `"`
y, err := Load([]byte(validMountTypeWindows+"\n"+images), "lima.yaml")
assert.NilError(t, err)

err = Validate(y, false)
assert.NilError(t, err)
}
}
Loading