diff --git a/examples/default.yaml b/examples/default.yaml index 13c7a2aef66..19cf99f6895 100644 --- a/examples/default.yaml +++ b/examples/default.yaml @@ -208,6 +208,7 @@ containerd: # Provisioning scripts need to be idempotent because they might be called # multiple times, e.g. when the host VM is being restarted. # The scripts can use the following template variables: {{.Home}}, {{.UID}}, {{.User}}, and {{.Param.Key}} +# They can be provided inline (as field `script`), or in external files (as field `path`). # 🟢 Builtin default: null # provision: # # `system` is executed with root privileges @@ -251,6 +252,7 @@ containerd: # Probe scripts to check readiness. # The scripts run in user mode. They must start with a '#!' line. # The scripts can use the following template variables: {{.Home}}, {{.UID}}, {{.User}}, and {{.Param.Key}} +# They can be provided inline (as field `script`), or in external files (as field `path`). # 🟢 Builtin default: null # probes: # # Only `readiness` probes are supported right now. diff --git a/pkg/cidata/cidata.go b/pkg/cidata/cidata.go index 80d8d6ad953..79510e5a230 100644 --- a/pkg/cidata/cidata.go +++ b/pkg/cidata/cidata.go @@ -311,7 +311,10 @@ func GenerateISO9660(instDir, name string, instConfig *limayaml.LimaYAML, udpDNS args.CACerts.Trusted = append(args.CACerts.Trusted, cert) } - args.BootCmds = getBootCmds(instConfig.Provision) + args.BootCmds, err = getBootCmds(instConfig.Provision) + if err != nil { + return err + } for _, f := range instConfig.Provision { if f.Mode == limayaml.ProvisionModeDependency && *f.SkipDefaultDependencyResolution { @@ -329,11 +332,19 @@ func GenerateISO9660(instDir, name string, instConfig *limayaml.LimaYAML, udpDNS } for i, f := range instConfig.Provision { + script := f.Script + if f.Path != "" { + b, err := os.ReadFile(f.Path) + if err != nil { + return err + } + script = string(b) + } switch f.Mode { case limayaml.ProvisionModeSystem, limayaml.ProvisionModeUser, limayaml.ProvisionModeDependency: layout = append(layout, iso9660util.Entry{ Path: fmt.Sprintf("provision.%s/%08d", f.Mode, i), - Reader: strings.NewReader(f.Script), + Reader: strings.NewReader(script), }) case limayaml.ProvisionModeBoot: continue @@ -406,21 +417,30 @@ func getCert(content string) Cert { return Cert{Lines: lines} } -func getBootCmds(p []limayaml.Provision) []BootCmds { +func getBootCmds(p []limayaml.Provision) ([]BootCmds, error) { var bootCmds []BootCmds for _, f := range p { - if f.Mode == limayaml.ProvisionModeBoot { - lines := []string{} - for _, line := range strings.Split(f.Script, "\n") { - if line == "" { - continue - } - lines = append(lines, strings.TrimSpace(line)) + if f.Mode != limayaml.ProvisionModeBoot { + continue + } + script := f.Script + if f.Path != "" { + b, err := os.ReadFile(f.Path) + if err != nil { + return nil, err + } + script = string(b) + } + lines := []string{} + for _, line := range strings.Split(script, "\n") { + if line == "" { + continue } - bootCmds = append(bootCmds, BootCmds{Lines: lines}) + lines = append(lines, strings.TrimSpace(line)) } + bootCmds = append(bootCmds, BootCmds{Lines: lines}) } - return bootCmds + return bootCmds, nil } func diskDeviceNameFromOrder(order int) string { diff --git a/pkg/hostagent/requirements.go b/pkg/hostagent/requirements.go index 2f290768499..983b35493a5 100644 --- a/pkg/hostagent/requirements.go +++ b/pkg/hostagent/requirements.go @@ -3,6 +3,7 @@ package hostagent import ( "errors" "fmt" + "os" "time" "github.com/lima-vm/lima/pkg/limayaml" @@ -201,10 +202,19 @@ Also see "/var/log/cloud-init-output.log" in the guest. }) } for _, probe := range a.instConfig.Probes { + script := probe.Script + if probe.Path != "" { + b, err := os.ReadFile(probe.Path) + if err != nil { + logrus.WithError(err).Errorf("failed to read script %q", probe.Path) + continue + } + script = string(b) + } if probe.Mode == limayaml.ProbeModeReadiness { req = append(req, requirement{ description: probe.Description, - script: probe.Script, + script: script, debugHint: probe.Hint, }) } diff --git a/pkg/limayaml/limayaml.go b/pkg/limayaml/limayaml.go index ee02018e3ff..7f8984a52a2 100644 --- a/pkg/limayaml/limayaml.go +++ b/pkg/limayaml/limayaml.go @@ -198,6 +198,7 @@ type Provision struct { Mode ProvisionMode `yaml:"mode" json:"mode"` // default: "system" SkipDefaultDependencyResolution *bool `yaml:"skipDefaultDependencyResolution,omitempty" json:"skipDefaultDependencyResolution,omitempty"` Script string `yaml:"script" json:"script"` + Path string `yaml:"path,omitempty" json:"path,omitempty"` Playbook string `yaml:"playbook,omitempty" json:"playbook,omitempty"` } @@ -217,6 +218,7 @@ type Probe struct { Mode ProbeMode // default: "readiness" Description string Script string + Path string `yaml:",omitempty" json:",omitempty"` Hint string } diff --git a/pkg/limayaml/validate.go b/pkg/limayaml/validate.go index 65a6d4bef16..4feec0bbc71 100644 --- a/pkg/limayaml/validate.go +++ b/pkg/limayaml/validate.go @@ -208,6 +208,14 @@ func Validate(y *LimaYAML, warn bool) error { return fmt.Errorf("field `provision[%d].mode` must one of %q, %q, %q, %q, or %q", i, ProvisionModeSystem, ProvisionModeUser, ProvisionModeBoot, ProvisionModeDependency, ProvisionModeAnsible) } + if p.Path != "" { + if p.Script != "" { + return fmt.Errorf("field `provision[%d].script must be empty if path is set", i) + } + if _, err := os.Stat(p.Path); err != nil { + return fmt.Errorf("field `provision[%d].path` refers to an inaccessible path: %q: %w", i, p.Path, err) + } + } if strings.Contains(p.Script, "LIMA_CIDATA") { logrus.Warn("provisioning scripts should not reference the LIMA_CIDATA variables") } @@ -232,6 +240,14 @@ func Validate(y *LimaYAML, warn bool) error { default: return fmt.Errorf("field `probe[%d].mode` can only be %q", i, ProbeModeReadiness) } + if p.Path != "" { + if p.Script != "" { + return fmt.Errorf("field `probe[%d].script must be empty if path is set", i) + } + if _, err := os.Stat(p.Path); err != nil { + return fmt.Errorf("field `probe[%d].path` refers to an inaccessible path: %q: %w", i, p.Path, err) + } + } } for i, rule := range y.PortForwards { field := fmt.Sprintf("portForwards[%d]", i)