Serve daemon-managed .vm names directly from bangerd on 127.0.0.1:42069 instead of shelling out to mapdns. This keeps DNS state tied to VM lifecycle and lets the daemon rebuild records from running VMs after startup or reconcile. Add a small in-process authoritative DNS server, register and remove records from the VM start/stop/delete paths, and show the listener in daemon status. Remove the mapdns config and preflight surface, stop helper-flow DNS publishing in customize.sh and interactive.sh, drop dns.sh from the runtime bundle, and update docs/tests for the new local-resolver integration model. Validated with GOCACHE=/tmp/banger-gocache go test ./..., GOCACHE=/tmp/banger-gocache make build, and bash -n customize.sh interactive.sh.
126 lines
3.3 KiB
Go
126 lines
3.3 KiB
Go
package vmdns
|
|
|
|
import (
|
|
"net"
|
|
"testing"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
func TestRecordName(t *testing.T) {
|
|
if got := RecordName("DevBox"); got != "devbox.vm" {
|
|
t.Fatalf("RecordName = %q, want devbox.vm", got)
|
|
}
|
|
if got := RecordName("already.vm"); got != "already.vm" {
|
|
t.Fatalf("RecordName = %q, want already.vm", got)
|
|
}
|
|
}
|
|
|
|
func TestServerAnswersVMQueries(t *testing.T) {
|
|
server := startTestServer(t)
|
|
if err := server.Set("devbox.vm", "172.16.0.8"); err != nil {
|
|
t.Fatalf("Set: %v", err)
|
|
}
|
|
|
|
t.Run("A record", func(t *testing.T) {
|
|
resp := exchangeQuery(t, server.Addr(), "devbox.vm.", dns.TypeA)
|
|
if resp.Rcode != dns.RcodeSuccess {
|
|
t.Fatalf("rcode = %d, want success", resp.Rcode)
|
|
}
|
|
if len(resp.Answer) != 1 {
|
|
t.Fatalf("answer count = %d, want 1", len(resp.Answer))
|
|
}
|
|
a, ok := resp.Answer[0].(*dns.A)
|
|
if !ok {
|
|
t.Fatalf("answer type = %T, want *dns.A", resp.Answer[0])
|
|
}
|
|
if got := a.A.String(); got != "172.16.0.8" {
|
|
t.Fatalf("A = %q, want 172.16.0.8", got)
|
|
}
|
|
})
|
|
|
|
t.Run("known AAAA returns NODATA", func(t *testing.T) {
|
|
resp := exchangeQuery(t, server.Addr(), "devbox.vm.", dns.TypeAAAA)
|
|
if resp.Rcode != dns.RcodeSuccess {
|
|
t.Fatalf("rcode = %d, want success", resp.Rcode)
|
|
}
|
|
if len(resp.Answer) != 0 {
|
|
t.Fatalf("answer count = %d, want 0", len(resp.Answer))
|
|
}
|
|
})
|
|
|
|
t.Run("unknown name returns NXDOMAIN", func(t *testing.T) {
|
|
resp := exchangeQuery(t, server.Addr(), "missing.vm.", dns.TypeA)
|
|
if resp.Rcode != dns.RcodeNameError {
|
|
t.Fatalf("rcode = %d, want NXDOMAIN", resp.Rcode)
|
|
}
|
|
})
|
|
|
|
t.Run("outside zone returns REFUSED", func(t *testing.T) {
|
|
resp := exchangeQuery(t, server.Addr(), "example.com.", dns.TypeA)
|
|
if resp.Rcode != dns.RcodeRefused {
|
|
t.Fatalf("rcode = %d, want REFUSED", resp.Rcode)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestServerReplaceSwapsRecordSet(t *testing.T) {
|
|
server := startTestServer(t)
|
|
if err := server.Replace(map[string]string{
|
|
"alpha.vm": "172.16.0.2",
|
|
"beta.vm": "172.16.0.3",
|
|
}); err != nil {
|
|
t.Fatalf("Replace: %v", err)
|
|
}
|
|
if _, ok := server.Lookup("alpha.vm"); !ok {
|
|
t.Fatal("alpha.vm missing after replace")
|
|
}
|
|
if err := server.Replace(map[string]string{"beta.vm": "172.16.0.4"}); err != nil {
|
|
t.Fatalf("Replace second set: %v", err)
|
|
}
|
|
if _, ok := server.Lookup("alpha.vm"); ok {
|
|
t.Fatal("alpha.vm should have been removed by replace")
|
|
}
|
|
addr, ok := server.Lookup("beta.vm")
|
|
if !ok || addr.String() != "172.16.0.4" {
|
|
t.Fatalf("beta.vm = %v, %v, want 172.16.0.4", addr, ok)
|
|
}
|
|
}
|
|
|
|
func TestServerFailsWhenAddressAlreadyInUse(t *testing.T) {
|
|
packetConn, err := net.ListenPacket("udp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatalf("ListenPacket: %v", err)
|
|
}
|
|
defer packetConn.Close()
|
|
|
|
if _, err := New(packetConn.LocalAddr().String(), nil); err == nil {
|
|
t.Fatal("New() succeeded on occupied UDP address, want failure")
|
|
}
|
|
}
|
|
|
|
func startTestServer(t *testing.T) *Server {
|
|
t.Helper()
|
|
server, err := New("127.0.0.1:0", nil)
|
|
if err != nil {
|
|
t.Fatalf("New: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
if err := server.Close(); err != nil {
|
|
t.Fatalf("Close: %v", err)
|
|
}
|
|
})
|
|
return server
|
|
}
|
|
|
|
func exchangeQuery(t *testing.T, addr, name string, qtype uint16) *dns.Msg {
|
|
t.Helper()
|
|
client := &dns.Client{Net: "udp"}
|
|
req := new(dns.Msg)
|
|
req.SetQuestion(name, qtype)
|
|
resp, _, err := client.Exchange(req, addr)
|
|
if err != nil {
|
|
t.Fatalf("Exchange(%s, %d): %v", name, qtype, err)
|
|
}
|
|
return resp
|
|
}
|