Skip to content

Commit 6331b56

Browse files
committed
limayaml: add a param field for defining variables used to customize scripts and other elements within lima.yaml.
The defined variables can be referenced within `lima.yaml` as `{{.Param.Key}}`. The fields where this can be utilized are as follows: - `provision[%d].script` - `probes[%d].script` - `copyToHost[%d].{guest,host}` - `portForwards[%d].{guestSocket,hostSocket}` Signed-off-by: Norio Nomura <[email protected]> limayaml: add a check to verify whether the variables defined in `param` are actually used This check needs to be performed before the template is expanded, so it should be added before calling `FillDefault()`. Signed-off-by: Norio Nomura <[email protected]> limayaml: add `{{if .Param.rootful}}{{else}}{{end}}` pattern to `TestValidateParamIsUsed` Signed-off-by: Norio Nomura <[email protected]> default.yaml: add a comment on the `probes` that supports variable substitution Signed-off-by: Norio Nomura <[email protected]>
1 parent be93284 commit 6331b56

File tree

11 files changed

+183
-41
lines changed

11 files changed

+183
-41
lines changed

Diff for: examples/default.yaml

+11-5
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ containerd:
193193

194194
# Provisioning scripts need to be idempotent because they might be called
195195
# multiple times, e.g. when the host VM is being restarted.
196-
# The scripts can use the following template variables: {{.Home}}, {{.UID}}, and {{.User}}
196+
# The scripts can use the following template variables: {{.Home}}, {{.UID}}, {{.User}}, and {{.Param.Key}}
197197
# 🟢 Builtin default: null
198198
# provision:
199199
# # `system` is executed with root privileges
@@ -235,6 +235,7 @@ containerd:
235235
# playbook: playbook.yaml
236236

237237
# Probe scripts to check readiness.
238+
# The scripts can use the following template variables: {{.Home}}, {{.UID}}, {{.User}}, and {{.Param.Key}}
238239
# 🟢 Builtin default: null
239240
# probes:
240241
# # Only `readiness` probes are supported right now.
@@ -377,8 +378,8 @@ networks:
377378
# - guestSocket: "/run/user/{{.UID}}/my.sock"
378379
# hostSocket: mysocket
379380
# # default: reverse: false
380-
# # "guestSocket" can include these template variables: {{.Home}}, {{.UID}}, and {{.User}}.
381-
# # "hostSocket" can include {{.Home}}, {{.Dir}}, {{.Name}}, {{.UID}}, and {{.User}}.
381+
# # "guestSocket" can include these template variables: {{.Home}}, {{.UID}}, {{.User}}, and {{.Param.Key}}.
382+
# # "hostSocket" can include {{.Home}}, {{.Dir}}, {{.Name}}, {{.UID}}, {{.User}}, and {{.Param.Key}}.
382383
# # "reverse" can only be used for unix sockets right now, not for tcp sockets.
383384
# # Put sockets into "{{.Dir}}/sock" to avoid collision with Lima internal sockets!
384385
# # Sockets can also be forwarded to ports and vice versa, but not to/from a range of ports.
@@ -397,8 +398,8 @@ networks:
397398
# - guest: "/etc/myconfig.cfg"
398399
# host: "{{.Dir}}/copied-from-guest/myconfig"
399400
# # deleteOnStop: false
400-
# # "guest" can include these template variables: {{.Home}}, {{.UID}}, and {{.User}}.
401-
# # "host" can include {{.Home}}, {{.Dir}}, {{.Name}}, {{.UID}}, and {{.User}}.
401+
# # "guest" can include these template variables: {{.Home}}, {{.UID}}, {{.User}}, and {{.Param.Key}}.
402+
# # "host" can include {{.Home}}, {{.Dir}}, {{.Name}}, {{.UID}}, {{.User}}, and {{.Param.Key}}.
402403
# # "deleteOnStop" will delete the file from the host when the instance is stopped.
403404

404405
# Message. Information to be shown to the user, given as a Go template for the instance.
@@ -418,6 +419,11 @@ networks:
418419
# env:
419420
# KEY: value
420421

422+
# Defines variables used for customizing the functionality.
423+
# These variables can be referenced as {{.Param.Key}} in lima.yaml.
424+
# param:
425+
# Key: value
426+
421427
# Lima will override the proxy environment variables with values from the current process
422428
# environment (the environment in effect when you run `limactl start`). It will automatically
423429
# replace the strings "localhost" and "127.0.0.1" with the host gateway address from inside

Diff for: pkg/hostagent/hostagent.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,13 @@ func New(instName string, stdout io.Writer, signalCh chan os.Signal, opts ...Opt
155155
// Block ports 22 and sshLocalPort on all IPs
156156
for _, port := range []int{sshGuestPort, sshLocalPort} {
157157
rule := limayaml.PortForward{GuestIP: net.IPv4zero, GuestPort: port, Ignore: true}
158-
limayaml.FillPortForwardDefaults(&rule, inst.Dir)
158+
limayaml.FillPortForwardDefaults(&rule, inst.Dir, inst.Param)
159159
rules = append(rules, rule)
160160
}
161161
rules = append(rules, y.PortForwards...)
162162
// Default forwards for all non-privileged ports from "127.0.0.1" and "::1"
163163
rule := limayaml.PortForward{}
164-
limayaml.FillPortForwardDefaults(&rule, inst.Dir)
164+
limayaml.FillPortForwardDefaults(&rule, inst.Dir, inst.Param)
165165
rules = append(rules, rule)
166166

167167
limaDriver := driverutil.CreateTargetDriverInstance(&driver.BaseDriver{

Diff for: pkg/limayaml/defaults.go

+41-22
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ func FillDefault(y, d, o *LimaYAML, filePath string) {
394394
if provision.Mode == ProvisionModeDependency && provision.SkipDefaultDependencyResolution == nil {
395395
provision.SkipDefaultDependencyResolution = ptr.Of(false)
396396
}
397-
if out, err := executeGuestTemplate(provision.Script, instDir); err == nil {
397+
if out, err := executeGuestTemplate(provision.Script, instDir, y.Param); err == nil {
398398
provision.Script = out.String()
399399
} else {
400400
logrus.WithError(err).Warnf("Couldn't process provisioning script %q as a template", provision.Script)
@@ -460,17 +460,22 @@ func FillDefault(y, d, o *LimaYAML, filePath string) {
460460
if probe.Description == "" {
461461
probe.Description = fmt.Sprintf("user probe %d/%d", i+1, len(y.Probes))
462462
}
463+
if out, err := executeGuestTemplate(probe.Script, instDir, y.Param); err == nil {
464+
probe.Script = out.String()
465+
} else {
466+
logrus.WithError(err).Warnf("Couldn't process probing script %q as a template", probe.Script)
467+
}
463468
}
464469

465470
y.PortForwards = append(append(o.PortForwards, y.PortForwards...), d.PortForwards...)
466471
for i := range y.PortForwards {
467-
FillPortForwardDefaults(&y.PortForwards[i], instDir)
472+
FillPortForwardDefaults(&y.PortForwards[i], instDir, y.Param)
468473
// After defaults processing the singular HostPort and GuestPort values should not be used again.
469474
}
470475

471476
y.CopyToHost = append(append(o.CopyToHost, y.CopyToHost...), d.CopyToHost...)
472477
for i := range y.CopyToHost {
473-
FillCopyToHostDefaults(&y.CopyToHost[i], instDir)
478+
FillCopyToHostDefaults(&y.CopyToHost[i], instDir, y.Param)
474479
}
475480

476481
if y.HostResolver.Enabled == nil {
@@ -669,6 +674,18 @@ func FillDefault(y, d, o *LimaYAML, filePath string) {
669674
}
670675
y.Env = env
671676

677+
param := make(map[string]string)
678+
for k, v := range d.Param {
679+
param[k] = v
680+
}
681+
for k, v := range y.Param {
682+
param[k] = v
683+
}
684+
for k, v := range o.Param {
685+
param[k] = v
686+
}
687+
y.Param = param
688+
672689
if y.CACertificates.RemoveDefaults == nil {
673690
y.CACertificates.RemoveDefaults = d.CACertificates.RemoveDefaults
674691
}
@@ -735,15 +752,16 @@ func fixUpForPlainMode(y *LimaYAML) {
735752
y.TimeZone = ptr.Of("")
736753
}
737754

738-
func executeGuestTemplate(format, instDir string) (bytes.Buffer, error) {
755+
func executeGuestTemplate(format, instDir string, param map[string]string) (bytes.Buffer, error) {
739756
tmpl, err := template.New("").Parse(format)
740757
if err == nil {
741758
user, _ := osutil.LimaUser(false)
742-
data := map[string]string{
743-
"Home": fmt.Sprintf("/home/%s.linux", user.Username),
744-
"Name": filepath.Base(instDir),
745-
"UID": user.Uid,
746-
"User": user.Username,
759+
data := map[string]interface{}{
760+
"Home": fmt.Sprintf("/home/%s.linux", user.Username),
761+
"Name": filepath.Base(instDir),
762+
"UID": user.Uid,
763+
"User": user.Username,
764+
"Param": param,
747765
}
748766
var out bytes.Buffer
749767
if err := tmpl.Execute(&out, data); err == nil {
@@ -753,18 +771,19 @@ func executeGuestTemplate(format, instDir string) (bytes.Buffer, error) {
753771
return bytes.Buffer{}, err
754772
}
755773

756-
func executeHostTemplate(format, instDir string) (bytes.Buffer, error) {
774+
func executeHostTemplate(format, instDir string, param map[string]string) (bytes.Buffer, error) {
757775
tmpl, err := template.New("").Parse(format)
758776
if err == nil {
759777
user, _ := osutil.LimaUser(false)
760778
home, _ := os.UserHomeDir()
761779
limaHome, _ := dirnames.LimaDir()
762-
data := map[string]string{
763-
"Dir": instDir,
764-
"Home": home,
765-
"Name": filepath.Base(instDir),
766-
"UID": user.Uid,
767-
"User": user.Username,
780+
data := map[string]interface{}{
781+
"Dir": instDir,
782+
"Home": home,
783+
"Name": filepath.Base(instDir),
784+
"UID": user.Uid,
785+
"User": user.Username,
786+
"Param": param,
768787

769788
"Instance": filepath.Base(instDir), // DEPRECATED, use `{{.Name}}`
770789
"LimaHome": limaHome, // DEPRECATED, use `{{.Dir}}` instead of `{{.LimaHome}}/{{.Instance}}`
@@ -777,7 +796,7 @@ func executeHostTemplate(format, instDir string) (bytes.Buffer, error) {
777796
return bytes.Buffer{}, err
778797
}
779798

780-
func FillPortForwardDefaults(rule *PortForward, instDir string) {
799+
func FillPortForwardDefaults(rule *PortForward, instDir string, param map[string]string) {
781800
if rule.Proto == "" {
782801
rule.Proto = TCP
783802
}
@@ -809,14 +828,14 @@ func FillPortForwardDefaults(rule *PortForward, instDir string) {
809828
}
810829
}
811830
if rule.GuestSocket != "" {
812-
if out, err := executeGuestTemplate(rule.GuestSocket, instDir); err == nil {
831+
if out, err := executeGuestTemplate(rule.GuestSocket, instDir, param); err == nil {
813832
rule.GuestSocket = out.String()
814833
} else {
815834
logrus.WithError(err).Warnf("Couldn't process guestSocket %q as a template", rule.GuestSocket)
816835
}
817836
}
818837
if rule.HostSocket != "" {
819-
if out, err := executeHostTemplate(rule.HostSocket, instDir); err == nil {
838+
if out, err := executeHostTemplate(rule.HostSocket, instDir, param); err == nil {
820839
rule.HostSocket = out.String()
821840
} else {
822841
logrus.WithError(err).Warnf("Couldn't process hostSocket %q as a template", rule.HostSocket)
@@ -827,16 +846,16 @@ func FillPortForwardDefaults(rule *PortForward, instDir string) {
827846
}
828847
}
829848

830-
func FillCopyToHostDefaults(rule *CopyToHost, instDir string) {
849+
func FillCopyToHostDefaults(rule *CopyToHost, instDir string, param map[string]string) {
831850
if rule.GuestFile != "" {
832-
if out, err := executeGuestTemplate(rule.GuestFile, instDir); err == nil {
851+
if out, err := executeGuestTemplate(rule.GuestFile, instDir, param); err == nil {
833852
rule.GuestFile = out.String()
834853
} else {
835854
logrus.WithError(err).Warnf("Couldn't process guest %q as a template", rule.GuestFile)
836855
}
837856
}
838857
if rule.HostFile != "" {
839-
if out, err := executeHostTemplate(rule.HostFile, instDir); err == nil {
858+
if out, err := executeHostTemplate(rule.HostFile, instDir, param); err == nil {
840859
rule.HostFile = out.String()
841860
} else {
842861
logrus.WithError(err).Warnf("Couldn't process host %q as a template", rule.HostFile)

Diff for: pkg/limayaml/defaults_test.go

+29-10
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,10 @@ func TestFillDefault(t *testing.T) {
129129
},
130130
MountType: ptr.Of(NINEP),
131131
Provision: []Provision{
132-
{Script: "#!/bin/true"},
132+
{Script: "#!/bin/true # {{.Param.ONE}}"},
133133
},
134134
Probes: []Probe{
135-
{Script: "#!/bin/false"},
135+
{Script: "#!/bin/false # {{.Param.ONE}}"},
136136
},
137137
Networks: []Network{
138138
{Lima: "shared"},
@@ -145,19 +145,22 @@ func TestFillDefault(t *testing.T) {
145145
{GuestPort: 80},
146146
{GuestPort: 8080, HostPort: 8888},
147147
{
148-
GuestSocket: "{{.Home}} | {{.UID}} | {{.User}}",
149-
HostSocket: "{{.Home}} | {{.Dir}} | {{.Name}} | {{.UID}} | {{.User}}",
148+
GuestSocket: "{{.Home}} | {{.UID}} | {{.User}} | {{.Param.ONE}}",
149+
HostSocket: "{{.Home}} | {{.Dir}} | {{.Name}} | {{.UID}} | {{.User}} | {{.Param.ONE}}",
150150
},
151151
},
152152
CopyToHost: []CopyToHost{
153153
{
154-
GuestFile: "{{.Home}} | {{.UID}} | {{.User}}",
155-
HostFile: "{{.Home}} | {{.Dir}} | {{.Name}} | {{.UID}} | {{.User}}",
154+
GuestFile: "{{.Home}} | {{.UID}} | {{.User}} | {{.Param.ONE}}",
155+
HostFile: "{{.Home}} | {{.Dir}} | {{.Name}} | {{.UID}} | {{.User}} | {{.Param.ONE}}",
156156
},
157157
},
158158
Env: map[string]string{
159159
"ONE": "Eins",
160160
},
161+
Param: map[string]string{
162+
"ONE": "Eins",
163+
},
161164
CACertificates: CACertificates{
162165
Files: []string{"ca.crt"},
163166
Certs: []string{
@@ -212,10 +215,12 @@ func TestFillDefault(t *testing.T) {
212215

213216
expect.Provision = y.Provision
214217
expect.Provision[0].Mode = ProvisionModeSystem
218+
expect.Provision[0].Script = "#!/bin/true # Eins"
215219

216220
expect.Probes = y.Probes
217221
expect.Probes[0].Mode = ProbeModeReadiness
218222
expect.Probes[0].Description = "user probe 1/1"
223+
expect.Probes[0].Script = "#!/bin/true # Eins"
219224

220225
expect.Networks = y.Networks
221226
expect.Networks[0].MACAddress = MACAddress(fmt.Sprintf("%s#%d", filePath, 0))
@@ -243,14 +248,16 @@ func TestFillDefault(t *testing.T) {
243248
expect.PortForwards[2].HostPort = 8888
244249
expect.PortForwards[2].HostPortRange = [2]int{8888, 8888}
245250

246-
expect.PortForwards[3].GuestSocket = fmt.Sprintf("%s | %s | %s", guestHome, user.Uid, user.Username)
247-
expect.PortForwards[3].HostSocket = fmt.Sprintf("%s | %s | %s | %s | %s", hostHome, instDir, instName, user.Uid, user.Username)
251+
expect.PortForwards[3].GuestSocket = fmt.Sprintf("%s | %s | %s | %s", guestHome, user.Uid, user.Username, y.Param["ONE"])
252+
expect.PortForwards[3].HostSocket = fmt.Sprintf("%s | %s | %s | %s | %s | %s", hostHome, instDir, instName, user.Uid, user.Username, y.Param["ONE"])
248253

249-
expect.CopyToHost[0].GuestFile = fmt.Sprintf("%s | %s | %s", guestHome, user.Uid, user.Username)
250-
expect.CopyToHost[0].HostFile = fmt.Sprintf("%s | %s | %s | %s | %s", hostHome, instDir, instName, user.Uid, user.Username)
254+
expect.CopyToHost[0].GuestFile = fmt.Sprintf("%s | %s | %s | %s", guestHome, user.Uid, user.Username, y.Param["ONE"])
255+
expect.CopyToHost[0].HostFile = fmt.Sprintf("%s | %s | %s | %s | %s | %s", hostHome, instDir, instName, user.Uid, user.Username, y.Param["ONE"])
251256

252257
expect.Env = y.Env
253258

259+
expect.Param = y.Param
260+
254261
expect.CACertificates = CACertificates{
255262
RemoveDefaults: ptr.Of(false),
256263
Files: []string{"ca.crt"},
@@ -380,6 +387,10 @@ func TestFillDefault(t *testing.T) {
380387
"ONE": "one",
381388
"TWO": "two",
382389
},
390+
Param: map[string]string{
391+
"ONE": "one",
392+
"TWO": "two",
393+
},
383394
CACertificates: CACertificates{
384395
RemoveDefaults: ptr.Of(true),
385396
Certs: []string{
@@ -459,6 +470,8 @@ func TestFillDefault(t *testing.T) {
459470
// "TWO" does not exist in filledDefaults.Env, so is set from d.Env
460471
expect.Env["TWO"] = d.Env["TWO"]
461472

473+
expect.Param["TWO"] = d.Param["TWO"]
474+
462475
FillDefault(&y, &d, &LimaYAML{}, filePath)
463476
assert.DeepEqual(t, &y, &expect, opts...)
464477

@@ -584,6 +597,10 @@ func TestFillDefault(t *testing.T) {
584597
"TWO": "deux",
585598
"THREE": "trois",
586599
},
600+
Param: map[string]string{
601+
"TWO": "deux",
602+
"THREE": "trois",
603+
},
587604
CACertificates: CACertificates{
588605
RemoveDefaults: ptr.Of(true),
589606
},
@@ -632,6 +649,8 @@ func TestFillDefault(t *testing.T) {
632649
// ONE remains from filledDefaults.Env; the rest are set from o
633650
expect.Env["ONE"] = y.Env["ONE"]
634651

652+
expect.Param["ONE"] = y.Param["ONE"]
653+
635654
expect.CACertificates.RemoveDefaults = ptr.Of(true)
636655
expect.CACertificates.Files = []string{"ca.crt"}
637656
expect.CACertificates.Certs = []string{

Diff for: pkg/limayaml/limayaml.go

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type LimaYAML struct {
3434
Networks []Network `yaml:"networks,omitempty" json:"networks,omitempty"`
3535
// `network` was deprecated in Lima v0.7.0, removed in Lima v0.14.0. Use `networks` instead.
3636
Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
37+
Param map[string]string `yaml:"param,omitempty" json:"param,omitempty"`
3738
DNS []net.IP `yaml:"dns,omitempty" json:"dns,omitempty"`
3839
HostResolver HostResolver `yaml:"hostResolver,omitempty" json:"hostResolver,omitempty"`
3940
// `useHostResolver` was deprecated in Lima v0.8.1, removed in Lima v0.14.0. Use `hostResolver.enabled` instead.

Diff for: pkg/limayaml/load.go

+5
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ func Load(b []byte, filePath string) (*LimaYAML, error) {
8686
return nil, err
8787
}
8888

89+
// It should be called before the `y` parameter is passed to FillDefault() that execute template.
90+
if err := ValidateParamIsUsed(&y); err != nil {
91+
return nil, err
92+
}
93+
8994
FillDefault(&y, &d, &o, filePath)
9095
return &y, nil
9196
}

0 commit comments

Comments
 (0)