Replace mapdns with daemon DNS

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.
This commit is contained in:
Thales Maciel 2026-03-17 15:49:35 -03:00
parent 430f66d5dd
commit 0a0b0b617b
No known key found for this signature in database
GPG key ID: 33112E6833C34679
24 changed files with 576 additions and 278 deletions

View file

@ -15,6 +15,7 @@ import (
"banger/internal/model"
"banger/internal/paths"
"banger/internal/system"
"banger/internal/vmdns"
)
func (d *Daemon) CreateVM(ctx context.Context, params api.VMCreateParams) (vm model.VMRecord, err error) {
@ -100,7 +101,7 @@ func (d *Daemon) CreateVM(ctx context.Context, params api.VMCreateParams) (vm mo
Runtime: model.VMRuntime{
State: model.VMStateCreated,
GuestIP: guestIP,
DNSName: name + ".vm",
DNSName: vmdns.RecordName(name),
VMDir: vmDir,
SystemOverlay: filepath.Join(vmDir, "system.cow"),
WorkDiskPath: filepath.Join(vmDir, "root.ext4"),
@ -859,30 +860,44 @@ func clearRuntimeHandles(vm *model.VMRecord) {
}
func (d *Daemon) setDNS(ctx context.Context, vmName, guestIP string) error {
if dataFile := strings.TrimSpace(d.config.MapDNSDataFile); dataFile != "" {
if err := os.MkdirAll(filepath.Dir(dataFile), 0o755); err != nil {
return err
}
if d.vmDNS == nil {
return nil
}
_, err := d.runner.Run(ctx, d.mapdnsBinary(), d.mapdnsArgs("set", vmName+".vm", guestIP)...)
if err == nil && d.logger != nil {
d.logger.Debug("dns record set", "dns_name", vmName+".vm", "guest_ip", guestIP)
}
return err
return d.vmDNS.Set(vmdns.RecordName(vmName), guestIP)
}
func (d *Daemon) removeDNS(ctx context.Context, dnsName string) error {
if dnsName == "" {
return nil
}
_, err := d.runner.Run(ctx, d.mapdnsBinary(), d.mapdnsArgs("rm", dnsName)...)
if err != nil && strings.Contains(err.Error(), "not found") {
if d.vmDNS == nil {
return nil
}
if err == nil && d.logger != nil {
d.logger.Debug("dns record removed", "dns_name", dnsName)
return d.vmDNS.Remove(dnsName)
}
func (d *Daemon) rebuildDNS(ctx context.Context) error {
if d.vmDNS == nil {
return nil
}
return err
vms, err := d.store.ListVMs(ctx)
if err != nil {
return err
}
records := make(map[string]string)
for _, vm := range vms {
if vm.State != model.VMStateRunning {
continue
}
if !system.ProcessRunning(vm.Runtime.PID, vm.Runtime.APISockPath) {
continue
}
if strings.TrimSpace(vm.Runtime.GuestIP) == "" {
continue
}
records[vmdns.RecordName(vm.Name)] = vm.Runtime.GuestIP
}
return d.vmDNS.Replace(records)
}
func (d *Daemon) killVMProcess(ctx context.Context, pid int) error {
@ -927,19 +942,3 @@ func validateOptionalPositiveSetting(label string, value *int) error {
}
return nil
}
func (d *Daemon) mapdnsBinary() string {
if value := strings.TrimSpace(d.config.MapDNSBin); value != "" {
return value
}
return "mapdns"
}
func (d *Daemon) mapdnsArgs(subcommand string, args ...string) []string {
out := []string{subcommand}
if value := strings.TrimSpace(d.config.MapDNSDataFile); value != "" {
out = append(out, "--data-file", value)
}
out = append(out, args...)
return out
}