Skip to content

Commit

Permalink
Merge pull request #2498 from norio-nomura/add-param-field-to-limayaml
Browse files Browse the repository at this point in the history
limayaml: add a `param` field for defining variables used to customize scripts and other elements within `lima.yaml`.
  • Loading branch information
AkihiroSuda authored Jul 27, 2024
2 parents be93284 + 6331b56 commit 18e18af
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 41 deletions.
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}}
# 🟢 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:
# 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

0 comments on commit 18e18af

Please sign in to comment.