VictoriaMetrics/lib/promscrape/discovery/dns/dns.go

195 lines
5.1 KiB
Go

package dns
import (
"context"
"flag"
"fmt"
"net"
"strconv"
"strings"
"time"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/netutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discoveryutils"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
)
// SDCheckInterval defines interval for targets refresh.
var SDCheckInterval = flag.Duration("promscrape.dnsSDCheckInterval", 30*time.Second, "Interval for checking for changes in dns. "+
"This works only if dns_sd_configs is configured in '-promscrape.config' file. "+
"See https://docs.victoriametrics.com/sd_configs.html#dns_sd_configs for details")
// SDConfig represents service discovery config for DNS.
//
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#dns_sd_config
type SDConfig struct {
Names []string `yaml:"names"`
Type string `yaml:"type,omitempty"`
Port *int `yaml:"port,omitempty"`
// RefreshInterval time.Duration `yaml:"refresh_interval"`
// refresh_interval is obtained from `-promscrape.dnsSDCheckInterval` command-line option.
}
// GetLabels returns DNS labels according to sdc.
func (sdc *SDConfig) GetLabels(_ string) ([]*promutils.Labels, error) {
if len(sdc.Names) == 0 {
return nil, fmt.Errorf("`names` cannot be empty in `dns_sd_config`")
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
typ := sdc.Type
if typ == "" {
typ = "SRV"
}
typ = strings.ToUpper(typ)
switch typ {
case "SRV":
ms := getSRVAddrLabels(ctx, sdc)
return ms, nil
case "MX":
ms := getMXAddrLabels(ctx, sdc)
return ms, nil
case "A", "AAAA":
return getAAddrLabels(ctx, sdc, typ)
default:
return nil, fmt.Errorf("unexpected `type` in `dns_sd_config`: %q; supported values: SRV, A, AAAA", typ)
}
}
// MustStop stops further usage for sdc.
func (sdc *SDConfig) MustStop() {
// nothing to do
}
func getMXAddrLabels(ctx context.Context, sdc *SDConfig) []*promutils.Labels {
port := 25
if sdc.Port != nil {
port = *sdc.Port
}
type result struct {
name string
mx []*net.MX
err error
}
ch := make(chan result, len(sdc.Names))
for _, name := range sdc.Names {
go func(name string) {
mx, err := netutil.Resolver.LookupMX(ctx, name)
ch <- result{
name: name,
mx: mx,
err: err,
}
}(name)
}
var ms []*promutils.Labels
for range sdc.Names {
r := <-ch
if r.err != nil {
logger.Errorf("dns_sd_config: skipping MX lookup for %q because of error: %s", r.name, r.err)
continue
}
for _, mx := range r.mx {
target := mx.Host
for strings.HasSuffix(target, ".") {
target = target[:len(target)-1]
}
ms = appendMXLabels(ms, r.name, target, port)
}
}
return ms
}
func getSRVAddrLabels(ctx context.Context, sdc *SDConfig) []*promutils.Labels {
type result struct {
name string
as []*net.SRV
err error
}
ch := make(chan result, len(sdc.Names))
for _, name := range sdc.Names {
go func(name string) {
_, as, err := netutil.Resolver.LookupSRV(ctx, "", "", name)
ch <- result{
name: name,
as: as,
err: err,
}
}(name)
}
var ms []*promutils.Labels
for range sdc.Names {
r := <-ch
if r.err != nil {
logger.Errorf("dns_sd_config: skipping SRV lookup for %q because of error: %s", r.name, r.err)
continue
}
for _, a := range r.as {
target := a.Target
for strings.HasSuffix(target, ".") {
target = target[:len(target)-1]
}
ms = appendAddrLabels(ms, r.name, target, int(a.Port))
}
}
return ms
}
func getAAddrLabels(ctx context.Context, sdc *SDConfig, lookupType string) ([]*promutils.Labels, error) {
if sdc.Port == nil {
return nil, fmt.Errorf("missing `port` in `dns_sd_config` for `type: %s`", lookupType)
}
port := *sdc.Port
type result struct {
name string
ips []net.IPAddr
err error
}
ch := make(chan result, len(sdc.Names))
for _, name := range sdc.Names {
go func(name string) {
ips, err := netutil.Resolver.LookupIPAddr(ctx, name)
ch <- result{
name: name,
ips: ips,
err: err,
}
}(name)
}
var ms []*promutils.Labels
for range sdc.Names {
r := <-ch
if r.err != nil {
logger.Errorf("error in %s lookup for %q: %s", lookupType, r.name, r.err)
continue
}
for _, ip := range r.ips {
isIPv4 := ip.IP.To4() != nil
if lookupType == "AAAA" && isIPv4 || lookupType == "A" && !isIPv4 {
continue
}
ms = appendAddrLabels(ms, r.name, ip.IP.String(), port)
}
}
return ms, nil
}
func appendMXLabels(ms []*promutils.Labels, name, target string, port int) []*promutils.Labels {
addr := discoveryutils.JoinHostPort(target, port)
m := promutils.NewLabels(3)
m.Add("__address__", addr)
m.Add("__meta_dns_name", name)
m.Add("__meta_dns_mx_record_target", target)
return append(ms, m)
}
func appendAddrLabels(ms []*promutils.Labels, name, target string, port int) []*promutils.Labels {
addr := discoveryutils.JoinHostPort(target, port)
m := promutils.NewLabels(4)
m.Add("__address__", addr)
m.Add("__meta_dns_name", name)
m.Add("__meta_dns_srv_record_target", target)
m.Add("__meta_dns_srv_record_port", strconv.Itoa(port))
return append(ms, m)
}