blob: 600061ce8ea8455e6266515f20aa5145083fad5c [file] [log] [blame]
khenaidooffe076b2019-01-15 16:08:08 -05001// Copyright 2015 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Package srv looks up DNS SRV records.
16package srv
17
18import (
19 "fmt"
20 "net"
21 "net/url"
22 "strings"
23
24 "github.com/coreos/etcd/pkg/types"
25)
26
27var (
28 // indirection for testing
29 lookupSRV = net.LookupSRV // net.DefaultResolver.LookupSRV when ctxs don't conflict
30 resolveTCPAddr = net.ResolveTCPAddr
31)
32
33// GetCluster gets the cluster information via DNS discovery.
34// Also sees each entry as a separate instance.
35func GetCluster(service, name, dns string, apurls types.URLs) ([]string, error) {
36 tempName := int(0)
37 tcp2ap := make(map[string]url.URL)
38
39 // First, resolve the apurls
40 for _, url := range apurls {
41 tcpAddr, err := resolveTCPAddr("tcp", url.Host)
42 if err != nil {
43 return nil, err
44 }
45 tcp2ap[tcpAddr.String()] = url
46 }
47
48 stringParts := []string{}
49 updateNodeMap := func(service, scheme string) error {
50 _, addrs, err := lookupSRV(service, "tcp", dns)
51 if err != nil {
52 return err
53 }
54 for _, srv := range addrs {
55 port := fmt.Sprintf("%d", srv.Port)
56 host := net.JoinHostPort(srv.Target, port)
57 tcpAddr, terr := resolveTCPAddr("tcp", host)
58 if terr != nil {
59 err = terr
60 continue
61 }
62 n := ""
63 url, ok := tcp2ap[tcpAddr.String()]
64 if ok {
65 n = name
66 }
67 if n == "" {
68 n = fmt.Sprintf("%d", tempName)
69 tempName++
70 }
71 // SRV records have a trailing dot but URL shouldn't.
72 shortHost := strings.TrimSuffix(srv.Target, ".")
73 urlHost := net.JoinHostPort(shortHost, port)
74 if ok && url.Scheme != scheme {
75 err = fmt.Errorf("bootstrap at %s from DNS for %s has scheme mismatch with expected peer %s", scheme+"://"+urlHost, service, url.String())
76 } else {
77 stringParts = append(stringParts, fmt.Sprintf("%s=%s://%s", n, scheme, urlHost))
78 }
79 }
80 if len(stringParts) == 0 {
81 return err
82 }
83 return nil
84 }
85
86 failCount := 0
87 err := updateNodeMap(service+"-ssl", "https")
88 srvErr := make([]string, 2)
89 if err != nil {
90 srvErr[0] = fmt.Sprintf("error querying DNS SRV records for _%s-ssl %s", service, err)
91 failCount++
92 }
93 err = updateNodeMap(service, "http")
94 if err != nil {
95 srvErr[1] = fmt.Sprintf("error querying DNS SRV records for _%s %s", service, err)
96 failCount++
97 }
98 if failCount == 2 {
99 return nil, fmt.Errorf("srv: too many errors querying DNS SRV records (%q, %q)", srvErr[0], srvErr[1])
100 }
101 return stringParts, nil
102}
103
104type SRVClients struct {
105 Endpoints []string
106 SRVs []*net.SRV
107}
108
109// GetClient looks up the client endpoints for a service and domain.
110func GetClient(service, domain string) (*SRVClients, error) {
111 var urls []*url.URL
112 var srvs []*net.SRV
113
114 updateURLs := func(service, scheme string) error {
115 _, addrs, err := lookupSRV(service, "tcp", domain)
116 if err != nil {
117 return err
118 }
119 for _, srv := range addrs {
120 urls = append(urls, &url.URL{
121 Scheme: scheme,
122 Host: net.JoinHostPort(srv.Target, fmt.Sprintf("%d", srv.Port)),
123 })
124 }
125 srvs = append(srvs, addrs...)
126 return nil
127 }
128
129 errHTTPS := updateURLs(service+"-ssl", "https")
130 errHTTP := updateURLs(service, "http")
131
132 if errHTTPS != nil && errHTTP != nil {
133 return nil, fmt.Errorf("dns lookup errors: %s and %s", errHTTPS, errHTTP)
134 }
135
136 endpoints := make([]string, len(urls))
137 for i := range urls {
138 endpoints[i] = urls[i].String()
139 }
140 return &SRVClients{Endpoints: endpoints, SRVs: srvs}, nil
141}