Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
lima-vm
GitHub Repository: lima-vm/lima
Path: blob/master/pkg/limayaml/defaults_test.go
2601 views
1
// SPDX-FileCopyrightText: Copyright The Lima Authors
2
// SPDX-License-Identifier: Apache-2.0
3
4
package limayaml
5
6
import (
7
"fmt"
8
"net"
9
"os"
10
"path"
11
"path/filepath"
12
"runtime"
13
"slices"
14
"strconv"
15
"testing"
16
17
"github.com/google/go-cmp/cmp"
18
"github.com/google/go-cmp/cmp/cmpopts"
19
"github.com/sirupsen/logrus"
20
"gotest.tools/v3/assert"
21
22
"github.com/lima-vm/lima/v2/pkg/ioutilx"
23
"github.com/lima-vm/lima/v2/pkg/limatype"
24
"github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
25
"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
26
"github.com/lima-vm/lima/v2/pkg/osutil"
27
"github.com/lima-vm/lima/v2/pkg/ptr"
28
)
29
30
func TestFillDefault(t *testing.T) {
31
logrus.SetLevel(logrus.DebugLevel)
32
var d, y, o limatype.LimaYAML
33
34
opts := []cmp.Option{
35
// Consider nil slices and empty slices to be identical
36
cmpopts.EquateEmpty(),
37
}
38
39
var arch limatype.Arch
40
switch runtime.GOARCH {
41
case "amd64":
42
arch = limatype.X8664
43
case "arm64":
44
arch = limatype.AARCH64
45
case "arm":
46
if runtime.GOOS != "linux" {
47
t.Skipf("unsupported GOOS: %s", runtime.GOOS)
48
}
49
if arm := limatype.Goarm(); arm < 7 {
50
t.Skipf("unsupported GOARM: %d", arm)
51
}
52
arch = limatype.ARMV7L
53
case "ppc64le":
54
arch = limatype.PPC64LE
55
case "riscv64":
56
arch = limatype.RISCV64
57
case "s390x":
58
arch = limatype.S390X
59
default:
60
t.Skipf("unknown GOARCH: %s", runtime.GOARCH)
61
}
62
63
hostHome, err := os.UserHomeDir()
64
assert.NilError(t, err)
65
limaHome, err := dirnames.LimaDir()
66
assert.NilError(t, err)
67
user := osutil.LimaUser(t.Context(), "0.0.0", false)
68
user.HomeDir = fmt.Sprintf("/home/%s.linux", user.Username)
69
uid, err := strconv.ParseUint(user.Uid, 10, 32)
70
assert.NilError(t, err)
71
72
instName := "instance"
73
instDir := filepath.Join(limaHome, instName)
74
filePath := filepath.Join(instDir, filenames.LimaYAML)
75
76
// Builtin default values
77
builtin := limatype.LimaYAML{
78
OS: ptr.Of(limatype.LINUX),
79
Arch: ptr.Of(arch),
80
CPUs: ptr.Of(defaultCPUs()),
81
Memory: ptr.Of(defaultMemoryAsString()),
82
Disk: ptr.Of(defaultDiskSizeAsString()),
83
GuestInstallPrefix: ptr.Of(defaultGuestInstallPrefix()),
84
UpgradePackages: ptr.Of(false),
85
Containerd: limatype.Containerd{
86
System: ptr.Of(false),
87
User: ptr.Of(true),
88
Archives: defaultContainerdArchives(),
89
},
90
SSH: limatype.SSH{
91
LocalPort: ptr.Of(0),
92
LoadDotSSHPubKeys: ptr.Of(false),
93
ForwardAgent: ptr.Of(false),
94
ForwardX11: ptr.Of(false),
95
ForwardX11Trusted: ptr.Of(false),
96
},
97
TimeZone: ptr.Of(hostTimeZone()),
98
Firmware: limatype.Firmware{
99
LegacyBIOS: ptr.Of(false),
100
},
101
Audio: limatype.Audio{
102
Device: ptr.Of(""),
103
},
104
Video: limatype.Video{
105
Display: ptr.Of("none"),
106
},
107
HostResolver: limatype.HostResolver{
108
Enabled: ptr.Of(true),
109
IPv6: ptr.Of(false),
110
},
111
PropagateProxyEnv: ptr.Of(true),
112
CACertificates: limatype.CACertificates{
113
RemoveDefaults: ptr.Of(false),
114
},
115
NestedVirtualization: ptr.Of(false),
116
Plain: ptr.Of(false),
117
User: limatype.User{
118
Name: ptr.Of(user.Username),
119
Comment: ptr.Of(user.Name),
120
Home: ptr.Of(user.HomeDir),
121
Shell: ptr.Of("/bin/bash"),
122
UID: ptr.Of(uint32(uid)),
123
},
124
}
125
126
defaultPortForward := limatype.PortForward{
127
GuestIP: IPv4loopback1,
128
GuestIPMustBeZero: ptr.Of(false),
129
GuestPortRange: [2]int{1, 65535},
130
HostIP: IPv4loopback1,
131
HostPortRange: [2]int{1, 65535},
132
Proto: limatype.ProtoAny,
133
Reverse: false,
134
}
135
136
// ------------------------------------------------------------------------------------
137
// Builtin defaults are set when y is (mostly) empty
138
139
// All these slices and maps are empty in "builtin". Add minimal entries here to see that
140
// their values are retained and defaults for their fields are applied correctly.
141
y = limatype.LimaYAML{
142
HostResolver: limatype.HostResolver{
143
Hosts: map[string]string{
144
"MY.Host": "host.lima.internal",
145
},
146
},
147
Mounts: []limatype.Mount{
148
//nolint:usetesting // We need the OS temp directory name here; it is not used to create temp files for testing
149
{Location: filepath.Clean(os.TempDir())},
150
{Location: filepath.Clean("{{.Dir}}/{{.Param.ONE}}"), MountPoint: ptr.Of("/mnt/{{.Param.ONE}}")},
151
},
152
MountType: ptr.Of(limatype.NINEP),
153
Provision: []limatype.Provision{
154
{Script: ptr.Of("#!/bin/true # {{.Param.ONE}}")},
155
},
156
Probes: []limatype.Probe{
157
{Script: ptr.Of("#!/bin/false # {{.Param.ONE}}")},
158
},
159
Networks: []limatype.Network{
160
{Lima: "shared"},
161
},
162
DNS: []net.IP{
163
net.ParseIP("1.0.1.0"),
164
},
165
PortForwards: []limatype.PortForward{
166
{},
167
{GuestPort: 80},
168
{GuestPort: 8080, HostPort: 8888},
169
{
170
GuestSocket: "{{.Home}} | {{.UID}} | {{.User}} | {{.Param.ONE}}",
171
HostSocket: "{{.Home}} | {{.Dir}} | {{.Name}} | {{.UID}} | {{.User}} | {{.Param.ONE}}",
172
},
173
},
174
CopyToHost: []limatype.CopyToHost{
175
{
176
GuestFile: "{{.Home}} | {{.UID}} | {{.User}} | {{.Param.ONE}}",
177
HostFile: "{{.Home}} | {{.Dir}} | {{.Name}} | {{.UID}} | {{.User}} | {{.Param.ONE}}",
178
},
179
},
180
Env: map[string]string{
181
"ONE": "Eins",
182
},
183
Param: map[string]string{
184
"ONE": "Eins",
185
},
186
CACertificates: limatype.CACertificates{
187
Files: []string{"ca.crt"},
188
Certs: []string{
189
"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT\n-----END CERTIFICATE-----\n",
190
},
191
},
192
TimeZone: ptr.Of("Antarctica/Troll"),
193
}
194
195
expect := builtin
196
// VMType should remain nil when not explicitly set (will be resolved by ValidateVMType later)
197
expect.VMType = nil
198
expect.HostResolver.Hosts = map[string]string{
199
"MY.Host": "host.lima.internal",
200
}
201
202
expect.Mounts = slices.Clone(y.Mounts)
203
expect.Mounts[0].MountPoint = ptr.Of(expect.Mounts[0].Location)
204
if runtime.GOOS == "windows" {
205
mountLocation, err := ioutilx.WindowsSubsystemPath(t.Context(), expect.Mounts[0].Location)
206
if err == nil {
207
expect.Mounts[0].MountPoint = ptr.Of(mountLocation)
208
}
209
}
210
expect.Mounts[0].Writable = ptr.Of(false)
211
expect.Mounts[0].SSHFS.Cache = ptr.Of(true)
212
expect.Mounts[0].SSHFS.FollowSymlinks = ptr.Of(false)
213
expect.Mounts[0].SSHFS.SFTPDriver = ptr.Of("")
214
expect.Mounts[0].NineP.SecurityModel = ptr.Of(Default9pSecurityModel)
215
expect.Mounts[0].NineP.ProtocolVersion = ptr.Of(Default9pProtocolVersion)
216
expect.Mounts[0].NineP.Msize = ptr.Of(Default9pMsize)
217
expect.Mounts[0].NineP.Cache = ptr.Of(Default9pCacheForRO)
218
expect.Mounts[0].Virtiofs.QueueSize = nil
219
// Only missing Mounts field is Writable, and the default value is also the null value: false
220
expect.Mounts[1].Location = filepath.Join(instDir, y.Param["ONE"])
221
expect.Mounts[1].MountPoint = ptr.Of(path.Join("/mnt", y.Param["ONE"]))
222
expect.Mounts[1].Writable = ptr.Of(false)
223
expect.Mounts[1].SSHFS.Cache = ptr.Of(true)
224
expect.Mounts[1].SSHFS.FollowSymlinks = ptr.Of(false)
225
expect.Mounts[1].SSHFS.SFTPDriver = ptr.Of("")
226
expect.Mounts[1].NineP.SecurityModel = ptr.Of(Default9pSecurityModel)
227
expect.Mounts[1].NineP.ProtocolVersion = ptr.Of(Default9pProtocolVersion)
228
expect.Mounts[1].NineP.Msize = ptr.Of(Default9pMsize)
229
expect.Mounts[1].NineP.Cache = ptr.Of(Default9pCacheForRO)
230
expect.Mounts[1].Virtiofs.QueueSize = nil
231
232
expect.MountType = ptr.Of(limatype.NINEP)
233
234
expect.MountInotify = ptr.Of(false)
235
236
expect.Provision = slices.Clone(y.Provision)
237
expect.Provision[0].Mode = limatype.ProvisionModeSystem
238
expect.Provision[0].Script = ptr.Of("#!/bin/true # Eins")
239
240
expect.Probes = slices.Clone(y.Probes)
241
expect.Probes[0].Mode = limatype.ProbeModeReadiness
242
expect.Probes[0].Description = "user probe 1/1"
243
expect.Probes[0].Script = ptr.Of("#!/bin/false # Eins")
244
245
expect.Networks = slices.Clone(y.Networks)
246
expect.Networks[0].MACAddress = MACAddress(fmt.Sprintf("%s#%d", filePath, 0))
247
expect.Networks[0].Interface = "lima0"
248
expect.Networks[0].Metric = ptr.Of(uint32(100))
249
250
expect.DNS = slices.Clone(y.DNS)
251
expect.PortForwards = []limatype.PortForward{
252
defaultPortForward,
253
defaultPortForward,
254
defaultPortForward,
255
defaultPortForward,
256
}
257
expect.CopyToHost = []limatype.CopyToHost{
258
{},
259
}
260
261
// Setting GuestPort and HostPort for DeepEqual(), but they are not supposed to be used
262
// after FillDefault() has been called and the ...PortRange fields have been set.
263
expect.PortForwards[1].GuestPort = 80
264
expect.PortForwards[1].GuestPortRange = [2]int{80, 80}
265
expect.PortForwards[1].HostPortRange = expect.PortForwards[1].GuestPortRange
266
267
expect.PortForwards[2].GuestPort = 8080
268
expect.PortForwards[2].GuestPortRange = [2]int{8080, 8080}
269
expect.PortForwards[2].HostPort = 8888
270
expect.PortForwards[2].HostPortRange = [2]int{8888, 8888}
271
272
expect.PortForwards[3].HostPortRange = [2]int{0, 0}
273
expect.PortForwards[3].GuestSocket = fmt.Sprintf("%s | %s | %s | %s", user.HomeDir, user.Uid, user.Username, y.Param["ONE"])
274
expect.PortForwards[3].HostSocket = fmt.Sprintf("%s | %s | %s | %s | %s | %s", hostHome, instDir, instName, currentUser.Uid, currentUser.Username, y.Param["ONE"])
275
276
expect.CopyToHost[0].GuestFile = fmt.Sprintf("%s | %s | %s | %s", user.HomeDir, user.Uid, user.Username, y.Param["ONE"])
277
expect.CopyToHost[0].HostFile = fmt.Sprintf("%s | %s | %s | %s | %s | %s", hostHome, instDir, instName, currentUser.Uid, currentUser.Username, y.Param["ONE"])
278
279
expect.Env = y.Env
280
281
expect.Param = y.Param
282
283
expect.CACertificates = limatype.CACertificates{
284
RemoveDefaults: ptr.Of(false),
285
Files: []string{"ca.crt"},
286
Certs: []string{
287
"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT\n-----END CERTIFICATE-----\n",
288
},
289
}
290
291
expect.TimeZone = y.TimeZone
292
// Set firmware expectations to match what FillDefault actually does
293
// FillDefault uses the builtin default values, which include LegacyBIOS: ptr.Of(false)
294
expect.Firmware = limatype.Firmware{
295
LegacyBIOS: ptr.Of(false), // This matches what FillDefault actually sets
296
Images: nil,
297
}
298
299
expect.NestedVirtualization = ptr.Of(false)
300
301
FillDefault(t.Context(), &y, &limatype.LimaYAML{}, &limatype.LimaYAML{}, filePath, false)
302
assert.DeepEqual(t, &y, &expect, opts...)
303
304
filledDefaults := y
305
306
// ------------------------------------------------------------------------------------
307
// User-provided defaults should override any builtin defaults
308
309
// Choose values that are different from the "builtin" defaults
310
311
// Calling filepath.Abs() to add a drive letter on Windows
312
varLog, _ := filepath.Abs("/var/log")
313
d = limatype.LimaYAML{
314
// Remove driver-specific VMType from defaults test
315
OS: ptr.Of("unknown"),
316
Arch: ptr.Of("unknown"),
317
CPUs: ptr.Of(7),
318
Memory: ptr.Of("5GiB"),
319
Disk: ptr.Of("105GiB"),
320
AdditionalDisks: []limatype.Disk{
321
{Name: "data"},
322
},
323
GuestInstallPrefix: ptr.Of("/opt"),
324
UpgradePackages: ptr.Of(true),
325
Containerd: limatype.Containerd{
326
System: ptr.Of(true),
327
User: ptr.Of(false),
328
Archives: []limatype.File{
329
{Location: "/tmp/nerdctl.tgz"},
330
},
331
},
332
SSH: limatype.SSH{
333
LocalPort: ptr.Of(888),
334
LoadDotSSHPubKeys: ptr.Of(false),
335
ForwardAgent: ptr.Of(true),
336
ForwardX11: ptr.Of(false),
337
ForwardX11Trusted: ptr.Of(false),
338
},
339
TimeZone: ptr.Of("Zulu"),
340
Firmware: limatype.Firmware{
341
LegacyBIOS: ptr.Of(true),
342
// Remove driver-specific firmware images from defaults
343
},
344
Audio: limatype.Audio{
345
Device: ptr.Of("coreaudio"),
346
},
347
Video: limatype.Video{
348
Display: ptr.Of("cocoa"),
349
// Remove driver-specific VNC configuration
350
},
351
HostResolver: limatype.HostResolver{
352
Enabled: ptr.Of(false),
353
IPv6: ptr.Of(true),
354
Hosts: map[string]string{
355
"default": "localhost",
356
},
357
},
358
PropagateProxyEnv: ptr.Of(false),
359
360
Mounts: []limatype.Mount{
361
{
362
Location: varLog,
363
Writable: ptr.Of(false),
364
},
365
},
366
Provision: []limatype.Provision{
367
{
368
Script: ptr.Of("#!/bin/true"),
369
Mode: limatype.ProvisionModeUser,
370
},
371
},
372
Probes: []limatype.Probe{
373
{
374
Script: ptr.Of("#!/bin/false"),
375
Mode: limatype.ProbeModeReadiness,
376
Description: "User Probe",
377
},
378
},
379
Networks: []limatype.Network{
380
{
381
MACAddress: "11:22:33:44:55:66",
382
Interface: "def0",
383
Metric: ptr.Of(uint32(50)),
384
},
385
},
386
DNS: []net.IP{
387
net.ParseIP("1.1.1.1"),
388
},
389
PortForwards: []limatype.PortForward{{
390
GuestIP: IPv4loopback1,
391
GuestIPMustBeZero: ptr.Of(false),
392
GuestPort: 80,
393
GuestPortRange: [2]int{80, 80},
394
HostIP: IPv4loopback1,
395
HostPort: 80,
396
HostPortRange: [2]int{80, 80},
397
Proto: limatype.ProtoTCP,
398
}},
399
CopyToHost: []limatype.CopyToHost{{}},
400
Env: map[string]string{
401
"ONE": "one",
402
"TWO": "two",
403
},
404
Param: map[string]string{
405
"ONE": "one",
406
"TWO": "two",
407
},
408
CACertificates: limatype.CACertificates{
409
RemoveDefaults: ptr.Of(true),
410
Certs: []string{
411
"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT\n-----END CERTIFICATE-----\n",
412
},
413
},
414
NestedVirtualization: ptr.Of(true),
415
User: limatype.User{
416
Name: ptr.Of("xxx"),
417
Comment: ptr.Of("Foo Bar"),
418
Home: ptr.Of("/tmp"),
419
Shell: ptr.Of("/bin/tcsh"),
420
UID: ptr.Of(uint32(8080)),
421
},
422
VMOpts: limatype.VMOpts{
423
"qemu": map[string]any{
424
"minimumVersion": "9.1.0",
425
},
426
},
427
}
428
429
expect = d
430
// VMType should remain nil when not explicitly set
431
expect.VMType = nil
432
// Also verify that archive arch is filled in
433
expect.Containerd.Archives = slices.Clone(d.Containerd.Archives)
434
expect.Containerd.Archives[0].Arch = *d.Arch
435
expect.Mounts = slices.Clone(d.Mounts)
436
expect.Mounts[0].MountPoint = ptr.Of(expect.Mounts[0].Location)
437
if runtime.GOOS == "windows" {
438
mountLocation, err := ioutilx.WindowsSubsystemPath(t.Context(), expect.Mounts[0].Location)
439
if err == nil {
440
expect.Mounts[0].MountPoint = ptr.Of(mountLocation)
441
}
442
}
443
expect.Mounts[0].SSHFS.Cache = ptr.Of(true)
444
expect.Mounts[0].SSHFS.FollowSymlinks = ptr.Of(false)
445
expect.Mounts[0].SSHFS.SFTPDriver = ptr.Of("")
446
expect.Mounts[0].NineP.SecurityModel = ptr.Of(Default9pSecurityModel)
447
expect.Mounts[0].NineP.ProtocolVersion = ptr.Of(Default9pProtocolVersion)
448
expect.Mounts[0].NineP.Msize = ptr.Of(Default9pMsize)
449
expect.Mounts[0].NineP.Cache = ptr.Of(Default9pCacheForRO)
450
expect.Mounts[0].Virtiofs.QueueSize = nil
451
expect.HostResolver.Hosts = map[string]string{
452
"default": d.HostResolver.Hosts["default"],
453
}
454
// Remove driver-specific mount type from defaults test
455
expect.MountType = nil
456
expect.MountInotify = ptr.Of(false)
457
expect.CACertificates.RemoveDefaults = ptr.Of(true)
458
expect.CACertificates.Certs = []string{
459
"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT\n-----END CERTIFICATE-----\n",
460
}
461
462
expect.Plain = ptr.Of(false)
463
464
y = limatype.LimaYAML{}
465
FillDefault(t.Context(), &y, &d, &limatype.LimaYAML{}, filePath, false)
466
assert.DeepEqual(t, &y, &expect, opts...)
467
468
dExpected := expect
469
470
// ------------------------------------------------------------------------------------
471
// User-provided defaults should not override user-provided config values
472
473
y = filledDefaults
474
y.DNS = []net.IP{net.ParseIP("8.8.8.8")}
475
y.AdditionalDisks = []limatype.Disk{{Name: "overridden"}}
476
y.User.Home = ptr.Of("/root")
477
y.VMOpts = limatype.VMOpts{
478
"vz": map[string]any{
479
"diskImageFormat": "raw",
480
},
481
}
482
483
expect = y
484
485
expect.Provision = slices.Concat(y.Provision, dExpected.Provision)
486
expect.Probes = slices.Concat(y.Probes, dExpected.Probes)
487
expect.PortForwards = slices.Concat(y.PortForwards, dExpected.PortForwards)
488
expect.CopyToHost = slices.Concat(y.CopyToHost, dExpected.CopyToHost)
489
expect.Containerd.Archives = slices.Concat(y.Containerd.Archives, dExpected.Containerd.Archives)
490
expect.Containerd.Archives[2].Arch = *expect.Arch
491
expect.AdditionalDisks = slices.Concat(y.AdditionalDisks, dExpected.AdditionalDisks)
492
expect.Firmware.Images = slices.Concat(y.Firmware.Images, dExpected.Firmware.Images)
493
494
// Mounts and Networks start with lowest priority first, so higher priority entries can overwrite
495
expect.Mounts = slices.Concat(dExpected.Mounts, y.Mounts)
496
expect.Networks = slices.Concat(dExpected.Networks, y.Networks)
497
498
expect.HostResolver.Hosts["default"] = dExpected.HostResolver.Hosts["default"]
499
500
// dExpected.DNS will be ignored, and not appended to y.DNS
501
502
// "TWO" does not exist in filledDefaults.Env, so is set from dExpected.Env
503
expect.Env["TWO"] = dExpected.Env["TWO"]
504
505
expect.Param["TWO"] = dExpected.Param["TWO"]
506
507
expect.VMOpts = limatype.VMOpts{
508
"qemu": dExpected.VMOpts["qemu"],
509
"vz": y.VMOpts["vz"],
510
}
511
512
t.Logf("d.vmType=%v, y.vmType=%v, expect.vmType=%v", d.VMType, y.VMType, expect.VMType)
513
514
FillDefault(t.Context(), &y, &d, &limatype.LimaYAML{}, filePath, false)
515
assert.DeepEqual(t, &y, &expect, opts...)
516
517
// ------------------------------------------------------------------------------------
518
// User-provided overrides should override user-provided config settings
519
520
o = limatype.LimaYAML{
521
// Remove driver-specific VMType from override test
522
OS: ptr.Of(limatype.LINUX),
523
Arch: ptr.Of(arch),
524
CPUs: ptr.Of(12),
525
Memory: ptr.Of("7GiB"),
526
Disk: ptr.Of("117GiB"),
527
AdditionalDisks: []limatype.Disk{
528
{Name: "test"},
529
},
530
GuestInstallPrefix: ptr.Of("/usr"),
531
UpgradePackages: ptr.Of(true),
532
Containerd: limatype.Containerd{
533
System: ptr.Of(true),
534
User: ptr.Of(false),
535
Archives: []limatype.File{
536
{
537
Arch: arch,
538
Location: "/tmp/nerdctl.tgz",
539
Digest: "$DIGEST",
540
},
541
},
542
},
543
SSH: limatype.SSH{
544
LocalPort: ptr.Of(4433),
545
LoadDotSSHPubKeys: ptr.Of(true),
546
ForwardAgent: ptr.Of(true),
547
ForwardX11: ptr.Of(false),
548
ForwardX11Trusted: ptr.Of(false),
549
},
550
TimeZone: ptr.Of("Universal"),
551
Firmware: limatype.Firmware{
552
LegacyBIOS: ptr.Of(true),
553
},
554
Audio: limatype.Audio{
555
Device: ptr.Of("coreaudio"),
556
},
557
Video: limatype.Video{
558
Display: ptr.Of("cocoa"),
559
// Remove driver-specific VNC configuration
560
},
561
HostResolver: limatype.HostResolver{
562
Enabled: ptr.Of(false),
563
IPv6: ptr.Of(false),
564
Hosts: map[string]string{
565
"override.": "underflow",
566
},
567
},
568
PropagateProxyEnv: ptr.Of(false),
569
570
Mounts: []limatype.Mount{
571
{
572
Location: varLog,
573
Writable: ptr.Of(true),
574
SSHFS: limatype.SSHFS{
575
Cache: ptr.Of(false),
576
FollowSymlinks: ptr.Of(true),
577
},
578
NineP: limatype.NineP{
579
SecurityModel: ptr.Of("mapped-file"),
580
ProtocolVersion: ptr.Of("9p2000"),
581
Msize: ptr.Of("8KiB"),
582
Cache: ptr.Of("none"),
583
},
584
Virtiofs: limatype.Virtiofs{
585
QueueSize: ptr.Of(2048),
586
},
587
},
588
},
589
MountInotify: ptr.Of(true),
590
Provision: []limatype.Provision{
591
{
592
Script: ptr.Of("#!/bin/true"),
593
Mode: limatype.ProvisionModeSystem,
594
},
595
},
596
Probes: []limatype.Probe{
597
{
598
Script: ptr.Of("#!/bin/false"),
599
Mode: limatype.ProbeModeReadiness,
600
Description: "Another Probe",
601
},
602
},
603
Networks: []limatype.Network{
604
{
605
Lima: "shared",
606
MACAddress: "10:20:30:40:50:60",
607
Interface: "def1",
608
Metric: ptr.Of(uint32(25)),
609
},
610
{
611
Lima: "bridged",
612
Interface: "def0",
613
},
614
},
615
DNS: []net.IP{
616
net.ParseIP("2.2.2.2"),
617
},
618
PortForwards: []limatype.PortForward{{
619
GuestIP: IPv4loopback1,
620
GuestIPMustBeZero: ptr.Of(false),
621
GuestPort: 88,
622
GuestPortRange: [2]int{88, 88},
623
HostIP: IPv4loopback1,
624
HostPort: 8080,
625
HostPortRange: [2]int{8080, 8080},
626
Proto: limatype.ProtoTCP,
627
}},
628
CopyToHost: []limatype.CopyToHost{{}},
629
Env: map[string]string{
630
"TWO": "deux",
631
"THREE": "trois",
632
},
633
Param: map[string]string{
634
"TWO": "deux",
635
"THREE": "trois",
636
},
637
CACertificates: limatype.CACertificates{
638
RemoveDefaults: ptr.Of(true),
639
},
640
NestedVirtualization: ptr.Of(false),
641
User: limatype.User{
642
Name: ptr.Of("foo"),
643
Comment: ptr.Of("foo bar baz"),
644
Home: ptr.Of("/override"),
645
Shell: ptr.Of("/bin/sh"),
646
UID: ptr.Of(uint32(1122)),
647
},
648
VMOpts: limatype.VMOpts{
649
"vz": map[string]any{
650
"rosetta": map[string]any{
651
"enabled": true,
652
"binfmt": true,
653
},
654
},
655
},
656
}
657
658
y = filledDefaults
659
660
expect = o
661
662
expect.Provision = slices.Concat(o.Provision, y.Provision, dExpected.Provision)
663
expect.Probes = slices.Concat(o.Probes, y.Probes, dExpected.Probes)
664
expect.PortForwards = slices.Concat(o.PortForwards, y.PortForwards, dExpected.PortForwards)
665
expect.CopyToHost = slices.Concat(o.CopyToHost, y.CopyToHost, dExpected.CopyToHost)
666
expect.Containerd.Archives = slices.Concat(o.Containerd.Archives, y.Containerd.Archives, dExpected.Containerd.Archives)
667
expect.Containerd.Archives[3].Arch = *expect.Arch
668
expect.AdditionalDisks = slices.Concat(o.AdditionalDisks, y.AdditionalDisks, dExpected.AdditionalDisks)
669
expect.Firmware.Images = slices.Concat(o.Firmware.Images, y.Firmware.Images, dExpected.Firmware.Images)
670
671
expect.HostResolver.Hosts["default"] = dExpected.HostResolver.Hosts["default"]
672
expect.HostResolver.Hosts["MY.Host"] = dExpected.HostResolver.Hosts["host.lima.internal"]
673
674
// o.Mounts just makes dExpected.Mounts[0] writable because the Location matches
675
expect.Mounts = slices.Concat(dExpected.Mounts, y.Mounts)
676
expect.Mounts[0].Writable = ptr.Of(true)
677
expect.Mounts[0].SSHFS.Cache = ptr.Of(false)
678
expect.Mounts[0].SSHFS.FollowSymlinks = ptr.Of(true)
679
expect.Mounts[0].NineP.SecurityModel = ptr.Of("mapped-file")
680
expect.Mounts[0].NineP.ProtocolVersion = ptr.Of("9p2000")
681
expect.Mounts[0].NineP.Msize = ptr.Of("8KiB")
682
expect.Mounts[0].NineP.Cache = ptr.Of("none")
683
expect.Mounts[0].Virtiofs.QueueSize = ptr.Of(2048)
684
685
expect.MountType = ptr.Of(limatype.NINEP)
686
expect.MountInotify = ptr.Of(true)
687
688
// o.Networks[1] is overriding the dExpected.Networks[0].Lima entry for the "def0" interface
689
expect.Networks = slices.Concat(dExpected.Networks, y.Networks, []limatype.Network{o.Networks[0]})
690
expect.Networks[0].Lima = o.Networks[1].Lima
691
692
// Only highest prio DNS are retained
693
expect.DNS = slices.Clone(o.DNS)
694
695
// ONE remains from filledDefaults.Env; the rest are set from o
696
expect.Env["ONE"] = y.Env["ONE"]
697
698
expect.Param["ONE"] = y.Param["ONE"]
699
700
expect.CACertificates.RemoveDefaults = ptr.Of(true)
701
expect.CACertificates.Files = []string{"ca.crt"}
702
expect.CACertificates.Certs = []string{
703
"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT\n-----END CERTIFICATE-----\n",
704
}
705
706
expect.Plain = ptr.Of(false)
707
708
expect.NestedVirtualization = ptr.Of(false)
709
710
expect.VMOpts = limatype.VMOpts{
711
"qemu": dExpected.VMOpts["qemu"],
712
"vz": map[string]any{
713
"rosetta": map[string]any{
714
"enabled": true,
715
"binfmt": true,
716
},
717
},
718
}
719
720
FillDefault(t.Context(), &y, &d, &o, filePath, false)
721
assert.DeepEqual(t, &y, &expect, opts...)
722
}
723
724
func TestContainerdDefault(t *testing.T) {
725
archives := defaultContainerdArchives()
726
assert.Assert(t, len(archives) > 0)
727
}
728
729
func TestStaticPortForwarding(t *testing.T) {
730
tests := []struct {
731
name string
732
config limatype.LimaYAML
733
expected []limatype.PortForward
734
}{
735
{
736
name: "plain mode with static port forwards",
737
config: limatype.LimaYAML{
738
Plain: ptr.Of(true),
739
PortForwards: []limatype.PortForward{
740
{
741
GuestPort: 8080,
742
HostPort: 8080,
743
Static: true,
744
},
745
{
746
GuestPort: 9000,
747
HostPort: 9000,
748
Static: false,
749
},
750
{
751
GuestPort: 8081,
752
HostPort: 8081,
753
},
754
},
755
},
756
expected: []limatype.PortForward{
757
{
758
GuestPort: 8080,
759
HostPort: 8080,
760
Static: true,
761
},
762
},
763
},
764
{
765
name: "non-plain mode with static port forwards",
766
config: limatype.LimaYAML{
767
Plain: ptr.Of(false),
768
PortForwards: []limatype.PortForward{
769
{
770
GuestPort: 8080,
771
HostPort: 8080,
772
Static: true,
773
},
774
{
775
GuestPort: 9000,
776
HostPort: 9000,
777
Static: false,
778
},
779
},
780
},
781
expected: []limatype.PortForward{
782
{
783
GuestPort: 8080,
784
HostPort: 8080,
785
Static: true,
786
},
787
{
788
GuestPort: 9000,
789
HostPort: 9000,
790
Static: false,
791
},
792
},
793
},
794
{
795
name: "plain mode with no static port forwards",
796
config: limatype.LimaYAML{
797
Plain: ptr.Of(true),
798
PortForwards: []limatype.PortForward{
799
{
800
GuestPort: 8080,
801
HostPort: 8080,
802
Static: false,
803
},
804
{
805
GuestPort: 9000,
806
HostPort: 9000,
807
},
808
},
809
},
810
expected: []limatype.PortForward{},
811
},
812
}
813
814
for _, tt := range tests {
815
t.Run(tt.name, func(t *testing.T) {
816
fixUpForPlainMode(&tt.config)
817
818
if *tt.config.Plain {
819
for _, pf := range tt.config.PortForwards {
820
if !pf.Static {
821
t.Errorf("Non-static port forward found in plain mode: %+v", pf)
822
}
823
}
824
}
825
826
assert.Equal(t, len(tt.config.PortForwards), len(tt.expected),
827
"Expected %d port forwards, got %d", len(tt.expected), len(tt.config.PortForwards))
828
829
for i, expected := range tt.expected {
830
if i >= len(tt.config.PortForwards) {
831
t.Errorf("Missing port forward at index %d", i)
832
continue
833
}
834
actual := tt.config.PortForwards[i]
835
assert.Equal(t, expected.Static, actual.Static,
836
"Port forward %d: expected Static=%v, got %v", i, expected.Static, actual.Static)
837
assert.Equal(t, expected.GuestPort, actual.GuestPort,
838
"Port forward %d: expected GuestPort=%d, got %d", i, expected.GuestPort, actual.GuestPort)
839
assert.Equal(t, expected.HostPort, actual.HostPort,
840
"Port forward %d: expected HostPort=%d, got %d", i, expected.HostPort, actual.HostPort)
841
}
842
})
843
}
844
}
845
846
func TestMountTag(t *testing.T) {
847
tests := []struct {
848
name string
849
location string
850
mountPoint string
851
}{
852
{"home directory", "/Users/testuser", "/Users/testuser"},
853
{"nested path", "/Users/testuser/Development/project", "/mnt/project"},
854
{"root path", "/", "/mnt/root"},
855
{"tmp directory", "/tmp/lima", "/tmp/lima"},
856
}
857
858
for _, tt := range tests {
859
t.Run(tt.name, func(t *testing.T) {
860
tag := MountTag(tt.location, tt.mountPoint)
861
assert.Assert(t, len(tag) > 5 && tag[:5] == "lima-")
862
assert.Assert(t, len(tag) <= 36)
863
assert.Equal(t, tag, MountTag(tt.location, tt.mountPoint))
864
})
865
}
866
}
867
868
func TestMountTagUniqueness(t *testing.T) {
869
mounts := []struct{ location, mountPoint string }{
870
{"/Users/user1", "/Users/user1"},
871
{"/Users/user2", "/Users/user2"},
872
{"/home/user1", "/home/user1"},
873
{"/mnt/data", "/mnt/data"},
874
{"/tmp/lima", "/tmp/lima"},
875
}
876
tags := make(map[string]bool)
877
for _, m := range mounts {
878
tag := MountTag(m.location, m.mountPoint)
879
assert.Assert(t, !tags[tag], "tag collision: %s", tag)
880
tags[tag] = true
881
}
882
}
883
884
func TestMountTagDuplicateLocation(t *testing.T) {
885
location := "/Users/testuser"
886
tag1 := MountTag(location, "/home/user")
887
tag2 := MountTag(location, "/mnt/home-writable")
888
assert.Assert(t, tag1 != tag2)
889
}
890
891