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 }