From d22d6434dd1a008dab81195094851754d9e80f6e Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Tue, 29 Oct 2024 02:06:46 +0900 Subject: [PATCH] Allow "." and "_" in instance names, disallow them in hostnames Now `limactl create template://ubuntu-24.10` and `limactl create ./ubuntu-24.10.yaml` creates an instance named `ubuntu-24.10` with the hostname `lima-ubuntu-24-10`. Eventually the hostname should be customizable via the YAML, but it is beyond the scope of this commit. Fix issue 2813 Alternative to PR 2815 Signed-off-by: Akihiro Suda --- cmd/limactl/guessarg/guessarg.go | 3 ++- cmd/limactl/show-ssh.go | 4 ++-- cmd/limactl/tunnel.go | 2 +- pkg/cidata/cidata.TEMPLATE.d/meta-data | 2 +- pkg/cidata/cidata.go | 2 ++ pkg/cidata/template.go | 1 + pkg/hostagent/hostagent.go | 4 +++- pkg/identifierutil/identifierutil.go | 9 +++++++++ pkg/identifierutil/identifierutil_test.go | 13 +++++++++++++ pkg/instance/ansible.go | 2 +- pkg/instance/start.go | 2 +- pkg/limayaml/defaults.go | 20 ++++++++++++-------- pkg/networks/usernet/client.go | 2 +- pkg/sshutil/format.go | 4 +++- pkg/store/instance.go | 11 ++++++++--- templates/default.yaml | 10 +++++----- 16 files changed, 65 insertions(+), 26 deletions(-) create mode 100644 pkg/identifierutil/identifierutil.go create mode 100644 pkg/identifierutil/identifierutil_test.go diff --git a/cmd/limactl/guessarg/guessarg.go b/cmd/limactl/guessarg/guessarg.go index d6aa725ad93..b0261fc411c 100644 --- a/cmd/limactl/guessarg/guessarg.go +++ b/cmd/limactl/guessarg/guessarg.go @@ -56,7 +56,8 @@ func InstNameFromURL(urlStr string) (string, error) { func InstNameFromYAMLPath(yamlPath string) (string, error) { s := strings.ToLower(filepath.Base(yamlPath)) s = strings.TrimSuffix(strings.TrimSuffix(s, ".yml"), ".yaml") - s = strings.ReplaceAll(s, ".", "-") + // "." is allowed in instance names, but replaced to "-" for hostnames. + // e.g., yaml: "ubuntu-24.04.yaml" , instance name: "ubuntu-24.04", hostname: "lima-ubuntu-24-04" if err := identifiers.Validate(s); err != nil { return "", fmt.Errorf("filename %q is invalid: %w", yamlPath, err) } diff --git a/cmd/limactl/show-ssh.go b/cmd/limactl/show-ssh.go index 757639d3a39..63e203c5c14 100644 --- a/cmd/limactl/show-ssh.go +++ b/cmd/limactl/show-ssh.go @@ -86,8 +86,8 @@ func showSSHAction(cmd *cobra.Command, args []string) error { } return err } - logrus.Warnf("`limactl show-ssh` is deprecated. Instead, use `ssh -F %s lima-%s`.", - filepath.Join(inst.Dir, filenames.SSHConfig), inst.Name) + logrus.Warnf("`limactl show-ssh` is deprecated. Instead, use `ssh -F %s %s`.", + filepath.Join(inst.Dir, filenames.SSHConfig), inst.Hostname) opts, err := sshutil.SSHOpts( inst.Dir, *inst.Config.SSH.LoadDotSSHPubKeys, diff --git a/cmd/limactl/tunnel.go b/cmd/limactl/tunnel.go index 40ac30fe412..c84c9b25ed5 100644 --- a/cmd/limactl/tunnel.go +++ b/cmd/limactl/tunnel.go @@ -146,7 +146,7 @@ func tunnelAction(cmd *cobra.Command, args []string) error { default: fmt.Fprintf(stdout, "Set `ALL_PROXY=socks5h://127.0.0.1:%d`, etc.\n", port) } - fmt.Fprintf(stdout, "The instance can be connected from the host as via a web browser.\n", inst.Name) + fmt.Fprintf(stdout, "The instance can be connected from the host as via a web browser.\n", inst.Hostname) // TODO: show the port in `limactl list --json` ? // TODO: add `--stop` flag to shut down the tunnel diff --git a/pkg/cidata/cidata.TEMPLATE.d/meta-data b/pkg/cidata/cidata.TEMPLATE.d/meta-data index 6d34c47b2bf..582470d73d8 100644 --- a/pkg/cidata/cidata.TEMPLATE.d/meta-data +++ b/pkg/cidata/cidata.TEMPLATE.d/meta-data @@ -1,2 +1,2 @@ instance-id: {{.IID}} -local-hostname: lima-{{.Name}} +local-hostname: {{.Hostname}} diff --git a/pkg/cidata/cidata.go b/pkg/cidata/cidata.go index 99d78a374e2..633581d5fc5 100644 --- a/pkg/cidata/cidata.go +++ b/pkg/cidata/cidata.go @@ -17,6 +17,7 @@ import ( "github.com/docker/go-units" "github.com/lima-vm/lima/pkg/debugutil" + "github.com/lima-vm/lima/pkg/identifierutil" "github.com/lima-vm/lima/pkg/iso9660util" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/localpathutil" @@ -128,6 +129,7 @@ func templateArgs(bootScripts bool, instDir, name string, instConfig *limayaml.L Debug: debugutil.Debug, BootScripts: bootScripts, Name: name, + Hostname: identifierutil.HostnameFromInstName(name), // TODO: support customization User: u.Username, UID: uid, Home: fmt.Sprintf("/home/%s.linux", u.Username), diff --git a/pkg/cidata/template.go b/pkg/cidata/template.go index a39e0664c47..ff83386da23 100644 --- a/pkg/cidata/template.go +++ b/pkg/cidata/template.go @@ -56,6 +56,7 @@ type Disk struct { type TemplateArgs struct { Debug bool Name string // instance name + Hostname string // instance hostname IID string // instance id User string // user name Home string // home directory diff --git a/pkg/hostagent/hostagent.go b/pkg/hostagent/hostagent.go index db9f84814e6..0706b6cc730 100644 --- a/pkg/hostagent/hostagent.go +++ b/pkg/hostagent/hostagent.go @@ -28,6 +28,7 @@ import ( hostagentapi "github.com/lima-vm/lima/pkg/hostagent/api" "github.com/lima-vm/lima/pkg/hostagent/dns" "github.com/lima-vm/lima/pkg/hostagent/events" + "github.com/lima-vm/lima/pkg/identifierutil" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/networks" "github.com/lima-vm/lima/pkg/osutil" @@ -288,7 +289,8 @@ func (a *HostAgent) Run(ctx context.Context) error { if limayaml.FirstUsernetIndex(a.instConfig) == -1 && *a.instConfig.HostResolver.Enabled { hosts := a.instConfig.HostResolver.Hosts hosts["host.lima.internal"] = networks.SlirpGateway - hosts[fmt.Sprintf("lima-%s", a.instName)] = networks.SlirpIPAddress + hostname := identifierutil.HostnameFromInstName(a.instName) // TODO: support customization + hosts[hostname] = networks.SlirpIPAddress srvOpts := dns.ServerOptions{ UDPPort: a.udpDNSLocalPort, TCPPort: a.tcpDNSLocalPort, diff --git a/pkg/identifierutil/identifierutil.go b/pkg/identifierutil/identifierutil.go new file mode 100644 index 00000000000..e9a23f0d018 --- /dev/null +++ b/pkg/identifierutil/identifierutil.go @@ -0,0 +1,9 @@ +package identifierutil + +import "strings" + +func HostnameFromInstName(instName string) string { + s := strings.ReplaceAll(instName, ".", "-") + s = strings.ReplaceAll(s, "_", "-") + return "lima-" + s +} diff --git a/pkg/identifierutil/identifierutil_test.go b/pkg/identifierutil/identifierutil_test.go new file mode 100644 index 00000000000..15286bfa5c6 --- /dev/null +++ b/pkg/identifierutil/identifierutil_test.go @@ -0,0 +1,13 @@ +package identifierutil + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestHostnameFromInstName(t *testing.T) { + assert.Equal(t, "lima-default", HostnameFromInstName("default")) + assert.Equal(t, "lima-ubuntu-24-04", HostnameFromInstName("ubuntu-24.04")) + assert.Equal(t, "lima-foo-bar-baz", HostnameFromInstName("foo_bar.baz")) +} diff --git a/pkg/instance/ansible.go b/pkg/instance/ansible.go index a4cf236a562..53460fa747f 100644 --- a/pkg/instance/ansible.go +++ b/pkg/instance/ansible.go @@ -41,7 +41,7 @@ func runAnsiblePlaybook(ctx context.Context, inst *store.Instance, playbook stri func createAnsibleInventory(inst *store.Instance) (string, error) { vars := map[string]interface{}{ "ansible_connection": "ssh", - "ansible_host": "lima-" + inst.Name, + "ansible_host": inst.Hostname, "ansible_ssh_common_args": "-F " + inst.SSHConfigFile, } hosts := map[string]interface{}{ diff --git a/pkg/instance/start.go b/pkg/instance/start.go index 4ccd2d93a77..f2b1c105b79 100644 --- a/pkg/instance/start.go +++ b/pkg/instance/start.go @@ -308,7 +308,7 @@ func watchHostAgentEvents(ctx context.Context, inst *store.Instance, haStdoutPat return true } if *inst.Config.Plain { - logrus.Infof("READY. Run `ssh -F %q lima-%s` to open the shell.", inst.SSHConfigFile, inst.Name) + logrus.Infof("READY. Run `ssh -F %q %s` to open the shell.", inst.SSHConfigFile, inst.Hostname) } else { logrus.Infof("READY. Run `%s` to open the shell.", LimactlShellCmd(inst.Name)) } diff --git a/pkg/limayaml/defaults.go b/pkg/limayaml/defaults.go index 18305a9f980..3d2a89a3296 100644 --- a/pkg/limayaml/defaults.go +++ b/pkg/limayaml/defaults.go @@ -22,6 +22,7 @@ import ( "github.com/sirupsen/logrus" "golang.org/x/sys/cpu" + "github.com/lima-vm/lima/pkg/identifierutil" "github.com/lima-vm/lima/pkg/networks" "github.com/lima-vm/lima/pkg/osutil" "github.com/lima-vm/lima/pkg/ptr" @@ -814,12 +815,14 @@ func executeGuestTemplate(format, instDir string, param map[string]string) (byte tmpl, err := template.New("").Parse(format) if err == nil { user, _ := osutil.LimaUser(false) + name := filepath.Base(instDir) data := map[string]interface{}{ - "Home": fmt.Sprintf("/home/%s.linux", user.Username), - "Name": filepath.Base(instDir), - "UID": user.Uid, - "User": user.Username, - "Param": param, + "Home": fmt.Sprintf("/home/%s.linux", user.Username), + "Name": name, + "Hostname": identifierutil.HostnameFromInstName(name), // TODO: support customization + "UID": user.Uid, + "User": user.Username, + "Param": param, } var out bytes.Buffer if err := tmpl.Execute(&out, data); err == nil { @@ -836,9 +839,10 @@ func executeHostTemplate(format, instDir string, param map[string]string) (bytes home, _ := os.UserHomeDir() limaHome, _ := dirnames.LimaDir() data := map[string]interface{}{ - "Dir": instDir, - "Home": home, - "Name": filepath.Base(instDir), + "Dir": instDir, + "Home": home, + "Name": filepath.Base(instDir), + // TODO: add hostname fields for the host and the guest "UID": user.Uid, "User": user.Username, "Param": param, diff --git a/pkg/networks/usernet/client.go b/pkg/networks/usernet/client.go index d6b8cb91cd4..1d6b4bf90d6 100644 --- a/pkg/networks/usernet/client.go +++ b/pkg/networks/usernet/client.go @@ -39,7 +39,7 @@ func (c *Client) ConfigureDriver(ctx context.Context, driver *driver.BaseDriver) return err } hosts := driver.Instance.Config.HostResolver.Hosts - hosts[fmt.Sprintf("lima-%s.internal", driver.Instance.Name)] = ipAddress + hosts[fmt.Sprintf("%s.internal", driver.Instance.Hostname)] = ipAddress err = c.AddDNSHosts(hosts) return err } diff --git a/pkg/sshutil/format.go b/pkg/sshutil/format.go index 4ea4ae5329e..e027257b48b 100644 --- a/pkg/sshutil/format.go +++ b/pkg/sshutil/format.go @@ -4,6 +4,8 @@ import ( "fmt" "io" "strings" + + "github.com/lima-vm/lima/pkg/identifierutil" ) // FormatT specifies the format type. @@ -57,7 +59,7 @@ func quoteOption(o string) string { // Format formats the ssh options. func Format(w io.Writer, instName string, format FormatT, opts []string) error { - fakeHostname := "lima-" + instName // corresponds to the default guest hostname + fakeHostname := identifierutil.HostnameFromInstName(instName) // TODO: support customization switch format { case FormatCmd: args := []string{"ssh"} diff --git a/pkg/store/instance.go b/pkg/store/instance.go index 8c4f3302a39..7daf4537e1e 100644 --- a/pkg/store/instance.go +++ b/pkg/store/instance.go @@ -18,6 +18,7 @@ import ( "github.com/docker/go-units" hostagentclient "github.com/lima-vm/lima/pkg/hostagent/api/client" + "github.com/lima-vm/lima/pkg/identifierutil" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/store/dirnames" "github.com/lima-vm/lima/pkg/store/filenames" @@ -38,7 +39,9 @@ const ( ) type Instance struct { - Name string `json:"name"` + Name string `json:"name"` + // Hostname, not HostName (corresponds to SSH's naming convention) + Hostname string `json:"hostname"` Status Status `json:"status"` Dir string `json:"dir"` VMType limayaml.VMType `json:"vmType"` @@ -66,8 +69,10 @@ type Instance struct { // Other errors are returned as *Instance.Errors. func Inspect(instName string) (*Instance, error) { inst := &Instance{ - Name: instName, - Status: StatusUnknown, + Name: instName, + // TODO: support customizing hostname + Hostname: identifierutil.HostnameFromInstName(instName), + Status: StatusUnknown, } // InstanceDir validates the instName but does not check whether the instance exists instDir, err := InstanceDir(instName) diff --git a/templates/default.yaml b/templates/default.yaml index 316bcc94ce9..31f13b820e7 100644 --- a/templates/default.yaml +++ b/templates/default.yaml @@ -56,7 +56,7 @@ disk: null # Expose host directories to the guest, the mount point might be accessible from all UIDs in the guest # "location" can use these template variables: {{.Home}}, {{.Dir}}, {{.Name}}, {{.UID}}, {{.User}}, and {{.Param.Key}}. -# "mountPoint" can use these template variables: {{.Home}}, {{.Name}}, {{.UID}}, {{.User}}, and {{.Param.Key}} +# "mountPoint" can use these template variables: {{.Home}}, {{.Name}}, {{.Hostname}}, {{.UID}}, {{.User}}, and {{.Param.Key}} # 🟢 Builtin default: [] (Mount nothing) # 🔵 This file: Mount the home as read-only, /tmp/lima as writable mounts: @@ -205,7 +205,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}} +# The scripts can use the following template variables: {{.Home}}, {{.Name}}, {{.Hostname}}, {{.UID}}, {{.User}}, and {{.Param.Key}} # 🟢 Builtin default: [] # provision: # # `system` is executed with root privileges @@ -248,7 +248,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}} +# The scripts can use the following template variables: {{.Home}}, {{.Name}}, {{.Hostname}}, {{.UID}}, {{.User}}, and {{.Param.Key}} # 🟢 Builtin default: [] # probes: # # Only `readiness` probes are supported right now. @@ -407,7 +407,7 @@ networks: # - guestSocket: "/run/user/{{.UID}}/my.sock" # hostSocket: mysocket # # default: reverse: false -# # "guestSocket" can include these template variables: {{.Home}}, {{.UID}}, {{.User}}, and {{.Param.Key}}. +# # "guestSocket" can include these template variables: {{.Home}}, {{.Name}}, {{.Hostname}}, {{.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! @@ -427,7 +427,7 @@ networks: # - guest: "/etc/myconfig.cfg" # host: "{{.Dir}}/copied-from-guest/myconfig" # # deleteOnStop: false -# # "guest" can include these template variables: {{.Home}}, {{.UID}}, {{.User}}, and {{.Param.Key}}. +# # "guest" can include these template variables: {{.Home}}, {{.Name}}, {{.Hostname}}, {{.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.