Repository: lima-vm/lima
Pull request #1104 (2022-10-14 19:08:41)

Convert built-in hosts to unambiguous domain names
This change ensures that user-defined host names are converted correctly to unambiguous domain names. Regardless if the user declares them as fully qualified or not we always convert them to fully qualified. 

Addresses: https://github.com/rancher-sandbox/rancher-desktop/issues/3163

Signed-off-by: Nino Kodabande <nkodabande@suse.com>


Patch:
diff --git a/Makefile b/Makefile
index d7cf12e1..acb054d6 100644
--- a/Makefile
+++ b/Makefile
@@ -31,7 +31,6 @@ binaries: clean \
 	_output/bin/lima$(bat) \
 	_output/bin/limactl$(exe) \
 	_output/bin/nerdctl.lima \
-	_output/bin/apptainer.lima \
 	_output/bin/docker.lima \
 	_output/bin/podman.lima \
 	_output/share/lima/lima-guestagent.Linux-x86_64 \
@@ -63,10 +62,6 @@ _output/bin/nerdctl.lima:
 	mkdir -p _output/bin
 	cp -a ./cmd/nerdctl.lima $@
 
-_output/bin/apptainer.lima: ./cmd/apptainer.lima
-	@mkdir -p _output/bin
-	cp -a $^ $@
-
 _output/bin/docker.lima: ./cmd/docker.lima
 	@mkdir -p _output/bin
 	cp -a $^ $@
@@ -107,7 +102,6 @@ install: uninstall
 	# Use tar rather than cp, for better symlink handling
 	( cd _output && tar c * | tar Cxv "$(DEST)" )
 	if [ "$(shell uname -s )" != "Linux" -a ! -e "$(DEST)/bin/nerdctl" ]; then ln -sf nerdctl.lima "$(DEST)/bin/nerdctl"; fi
-	if [ "$(shell uname -s )" != "Linux" -a ! -e "$(DEST)/bin/apptainer" ]; then ln -sf apptainer.lima "$(DEST)/bin/apptainer"; fi
 
 .PHONY: uninstall
 uninstall:
@@ -117,12 +111,10 @@ uninstall:
 		"$(DEST)/bin/lima$(bat)" \
 		"$(DEST)/bin/limactl$(exe)" \
 		"$(DEST)/bin/nerdctl.lima" \
-		"$(DEST)/bin/apptainer.lima" \
 		"$(DEST)/bin/docker.lima" \
 		"$(DEST)/bin/podman.lima" \
 		"$(DEST)/share/lima" "$(DEST)/share/doc/lima"
 	if [ "$$(readlink "$(DEST)/bin/nerdctl")" = "nerdctl.lima" ]; then rm "$(DEST)/bin/nerdctl"; fi
-	if [ "$$(readlink "$(DEST)/bin/apptainer")" = "apptainer.lima" ]; then rm "$(DEST)/bin/apptainer"; fi
 
 .PHONY: lint
 lint:
diff --git a/cmd/apptainer.lima b/cmd/apptainer.lima
deleted file mode 100755
index cd3cd9c6..00000000
--- a/cmd/apptainer.lima
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-set -eu
-: "${LIMA_INSTANCE:=apptainer}"
-
-if [ "$(limactl ls -q "$LIMA_INSTANCE" 2>/dev/null)" != "$LIMA_INSTANCE" ]; then
-  echo "instance \"$LIMA_INSTANCE\" does not exist, run \`limactl start --name=$LIMA_INSTANCE template://apptainer\` to create a new instance" >&2
-  exit 1
-fi
-export LIMA_INSTANCE
-exec lima APPTAINER_HOME="$HOME" apptainer "$@"
diff --git a/examples/alpine.yaml b/examples/alpine.yaml
index a10ebd07..8433cfb4 100644
--- a/examples/alpine.yaml
+++ b/examples/alpine.yaml
@@ -1,11 +1,11 @@
 # This example requires Lima v0.7.0 or later.
 images:
-- location: "https://github.com/lima-vm/alpine-lima/releases/download/v0.2.22/alpine-lima-std-3.16.0-x86_64.iso"
+- location: "https://github.com/lima-vm/alpine-lima/releases/download/v0.2.21/alpine-lima-std-3.16.0-x86_64.iso"
   arch: "x86_64"
-  digest: "sha512:b2e7dfd946eea14d52b3958b9a26d69b97f9c03ef4c68666717a75c54e4c192e6fc9a8827683746dd1d6a93160f302d77acf4aa5358b9cd99f6cd9039d55c950"
-- location: "https://github.com/lima-vm/alpine-lima/releases/download/v0.2.22/alpine-lima-std-3.16.0-aarch64.iso"
+  digest: "sha512:1929516c3deb8846b52f2404db1c5ed795a88fa3c7809b07df5365fc4223d83bce34a4bb31315a25325b62e4105433e7cb22327532da7705a807c9747eb427ea"
+- location: "https://github.com/lima-vm/alpine-lima/releases/download/v0.2.21/alpine-lima-std-3.16.0-aarch64.iso"
   arch: "aarch64"
-  digest: "sha512:5e218e863f1edb8d138d1fa2c63fc470c6c31427f71227fc6a7e6e53c7f06c339112102375e1a2fe7e008129fd4d48f66b9169cd1f60f9ac6a5670f8696fdff6"
+  digest: "sha512:9cf67ab5e8b0373acc1985048e571efcfa17523b1391cdd24a4adf45ec69b3b51bf7339215b383d64100b5f492bbcf673b0518947e2c2cc4e4e11df4c8946be3"
 
 mounts:
 - location: "~"
diff --git a/examples/apptainer-rootful.yaml b/examples/apptainer-rootful.yaml
index ff40b0f2..5386f4ae 100644
--- a/examples/apptainer-rootful.yaml
+++ b/examples/apptainer-rootful.yaml
@@ -31,9 +31,6 @@ provision:
     set -eux -o pipefail
     command -v apptainer >/dev/null 2>&1 && exit 0
     dnf -y install apptainer apptainer-suid
-    # See https://fedoraproject.org/wiki/Features/tmp-on-tmpfs
-    echo "APPTAINER_TMPDIR=/var/tmp" > /etc/profile.d/apptainer.sh
-    echo "export APPTAINER_TMPDIR" >> /etc/profile.d/apptainer.sh
 probes:
 - script: |
     #!/bin/bash
diff --git a/examples/apptainer.yaml b/examples/apptainer.yaml
index 62144383..503001c0 100644
--- a/examples/apptainer.yaml
+++ b/examples/apptainer.yaml
@@ -25,9 +25,6 @@ provision:
     set -eux -o pipefail
     command -v apptainer >/dev/null 2>&1 && exit 0
     dnf -y install apptainer
-    # See https://fedoraproject.org/wiki/Features/tmp-on-tmpfs
-    echo "APPTAINER_TMPDIR=/var/tmp" > /etc/profile.d/apptainer.sh
-    echo "export APPTAINER_TMPDIR" >> /etc/profile.d/apptainer.sh
 probes:
 - script: |
     #!/bin/bash
diff --git a/pkg/hostagent/dns/dns.go b/pkg/hostagent/dns/dns.go
index b0a06fc1..f30b6ddd 100644
--- a/pkg/hostagent/dns/dns.go
+++ b/pkg/hostagent/dns/dns.go
@@ -8,7 +8,6 @@ import (
 	"runtime"
 	"strings"
 
-	"github.com/lima-vm/lima/pkg/limayaml"
 	"github.com/miekg/dns"
 	"github.com/sirupsen/logrus"
 )
@@ -132,10 +131,11 @@ func NewHandler(opts HandlerOptions) (dns.Handler, error) {
 		hostToIP:     make(map[string]net.IP),
 	}
 	for host, address := range opts.StaticHosts {
+		cname := dns.CanonicalName(host)
 		if ip := net.ParseIP(address); ip != nil {
-			h.hostToIP[host] = ip
+			h.hostToIP[cname] = ip
 		} else {
-			h.cnameToHost[host] = limayaml.Cname(address)
+			h.cnameToHost[cname] = dns.CanonicalName(address)
 		}
 	}
 	return h, nil
diff --git a/pkg/hostagent/hostagent.go b/pkg/hostagent/hostagent.go
index 68010929..50bbb42f 100644
--- a/pkg/hostagent/hostagent.go
+++ b/pkg/hostagent/hostagent.go
@@ -307,8 +307,8 @@ func (a *HostAgent) Run(ctx context.Context) error {
 
 	if *a.y.HostResolver.Enabled {
 		hosts := a.y.HostResolver.Hosts
-		hosts["host.lima.internal."] = qemuconst.SlirpGateway
-		hosts[fmt.Sprintf("lima-%s.", a.instName)] = qemuconst.SlirpIPAddress
+		hosts["host.lima.internal"] = qemuconst.SlirpGateway
+		hosts[fmt.Sprintf("lima-%s", a.instName)] = qemuconst.SlirpIPAddress
 		srvOpts := dns.ServerOptions{
 			UDPPort: a.udpDNSLocalPort,
 			TCPPort: a.tcpDNSLocalPort,
diff --git a/pkg/limayaml/defaults.go b/pkg/limayaml/defaults.go
index 358c2b39..3e3846e3 100644
--- a/pkg/limayaml/defaults.go
+++ b/pkg/limayaml/defaults.go
@@ -9,7 +9,6 @@ import (
 	"path/filepath"
 	"runtime"
 	"strconv"
-	"strings"
 	"text/template"
 
 	"github.com/lima-vm/lima/pkg/guestagent/api"
@@ -233,13 +232,13 @@ func FillDefault(y, d, o *LimaYAML, filePath string) {
 	hosts := make(map[string]string)
 	// Values can be either names or IP addresses. Name values are canonicalized in the hostResolver.
 	for k, v := range d.HostResolver.Hosts {
-		hosts[Cname(k)] = v
+		hosts[k] = v
 	}
 	for k, v := range y.HostResolver.Hosts {
-		hosts[Cname(k)] = v
+		hosts[k] = v
 	}
 	for k, v := range o.HostResolver.Hosts {
-		hosts[Cname(k)] = v
+		hosts[k] = v
 	}
 	y.HostResolver.Hosts = hosts
 
@@ -667,14 +666,6 @@ func IsNativeArch(arch Arch) bool {
 	return nativeX8664 || nativeAARCH64 || nativeRISCV64
 }
 
-func Cname(host string) string {
-	host = strings.ToLower(host)
-	if !strings.HasSuffix(host, ".") {
-		host += "."
-	}
-	return host
-}
-
 func unique(s []string) []string {
 	keys := make(map[string]bool)
 	list := []string{}


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index e6e0d492..92e42cbb 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -39,7 +39,7 @@ jobs:
     - name: Check hyperlinks
       uses: docker://dkhamsing/awesome_bot:latest
       with:
-        args: /github/workspace/README.md --allow-dupe --allow-redirect --request-delay 1 --white-list https://img.shields.io,http://127.0.0.1:8080,https://github.com/lima-vm/lima/releases/download,https://xbarapp.com
+        args: /github/workspace/README.md --allow-dupe --allow-redirect --request-delay 1 --white-list https://img.shields.io,http://127.0.0.1:8080,https://github.com/lima-vm/lima/releases/download
     - name: Unit tests
       run: go test -v ./...
     - name: Make
diff --git a/pkg/hostagent/dns/dns_test.go b/pkg/hostagent/dns/dns_test.go
index 19fb33b0..e6391545 100644
--- a/pkg/hostagent/dns/dns_test.go
+++ b/pkg/hostagent/dns/dns_test.go
@@ -19,16 +19,7 @@ var (
 	dnsResult *dns.Msg
 )
 
-func TestTXTRecords(t *testing.T) {
-	testDomains := make([]string, 3)
-	testDomains[0] = "onerecord.com"
-	testDomains[1] = "multistringrecord.com"
-	testDomains[2] = "multiplerecords.com"
-
-	expectedResults := make([]string, 3)
-	expectedResults[0] = `onerecord.com.\s+5\s+IN\s+TXT\s+"My txt record"`
-	expectedResults[1] = `multistringrecord.com.\s+5\s+IN\s+TXT\s+"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345" "67890123456789012345678901234567890"`
-	expectedResults[2] = `multiplerecords.com.\s+5\s+IN\s+TXT\s+"record 1"\nmultiplerecords.com.\s*5\s*IN\s*TXT\s*"record 2"`
+func TestDNSRecords(t *testing.T) {
 
 	srv, _ := mockdns.NewServerWithLogger(map[string]mockdns.Zone{
 		"onerecord.com.": {
@@ -52,33 +43,79 @@ func TestTXTRecords(t *testing.T) {
 	}
 	srv.PatchNet(net.DefaultResolver)
 	defer mockdns.UnpatchNet(net.DefaultResolver)
+	w := new(TestResponseWriter)
+	options := HandlerOptions{
+		IPv6: true,
+		StaticHosts: map[string]string{
+			"MY.DOMAIN.COM":      "192.168.0.23",
+			"host.lima.internal": "10.10.0.34",
+			"my.host":            "host.lima.internal",
+			"default":            "my.domain.com",
+		},
+	}
 
+	h, err := NewHandler(options)
+	assert.NilError(t, err)
+
+	regexMatch := func(value string, pattern string) cmp.Comparison {
+		return func() cmp.Result {
+			re := regexp.MustCompile(pattern)
+			if re.MatchString(value) {
+				return cmp.ResultSuccess
+			}
+			return cmp.ResultFailure(
+				fmt.Sprintf("%q did not match pattern %q", value, pattern))
+		}
+	}
 	t.Run("test TXT records", func(t *testing.T) {
-		w := new(TestResponseWriter)
-		options := HandlerOptions{
-			IPv6: true,
-			StaticHosts: map[string]string{
-				"MY.Host": "host.lima.internal",
-			},
+		tests := []struct {
+			testDomain        string
+			expectedTXTRecord string
+		}{
+			{testDomain: "onerecord.com", expectedTXTRecord: `onerecord.com.\s+5\s+IN\s+TXT\s+"My txt record"`},
+			{testDomain: "multistringrecord.com", expectedTXTRecord: `multistringrecord.com.\s+5\s+IN\s+TXT\s+"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345" "67890123456789012345678901234567890"`},
+			{testDomain: "multiplerecords.com", expectedTXTRecord: `multiplerecords.com.\s+5\s+IN\s+TXT\s+"record 1"\nmultiplerecords.com.\s*5\s*IN\s*TXT\s*"record 2"`},
 		}
-		h, err := NewHandler(options)
-		if err == nil {
-			for i := 0; i < len(testDomains); i++ {
-				req := new(dns.Msg)
-				req.SetQuestion(dns.Fqdn(testDomains[i]), dns.TypeTXT)
-				h.ServeDNS(w, req)
-				regexMatch := func(value string, pattern string) cmp.Comparison {
-					return func() cmp.Result {
-						re := regexp.MustCompile(pattern)
-						if re.MatchString(value) {
-							return cmp.ResultSuccess
-						}
-						return cmp.ResultFailure(
-							fmt.Sprintf("%q did not match pattern %q", value, pattern))
-					}
-				}
-				assert.Assert(t, regexMatch(dnsResult.String(), expectedResults[i]))
-			}
+
+		for _, tc := range tests {
+			req := new(dns.Msg)
+			req.SetQuestion(dns.Fqdn(tc.testDomain), dns.TypeTXT)
+			h.ServeDNS(w, req)
+			assert.Assert(t, regexMatch(dnsResult.String(), tc.expectedTXTRecord))
+		}
+	})
+
+	t.Run("test A records", func(t *testing.T) {
+		tests := []struct {
+			testDomain      string
+			expectedARecord string
+		}{
+			{testDomain: "my.domain.com", expectedARecord: `my.domain.com.\s+5\s+IN\s+A\s+192.168.0.23`},
+			{testDomain: "host.lima.internal", expectedARecord: `host.lima.internal.\s+5\s+IN\s+A\s+10.10.0.34`},
+		}
+
+		for _, tc := range tests {
+			req := new(dns.Msg)
+			req.SetQuestion(dns.Fqdn(tc.testDomain), dns.TypeA)
+			h.ServeDNS(w, req)
+			assert.Assert(t, regexMatch(dnsResult.String(), tc.expectedARecord))
+		}
+	})
+
+	t.Run("test CNAME records", func(t *testing.T) {
+		tests := []struct {
+			testDomain    string
+			expectedCNAME string
+		}{
+			{testDomain: "my.host", expectedCNAME: `my.host.\s+5\s+IN\s+CNAME\s+host.lima.internal.`},
+			{testDomain: "default", expectedCNAME: `default.\s+5\s+IN\s+CNAME\s+my.domain.com.`},
+		}
+
+		for _, tc := range tests {
+			req := new(dns.Msg)
+			req.SetQuestion(dns.Fqdn(tc.testDomain), dns.TypeCNAME)
+			h.ServeDNS(w, req)
+			assert.Assert(t, regexMatch(dnsResult.String(), tc.expectedCNAME))
 		}
 	})
 }
diff --git a/pkg/limayaml/defaults_test.go b/pkg/limayaml/defaults_test.go
index da9b93c4..6a98fb41 100644
--- a/pkg/limayaml/defaults_test.go
+++ b/pkg/limayaml/defaults_test.go
@@ -149,7 +149,7 @@ func TestFillDefault(t *testing.T) {
 
 	expect := builtin
 	expect.HostResolver.Hosts = map[string]string{
-		"my.host.": "host.lima.internal",
+		"MY.Host": "host.lima.internal",
 	}
 
 	expect.Mounts = y.Mounts
@@ -319,7 +319,7 @@ func TestFillDefault(t *testing.T) {
 	expect.Mounts[0].NineP.Msize = pointer.String(Default9pMsize)
 	expect.Mounts[0].NineP.Cache = pointer.String(Default9pCacheForRO)
 	expect.HostResolver.Hosts = map[string]string{
-		"default.": d.HostResolver.Hosts["default"],
+		"default": d.HostResolver.Hosts["default"],
 	}
 	expect.MountType = pointer.String(REVSSHFS)
 	expect.CACertificates.RemoveDefaults = pointer.Bool(true)
@@ -348,7 +348,7 @@ func TestFillDefault(t *testing.T) {
 	expect.Mounts = append(d.Mounts, y.Mounts...)
 	expect.Networks = append(d.Networks, y.Networks...)
 
-	expect.HostResolver.Hosts["default."] = d.HostResolver.Hosts["default"]
+	expect.HostResolver.Hosts["default"] = d.HostResolver.Hosts["default"]
 
 	// d.DNS will be ignored, and not appended to y.DNS
 
@@ -474,8 +474,8 @@ func TestFillDefault(t *testing.T) {
 	expect.PortForwards = append(append(o.PortForwards, y.PortForwards...), d.PortForwards...)
 	expect.Containerd.Archives = append(append(o.Containerd.Archives, y.Containerd.Archives...), d.Containerd.Archives...)
 
-	expect.HostResolver.Hosts["default."] = d.HostResolver.Hosts["default"]
-	expect.HostResolver.Hosts["my.host."] = d.HostResolver.Hosts["host.lima.internal"]
+	expect.HostResolver.Hosts["default"] = d.HostResolver.Hosts["default"]
+	expect.HostResolver.Hosts["MY.Host"] = d.HostResolver.Hosts["host.lima.internal"]
 
 	// o.Mounts just makes d.Mounts[0] writable because the Location matches
 	expect.Mounts = append(d.Mounts, y.Mounts...)