diff --git a/discovery/dns/dns.go b/discovery/dns/dns.go index a2294d930..e25c7bbb5 100644 --- a/discovery/dns/dns.go +++ b/discovery/dns/dns.go @@ -40,6 +40,8 @@ const ( dnsSrvRecordPrefix = model.MetaLabelPrefix + "dns_srv_record_" dnsSrvRecordTargetLabel = dnsSrvRecordPrefix + "target" dnsSrvRecordPortLabel = dnsSrvRecordPrefix + "port" + dnsMxRecordPrefix = model.MetaLabelPrefix + "dns_mx_record_" + dnsMxRecordTargetLabel = dnsMxRecordPrefix + "target" // Constants for instrumentation. namespace = "prometheus" @@ -100,7 +102,7 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { } switch strings.ToUpper(c.Type) { case "SRV": - case "A", "AAAA": + case "A", "AAAA", "MX": if c.Port == 0 { return errors.New("a port is required in DNS-SD configs for all record types except SRV") } @@ -136,6 +138,8 @@ func NewDiscovery(conf SDConfig, logger log.Logger) *Discovery { qtype = dns.TypeAAAA case "SRV": qtype = dns.TypeSRV + case "MX": + qtype = dns.TypeMX } d := &Discovery{ names: conf.Names, @@ -195,7 +199,7 @@ func (d *Discovery) refreshOne(ctx context.Context, name string, ch chan<- *targ } for _, record := range response.Answer { - var target, dnsSrvRecordTarget, dnsSrvRecordPort model.LabelValue + var target, dnsSrvRecordTarget, dnsSrvRecordPort, dnsMxRecordTarget model.LabelValue switch addr := record.(type) { case *dns.SRV: @@ -206,6 +210,13 @@ func (d *Discovery) refreshOne(ctx context.Context, name string, ch chan<- *targ addr.Target = strings.TrimRight(addr.Target, ".") target = hostPort(addr.Target, int(addr.Port)) + case *dns.MX: + dnsMxRecordTarget = model.LabelValue(addr.Mx) + + // Remove the final dot from rooted DNS names to make them look more usual. + addr.Mx = strings.TrimRight(addr.Mx, ".") + + target = hostPort(addr.Mx, d.port) case *dns.A: target = hostPort(addr.A.String(), d.port) case *dns.AAAA: @@ -222,6 +233,7 @@ func (d *Discovery) refreshOne(ctx context.Context, name string, ch chan<- *targ dnsNameLabel: model.LabelValue(name), dnsSrvRecordTargetLabel: dnsSrvRecordTarget, dnsSrvRecordPortLabel: dnsSrvRecordPort, + dnsMxRecordTargetLabel: dnsMxRecordTarget, }) } diff --git a/discovery/dns/dns_test.go b/discovery/dns/dns_test.go index a2311fc2d..50b286049 100644 --- a/discovery/dns/dns_test.go +++ b/discovery/dns/dns_test.go @@ -80,6 +80,7 @@ func TestDNS(t *testing.T) { "__meta_dns_name": "web.example.com.", "__meta_dns_srv_record_target": "", "__meta_dns_srv_record_port": "", + "__meta_dns_mx_record_target": "", }, }, }, @@ -110,6 +111,7 @@ func TestDNS(t *testing.T) { "__meta_dns_name": "web.example.com.", "__meta_dns_srv_record_target": "", "__meta_dns_srv_record_port": "", + "__meta_dns_mx_record_target": "", }, }, }, @@ -140,12 +142,14 @@ func TestDNS(t *testing.T) { "__meta_dns_name": "_mysql._tcp.db.example.com.", "__meta_dns_srv_record_target": "db1.example.com.", "__meta_dns_srv_record_port": "3306", + "__meta_dns_mx_record_target": "", }, { "__address__": "db2.example.com:3306", "__meta_dns_name": "_mysql._tcp.db.example.com.", "__meta_dns_srv_record_target": "db2.example.com.", "__meta_dns_srv_record_port": "3306", + "__meta_dns_mx_record_target": "", }, }, }, @@ -175,6 +179,7 @@ func TestDNS(t *testing.T) { "__meta_dns_name": "_mysql._tcp.db.example.com.", "__meta_dns_srv_record_target": "db1.example.com.", "__meta_dns_srv_record_port": "3306", + "__meta_dns_mx_record_target": "", }, }, }, @@ -195,6 +200,45 @@ func TestDNS(t *testing.T) { }, }, }, + { + name: "MX record query", + config: SDConfig{ + Names: []string{"example.com."}, + Type: "MX", + Port: 25, + RefreshInterval: model.Duration(time.Minute), + }, + lookup: func(name string, qtype uint16, logger log.Logger) (*dns.Msg, error) { + return &dns.Msg{ + Answer: []dns.RR{ + &dns.MX{Preference: 0, Mx: "smtp1.example.com."}, + &dns.MX{Preference: 10, Mx: "smtp2.example.com."}, + }, + }, + nil + }, + expected: []*targetgroup.Group{ + { + Source: "example.com.", + Targets: []model.LabelSet{ + { + "__address__": "smtp1.example.com:25", + "__meta_dns_name": "example.com.", + "__meta_dns_srv_record_target": "", + "__meta_dns_srv_record_port": "", + "__meta_dns_mx_record_target": "smtp1.example.com.", + }, + { + "__address__": "smtp2.example.com:25", + "__meta_dns_name": "example.com.", + "__meta_dns_srv_record_target": "", + "__meta_dns_srv_record_port": "", + "__meta_dns_mx_record_target": "smtp2.example.com.", + }, + }, + }, + }, + }, } for _, tc := range testCases { diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md index a0c8e2142..cd8b388c3 100644 --- a/docs/configuration/configuration.md +++ b/docs/configuration/configuration.md @@ -961,8 +961,8 @@ A DNS-based service discovery configuration allows specifying a set of DNS domain names which are periodically queried to discover a list of targets. The DNS servers to be contacted are read from `/etc/resolv.conf`. -This service discovery method only supports basic DNS A, AAAA and SRV record -queries, but not the advanced DNS-SD approach specified in +This service discovery method only supports basic DNS A, AAAA, MX and SRV +record queries, but not the advanced DNS-SD approach specified in [RFC6763](https://tools.ietf.org/html/rfc6763). The following meta labels are available on targets during [relabeling](#relabel_config): @@ -970,13 +970,14 @@ The following meta labels are available on targets during [relabeling](#relabel_ * `__meta_dns_name`: the record name that produced the discovered target. * `__meta_dns_srv_record_target`: the target field of the SRV record * `__meta_dns_srv_record_port`: the port field of the SRV record +* `__meta_dns_mx_record_target`: the target field of the MX record ```yaml # A list of DNS domain names to be queried. names: [ - ] -# The type of DNS query to perform. One of SRV, A, or AAAA. +# The type of DNS query to perform. One of SRV, A, AAAA or MX. [ type: | default = 'SRV' ] # The port number used if the query type is not SRV.