From 9ab340e95e644044340693a31593439226842521 Mon Sep 17 00:00:00 2001 From: Johannes 'fish' Ziemke Date: Thu, 30 Jul 2015 10:56:48 +0200 Subject: [PATCH] Add support for A record based DNS SD If using A records, the user needs to specify "port" and set "type" to "A". --- config/config.go | 13 ++++++++++++- config/config_test.go | 2 ++ retrieval/discovery/dns.go | 40 ++++++++++++++++++++++++++++---------- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/config/config.go b/config/config.go index 46fbc9e75..22ae0d519 100644 --- a/config/config.go +++ b/config/config.go @@ -80,6 +80,7 @@ var ( // The default DNS SD configuration. DefaultDNSSDConfig = DNSSDConfig{ RefreshInterval: Duration(30 * time.Second), + Type: "SRV", } // The default file SD configuration. @@ -362,7 +363,8 @@ func (tg *TargetGroup) UnmarshalJSON(b []byte) error { type DNSSDConfig struct { Names []string `yaml:"names"` RefreshInterval Duration `yaml:"refresh_interval,omitempty"` - + Type string `yaml:"type"` + Port int `yaml:"port"` // Ignored for SRV records // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } @@ -378,6 +380,15 @@ func (c *DNSSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if len(c.Names) == 0 { return fmt.Errorf("DNS-SD config must contain at least one SRV record name") } + switch strings.ToUpper(c.Type) { + case "SRV": + case "A", "AAAA": + if c.Port == 0 { + return fmt.Errorf("a port is required in DNS-SD configs for all record types except SRV") + } + default: + return fmt.Errorf("invalid DNS-SD records type %s", c.Type) + } return checkOverflow(c.XXX, "dns_sd_config") } diff --git a/config/config_test.go b/config/config_test.go index 844aef8cd..9d78f6522 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -98,12 +98,14 @@ var expectedConf = &Config{ "second.dns.address.domain.com", }, RefreshInterval: Duration(15 * time.Second), + Type: "SRV", }, { Names: []string{ "first.dns.address.domain.com", }, RefreshInterval: Duration(30 * time.Second), + Type: "SRV", }, }, diff --git a/retrieval/discovery/dns.go b/retrieval/discovery/dns.go index 617011439..70aa2a7c2 100644 --- a/retrieval/discovery/dns.go +++ b/retrieval/discovery/dns.go @@ -68,14 +68,27 @@ type DNSDiscovery struct { done chan struct{} ticker *time.Ticker m sync.RWMutex + port int + qtype uint16 } // NewDNSDiscovery returns a new DNSDiscovery which periodically refreshes its targets. func NewDNSDiscovery(conf *config.DNSSDConfig) *DNSDiscovery { + qtype := dns.TypeSRV + switch strings.ToUpper(conf.Type) { + case "A": + qtype = dns.TypeA + case "AAAA": + qtype = dns.TypeAAAA + case "SRV": + qtype = dns.TypeSRV + } return &DNSDiscovery{ names: conf.Names, done: make(chan struct{}), ticker: time.NewTicker(time.Duration(conf.RefreshInterval)), + qtype: qtype, + port: conf.Port, } } @@ -130,7 +143,7 @@ func (dd *DNSDiscovery) refreshAll(ch chan<- *config.TargetGroup) { } func (dd *DNSDiscovery) refresh(name string, ch chan<- *config.TargetGroup) error { - response, err := lookupSRV(name) + response, err := lookupAll(name, dd.qtype) dnsSDLookupsCount.Inc() if err != nil { dnsSDLookupFailuresCount.Inc() @@ -139,15 +152,22 @@ func (dd *DNSDiscovery) refresh(name string, ch chan<- *config.TargetGroup) erro tg := &config.TargetGroup{} for _, record := range response.Answer { - addr, ok := record.(*dns.SRV) - if !ok { + target := clientmodel.LabelValue("") + switch addr := record.(type) { + case *dns.SRV: + // Remove the final dot from rooted DNS names to make them look more usual. + addr.Target = strings.TrimRight(addr.Target, ".") + + target = clientmodel.LabelValue(fmt.Sprintf("%s:%d", addr.Target, addr.Port)) + case *dns.A: + target = clientmodel.LabelValue(fmt.Sprintf("%s:%d", addr.A, dd.port)) + case *dns.AAAA: + target = clientmodel.LabelValue(fmt.Sprintf("%s:%d", addr.AAAA, dd.port)) + default: log.Warnf("%q is not a valid SRV record", record) continue - } - // Remove the final dot from rooted DNS names to make them look more usual. - addr.Target = strings.TrimRight(addr.Target, ".") - target := clientmodel.LabelValue(fmt.Sprintf("%s:%d", addr.Target, addr.Port)) + } tg.Targets = append(tg.Targets, clientmodel.LabelSet{ clientmodel.AddressLabel: target, DNSNameLabel: clientmodel.LabelValue(name), @@ -160,7 +180,7 @@ func (dd *DNSDiscovery) refresh(name string, ch chan<- *config.TargetGroup) erro return nil } -func lookupSRV(name string) (*dns.Msg, error) { +func lookupAll(name string, qtype uint16) (*dns.Msg, error) { conf, err := dns.ClientConfigFromFile(resolvConf) if err != nil { return nil, fmt.Errorf("could not load resolv.conf: %s", err) @@ -172,7 +192,7 @@ func lookupSRV(name string) (*dns.Msg, error) { for _, server := range conf.Servers { servAddr := net.JoinHostPort(server, conf.Port) for _, suffix := range conf.Search { - response, err = lookup(name, dns.TypeSRV, client, servAddr, suffix, false) + response, err = lookup(name, qtype, client, servAddr, suffix, false) if err != nil { log.Warnf("resolving %s.%s failed: %s", name, suffix, err) continue @@ -181,7 +201,7 @@ func lookupSRV(name string) (*dns.Msg, error) { return response, nil } } - response, err = lookup(name, dns.TypeSRV, client, servAddr, "", false) + response, err = lookup(name, qtype, client, servAddr, "", false) if err == nil { return response, nil }