Skip to content

Commit 22676c1

Browse files
committed
Add limayaml param settings to provisioning script environment
They will be prefixed with `PARAM_`, so `param.FOO` becomes `PARAM_FOO`. This is useful because parameter substitution happens when a template is instantiated, so `[ "{{.Param.ROOTFUL}}" = true ]` becomes `[ "true" = true ]` in the cloud-init-output.log. This mechanism also works better when the parameter contains quotes, which would break a simplistic `FOO="{{.Param.FOO}}"`. Signed-off-by: Jan Dubois <[email protected]>
1 parent 536f375 commit 22676c1

File tree

9 files changed

+45
-5
lines changed

9 files changed

+45
-5
lines changed

examples/default.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ containerd:
237237
# playbook: playbook.yaml
238238

239239
# Probe scripts to check readiness.
240+
# The scripts run in user mode. They must start with a '#!' line.
240241
# The scripts can use the following template variables: {{.Home}}, {{.UID}}, {{.User}}, and {{.Param.Key}}
241242
# 🟢 Builtin default: null
242243
# probes:
@@ -422,7 +423,12 @@ networks:
422423
# KEY: value
423424

424425
# Defines variables used for customizing the functionality.
426+
# Key names must start with an uppercase or lowercase letter followed by
427+
# any number of letters, numbers, and underscores.
428+
# Values must not contain non-printable characters except for spaces and tabs.
425429
# These variables can be referenced as {{.Param.Key}} in lima.yaml.
430+
# In provisioning scripts and probes they are also available as predefined
431+
# environment variables, prefixed with "PARAM` (so `Key` → `$PARAM_Key`).
426432
# param:
427433
# Key: value
428434

pkg/cidata/cidata.TEMPLATE.d/boot.sh

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ WARNING() {
1010
}
1111

1212
# shellcheck disable=SC2163
13-
while read -r line; do export "$line"; done <"${LIMA_CIDATA_MNT}"/lima.env
13+
while read -r line; do [ -n "$line" ] && export "$line"; done <"${LIMA_CIDATA_MNT}"/lima.env
14+
# shellcheck disable=SC2163
15+
while read -r line; do [ -n "$line" ] && export "$line"; done <"${LIMA_CIDATA_MNT}"/param.env
1416

1517
# shellcheck disable=SC2163
1618
while read -r line; do
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{{range $key, $val := .Param -}}
2+
PARAM_{{ $key }}={{ $val }}
3+
{{end -}}

pkg/cidata/cidata.go

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ func GenerateISO9660(instDir, name string, y *limayaml.LimaYAML, udpDNSLocalPort
140140
VirtioPort: virtioPort,
141141
Plain: *y.Plain,
142142
TimeZone: *y.TimeZone,
143+
Param: y.Param,
143144
}
144145

145146
firstUsernetIndex := limayaml.FirstUsernetIndex(y)

pkg/cidata/template.go

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ type TemplateArgs struct {
7373
UDPDNSLocalPort int
7474
TCPDNSLocalPort int
7575
Env map[string]string
76+
Param map[string]string
7677
DNSAddresses []string
7778
CACerts CACerts
7879
HostHomeMountPoint string

pkg/hostagent/requirements.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,14 @@ func (a *HostAgent) waitForRequirements(label string, requirements []requirement
4343

4444
func (a *HostAgent) waitForRequirement(r requirement) error {
4545
logrus.Debugf("executing script %q", r.description)
46-
stdout, stderr, err := ssh.ExecuteScript(a.instSSHAddress, a.sshLocalPort, a.sshConfig, r.script, r.description)
46+
interpreter, err := ssh.ParseScriptInterpreter(r.script)
47+
if err != nil {
48+
return err
49+
}
50+
// TODO we should have a symbolic constant for `/mnt/lima-cidata`
51+
exportParam := `while read -r line; do [ -n "$line" ] && export "$line"; done<<EOF\n$(sudo cat /mnt/lima-cidata/param.env)\nEOF\n`
52+
script := fmt.Sprintf("#!/bin/bash -c $'%s%s'\n%s", exportParam, interpreter, r.script)
53+
stdout, stderr, err := ssh.ExecuteScript(a.instSSHAddress, a.sshLocalPort, a.sshConfig, script, r.description)
4754
logrus.Debugf("stdout=%q, stderr=%q, err=%v", stdout, stderr, err)
4855
if err != nil {
4956
return fmt.Errorf("stdout=%q, stderr=%q: %w", stdout, stderr, err)

pkg/limayaml/validate.go

+21-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"regexp"
1111
"runtime"
1212
"strings"
13+
"unicode"
1314

1415
"github.com/docker/go-units"
1516
"github.com/lima-vm/lima/pkg/localpathutil"
@@ -205,11 +206,13 @@ func Validate(y *LimaYAML, warn bool) error {
205206
}
206207
}
207208
for i, p := range y.Probes {
209+
if !strings.HasPrefix(p.Script, "#!") {
210+
return fmt.Errorf("field `probe[%d].mode` must start with a '#!' line", i)
211+
}
208212
switch p.Mode {
209213
case ProbeModeReadiness:
210214
default:
211-
return fmt.Errorf("field `probe[%d].mode` can only be %q",
212-
i, ProbeModeReadiness)
215+
return fmt.Errorf("field `probe[%d].mode` can only be %q", i, ProbeModeReadiness)
213216
}
214217
}
215218
for i, rule := range y.PortForwards {
@@ -315,6 +318,21 @@ func Validate(y *LimaYAML, warn bool) error {
315318
if warn {
316319
warnExperimental(y)
317320
}
321+
322+
// Validate Param settings
323+
// Names must start with a letter, followed by any number of letters, digits, or underscores
324+
validParamName := regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_]*$`)
325+
for param, value := range y.Param {
326+
if !validParamName.MatchString(param) {
327+
return fmt.Errorf("param %q name does not match regex %q", param, validParamName.String())
328+
}
329+
for _, r := range value {
330+
if !unicode.IsPrint(r) && r != '\t' && r != ' ' {
331+
return fmt.Errorf("param %q value contains unprintable character %q", param, r)
332+
}
333+
}
334+
}
335+
318336
return nil
319337
}
320338

@@ -397,7 +415,7 @@ func validateNetwork(y *LimaYAML) error {
397415
// It should be called before the `y` parameter is passed to FillDefault() that execute template.
398416
func ValidateParamIsUsed(y *LimaYAML) error {
399417
for key := range y.Param {
400-
re, err := regexp.Compile(`{{[^}]*\.Param\.` + key + `[^}]*}}`)
418+
re, err := regexp.Compile(`{{[^}]*\.Param\.` + key + `[^}]*}}|\bPARAM_` + key + `\b`)
401419
if err != nil {
402420
return fmt.Errorf("field to compile regexp for key %q: %w", key, err)
403421
}

pkg/limayaml/validate_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func TestValidateParamIsUsed(t *testing.T) {
4141
`mounts: [{"location": "/tmp/{{ .Param.name }}"}]`,
4242
`mounts: [{"location": "/tmp", mountPoint: "/tmp/{{ .Param.name }}"}]`,
4343
`provision: [{"script": "echo {{ .Param.name }}"}]`,
44+
`provision: [{"script": "echo $PARAM_name }}"}]`,
4445
`probes: [{"script": "echo {{ .Param.name }}"}]`,
4546
`copyToHost: [{"guest": "/tmp/{{ .Param.name }}", "host": "/tmp"}]`,
4647
`copyToHost: [{"guest": "/tmp", "host": "/tmp/{{ .Param.name }}"}]`,

website/content/en/docs/dev/internals/_index.md

+1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ See [Building Ansible inventories](https://docs.ansible.com/ansible/latest/inven
157157
- `meta-data`: [Cloud-init meta-data](https://docs.cloud-init.io/en/latest/explanation/instancedata.html)
158158
- `network-config`: [Cloud-init Networking Config Version 2](https://docs.cloud-init.io/en/latest/reference/network-config-format-v2.html)
159159
- `lima.env`: The `LIMA_CIDATA_*` environment variables (see below) available during `boot.sh` processing
160+
- `param.env`: The `PARAM_*` environment variables corresponding to the `param` settings from `lima.yaml`
160161
- `lima-guestagent`: Lima guest agent binary
161162
- `nerdctl-full.tgz`: [`nerdctl-full-<VERSION>-<OS>-<ARCH>.tar.gz`](https://github.com/containerd/nerdctl/releases)
162163
- `boot.sh`: Boot script

0 commit comments

Comments
 (0)