Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

limayaml: add a param field for defining variables used to customize scripts and other elements within lima.yaml. #2498

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions examples/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,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}}, and {{.User}}
# The scripts can use the following template variables: {{.Home}}, {{.UID}}, {{.User}}, and {{.Param.Key}}
norio-nomura marked this conversation as resolved.
Show resolved Hide resolved
# 🟢 Builtin default: null
# provision:
# # `system` is executed with root privileges
Expand Down Expand Up @@ -235,6 +235,7 @@ containerd:
# playbook: playbook.yaml

# Probe scripts to check readiness.
# The scripts can use the following template variables: {{.Home}}, {{.UID}}, {{.User}}, and {{.Param.Key}}
# 🟢 Builtin default: null
# probes:
# # Only `readiness` probes are supported right now.
Expand Down Expand Up @@ -377,8 +378,8 @@ networks:
# - guestSocket: "/run/user/{{.UID}}/my.sock"
# hostSocket: mysocket
# # default: reverse: false
# # "guestSocket" can include these template variables: {{.Home}}, {{.UID}}, and {{.User}}.
# # "hostSocket" can include {{.Home}}, {{.Dir}}, {{.Name}}, {{.UID}}, and {{.User}}.
# # "guestSocket" can include these template variables: {{.Home}}, {{.UID}}, {{.User}}, and {{.Param.Key}}.
# # "hostSocket" can include {{.Home}}, {{.Dir}}, {{.Name}}, {{.UID}}, {{.User}}, and {{.Param.Key}}.
# # "reverse" can only be used for unix sockets right now, not for tcp sockets.
# # Put sockets into "{{.Dir}}/sock" to avoid collision with Lima internal sockets!
# # Sockets can also be forwarded to ports and vice versa, but not to/from a range of ports.
Expand All @@ -397,8 +398,8 @@ networks:
# - guest: "/etc/myconfig.cfg"
# host: "{{.Dir}}/copied-from-guest/myconfig"
# # deleteOnStop: false
# # "guest" can include these template variables: {{.Home}}, {{.UID}}, and {{.User}}.
# # "host" can include {{.Home}}, {{.Dir}}, {{.Name}}, {{.UID}}, and {{.User}}.
# # "guest" can include these template variables: {{.Home}}, {{.UID}}, {{.User}}, and {{.Param.Key}}.
# # "host" can include {{.Home}}, {{.Dir}}, {{.Name}}, {{.UID}}, {{.User}}, and {{.Param.Key}}.
# # "deleteOnStop" will delete the file from the host when the instance is stopped.

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

# Defines variables used for customizing the functionality.
# These variables can be referenced as {{.Param.Key}} in lima.yaml.
# param:
jandubois marked this conversation as resolved.
Show resolved Hide resolved
# Key: value

# Lima will override the proxy environment variables with values from the current process
# environment (the environment in effect when you run `limactl start`). It will automatically
# replace the strings "localhost" and "127.0.0.1" with the host gateway address from inside
Expand Down
4 changes: 2 additions & 2 deletions pkg/hostagent/hostagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,13 @@ func New(instName string, stdout io.Writer, signalCh chan os.Signal, opts ...Opt
// Block ports 22 and sshLocalPort on all IPs
for _, port := range []int{sshGuestPort, sshLocalPort} {
rule := limayaml.PortForward{GuestIP: net.IPv4zero, GuestPort: port, Ignore: true}
limayaml.FillPortForwardDefaults(&rule, inst.Dir)
limayaml.FillPortForwardDefaults(&rule, inst.Dir, inst.Param)
rules = append(rules, rule)
}
rules = append(rules, y.PortForwards...)
// Default forwards for all non-privileged ports from "127.0.0.1" and "::1"
rule := limayaml.PortForward{}
limayaml.FillPortForwardDefaults(&rule, inst.Dir)
limayaml.FillPortForwardDefaults(&rule, inst.Dir, inst.Param)
rules = append(rules, rule)

limaDriver := driverutil.CreateTargetDriverInstance(&driver.BaseDriver{
Expand Down
63 changes: 41 additions & 22 deletions pkg/limayaml/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ func FillDefault(y, d, o *LimaYAML, filePath string) {
if provision.Mode == ProvisionModeDependency && provision.SkipDefaultDependencyResolution == nil {
provision.SkipDefaultDependencyResolution = ptr.Of(false)
}
if out, err := executeGuestTemplate(provision.Script, instDir); err == nil {
if out, err := executeGuestTemplate(provision.Script, instDir, y.Param); err == nil {
provision.Script = out.String()
} else {
logrus.WithError(err).Warnf("Couldn't process provisioning script %q as a template", provision.Script)
Expand Down Expand Up @@ -460,17 +460,22 @@ func FillDefault(y, d, o *LimaYAML, filePath string) {
if probe.Description == "" {
probe.Description = fmt.Sprintf("user probe %d/%d", i+1, len(y.Probes))
}
if out, err := executeGuestTemplate(probe.Script, instDir, y.Param); err == nil {
probe.Script = out.String()
} else {
logrus.WithError(err).Warnf("Couldn't process probing script %q as a template", probe.Script)
}
}

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

y.CopyToHost = append(append(o.CopyToHost, y.CopyToHost...), d.CopyToHost...)
for i := range y.CopyToHost {
FillCopyToHostDefaults(&y.CopyToHost[i], instDir)
FillCopyToHostDefaults(&y.CopyToHost[i], instDir, y.Param)
}

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

param := make(map[string]string)
for k, v := range d.Param {
param[k] = v
}
for k, v := range y.Param {
param[k] = v
}
for k, v := range o.Param {
param[k] = v
}
y.Param = param

if y.CACertificates.RemoveDefaults == nil {
y.CACertificates.RemoveDefaults = d.CACertificates.RemoveDefaults
}
Expand Down Expand Up @@ -735,15 +752,16 @@ func fixUpForPlainMode(y *LimaYAML) {
y.TimeZone = ptr.Of("")
}

func executeGuestTemplate(format, instDir string) (bytes.Buffer, error) {
func executeGuestTemplate(format, instDir string, param map[string]string) (bytes.Buffer, error) {
tmpl, err := template.New("").Parse(format)
if err == nil {
user, _ := osutil.LimaUser(false)
data := map[string]string{
"Home": fmt.Sprintf("/home/%s.linux", user.Username),
"Name": filepath.Base(instDir),
"UID": user.Uid,
"User": user.Username,
data := map[string]interface{}{
"Home": fmt.Sprintf("/home/%s.linux", user.Username),
"Name": filepath.Base(instDir),
"UID": user.Uid,
"User": user.Username,
"Param": param,
}
var out bytes.Buffer
if err := tmpl.Execute(&out, data); err == nil {
Expand All @@ -753,18 +771,19 @@ func executeGuestTemplate(format, instDir string) (bytes.Buffer, error) {
return bytes.Buffer{}, err
}

func executeHostTemplate(format, instDir string) (bytes.Buffer, error) {
func executeHostTemplate(format, instDir string, param map[string]string) (bytes.Buffer, error) {
tmpl, err := template.New("").Parse(format)
if err == nil {
user, _ := osutil.LimaUser(false)
home, _ := os.UserHomeDir()
limaHome, _ := dirnames.LimaDir()
data := map[string]string{
"Dir": instDir,
"Home": home,
"Name": filepath.Base(instDir),
"UID": user.Uid,
"User": user.Username,
data := map[string]interface{}{
"Dir": instDir,
"Home": home,
"Name": filepath.Base(instDir),
"UID": user.Uid,
"User": user.Username,
"Param": param,

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

func FillPortForwardDefaults(rule *PortForward, instDir string) {
func FillPortForwardDefaults(rule *PortForward, instDir string, param map[string]string) {
if rule.Proto == "" {
rule.Proto = TCP
}
Expand Down Expand Up @@ -809,14 +828,14 @@ func FillPortForwardDefaults(rule *PortForward, instDir string) {
}
}
if rule.GuestSocket != "" {
if out, err := executeGuestTemplate(rule.GuestSocket, instDir); err == nil {
if out, err := executeGuestTemplate(rule.GuestSocket, instDir, param); err == nil {
rule.GuestSocket = out.String()
} else {
logrus.WithError(err).Warnf("Couldn't process guestSocket %q as a template", rule.GuestSocket)
}
}
if rule.HostSocket != "" {
if out, err := executeHostTemplate(rule.HostSocket, instDir); err == nil {
if out, err := executeHostTemplate(rule.HostSocket, instDir, param); err == nil {
rule.HostSocket = out.String()
} else {
logrus.WithError(err).Warnf("Couldn't process hostSocket %q as a template", rule.HostSocket)
Expand All @@ -827,16 +846,16 @@ func FillPortForwardDefaults(rule *PortForward, instDir string) {
}
}

func FillCopyToHostDefaults(rule *CopyToHost, instDir string) {
func FillCopyToHostDefaults(rule *CopyToHost, instDir string, param map[string]string) {
if rule.GuestFile != "" {
if out, err := executeGuestTemplate(rule.GuestFile, instDir); err == nil {
if out, err := executeGuestTemplate(rule.GuestFile, instDir, param); err == nil {
rule.GuestFile = out.String()
} else {
logrus.WithError(err).Warnf("Couldn't process guest %q as a template", rule.GuestFile)
}
}
if rule.HostFile != "" {
if out, err := executeHostTemplate(rule.HostFile, instDir); err == nil {
if out, err := executeHostTemplate(rule.HostFile, instDir, param); err == nil {
rule.HostFile = out.String()
} else {
logrus.WithError(err).Warnf("Couldn't process host %q as a template", rule.HostFile)
Expand Down
39 changes: 29 additions & 10 deletions pkg/limayaml/defaults_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,10 @@ func TestFillDefault(t *testing.T) {
},
MountType: ptr.Of(NINEP),
Provision: []Provision{
{Script: "#!/bin/true"},
{Script: "#!/bin/true # {{.Param.ONE}}"},
},
Probes: []Probe{
{Script: "#!/bin/false"},
{Script: "#!/bin/false # {{.Param.ONE}}"},
},
Networks: []Network{
{Lima: "shared"},
Expand All @@ -145,19 +145,22 @@ func TestFillDefault(t *testing.T) {
{GuestPort: 80},
{GuestPort: 8080, HostPort: 8888},
{
GuestSocket: "{{.Home}} | {{.UID}} | {{.User}}",
HostSocket: "{{.Home}} | {{.Dir}} | {{.Name}} | {{.UID}} | {{.User}}",
GuestSocket: "{{.Home}} | {{.UID}} | {{.User}} | {{.Param.ONE}}",
HostSocket: "{{.Home}} | {{.Dir}} | {{.Name}} | {{.UID}} | {{.User}} | {{.Param.ONE}}",
},
},
CopyToHost: []CopyToHost{
{
GuestFile: "{{.Home}} | {{.UID}} | {{.User}}",
HostFile: "{{.Home}} | {{.Dir}} | {{.Name}} | {{.UID}} | {{.User}}",
GuestFile: "{{.Home}} | {{.UID}} | {{.User}} | {{.Param.ONE}}",
HostFile: "{{.Home}} | {{.Dir}} | {{.Name}} | {{.UID}} | {{.User}} | {{.Param.ONE}}",
},
},
Env: map[string]string{
"ONE": "Eins",
},
Param: map[string]string{
"ONE": "Eins",
},
CACertificates: CACertificates{
Files: []string{"ca.crt"},
Certs: []string{
Expand Down Expand Up @@ -212,10 +215,12 @@ func TestFillDefault(t *testing.T) {

expect.Provision = y.Provision
expect.Provision[0].Mode = ProvisionModeSystem
expect.Provision[0].Script = "#!/bin/true # Eins"

expect.Probes = y.Probes
expect.Probes[0].Mode = ProbeModeReadiness
expect.Probes[0].Description = "user probe 1/1"
expect.Probes[0].Script = "#!/bin/true # Eins"

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

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

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

expect.Env = y.Env

expect.Param = y.Param

expect.CACertificates = CACertificates{
RemoveDefaults: ptr.Of(false),
Files: []string{"ca.crt"},
Expand Down Expand Up @@ -380,6 +387,10 @@ func TestFillDefault(t *testing.T) {
"ONE": "one",
"TWO": "two",
},
Param: map[string]string{
"ONE": "one",
"TWO": "two",
},
CACertificates: CACertificates{
RemoveDefaults: ptr.Of(true),
Certs: []string{
Expand Down Expand Up @@ -459,6 +470,8 @@ func TestFillDefault(t *testing.T) {
// "TWO" does not exist in filledDefaults.Env, so is set from d.Env
expect.Env["TWO"] = d.Env["TWO"]

expect.Param["TWO"] = d.Param["TWO"]

FillDefault(&y, &d, &LimaYAML{}, filePath)
assert.DeepEqual(t, &y, &expect, opts...)

Expand Down Expand Up @@ -584,6 +597,10 @@ func TestFillDefault(t *testing.T) {
"TWO": "deux",
"THREE": "trois",
},
Param: map[string]string{
"TWO": "deux",
"THREE": "trois",
},
CACertificates: CACertificates{
RemoveDefaults: ptr.Of(true),
},
Expand Down Expand Up @@ -632,6 +649,8 @@ func TestFillDefault(t *testing.T) {
// ONE remains from filledDefaults.Env; the rest are set from o
expect.Env["ONE"] = y.Env["ONE"]

expect.Param["ONE"] = y.Param["ONE"]

expect.CACertificates.RemoveDefaults = ptr.Of(true)
expect.CACertificates.Files = []string{"ca.crt"}
expect.CACertificates.Certs = []string{
Expand Down
1 change: 1 addition & 0 deletions pkg/limayaml/limayaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type LimaYAML struct {
Networks []Network `yaml:"networks,omitempty" json:"networks,omitempty"`
// `network` was deprecated in Lima v0.7.0, removed in Lima v0.14.0. Use `networks` instead.
Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
Param map[string]string `yaml:"param,omitempty" json:"param,omitempty"`
DNS []net.IP `yaml:"dns,omitempty" json:"dns,omitempty"`
HostResolver HostResolver `yaml:"hostResolver,omitempty" json:"hostResolver,omitempty"`
// `useHostResolver` was deprecated in Lima v0.8.1, removed in Lima v0.14.0. Use `hostResolver.enabled` instead.
Expand Down
5 changes: 5 additions & 0 deletions pkg/limayaml/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ func Load(b []byte, filePath string) (*LimaYAML, error) {
return nil, err
}

// It should be called before the `y` parameter is passed to FillDefault() that execute template.
if err := ValidateParamIsUsed(&y); err != nil {
return nil, err
}

FillDefault(&y, &d, &o, filePath)
return &y, nil
}
Loading
Loading