Skip to content

Commit e68bff0

Browse files
committed
Convert limayaml.VMType to an abstract map
The format of the VMOpts is known only to the driver, everyone else will see a basic map[string]any or something similar to it. Converting from the abstract format to the actual format is done using YAML, just like it was before when the format was known. Signed-off-by: Anders F Björklund <[email protected]>
1 parent 44b65fa commit e68bff0

File tree

9 files changed

+126
-23
lines changed

9 files changed

+126
-23
lines changed

pkg/driver/qemu/qemu.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,13 @@ func defaultCPUType() limayaml.CPUType {
448448
func resolveCPUType(y *limayaml.LimaYAML) string {
449449
cpuType := defaultCPUType()
450450
var overrideCPUType bool
451-
for k, v := range y.VMOpts.QEMU.CPUType {
451+
var qemuOpts limayaml.QEMUOpts
452+
if y.VMOpts[limayaml.QEMU] != nil {
453+
if err := limayaml.Convert(y.VMOpts[limayaml.QEMU], &qemuOpts, "vmOpts.qemu"); err != nil {
454+
logrus.WithError(err).Warnf("Couldn't convert %q", y.VMOpts[limayaml.QEMU])
455+
}
456+
}
457+
for k, v := range qemuOpts.CPUType {
452458
if !slices.Contains(limayaml.ArchTypes, *y.Arch) {
453459
logrus.Warnf("field `vmOpts.qemu.cpuType` uses unsupported arch %q", k)
454460
continue
@@ -459,7 +465,10 @@ func resolveCPUType(y *limayaml.LimaYAML) string {
459465
}
460466
}
461467
if overrideCPUType {
462-
y.VMOpts.QEMU.CPUType = cpuType
468+
qemuOpts.CPUType = cpuType
469+
}
470+
if y.VMOpts[limayaml.QEMU] != nil {
471+
y.VMOpts[limayaml.QEMU] = qemuOpts
463472
}
464473

465474
return cpuType[*y.Arch]
@@ -489,8 +498,12 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
489498
if version.LessThan(softMin) {
490499
logrus.Warnf("QEMU %v is too old, %v or later is recommended", version, softMin)
491500
}
492-
if y.VMOpts.QEMU.MinimumVersion != nil && version.LessThan(*semver.New(*y.VMOpts.QEMU.MinimumVersion)) {
493-
logrus.Fatalf("QEMU %v is too old, template requires %q or later", version, *y.VMOpts.QEMU.MinimumVersion)
501+
var qemuOpts limayaml.QEMUOpts
502+
if err := limayaml.Convert(y.VMOpts[limayaml.QEMU], &qemuOpts, "vmOpts.qemu"); err != nil {
503+
logrus.WithError(err).Warnf("Couldn't convert %q", y.VMOpts[limayaml.QEMU])
504+
}
505+
if qemuOpts.MinimumVersion != nil && version.LessThan(*semver.New(*qemuOpts.MinimumVersion)) {
506+
logrus.Fatalf("QEMU %v is too old, template requires %q or later", version, *qemuOpts.MinimumVersion)
494507
}
495508
}
496509

pkg/limatmpl/embed.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,22 @@ func (tmpl *Template) mergeBase(base *Template) error {
179179
tmpl.copyField(minimumLimaVersion, minimumLimaVersion)
180180
}
181181
}
182-
if tmpl.Config.VMOpts.QEMU.MinimumVersion != nil && base.Config.VMOpts.QEMU.MinimumVersion != nil {
183-
tmplVersion := *semver.New(*tmpl.Config.VMOpts.QEMU.MinimumVersion)
184-
baseVersion := *semver.New(*base.Config.VMOpts.QEMU.MinimumVersion)
185-
if tmplVersion.LessThan(baseVersion) {
186-
const minimumQEMUVersion = "vmOpts.qemu.minimumVersion"
187-
tmpl.copyField(minimumQEMUVersion, minimumQEMUVersion)
182+
if tmpl.Config.VMOpts[limayaml.QEMU] != nil && base.Config.VMOpts[limayaml.QEMU] != nil {
183+
var tmplOpts limayaml.QEMUOpts
184+
if err := limayaml.Convert(tmpl.Config.VMOpts[limayaml.QEMU], &tmplOpts, "vmOpts.qemu"); err != nil {
185+
return err
186+
}
187+
var baseOpts limayaml.QEMUOpts
188+
if err := limayaml.Convert(base.Config.VMOpts[limayaml.QEMU], &baseOpts, "vmOpts.qemu"); err != nil {
189+
return err
190+
}
191+
if tmplOpts.MinimumVersion != nil && baseOpts.MinimumVersion != nil {
192+
tmplVersion := *semver.New(*tmplOpts.MinimumVersion)
193+
baseVersion := *semver.New(*baseOpts.MinimumVersion)
194+
if tmplVersion.LessThan(baseVersion) {
195+
const minimumQEMUVersion = "vmOpts.qemu.minimumVersion"
196+
tmpl.copyField(minimumQEMUVersion, minimumQEMUVersion)
197+
}
188198
}
189199
}
190200
return nil

pkg/limayaml/defaults.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -252,24 +252,39 @@ func FillDefault(ctx context.Context, y, d, o *LimaYAML, filePath string, warn b
252252
}
253253
}
254254

255-
if y.VMOpts.QEMU.CPUType == nil {
256-
y.VMOpts.QEMU.CPUType = CPUType{}
257-
}
258255
// TODO: This check should be removed when we completely eliminate `CPUType` from limayaml.
259256
if len(y.CPUType) > 0 {
260257
if warn {
261258
logrus.Warn("The top-level `cpuType` field is deprecated and will be removed in a future release. Please migrate to `vmOpts.qemu.cpuType`.")
262259
}
260+
if y.VMOpts == nil {
261+
y.VMOpts = VMOpts{}
262+
}
263+
if y.VMOpts[QEMU] == nil {
264+
y.VMOpts[QEMU] = map[string]any{}
265+
}
266+
var qemuOpts QEMUOpts
267+
if err := Convert(y.VMOpts[QEMU], &qemuOpts, "vmOpts.qemu"); err != nil {
268+
logrus.WithError(err).Warnf("Couldn't convert %q", y.VMOpts[QEMU])
269+
}
270+
if qemuOpts.CPUType == nil {
271+
qemuOpts.CPUType = CPUType{}
272+
}
263273
for arch, v := range y.CPUType {
264274
if v == "" {
265275
continue
266276
}
267-
if existing, ok := y.VMOpts.QEMU.CPUType[arch]; ok && existing != "" && existing != v {
277+
if existing, ok := qemuOpts.CPUType[arch]; ok && existing != "" && existing != v {
268278
logrus.Warnf("Conflicting cpuType for arch %q: top-level=%q, vmOpts.qemu=%q; using vmOpts.qemu value", arch, v, existing)
269279
continue
270280
}
271-
y.VMOpts.QEMU.CPUType[arch] = v
281+
qemuOpts.CPUType[arch] = v
282+
}
283+
var opts any
284+
if err := Convert(qemuOpts, &opts, ""); err != nil {
285+
logrus.WithError(err).Warnf("Couldn't convert %+v", qemuOpts)
272286
}
287+
y.VMOpts[QEMU] = opts
273288
y.CPUType = nil
274289
}
275290

pkg/limayaml/limayaml.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ type LimaYAML struct {
1717
OS *OS `yaml:"os,omitempty" json:"os,omitempty" jsonschema:"nullable"`
1818
Arch *Arch `yaml:"arch,omitempty" json:"arch,omitempty" jsonschema:"nullable"`
1919
Images []Image `yaml:"images,omitempty" json:"images,omitempty" jsonschema:"nullable"`
20-
// Deprecated: Use VMOpts.Qemu.CPUType instead.
20+
// Deprecated: Use vmOpts.qemu.cpuType instead.
2121
CPUType CPUType `yaml:"cpuType,omitempty" json:"cpuType,omitempty" jsonschema:"nullable"`
2222
CPUs *int `yaml:"cpus,omitempty" json:"cpus,omitempty" jsonschema:"nullable"`
2323
Memory *string `yaml:"memory,omitempty" json:"memory,omitempty" jsonschema:"nullable"` // go-units.RAMInBytes
@@ -106,9 +106,7 @@ type User struct {
106106
UID *uint32 `yaml:"uid,omitempty" json:"uid,omitempty" jsonschema:"nullable"`
107107
}
108108

109-
type VMOpts struct {
110-
QEMU QEMUOpts `yaml:"qemu,omitempty" json:"qemu,omitempty"`
111-
}
109+
type VMOpts map[VMType]any
112110

113111
type QEMUOpts struct {
114112
MinimumVersion *string `yaml:"minimumVersion,omitempty" json:"minimumVersion,omitempty" jsonschema:"nullable"`

pkg/limayaml/limayaml_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func TestDefaultYAML(t *testing.T) {
4949
y.Images = nil // remove default images
5050
y.Mounts = nil // remove default mounts
5151
y.Base = nil // remove default base templates
52+
y.VMOpts = nil // remove default qemu vmopts
5253
y.MinimumLimaVersion = nil // remove minimum Lima version
5354
y.MountTypesUnsupported = nil // remove default workaround for kernel 6.9-6.11
5455
t.Log(dumpJSON(t, y))

pkg/limayaml/marshal.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,16 @@ func Unmarshal(data []byte, y *LimaYAML, comment string) error {
8181
}
8282
return nil
8383
}
84+
85+
// Convert converts from x to y, using YAML.
86+
func Convert(x, y any, comment string) error {
87+
b, err := yaml.Marshal(x)
88+
if err != nil {
89+
return err
90+
}
91+
err = yaml.Unmarshal(b, y)
92+
if err != nil {
93+
return fmt.Errorf("failed to unmarshal YAML (%s): %w", comment, err)
94+
}
95+
return nil
96+
}

pkg/limayaml/marshal_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package limayaml
66
import (
77
"testing"
88

9+
"github.com/goccy/go-yaml"
910
"gotest.tools/v3/assert"
1011

1112
"github.com/lima-vm/lima/v2/pkg/ptr"
@@ -38,3 +39,43 @@ mounts:
3839
...
3940
`)
4041
}
42+
43+
type Opts struct {
44+
Foo int
45+
Bar string
46+
}
47+
48+
var (
49+
opts = Opts{Foo: 1, Bar: "two"}
50+
text = `{"foo":1,"bar":"two"}`
51+
code any
52+
)
53+
54+
func TestConvert(t *testing.T) {
55+
err := yaml.Unmarshal([]byte(text), &code)
56+
assert.NilError(t, err)
57+
o := opts
58+
var a any
59+
err = Convert(o, &a, "")
60+
assert.NilError(t, err)
61+
assert.DeepEqual(t, a, code)
62+
err = Convert(a, &o, "")
63+
assert.NilError(t, err)
64+
assert.Equal(t, o, opts)
65+
}
66+
67+
func TestQEMUOpts(t *testing.T) {
68+
text := `
69+
vmType: "qemu"
70+
vmOpts:
71+
qemu:
72+
minimumVersion: null
73+
cpuType:
74+
`
75+
var y LimaYAML
76+
err := Unmarshal([]byte(text), &y, "lima.yaml")
77+
assert.NilError(t, err)
78+
var o QEMUOpts
79+
err = Convert(y.VMOpts[QEMU], &o, QEMU)
80+
assert.NilError(t, err)
81+
}

pkg/limayaml/validate.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,15 @@ func Validate(y *LimaYAML, warn bool) error {
4747
errs = errors.Join(errs, fmt.Errorf("template requires Lima version %q; this is only %q", *y.MinimumLimaVersion, version.Version))
4848
}
4949
}
50-
if y.VMOpts.QEMU.MinimumVersion != nil {
51-
if _, err := semver.NewVersion(*y.VMOpts.QEMU.MinimumVersion); err != nil {
52-
errs = errors.Join(errs, fmt.Errorf("field `vmOpts.qemu.minimumVersion` must be a semvar value, got %q: %w", *y.VMOpts.QEMU.MinimumVersion, err))
50+
var qemuOpts QEMUOpts
51+
if y.VMOpts[QEMU] != nil {
52+
if err := Convert(y.VMOpts[QEMU], &qemuOpts, "vmOpts.qemu"); err != nil {
53+
errs = errors.Join(errs, fmt.Errorf("field `vmOpts.qemu` must be a valid value, got %q: %w", y.VMOpts["qemu"], err))
54+
}
55+
}
56+
if qemuOpts.MinimumVersion != nil {
57+
if _, err := semver.NewVersion(*qemuOpts.MinimumVersion); err != nil {
58+
errs = errors.Join(errs, fmt.Errorf("field `vmOpts.qemu.minimumVersion` must be a semver value, got %q: %w", *qemuOpts.MinimumVersion, err))
5359
}
5460
}
5561
switch *y.OS {

pkg/store/instance.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,13 @@ func Inspect(ctx context.Context, instName string) (*Instance, error) {
9797
inst.Config = y
9898
inst.Arch = *y.Arch
9999
inst.VMType = *y.VMType
100-
inst.CPUType = y.VMOpts.QEMU.CPUType[*y.Arch]
100+
if y.VMOpts[limayaml.QEMU] != nil {
101+
var qemuOpts limayaml.QEMUOpts
102+
if err := limayaml.Convert(y.VMOpts[limayaml.QEMU], &qemuOpts, "vmOpts.qemu"); err != nil {
103+
return nil, err
104+
}
105+
inst.CPUType = qemuOpts.CPUType[*y.Arch]
106+
}
101107
inst.SSHAddress = "127.0.0.1"
102108
inst.SSHLocalPort = *y.SSH.LocalPort // maybe 0
103109
inst.SSHConfigFile = filepath.Join(instDir, filenames.SSHConfig)

0 commit comments

Comments
 (0)