Merge pull request #946 from prometheus/add-sd-dns-a

Add support for A record based DNS SD
This commit is contained in:
Johannes 'fish' Ziemke 2015-07-30 16:01:47 +02:00
commit 6e7d743cd4
3 changed files with 44 additions and 11 deletions

View File

@ -80,6 +80,7 @@ var (
// The default DNS SD configuration. // The default DNS SD configuration.
DefaultDNSSDConfig = DNSSDConfig{ DefaultDNSSDConfig = DNSSDConfig{
RefreshInterval: Duration(30 * time.Second), RefreshInterval: Duration(30 * time.Second),
Type: "SRV",
} }
// The default file SD configuration. // The default file SD configuration.
@ -362,7 +363,8 @@ func (tg *TargetGroup) UnmarshalJSON(b []byte) error {
type DNSSDConfig struct { type DNSSDConfig struct {
Names []string `yaml:"names"` Names []string `yaml:"names"`
RefreshInterval Duration `yaml:"refresh_interval,omitempty"` 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. // Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"` XXX map[string]interface{} `yaml:",inline"`
} }
@ -378,6 +380,15 @@ func (c *DNSSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
if len(c.Names) == 0 { if len(c.Names) == 0 {
return fmt.Errorf("DNS-SD config must contain at least one SRV record name") 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") return checkOverflow(c.XXX, "dns_sd_config")
} }

View File

@ -98,12 +98,14 @@ var expectedConf = &Config{
"second.dns.address.domain.com", "second.dns.address.domain.com",
}, },
RefreshInterval: Duration(15 * time.Second), RefreshInterval: Duration(15 * time.Second),
Type: "SRV",
}, },
{ {
Names: []string{ Names: []string{
"first.dns.address.domain.com", "first.dns.address.domain.com",
}, },
RefreshInterval: Duration(30 * time.Second), RefreshInterval: Duration(30 * time.Second),
Type: "SRV",
}, },
}, },

View File

@ -68,14 +68,27 @@ type DNSDiscovery struct {
done chan struct{} done chan struct{}
ticker *time.Ticker ticker *time.Ticker
m sync.RWMutex m sync.RWMutex
port int
qtype uint16
} }
// NewDNSDiscovery returns a new DNSDiscovery which periodically refreshes its targets. // NewDNSDiscovery returns a new DNSDiscovery which periodically refreshes its targets.
func NewDNSDiscovery(conf *config.DNSSDConfig) *DNSDiscovery { 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{ return &DNSDiscovery{
names: conf.Names, names: conf.Names,
done: make(chan struct{}), done: make(chan struct{}),
ticker: time.NewTicker(time.Duration(conf.RefreshInterval)), 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 { func (dd *DNSDiscovery) refresh(name string, ch chan<- *config.TargetGroup) error {
response, err := lookupSRV(name) response, err := lookupAll(name, dd.qtype)
dnsSDLookupsCount.Inc() dnsSDLookupsCount.Inc()
if err != nil { if err != nil {
dnsSDLookupFailuresCount.Inc() dnsSDLookupFailuresCount.Inc()
@ -139,15 +152,22 @@ func (dd *DNSDiscovery) refresh(name string, ch chan<- *config.TargetGroup) erro
tg := &config.TargetGroup{} tg := &config.TargetGroup{}
for _, record := range response.Answer { for _, record := range response.Answer {
addr, ok := record.(*dns.SRV) target := clientmodel.LabelValue("")
if !ok { 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) log.Warnf("%q is not a valid SRV record", record)
continue 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{ tg.Targets = append(tg.Targets, clientmodel.LabelSet{
clientmodel.AddressLabel: target, clientmodel.AddressLabel: target,
DNSNameLabel: clientmodel.LabelValue(name), DNSNameLabel: clientmodel.LabelValue(name),
@ -160,7 +180,7 @@ func (dd *DNSDiscovery) refresh(name string, ch chan<- *config.TargetGroup) erro
return nil return nil
} }
func lookupSRV(name string) (*dns.Msg, error) { func lookupAll(name string, qtype uint16) (*dns.Msg, error) {
conf, err := dns.ClientConfigFromFile(resolvConf) conf, err := dns.ClientConfigFromFile(resolvConf)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not load resolv.conf: %s", err) 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 { for _, server := range conf.Servers {
servAddr := net.JoinHostPort(server, conf.Port) servAddr := net.JoinHostPort(server, conf.Port)
for _, suffix := range conf.Search { 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 { if err != nil {
log.Warnf("resolving %s.%s failed: %s", name, suffix, err) log.Warnf("resolving %s.%s failed: %s", name, suffix, err)
continue continue
@ -181,7 +201,7 @@ func lookupSRV(name string) (*dns.Msg, error) {
return response, nil return response, nil
} }
} }
response, err = lookup(name, dns.TypeSRV, client, servAddr, "", false) response, err = lookup(name, qtype, client, servAddr, "", false)
if err == nil { if err == nil {
return response, nil return response, nil
} }