diff --git a/cmd/dnsmasq-nanny/main.go b/cmd/dnsmasq-nanny/main.go index ef463f4a8..2a5f8c684 100644 --- a/cmd/dnsmasq-nanny/main.go +++ b/cmd/dnsmasq-nanny/main.go @@ -31,15 +31,17 @@ import ( var ( opts = struct { dnsmasq.RunNannyOpts - configDir string - syncInterval time.Duration + configDir string + syncInterval time.Duration + kubednsServer string }{ RunNannyOpts: dnsmasq.RunNannyOpts{ DnsmasqExec: "/usr/sbin/dnsmasq", RestartOnChange: false, }, - configDir: "/etc/k8s/dns/dnsmasq-nanny", - syncInterval: 10 * time.Second, + configDir: "/etc/k8s/dns/dnsmasq-nanny", + syncInterval: 10 * time.Second, + kubednsServer: "127.0.0.1:10053", } ) @@ -66,6 +68,8 @@ Any arguments given after "--" will be passed directly to dnsmasq itself. flag.DurationVar(&opts.syncInterval, "syncInterval", opts.syncInterval, "interval to check for configuration updates") + flag.StringVar(&opts.kubednsServer, "kubednsServer", opts.kubednsServer, + "local kubedns instance address for non-IP name resolution") flag.Parse() } @@ -75,5 +79,5 @@ func main() { sync := config.NewFileSync(opts.configDir, opts.syncInterval) - dnsmasq.RunNanny(sync, opts.RunNannyOpts) + dnsmasq.RunNanny(sync, opts.RunNannyOpts, opts.kubednsServer) } diff --git a/pkg/dnsmasq/nanny.go b/pkg/dnsmasq/nanny.go index 4786fe494..bb8dcf93c 100644 --- a/pkg/dnsmasq/nanny.go +++ b/pkg/dnsmasq/nanny.go @@ -18,8 +18,10 @@ package dnsmasq import ( "bufio" + "context" "fmt" "io" + "net" "os/exec" "strings" @@ -52,7 +54,8 @@ func ExtractDnsmasqArgs(cmdlineArgs *[]string) []string { } // Configure the nanny. This must be called before Start(). -func (n *Nanny) Configure(args []string, config *config.Config) { +// kubednsServer is the address of the local kubedns instance used to do name resolution for non-IP names. +func (n *Nanny) Configure(args []string, config *config.Config, kubednsServer string) { n.args = args munge := func(s string) string { @@ -68,7 +71,34 @@ func (n *Nanny) Configure(args []string, config *config.Config) { } for domain, serverList := range config.StubDomains { + resolver := &net.Resolver{ + PreferGo: true, + Dial: func(ctx context.Context, network, address string) (net.Conn, error) { + d := net.Dialer{} + return d.DialContext(ctx, "udp", kubednsServer) + }, + } for _, server := range serverList { + if isIP := (net.ParseIP(server) != nil); !isIP { + switch { + case strings.HasSuffix(server, "cluster.local"): + if IPs, err := resolver.LookupIPAddr(context.Background(), server); err != nil { + glog.Errorf("Error looking up IP for name %q: %v", server, err) + } else if len(IPs) > 0 { + server = IPs[0].String() + } else { + glog.Errorf("Name %q does not resolve to any IPs", server) + } + default: + if IPs, err := net.LookupIP(server); err != nil { + glog.Errorf("Error looking up IP for name %q: %v", server, err) + } else if len(IPs) > 0 { + server = IPs[0].String() + } else { + glog.Errorf("Name %q does not resolve to any IPs", server) + } + } + } // dnsmasq port separator is '#' for some reason. server = munge(server) n.args = append( @@ -166,7 +196,7 @@ type RunNannyOpts struct { } // RunNanny runs the nanny and handles configuration updates. -func RunNanny(sync config.Sync, opts RunNannyOpts) { +func RunNanny(sync config.Sync, opts RunNannyOpts, kubednsServer string) { defer glog.Flush() currentConfig, err := sync.Once() @@ -176,7 +206,7 @@ func RunNanny(sync config.Sync, opts RunNannyOpts) { } nanny := &Nanny{Exec: opts.DnsmasqExec} - nanny.Configure(opts.DnsmasqArgs, currentConfig) + nanny.Configure(opts.DnsmasqArgs, currentConfig, kubednsServer) if err := nanny.Start(); err != nil { glog.Fatalf("Could not start dnsmasq with initial configuration: %v", err) } @@ -194,7 +224,7 @@ func RunNanny(sync config.Sync, opts RunNannyOpts) { glog.V(0).Infof("Restarting dnsmasq with new configuration") nanny.Kill() nanny = &Nanny{Exec: opts.DnsmasqExec} - nanny.Configure(opts.DnsmasqArgs, currentConfig) + nanny.Configure(opts.DnsmasqArgs, currentConfig, kubednsServer) nanny.Start() } else { glog.V(2).Infof("Not restarting dnsmasq (--restartDnsmasq=false)") diff --git a/pkg/dnsmasq/nanny_test.go b/pkg/dnsmasq/nanny_test.go index 20232e70e..163a73d39 100644 --- a/pkg/dnsmasq/nanny_test.go +++ b/pkg/dnsmasq/nanny_test.go @@ -68,13 +68,16 @@ func TestNannyConfig(t *testing.T) { StubDomains: map[string][]string{ "acme.local": []string{"1.1.1.1"}, "widget.local": []string{"2.2.2.2:10053", "3.3.3.3"}, + "google.local": []string{"google-public-dns-a.google.com"}, }}, e: []string{ "--abc", "--server", "--server", "--server", + "--server", "/acme.local/1.1.1.1", + "/google.local/8.8.8.8", "/widget.local/2.2.2.2#10053", "/widget.local/3.3.3.3", }, @@ -108,7 +111,7 @@ func TestNannyConfig(t *testing.T) { }, } { nanny := &Nanny{Exec: "dnsmasq"} - nanny.Configure([]string{"--abc"}, testCase.c) + nanny.Configure([]string{"--abc"}, testCase.c, "127.0.0.1:10053") if testCase.sort { sort.Sort(sort.StringSlice(nanny.args)) } @@ -121,12 +124,14 @@ func TestNannyLifecycle(t *testing.T) { const mockDnsmasq = "../../test/fixtures/mock-dnsmasq.sh" var nanny *Nanny + kubednsServer := "127.0.0.1:10053" // Exit with success. nanny = &Nanny{Exec: mockDnsmasq} nanny.Configure( []string{"--exitWithSuccess"}, - &config.Config{}) + &config.Config{}, + kubednsServer) gomega.Expect(nanny.Start()).To(gomega.Succeed()) gomega.Expect(<-nanny.ExitChannel).To(gomega.Succeed()) @@ -134,7 +139,8 @@ func TestNannyLifecycle(t *testing.T) { nanny = &Nanny{Exec: mockDnsmasq} nanny.Configure( []string{"--exitWithError"}, - &config.Config{}) + &config.Config{}, + kubednsServer) gomega.Expect(nanny.Start()).To(gomega.Succeed()) gomega.Expect(<-nanny.ExitChannel).NotTo(gomega.Succeed()) @@ -142,7 +148,8 @@ func TestNannyLifecycle(t *testing.T) { nanny = &Nanny{Exec: mockDnsmasq} nanny.Configure( []string{"--sleepThenError"}, - &config.Config{}) + &config.Config{}, + kubednsServer) gomega.Expect(nanny.Start()).To(gomega.Succeed()) gomega.Expect(<-nanny.ExitChannel).NotTo(gomega.Succeed()) @@ -150,7 +157,8 @@ func TestNannyLifecycle(t *testing.T) { nanny = &Nanny{Exec: mockDnsmasq} nanny.Configure( []string{"--runForever"}, - &config.Config{}) + &config.Config{}, + kubednsServer) gomega.Expect(nanny.Start()).To(gomega.Succeed()) time.Sleep(250 * time.Millisecond) gomega.Expect(nanny.Kill()).To(gomega.Succeed())