github.com/thanos-io/thanos@v0.32.5/pkg/discovery/dns/provider.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package dns
     5  
     6  import (
     7  	"context"
     8  	"net"
     9  	"strings"
    10  	"sync"
    11  
    12  	"github.com/go-kit/log"
    13  	"github.com/go-kit/log/level"
    14  	"github.com/prometheus/client_golang/prometheus"
    15  	"github.com/prometheus/client_golang/prometheus/promauto"
    16  
    17  	"github.com/thanos-io/thanos/pkg/discovery/dns/godns"
    18  	"github.com/thanos-io/thanos/pkg/discovery/dns/miekgdns"
    19  	"github.com/thanos-io/thanos/pkg/errutil"
    20  	"github.com/thanos-io/thanos/pkg/extprom"
    21  )
    22  
    23  // Provider is a stateful cache for asynchronous DNS resolutions. It provides a way to resolve addresses and obtain them.
    24  type Provider struct {
    25  	sync.RWMutex
    26  	resolver Resolver
    27  	// A map from domain name to a slice of resolved targets.
    28  	resolved map[string][]string
    29  	logger   log.Logger
    30  
    31  	resolverAddrs         *extprom.TxGaugeVec
    32  	resolverLookupsCount  prometheus.Counter
    33  	resolverFailuresCount prometheus.Counter
    34  }
    35  
    36  type ResolverType string
    37  
    38  const (
    39  	GolangResolverType   ResolverType = "golang"
    40  	MiekgdnsResolverType ResolverType = "miekgdns"
    41  )
    42  
    43  func (t ResolverType) ToResolver(logger log.Logger) ipLookupResolver {
    44  	var r ipLookupResolver
    45  	switch t {
    46  	case GolangResolverType:
    47  		r = &godns.Resolver{Resolver: net.DefaultResolver}
    48  	case MiekgdnsResolverType:
    49  		r = &miekgdns.Resolver{ResolvConf: miekgdns.DefaultResolvConfPath}
    50  	default:
    51  		level.Warn(logger).Log("msg", "no such resolver type, defaulting to golang", "type", t)
    52  		r = &godns.Resolver{Resolver: net.DefaultResolver}
    53  	}
    54  	return r
    55  }
    56  
    57  // NewProvider returns a new empty provider with a given resolver type.
    58  // If empty resolver type is net.DefaultResolver.
    59  func NewProvider(logger log.Logger, reg prometheus.Registerer, resolverType ResolverType) *Provider {
    60  	p := &Provider{
    61  		resolver: NewResolver(resolverType.ToResolver(logger), logger),
    62  		resolved: make(map[string][]string),
    63  		logger:   logger,
    64  		resolverAddrs: extprom.NewTxGaugeVec(reg, prometheus.GaugeOpts{
    65  			Name: "dns_provider_results",
    66  			Help: "The number of resolved endpoints for each configured address",
    67  		}, []string{"addr"}),
    68  		resolverLookupsCount: promauto.With(reg).NewCounter(prometheus.CounterOpts{
    69  			Name: "dns_lookups_total",
    70  			Help: "The number of DNS lookups resolutions attempts",
    71  		}),
    72  		resolverFailuresCount: promauto.With(reg).NewCounter(prometheus.CounterOpts{
    73  			Name: "dns_failures_total",
    74  			Help: "The number of DNS lookup failures",
    75  		}),
    76  	}
    77  
    78  	return p
    79  }
    80  
    81  // Clone returns a new provider from an existing one.
    82  func (p *Provider) Clone() *Provider {
    83  	return &Provider{
    84  		resolver:              p.resolver,
    85  		resolved:              make(map[string][]string),
    86  		logger:                p.logger,
    87  		resolverAddrs:         p.resolverAddrs,
    88  		resolverLookupsCount:  p.resolverLookupsCount,
    89  		resolverFailuresCount: p.resolverFailuresCount,
    90  	}
    91  }
    92  
    93  // IsDynamicNode returns if the specified StoreAPI addr uses
    94  // any kind of SD mechanism.
    95  func IsDynamicNode(addr string) bool {
    96  	qtype, _ := GetQTypeName(addr)
    97  	return qtype != ""
    98  }
    99  
   100  // GetQTypeName splits the provided addr into two parts: the QType (if any)
   101  // and the name.
   102  func GetQTypeName(addr string) (qtype, name string) {
   103  	qtypeAndName := strings.SplitN(addr, "+", 2)
   104  	if len(qtypeAndName) != 2 {
   105  		return "", addr
   106  	}
   107  	return qtypeAndName[0], qtypeAndName[1]
   108  }
   109  
   110  // Resolve stores a list of provided addresses or their DNS records if requested.
   111  // Addresses prefixed with `dns+` or `dnssrv+` will be resolved through respective DNS lookup (A/AAAA or SRV).
   112  // For non-SRV records, it will return an error if a port is not supplied.
   113  func (p *Provider) Resolve(ctx context.Context, addrs []string) error {
   114  	resolvedAddrs := map[string][]string{}
   115  	errs := errutil.MultiError{}
   116  
   117  	for _, addr := range addrs {
   118  		var resolved []string
   119  		qtype, name := GetQTypeName(addr)
   120  		if qtype == "" {
   121  			resolvedAddrs[name] = []string{name}
   122  			continue
   123  		}
   124  
   125  		resolved, err := p.resolver.Resolve(ctx, name, QType(qtype))
   126  		p.resolverLookupsCount.Inc()
   127  		if err != nil {
   128  			// Append all the failed dns resolution in the error list.
   129  			errs.Add(err)
   130  			// The DNS resolution failed. Continue without modifying the old records.
   131  			p.resolverFailuresCount.Inc()
   132  			// Use cached values.
   133  			p.RLock()
   134  			resolved = p.resolved[addr]
   135  			p.RUnlock()
   136  		}
   137  		resolvedAddrs[addr] = resolved
   138  	}
   139  
   140  	// All addresses have been resolved. We can now take an exclusive lock to
   141  	// update the resolved addresses metric and update the local state.
   142  	p.Lock()
   143  	defer p.Unlock()
   144  
   145  	p.resolverAddrs.ResetTx()
   146  	for name, addrs := range resolvedAddrs {
   147  		p.resolverAddrs.WithLabelValues(name).Set(float64(len(addrs)))
   148  	}
   149  	p.resolverAddrs.Submit()
   150  
   151  	p.resolved = resolvedAddrs
   152  
   153  	return errs.Err()
   154  }
   155  
   156  // Addresses returns the latest addresses present in the Provider.
   157  func (p *Provider) Addresses() []string {
   158  	p.RLock()
   159  	defer p.RUnlock()
   160  
   161  	var result []string
   162  	for _, addrs := range p.resolved {
   163  		result = append(result, addrs...)
   164  	}
   165  	return result
   166  }