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.
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")
}

View File

@ -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",
},
},

View File

@ -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
}