vendor files

This commit is contained in:
Serguei Bezverkhi
2018-01-09 13:57:14 -05:00
parent 558bc6c02a
commit 7b24313bd6
16547 changed files with 4527373 additions and 0 deletions

View File

@ -0,0 +1,377 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package dns implements a dns resolver to be installed as the default resolver
// in grpc.
package dns
import (
"encoding/json"
"errors"
"fmt"
"math/rand"
"net"
"os"
"strconv"
"strings"
"sync"
"time"
"golang.org/x/net/context"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/resolver"
)
func init() {
resolver.Register(NewBuilder())
}
const (
defaultPort = "443"
defaultFreq = time.Minute * 30
golang = "GO"
// In DNS, service config is encoded in a TXT record via the mechanism
// described in RFC-1464 using the attribute name grpc_config.
txtAttribute = "grpc_config="
)
var errMissingAddr = errors.New("missing address")
// NewBuilder creates a dnsBuilder which is used to factory DNS resolvers.
func NewBuilder() resolver.Builder {
return &dnsBuilder{freq: defaultFreq}
}
type dnsBuilder struct {
// frequency of polling the DNS server.
freq time.Duration
}
// Build creates and starts a DNS resolver that watches the name resolution of the target.
func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) {
host, port, err := parseTarget(target.Endpoint)
if err != nil {
return nil, err
}
// IP address.
if net.ParseIP(host) != nil {
host, _ = formatIP(host)
addr := []resolver.Address{{Addr: host + ":" + port}}
i := &ipResolver{
cc: cc,
ip: addr,
rn: make(chan struct{}, 1),
q: make(chan struct{}),
}
cc.NewAddress(addr)
go i.watcher()
return i, nil
}
// DNS address (non-IP).
ctx, cancel := context.WithCancel(context.Background())
d := &dnsResolver{
freq: b.freq,
host: host,
port: port,
ctx: ctx,
cancel: cancel,
cc: cc,
t: time.NewTimer(0),
rn: make(chan struct{}, 1),
}
d.wg.Add(1)
go d.watcher()
return d, nil
}
// Scheme returns the naming scheme of this resolver builder, which is "dns".
func (b *dnsBuilder) Scheme() string {
return "dns"
}
// ipResolver watches for the name resolution update for an IP address.
type ipResolver struct {
cc resolver.ClientConn
ip []resolver.Address
// rn channel is used by ResolveNow() to force an immediate resolution of the target.
rn chan struct{}
q chan struct{}
}
// ResolveNow resend the address it stores, no resolution is needed.
func (i *ipResolver) ResolveNow(opt resolver.ResolveNowOption) {
select {
case i.rn <- struct{}{}:
default:
}
}
// Close closes the ipResolver.
func (i *ipResolver) Close() {
close(i.q)
}
func (i *ipResolver) watcher() {
for {
select {
case <-i.rn:
i.cc.NewAddress(i.ip)
case <-i.q:
return
}
}
}
// dnsResolver watches for the name resolution update for a non-IP target.
type dnsResolver struct {
freq time.Duration
host string
port string
ctx context.Context
cancel context.CancelFunc
cc resolver.ClientConn
// rn channel is used by ResolveNow() to force an immediate resolution of the target.
rn chan struct{}
t *time.Timer
// wg is used to enforce Close() to return after the watcher() goroutine has finished.
// Otherwise, data race will be possible. [Race Example] in dns_resolver_test we
// replace the real lookup functions with mocked ones to facilitate testing.
// If Close() doesn't wait for watcher() goroutine finishes, race detector sometimes
// will warns lookup (READ the lookup function pointers) inside watcher() goroutine
// has data race with replaceNetFunc (WRITE the lookup function pointers).
wg sync.WaitGroup
}
// ResolveNow invoke an immediate resolution of the target that this dnsResolver watches.
func (d *dnsResolver) ResolveNow(opt resolver.ResolveNowOption) {
select {
case d.rn <- struct{}{}:
default:
}
}
// Close closes the dnsResolver.
func (d *dnsResolver) Close() {
d.cancel()
d.wg.Wait()
d.t.Stop()
}
func (d *dnsResolver) watcher() {
defer d.wg.Done()
for {
select {
case <-d.ctx.Done():
return
case <-d.t.C:
case <-d.rn:
}
result, sc := d.lookup()
// Next lookup should happen after an interval defined by d.freq.
d.t.Reset(d.freq)
d.cc.NewServiceConfig(string(sc))
d.cc.NewAddress(result)
}
}
func (d *dnsResolver) lookupSRV() []resolver.Address {
var newAddrs []resolver.Address
_, srvs, err := lookupSRV(d.ctx, "grpclb", "tcp", d.host)
if err != nil {
grpclog.Infof("grpc: failed dns SRV record lookup due to %v.\n", err)
return nil
}
for _, s := range srvs {
lbAddrs, err := lookupHost(d.ctx, s.Target)
if err != nil {
grpclog.Warningf("grpc: failed load banlacer address dns lookup due to %v.\n", err)
continue
}
for _, a := range lbAddrs {
a, ok := formatIP(a)
if !ok {
grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err)
continue
}
addr := a + ":" + strconv.Itoa(int(s.Port))
newAddrs = append(newAddrs, resolver.Address{Addr: addr, Type: resolver.GRPCLB, ServerName: s.Target})
}
}
return newAddrs
}
func (d *dnsResolver) lookupTXT() string {
ss, err := lookupTXT(d.ctx, d.host)
if err != nil {
grpclog.Warningf("grpc: failed dns TXT record lookup due to %v.\n", err)
return ""
}
var res string
for _, s := range ss {
res += s
}
// TXT record must have "grpc_config=" attribute in order to be used as service config.
if !strings.HasPrefix(res, txtAttribute) {
grpclog.Warningf("grpc: TXT record %v missing %v attribute", res, txtAttribute)
return ""
}
return strings.TrimPrefix(res, txtAttribute)
}
func (d *dnsResolver) lookupHost() []resolver.Address {
var newAddrs []resolver.Address
addrs, err := lookupHost(d.ctx, d.host)
if err != nil {
grpclog.Warningf("grpc: failed dns A record lookup due to %v.\n", err)
return nil
}
for _, a := range addrs {
a, ok := formatIP(a)
if !ok {
grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err)
continue
}
addr := a + ":" + d.port
newAddrs = append(newAddrs, resolver.Address{Addr: addr})
}
return newAddrs
}
func (d *dnsResolver) lookup() ([]resolver.Address, string) {
var newAddrs []resolver.Address
newAddrs = d.lookupSRV()
// Support fallback to non-balancer address.
newAddrs = append(newAddrs, d.lookupHost()...)
sc := d.lookupTXT()
return newAddrs, canaryingSC(sc)
}
// formatIP returns ok = false if addr is not a valid textual representation of an IP address.
// If addr is an IPv4 address, return the addr and ok = true.
// If addr is an IPv6 address, return the addr enclosed in square brackets and ok = true.
func formatIP(addr string) (addrIP string, ok bool) {
ip := net.ParseIP(addr)
if ip == nil {
return "", false
}
if ip.To4() != nil {
return addr, true
}
return "[" + addr + "]", true
}
// parseTarget takes the user input target string, returns formatted host and port info.
// If target doesn't specify a port, set the port to be the defaultPort.
// If target is in IPv6 format and host-name is enclosed in sqarue brackets, brackets
// are strippd when setting the host.
// examples:
// target: "www.google.com" returns host: "www.google.com", port: "443"
// target: "ipv4-host:80" returns host: "ipv4-host", port: "80"
// target: "[ipv6-host]" returns host: "ipv6-host", port: "443"
// target: ":80" returns host: "localhost", port: "80"
// target: ":" returns host: "localhost", port: "443"
func parseTarget(target string) (host, port string, err error) {
if target == "" {
return "", "", errMissingAddr
}
if ip := net.ParseIP(target); ip != nil {
// target is an IPv4 or IPv6(without brackets) address
return target, defaultPort, nil
}
if host, port, err = net.SplitHostPort(target); err == nil {
// target has port, i.e ipv4-host:port, [ipv6-host]:port, host-name:port
if host == "" {
// Keep consistent with net.Dial(): If the host is empty, as in ":80", the local system is assumed.
host = "localhost"
}
if port == "" {
// If the port field is empty(target ends with colon), e.g. "[::1]:", defaultPort is used.
port = defaultPort
}
return host, port, nil
}
if host, port, err = net.SplitHostPort(target + ":" + defaultPort); err == nil {
// target doesn't have port
return host, port, nil
}
return "", "", fmt.Errorf("invalid target address %v, error info: %v", target, err)
}
type rawChoice struct {
ClientLanguage *[]string `json:"clientLanguage,omitempty"`
Percentage *int `json:"percentage,omitempty"`
ClientHostName *[]string `json:"clientHostName,omitempty"`
ServiceConfig *json.RawMessage `json:"serviceConfig,omitempty"`
}
func containsString(a *[]string, b string) bool {
if a == nil {
return true
}
for _, c := range *a {
if c == b {
return true
}
}
return false
}
func chosenByPercentage(a *int) bool {
if a == nil {
return true
}
s := rand.NewSource(time.Now().UnixNano())
r := rand.New(s)
if r.Intn(100)+1 > *a {
return false
}
return true
}
func canaryingSC(js string) string {
if js == "" {
return ""
}
var rcs []rawChoice
err := json.Unmarshal([]byte(js), &rcs)
if err != nil {
grpclog.Warningf("grpc: failed to parse service config json string due to %v.\n", err)
return ""
}
cliHostname, err := os.Hostname()
if err != nil {
grpclog.Warningf("grpc: failed to get client hostname due to %v.\n", err)
return ""
}
var sc string
for _, c := range rcs {
if !containsString(c.ClientLanguage, golang) ||
!chosenByPercentage(c.Percentage) ||
!containsString(c.ClientHostName, cliHostname) ||
c.ServiceConfig == nil {
continue
}
sc = string(*c.ServiceConfig)
break
}
return sc
}

View File

@ -0,0 +1,894 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package dns
import (
"fmt"
"net"
"os"
"reflect"
"sync"
"testing"
"time"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/test/leakcheck"
)
func TestMain(m *testing.M) {
cleanup := replaceNetFunc()
code := m.Run()
cleanup()
os.Exit(code)
}
const (
txtBytesLimit = 255
)
type testClientConn struct {
target string
m1 sync.Mutex
addrs []resolver.Address
a int
m2 sync.Mutex
sc string
s int
}
func (t *testClientConn) NewAddress(addresses []resolver.Address) {
t.m1.Lock()
defer t.m1.Unlock()
t.addrs = addresses
t.a++
}
func (t *testClientConn) getAddress() ([]resolver.Address, int) {
t.m1.Lock()
defer t.m1.Unlock()
return t.addrs, t.a
}
func (t *testClientConn) NewServiceConfig(serviceConfig string) {
t.m2.Lock()
defer t.m2.Unlock()
t.sc = serviceConfig
t.s++
}
func (t *testClientConn) getSc() (string, int) {
t.m2.Lock()
defer t.m2.Unlock()
return t.sc, t.s
}
var hostLookupTbl = struct {
sync.Mutex
tbl map[string][]string
}{
tbl: map[string][]string{
"foo.bar.com": {"1.2.3.4", "5.6.7.8"},
"ipv4.single.fake": {"1.2.3.4"},
"srv.ipv4.single.fake": {"2.4.6.8"},
"ipv4.multi.fake": {"1.2.3.4", "5.6.7.8", "9.10.11.12"},
"ipv6.single.fake": {"2607:f8b0:400a:801::1001"},
"ipv6.multi.fake": {"2607:f8b0:400a:801::1001", "2607:f8b0:400a:801::1002", "2607:f8b0:400a:801::1003"},
},
}
func hostLookup(host string) ([]string, error) {
hostLookupTbl.Lock()
defer hostLookupTbl.Unlock()
if addrs, cnt := hostLookupTbl.tbl[host]; cnt {
return addrs, nil
}
return nil, fmt.Errorf("failed to lookup host:%s resolution in hostLookupTbl", host)
}
var srvLookupTbl = struct {
sync.Mutex
tbl map[string][]*net.SRV
}{
tbl: map[string][]*net.SRV{
"_grpclb._tcp.srv.ipv4.single.fake": {&net.SRV{Target: "ipv4.single.fake", Port: 1234}},
"_grpclb._tcp.srv.ipv4.multi.fake": {&net.SRV{Target: "ipv4.multi.fake", Port: 1234}},
"_grpclb._tcp.srv.ipv6.single.fake": {&net.SRV{Target: "ipv6.single.fake", Port: 1234}},
"_grpclb._tcp.srv.ipv6.multi.fake": {&net.SRV{Target: "ipv6.multi.fake", Port: 1234}},
},
}
func srvLookup(service, proto, name string) (string, []*net.SRV, error) {
cname := "_" + service + "._" + proto + "." + name
srvLookupTbl.Lock()
defer srvLookupTbl.Unlock()
if srvs, cnt := srvLookupTbl.tbl[cname]; cnt {
return cname, srvs, nil
}
return "", nil, fmt.Errorf("failed to lookup srv record for %s in srvLookupTbl", cname)
}
// div divides a byte slice into a slice of strings, each of which is of maximum
// 255 bytes length, which is the length limit per TXT record in DNS.
func div(b []byte) []string {
var r []string
for i := 0; i < len(b); i += txtBytesLimit {
if i+txtBytesLimit > len(b) {
r = append(r, string(b[i:]))
} else {
r = append(r, string(b[i:i+txtBytesLimit]))
}
}
return r
}
// scfs contains an array of service config file string in JSON format.
// Notes about the scfs contents and usage:
// scfs contains 4 service config file JSON strings for testing. Inside each
// service config file, there are multiple choices. scfs[0:3] each contains 5
// choices, and first 3 choices are nonmatching choices based on canarying rule,
// while the last two are matched choices. scfs[3] only contains 3 choices, and
// all of them are nonmatching based on canarying rule. For each of scfs[0:3],
// the eventually returned service config, which is from the first of the two
// matched choices, is stored in the corresponding scs element (e.g.
// scfs[0]->scs[0]). scfs and scs elements are used in pair to test the dns
// resolver functionality, with scfs as the input and scs used for validation of
// the output. For scfs[3], it corresponds to empty service config, since there
// isn't a matched choice.
var scfs = []string{
`[
{
"clientLanguage": [
"CPP",
"JAVA"
],
"serviceConfig": {
"loadBalancingPolicy": "grpclb",
"methodConfig": [
{
"name": [
{
"service": "all"
}
],
"timeout": "1s"
}
]
}
},
{
"percentage": 0,
"serviceConfig": {
"loadBalancingPolicy": "grpclb",
"methodConfig": [
{
"name": [
{
"service": "all"
}
],
"timeout": "1s"
}
]
}
},
{
"clientHostName": [
"localhost"
],
"serviceConfig": {
"loadBalancingPolicy": "grpclb",
"methodConfig": [
{
"name": [
{
"service": "all"
}
],
"timeout": "1s"
}
]
}
},
{
"clientLanguage": [
"GO"
],
"percentage": 100,
"serviceConfig": {
"methodConfig": [
{
"name": [
{
"method": "bar"
}
],
"maxRequestMessageBytes": 1024,
"maxResponseMessageBytes": 1024
}
]
}
},
{
"serviceConfig": {
"loadBalancingPolicy": "round_robin",
"methodConfig": [
{
"name": [
{
"service": "foo",
"method": "bar"
}
],
"waitForReady": true
}
]
}
}
]`,
`[
{
"clientLanguage": [
"CPP",
"JAVA"
],
"serviceConfig": {
"loadBalancingPolicy": "grpclb",
"methodConfig": [
{
"name": [
{
"service": "all"
}
],
"timeout": "1s"
}
]
}
},
{
"percentage": 0,
"serviceConfig": {
"loadBalancingPolicy": "grpclb",
"methodConfig": [
{
"name": [
{
"service": "all"
}
],
"timeout": "1s"
}
]
}
},
{
"clientHostName": [
"localhost"
],
"serviceConfig": {
"loadBalancingPolicy": "grpclb",
"methodConfig": [
{
"name": [
{
"service": "all"
}
],
"timeout": "1s"
}
]
}
},
{
"clientLanguage": [
"GO"
],
"percentage": 100,
"serviceConfig": {
"methodConfig": [
{
"name": [
{
"service": "foo",
"method": "bar"
}
],
"waitForReady": true,
"timeout": "1s",
"maxRequestMessageBytes": 1024,
"maxResponseMessageBytes": 1024
}
]
}
},
{
"serviceConfig": {
"loadBalancingPolicy": "round_robin",
"methodConfig": [
{
"name": [
{
"service": "foo",
"method": "bar"
}
],
"waitForReady": true
}
]
}
}
]`,
`[
{
"clientLanguage": [
"CPP",
"JAVA"
],
"serviceConfig": {
"loadBalancingPolicy": "grpclb",
"methodConfig": [
{
"name": [
{
"service": "all"
}
],
"timeout": "1s"
}
]
}
},
{
"percentage": 0,
"serviceConfig": {
"loadBalancingPolicy": "grpclb",
"methodConfig": [
{
"name": [
{
"service": "all"
}
],
"timeout": "1s"
}
]
}
},
{
"clientHostName": [
"localhost"
],
"serviceConfig": {
"loadBalancingPolicy": "grpclb",
"methodConfig": [
{
"name": [
{
"service": "all"
}
],
"timeout": "1s"
}
]
}
},
{
"clientLanguage": [
"GO"
],
"percentage": 100,
"serviceConfig": {
"loadBalancingPolicy": "round_robin",
"methodConfig": [
{
"name": [
{
"service": "foo"
}
],
"waitForReady": true,
"timeout": "1s"
},
{
"name": [
{
"service": "bar"
}
],
"waitForReady": false
}
]
}
},
{
"serviceConfig": {
"loadBalancingPolicy": "round_robin",
"methodConfig": [
{
"name": [
{
"service": "foo",
"method": "bar"
}
],
"waitForReady": true
}
]
}
}
]`,
`[
{
"clientLanguage": [
"CPP",
"JAVA"
],
"serviceConfig": {
"loadBalancingPolicy": "grpclb",
"methodConfig": [
{
"name": [
{
"service": "all"
}
],
"timeout": "1s"
}
]
}
},
{
"percentage": 0,
"serviceConfig": {
"loadBalancingPolicy": "grpclb",
"methodConfig": [
{
"name": [
{
"service": "all"
}
],
"timeout": "1s"
}
]
}
},
{
"clientHostName": [
"localhost"
],
"serviceConfig": {
"loadBalancingPolicy": "grpclb",
"methodConfig": [
{
"name": [
{
"service": "all"
}
],
"timeout": "1s"
}
]
}
}
]`,
}
// scs contains an array of service config string in JSON format.
var scs = []string{
`{
"methodConfig": [
{
"name": [
{
"method": "bar"
}
],
"maxRequestMessageBytes": 1024,
"maxResponseMessageBytes": 1024
}
]
}`,
`{
"methodConfig": [
{
"name": [
{
"service": "foo",
"method": "bar"
}
],
"waitForReady": true,
"timeout": "1s",
"maxRequestMessageBytes": 1024,
"maxResponseMessageBytes": 1024
}
]
}`,
`{
"loadBalancingPolicy": "round_robin",
"methodConfig": [
{
"name": [
{
"service": "foo"
}
],
"waitForReady": true,
"timeout": "1s"
},
{
"name": [
{
"service": "bar"
}
],
"waitForReady": false
}
]
}`,
}
// scLookupTbl is a set, which contains targets that have service config. Target
// not in this set should not have service config.
var scLookupTbl = map[string]bool{
"foo.bar.com": true,
"srv.ipv4.single.fake": true,
"srv.ipv4.multi.fake": true,
"no.attribute": true,
}
// generateSCF generates a slice of strings (aggregately representing a single
// service config file) for the input name, which mocks the result from a real
// DNS TXT record lookup.
func generateSCF(name string) []string {
var b []byte
switch name {
case "foo.bar.com":
b = []byte(scfs[0])
case "srv.ipv4.single.fake":
b = []byte(scfs[1])
case "srv.ipv4.multi.fake":
b = []byte(scfs[2])
default:
b = []byte(scfs[3])
}
if name == "no.attribute" {
return div(b)
}
return div(append([]byte(txtAttribute), b...))
}
// generateSC returns a service config string in JSON format for the input name.
func generateSC(name string) string {
_, cnt := scLookupTbl[name]
if !cnt || name == "no.attribute" {
return ""
}
switch name {
case "foo.bar.com":
return scs[0]
case "srv.ipv4.single.fake":
return scs[1]
case "srv.ipv4.multi.fake":
return scs[2]
default:
return ""
}
}
var txtLookupTbl = struct {
sync.Mutex
tbl map[string][]string
}{
tbl: map[string][]string{
"foo.bar.com": generateSCF("foo.bar.com"),
"srv.ipv4.single.fake": generateSCF("srv.ipv4.single.fake"),
"srv.ipv4.multi.fake": generateSCF("srv.ipv4.multi.fake"),
"srv.ipv6.single.fake": generateSCF("srv.ipv6.single.fake"),
"srv.ipv6.multi.fake": generateSCF("srv.ipv6.multi.fake"),
"no.attribute": generateSCF("no.attribute"),
},
}
func txtLookup(host string) ([]string, error) {
txtLookupTbl.Lock()
defer txtLookupTbl.Unlock()
if scs, cnt := txtLookupTbl.tbl[host]; cnt {
return scs, nil
}
return nil, fmt.Errorf("failed to lookup TXT:%s resolution in txtLookupTbl", host)
}
func TestResolve(t *testing.T) {
testDNSResolver(t)
testDNSResolveNow(t)
testIPResolver(t)
}
func testDNSResolver(t *testing.T) {
defer leakcheck.Check(t)
tests := []struct {
target string
addrWant []resolver.Address
scWant string
}{
{
"foo.bar.com",
[]resolver.Address{{Addr: "1.2.3.4" + colonDefaultPort}, {Addr: "5.6.7.8" + colonDefaultPort}},
generateSC("foo.bar.com"),
},
{
"foo.bar.com:1234",
[]resolver.Address{{Addr: "1.2.3.4:1234"}, {Addr: "5.6.7.8:1234"}},
generateSC("foo.bar.com"),
},
{
"srv.ipv4.single.fake",
[]resolver.Address{{Addr: "1.2.3.4:1234", Type: resolver.GRPCLB, ServerName: "ipv4.single.fake"}, {Addr: "2.4.6.8" + colonDefaultPort}},
generateSC("srv.ipv4.single.fake"),
},
{
"srv.ipv4.multi.fake",
[]resolver.Address{
{Addr: "1.2.3.4:1234", Type: resolver.GRPCLB, ServerName: "ipv4.multi.fake"},
{Addr: "5.6.7.8:1234", Type: resolver.GRPCLB, ServerName: "ipv4.multi.fake"},
{Addr: "9.10.11.12:1234", Type: resolver.GRPCLB, ServerName: "ipv4.multi.fake"},
},
generateSC("srv.ipv4.multi.fake"),
},
{
"srv.ipv6.single.fake",
[]resolver.Address{{Addr: "[2607:f8b0:400a:801::1001]:1234", Type: resolver.GRPCLB, ServerName: "ipv6.single.fake"}},
generateSC("srv.ipv6.single.fake"),
},
{
"srv.ipv6.multi.fake",
[]resolver.Address{
{Addr: "[2607:f8b0:400a:801::1001]:1234", Type: resolver.GRPCLB, ServerName: "ipv6.multi.fake"},
{Addr: "[2607:f8b0:400a:801::1002]:1234", Type: resolver.GRPCLB, ServerName: "ipv6.multi.fake"},
{Addr: "[2607:f8b0:400a:801::1003]:1234", Type: resolver.GRPCLB, ServerName: "ipv6.multi.fake"},
},
generateSC("srv.ipv6.multi.fake"),
},
{
"no.attribute",
nil,
generateSC("no.attribute"),
},
}
for _, a := range tests {
b := NewBuilder()
cc := &testClientConn{target: a.target}
r, err := b.Build(resolver.Target{Endpoint: a.target}, cc, resolver.BuildOption{})
if err != nil {
t.Fatalf("%v\n", err)
}
var addrs []resolver.Address
var cnt int
for {
addrs, cnt = cc.getAddress()
if cnt > 0 {
break
}
time.Sleep(time.Millisecond)
}
var sc string
for {
sc, cnt = cc.getSc()
if cnt > 0 {
break
}
time.Sleep(time.Millisecond)
}
if !reflect.DeepEqual(a.addrWant, addrs) {
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", a.target, addrs, a.addrWant)
}
if !reflect.DeepEqual(a.scWant, sc) {
t.Errorf("Resolved service config of target: %q = %+v, want %+v\n", a.target, sc, a.scWant)
}
r.Close()
}
}
func mutateTbl(target string) func() {
hostLookupTbl.Lock()
oldHostTblEntry := hostLookupTbl.tbl[target]
hostLookupTbl.tbl[target] = hostLookupTbl.tbl[target][:len(oldHostTblEntry)-1]
hostLookupTbl.Unlock()
txtLookupTbl.Lock()
oldTxtTblEntry := txtLookupTbl.tbl[target]
txtLookupTbl.tbl[target] = []string{""}
txtLookupTbl.Unlock()
return func() {
hostLookupTbl.Lock()
hostLookupTbl.tbl[target] = oldHostTblEntry
hostLookupTbl.Unlock()
txtLookupTbl.Lock()
txtLookupTbl.tbl[target] = oldTxtTblEntry
txtLookupTbl.Unlock()
}
}
func testDNSResolveNow(t *testing.T) {
defer leakcheck.Check(t)
tests := []struct {
target string
addrWant []resolver.Address
addrNext []resolver.Address
scWant string
scNext string
}{
{
"foo.bar.com",
[]resolver.Address{{Addr: "1.2.3.4" + colonDefaultPort}, {Addr: "5.6.7.8" + colonDefaultPort}},
[]resolver.Address{{Addr: "1.2.3.4" + colonDefaultPort}},
generateSC("foo.bar.com"),
"",
},
}
for _, a := range tests {
b := NewBuilder()
cc := &testClientConn{target: a.target}
r, err := b.Build(resolver.Target{Endpoint: a.target}, cc, resolver.BuildOption{})
if err != nil {
t.Fatalf("%v\n", err)
}
var addrs []resolver.Address
var cnt int
for {
addrs, cnt = cc.getAddress()
if cnt > 0 {
break
}
time.Sleep(time.Millisecond)
}
var sc string
for {
sc, cnt = cc.getSc()
if cnt > 0 {
break
}
time.Sleep(time.Millisecond)
}
if !reflect.DeepEqual(a.addrWant, addrs) {
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", a.target, addrs, a.addrWant)
}
if !reflect.DeepEqual(a.scWant, sc) {
t.Errorf("Resolved service config of target: %q = %+v, want %+v\n", a.target, sc, a.scWant)
}
revertTbl := mutateTbl(a.target)
r.ResolveNow(resolver.ResolveNowOption{})
for {
addrs, cnt = cc.getAddress()
if cnt == 2 {
break
}
time.Sleep(time.Millisecond)
}
for {
sc, cnt = cc.getSc()
if cnt == 2 {
break
}
time.Sleep(time.Millisecond)
}
if !reflect.DeepEqual(a.addrNext, addrs) {
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", a.target, addrs, a.addrNext)
}
if !reflect.DeepEqual(a.scNext, sc) {
t.Errorf("Resolved service config of target: %q = %+v, want %+v\n", a.target, sc, a.scNext)
}
revertTbl()
r.Close()
}
}
const colonDefaultPort = ":" + defaultPort
func testIPResolver(t *testing.T) {
defer leakcheck.Check(t)
tests := []struct {
target string
want []resolver.Address
}{
{"127.0.0.1", []resolver.Address{{Addr: "127.0.0.1" + colonDefaultPort}}},
{"127.0.0.1:12345", []resolver.Address{{Addr: "127.0.0.1:12345"}}},
{"::1", []resolver.Address{{Addr: "[::1]" + colonDefaultPort}}},
{"[::1]:12345", []resolver.Address{{Addr: "[::1]:12345"}}},
{"[::1]:", []resolver.Address{{Addr: "[::1]:443"}}},
{"2001:db8:85a3::8a2e:370:7334", []resolver.Address{{Addr: "[2001:db8:85a3::8a2e:370:7334]" + colonDefaultPort}}},
{"[2001:db8:85a3::8a2e:370:7334]", []resolver.Address{{Addr: "[2001:db8:85a3::8a2e:370:7334]" + colonDefaultPort}}},
{"[2001:db8:85a3::8a2e:370:7334]:12345", []resolver.Address{{Addr: "[2001:db8:85a3::8a2e:370:7334]:12345"}}},
{"[2001:db8::1]:http", []resolver.Address{{Addr: "[2001:db8::1]:http"}}},
// TODO(yuxuanli): zone support?
}
for _, v := range tests {
b := NewBuilder()
cc := &testClientConn{target: v.target}
r, err := b.Build(resolver.Target{Endpoint: v.target}, cc, resolver.BuildOption{})
if err != nil {
t.Fatalf("%v\n", err)
}
var addrs []resolver.Address
var cnt int
for {
addrs, cnt = cc.getAddress()
if cnt > 0 {
break
}
time.Sleep(time.Millisecond)
}
if !reflect.DeepEqual(v.want, addrs) {
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", v.target, addrs, v.want)
}
r.ResolveNow(resolver.ResolveNowOption{})
for {
addrs, cnt = cc.getAddress()
if cnt == 2 {
break
}
time.Sleep(time.Millisecond)
}
if !reflect.DeepEqual(v.want, addrs) {
t.Errorf("Resolved addresses of target: %q = %+v, want %+v\n", v.target, addrs, v.want)
}
r.Close()
}
}
func TestResolveFunc(t *testing.T) {
defer leakcheck.Check(t)
tests := []struct {
addr string
want error
}{
// TODO(yuxuanli): More false cases?
{"www.google.com", nil},
{"foo.bar:12345", nil},
{"127.0.0.1", nil},
{"127.0.0.1:12345", nil},
{"[::1]:80", nil},
{"[2001:db8:a0b:12f0::1]:21", nil},
{":80", nil},
{"127.0.0...1:12345", nil},
{"[fe80::1%lo0]:80", nil},
{"golang.org:http", nil},
{"[2001:db8::1]:http", nil},
{":", nil},
{"", errMissingAddr},
{"[2001:db8:a0b:12f0::1", errForInvalidTarget},
}
b := NewBuilder()
for _, v := range tests {
cc := &testClientConn{target: v.addr}
r, err := b.Build(resolver.Target{Endpoint: v.addr}, cc, resolver.BuildOption{})
if err == nil {
r.Close()
}
if !reflect.DeepEqual(err, v.want) {
t.Errorf("Build(%q, cc, resolver.BuildOption{}) = %v, want %v", v.addr, err, v.want)
}
}
}

35
vendor/google.golang.org/grpc/resolver/dns/go17.go generated vendored Normal file
View File

@ -0,0 +1,35 @@
// +build go1.6, !go1.8
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package dns
import (
"net"
"golang.org/x/net/context"
)
var (
lookupHost = func(ctx context.Context, host string) ([]string, error) { return net.LookupHost(host) }
lookupSRV = func(ctx context.Context, service, proto, name string) (string, []*net.SRV, error) {
return net.LookupSRV(service, proto, name)
}
lookupTXT = func(ctx context.Context, name string) ([]string, error) { return net.LookupTXT(name) }
)

View File

@ -0,0 +1,50 @@
// +build go1.6, !go1.8
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package dns
import (
"fmt"
"net"
"golang.org/x/net/context"
)
var errForInvalidTarget = fmt.Errorf("invalid target address [2001:db8:a0b:12f0::1, error info: missing ']' in address [2001:db8:a0b:12f0::1:443")
func replaceNetFunc() func() {
oldLookupHost := lookupHost
oldLookupSRV := lookupSRV
oldLookupTXT := lookupTXT
lookupHost = func(ctx context.Context, host string) ([]string, error) {
return hostLookup(host)
}
lookupSRV = func(ctx context.Context, service, proto, name string) (string, []*net.SRV, error) {
return srvLookup(service, proto, name)
}
lookupTXT = func(ctx context.Context, host string) ([]string, error) {
return txtLookup(host)
}
return func() {
lookupHost = oldLookupHost
lookupSRV = oldLookupSRV
lookupTXT = oldLookupTXT
}
}

29
vendor/google.golang.org/grpc/resolver/dns/go18.go generated vendored Normal file
View File

@ -0,0 +1,29 @@
// +build go1.8
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package dns
import "net"
var (
lookupHost = net.DefaultResolver.LookupHost
lookupSRV = net.DefaultResolver.LookupSRV
lookupTXT = net.DefaultResolver.LookupTXT
)

View File

@ -0,0 +1,49 @@
// +build go1.8
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package dns
import (
"context"
"fmt"
"net"
)
var errForInvalidTarget = fmt.Errorf("invalid target address [2001:db8:a0b:12f0::1, error info: address [2001:db8:a0b:12f0::1:443: missing ']' in address")
func replaceNetFunc() func() {
oldLookupHost := lookupHost
oldLookupSRV := lookupSRV
oldLookupTXT := lookupTXT
lookupHost = func(ctx context.Context, host string) ([]string, error) {
return hostLookup(host)
}
lookupSRV = func(ctx context.Context, service, proto, name string) (string, []*net.SRV, error) {
return srvLookup(service, proto, name)
}
lookupTXT = func(ctx context.Context, host string) ([]string, error) {
return txtLookup(host)
}
return func() {
lookupHost = oldLookupHost
lookupSRV = oldLookupSRV
lookupTXT = oldLookupTXT
}
}

View File

@ -0,0 +1,91 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package manual defines a resolver that can be used to manually send resolved
// addresses to ClientConn.
package manual
import (
"strconv"
"time"
"google.golang.org/grpc/resolver"
)
// NewBuilderWithScheme creates a new test resolver builder with the given scheme.
func NewBuilderWithScheme(scheme string) *Resolver {
return &Resolver{
scheme: scheme,
}
}
// Resolver is also a resolver builder.
// It's build() function always returns itself.
type Resolver struct {
scheme string
// Fields actually belong to the resolver.
cc resolver.ClientConn
bootstrapAddrs []resolver.Address
}
// InitialAddrs adds resolved addresses to the resolver so that
// NewAddress doesn't need to be explicitly called after Dial.
func (r *Resolver) InitialAddrs(addrs []resolver.Address) {
r.bootstrapAddrs = addrs
}
// Build returns itself for Resolver, because it's both a builder and a resolver.
func (r *Resolver) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) {
r.cc = cc
if r.bootstrapAddrs != nil {
r.NewAddress(r.bootstrapAddrs)
}
return r, nil
}
// Scheme returns the test scheme.
func (r *Resolver) Scheme() string {
return r.scheme
}
// ResolveNow is a noop for Resolver.
func (*Resolver) ResolveNow(o resolver.ResolveNowOption) {}
// Close is a noop for Resolver.
func (*Resolver) Close() {}
// NewAddress calls cc.NewAddress.
func (r *Resolver) NewAddress(addrs []resolver.Address) {
r.cc.NewAddress(addrs)
}
// NewServiceConfig calls cc.NewServiceConfig.
func (r *Resolver) NewServiceConfig(sc string) {
r.cc.NewServiceConfig(sc)
}
// GenerateAndRegisterManualResolver generates a random scheme and a Resolver
// with it. It also regieter this Resolver.
// It returns the Resolver and a cleanup function to unregister it.
func GenerateAndRegisterManualResolver() (*Resolver, func()) {
scheme := strconv.FormatInt(time.Now().UnixNano(), 36)
r := NewBuilderWithScheme(scheme)
resolver.Register(r)
return r, func() { resolver.UnregisterForTesting(scheme) }
}

View File

@ -0,0 +1,57 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package passthrough implements a pass-through resolver. It sends the target
// name without scheme back to gRPC as resolved address.
package passthrough
import "google.golang.org/grpc/resolver"
const scheme = "passthrough"
type passthroughBuilder struct{}
func (*passthroughBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) {
r := &passthroughResolver{
target: target,
cc: cc,
}
r.start()
return r, nil
}
func (*passthroughBuilder) Scheme() string {
return scheme
}
type passthroughResolver struct {
target resolver.Target
cc resolver.ClientConn
}
func (r *passthroughResolver) start() {
r.cc.NewAddress([]resolver.Address{{Addr: r.target.Endpoint}})
}
func (*passthroughResolver) ResolveNow(o resolver.ResolveNowOption) {}
func (*passthroughResolver) Close() {}
func init() {
resolver.Register(&passthroughBuilder{})
}

155
vendor/google.golang.org/grpc/resolver/resolver.go generated vendored Normal file
View File

@ -0,0 +1,155 @@
/*
*
* Copyright 2017 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package resolver defines APIs for name resolution in gRPC.
// All APIs in this package are experimental.
package resolver
var (
// m is a map from scheme to resolver builder.
m = make(map[string]Builder)
// defaultScheme is the default scheme to use.
defaultScheme = "passthrough"
)
// TODO(bar) install dns resolver in init(){}.
// Register registers the resolver builder to the resolver map.
// b.Scheme will be used as the scheme registered with this builder.
func Register(b Builder) {
m[b.Scheme()] = b
}
// Get returns the resolver builder registered with the given scheme.
// If no builder is register with the scheme, the default scheme will
// be used.
// If the default scheme is not modified, "passthrough" will be the default
// scheme, and the preinstalled dns resolver will be used.
// If the default scheme is modified, and a resolver is registered with
// the scheme, that resolver will be returned.
// If the default scheme is modified, and no resolver is registered with
// the scheme, nil will be returned.
func Get(scheme string) Builder {
if b, ok := m[scheme]; ok {
return b
}
if b, ok := m[defaultScheme]; ok {
return b
}
return nil
}
// SetDefaultScheme sets the default scheme that will be used.
// The default default scheme is "passthrough".
func SetDefaultScheme(scheme string) {
defaultScheme = scheme
}
// AddressType indicates the address type returned by name resolution.
type AddressType uint8
const (
// Backend indicates the address is for a backend server.
Backend AddressType = iota
// GRPCLB indicates the address is for a grpclb load balancer.
GRPCLB
)
// Address represents a server the client connects to.
// This is the EXPERIMENTAL API and may be changed or extended in the future.
type Address struct {
// Addr is the server address on which a connection will be established.
Addr string
// Type is the type of this address.
Type AddressType
// ServerName is the name of this address.
//
// e.g. if Type is GRPCLB, ServerName should be the name of the remote load
// balancer, not the name of the backend.
ServerName string
// Metadata is the information associated with Addr, which may be used
// to make load balancing decision.
Metadata interface{}
}
// BuildOption includes additional information for the builder to create
// the resolver.
type BuildOption struct {
// UserOptions can be used to pass configuration between DialOptions and the
// resolver.
UserOptions interface{}
}
// ClientConn contains the callbacks for resolver to notify any updates
// to the gRPC ClientConn.
//
// This interface is to be implemented by gRPC. Users should not need a
// brand new implementation of this interface. For the situations like
// testing, the new implementation should embed this interface. This allows
// gRPC to add new methods to this interface.
type ClientConn interface {
// NewAddress is called by resolver to notify ClientConn a new list
// of resolved addresses.
// The address list should be the complete list of resolved addresses.
NewAddress(addresses []Address)
// NewServiceConfig is called by resolver to notify ClientConn a new
// service config. The service config should be provided as a json string.
NewServiceConfig(serviceConfig string)
}
// Target represents a target for gRPC, as specified in:
// https://github.com/grpc/grpc/blob/master/doc/naming.md.
type Target struct {
Scheme string
Authority string
Endpoint string
}
// Builder creates a resolver that will be used to watch name resolution updates.
type Builder interface {
// Build creates a new resolver for the given target.
//
// gRPC dial calls Build synchronously, and fails if the returned error is
// not nil.
Build(target Target, cc ClientConn, opts BuildOption) (Resolver, error)
// Scheme returns the scheme supported by this resolver.
// Scheme is defined at https://github.com/grpc/grpc/blob/master/doc/naming.md.
Scheme() string
}
// ResolveNowOption includes additional information for ResolveNow.
type ResolveNowOption struct{}
// Resolver watches for the updates on the specified target.
// Updates include address updates and service config updates.
type Resolver interface {
// ResolveNow will be called by gRPC to try to resolve the target name
// again. It's just a hint, resolver can ignore this if it's not necessary.
//
// It could be called multiple times concurrently.
ResolveNow(ResolveNowOption)
// Close closes the resolver.
Close()
}
// UnregisterForTesting removes the resolver builder with the given scheme from the
// resolver map.
// This function is for testing only.
func UnregisterForTesting(scheme string) {
delete(m, scheme)
}