*: move srv into pkg discovery
parent
7ec2e382bd
commit
08e9c25ea5
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
Copyright 2014 CoreOS, Inc.
|
||||||
|
|
||||||
|
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 discovery
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// indirection for testing
|
||||||
|
lookupSRV = net.LookupSRV
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(barakmich): Currently ignores priority and weight (as they don't make as much sense for a bootstrap)
|
||||||
|
// Also doesn't do any lookups for the token (though it could)
|
||||||
|
// Also sees each entry as a separate instance.
|
||||||
|
func SRVGetCluster(name, dns string, defaultToken string, apurls types.URLs) (string, string, error) {
|
||||||
|
stringParts := make([]string, 0)
|
||||||
|
tempName := int(0)
|
||||||
|
tcpAPUrls := make([]string, 0)
|
||||||
|
|
||||||
|
// First, resolve the apurls
|
||||||
|
for _, url := range apurls {
|
||||||
|
tcpAddr, err := net.ResolveTCPAddr("tcp", url.Host)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("discovery: Couldn't resolve host %s during SRV discovery", url.Host)
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
tcpAPUrls = append(tcpAPUrls, tcpAddr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNodeMap := func(service, prefix string) error {
|
||||||
|
_, addrs, err := lookupSRV(service, "tcp", dns)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, srv := range addrs {
|
||||||
|
host := net.JoinHostPort(srv.Target, fmt.Sprintf("%d", srv.Port))
|
||||||
|
tcpAddr, err := net.ResolveTCPAddr("tcp", host)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("discovery: Couldn't resolve host %s during SRV discovery", host)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
n := ""
|
||||||
|
for _, url := range tcpAPUrls {
|
||||||
|
if url == tcpAddr.String() {
|
||||||
|
n = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n == "" {
|
||||||
|
n = fmt.Sprintf("%d", tempName)
|
||||||
|
tempName += 1
|
||||||
|
}
|
||||||
|
stringParts = append(stringParts, fmt.Sprintf("%s=%s%s", n, prefix, tcpAddr.String()))
|
||||||
|
log.Printf("discovery: Got bootstrap from DNS for %s at host %s to %s%s", service, host, prefix, tcpAddr.String())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
failCount := 0
|
||||||
|
err := updateNodeMap("etcd-server-ssl", "https://")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("discovery: Error querying DNS SRV records for _etcd-server-ssl %s", err)
|
||||||
|
failCount += 1
|
||||||
|
}
|
||||||
|
err = updateNodeMap("etcd-server", "http://")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("discovery: Error querying DNS SRV records for _etcd-server %s", err)
|
||||||
|
failCount += 1
|
||||||
|
}
|
||||||
|
if failCount == 2 {
|
||||||
|
log.Printf("discovery: SRV discovery failed: too many errors querying DNS SRV records")
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
return strings.Join(stringParts, ","), defaultToken, nil
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
Copyright 2014 CoreOS, Inc.
|
||||||
|
|
||||||
|
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 discovery
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/pkg/testutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSRVGetCluster(t *testing.T) {
|
||||||
|
defer func() { lookupSRV = net.LookupSRV }()
|
||||||
|
|
||||||
|
name := "dnsClusterTest"
|
||||||
|
tests := []struct {
|
||||||
|
withSSL []*net.SRV
|
||||||
|
withoutSSL []*net.SRV
|
||||||
|
urls []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
[]*net.SRV{},
|
||||||
|
[]*net.SRV{},
|
||||||
|
nil,
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]*net.SRV{
|
||||||
|
&net.SRV{Target: "10.0.0.1", Port: 2480},
|
||||||
|
&net.SRV{Target: "10.0.0.2", Port: 2480},
|
||||||
|
&net.SRV{Target: "10.0.0.3", Port: 2480},
|
||||||
|
},
|
||||||
|
[]*net.SRV{},
|
||||||
|
nil,
|
||||||
|
"0=https://10.0.0.1:2480,1=https://10.0.0.2:2480,2=https://10.0.0.3:2480",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]*net.SRV{
|
||||||
|
&net.SRV{Target: "10.0.0.1", Port: 2480},
|
||||||
|
&net.SRV{Target: "10.0.0.2", Port: 2480},
|
||||||
|
&net.SRV{Target: "10.0.0.3", Port: 2480},
|
||||||
|
},
|
||||||
|
[]*net.SRV{
|
||||||
|
&net.SRV{Target: "10.0.0.1", Port: 7001},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
"0=https://10.0.0.1:2480,1=https://10.0.0.2:2480,2=https://10.0.0.3:2480,3=http://10.0.0.1:7001",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]*net.SRV{
|
||||||
|
&net.SRV{Target: "10.0.0.1", Port: 2480},
|
||||||
|
&net.SRV{Target: "10.0.0.2", Port: 2480},
|
||||||
|
&net.SRV{Target: "10.0.0.3", Port: 2480},
|
||||||
|
},
|
||||||
|
[]*net.SRV{
|
||||||
|
&net.SRV{Target: "10.0.0.1", Port: 7001},
|
||||||
|
},
|
||||||
|
[]string{"https://10.0.0.1:2480"},
|
||||||
|
"dnsClusterTest=https://10.0.0.1:2480,0=https://10.0.0.2:2480,1=https://10.0.0.3:2480,2=http://10.0.0.1:7001",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
lookupSRV = func(service string, proto string, domain string) (string, []*net.SRV, error) {
|
||||||
|
if service == "etcd-server-ssl" {
|
||||||
|
return "", tt.withSSL, nil
|
||||||
|
}
|
||||||
|
if service == "etcd-server" {
|
||||||
|
return "", tt.withoutSSL, nil
|
||||||
|
}
|
||||||
|
return "", nil, errors.New("Unkown service in mock")
|
||||||
|
}
|
||||||
|
urls := testutil.MustNewURLs(t, tt.urls)
|
||||||
|
str, token, err := SRVGetCluster(name, "example.com", "token", urls)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%d: err: %#v", i, err)
|
||||||
|
}
|
||||||
|
if token != "token" {
|
||||||
|
t.Errorf("%d: token: %s", i, token)
|
||||||
|
}
|
||||||
|
if str != tt.expected {
|
||||||
|
t.Errorf("#%d: cluster = %s, want %s", i, str, tt.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,11 +40,6 @@ const (
|
||||||
privateDirMode = 0700
|
privateDirMode = 0700
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
// indirection for testing
|
|
||||||
lookupSRV = net.LookupSRV
|
|
||||||
)
|
|
||||||
|
|
||||||
func Main() {
|
func Main() {
|
||||||
cfg := NewConfig()
|
cfg := NewConfig()
|
||||||
err := cfg.Parse(os.Args[1:])
|
err := cfg.Parse(os.Args[1:])
|
||||||
|
@ -260,7 +255,7 @@ func setupCluster(cfg *config) (*etcdserver.Cluster, error) {
|
||||||
clusterStr := genClusterString(cfg.name, cfg.apurls)
|
clusterStr := genClusterString(cfg.name, cfg.apurls)
|
||||||
cls, err = etcdserver.NewClusterFromString(cfg.durl, clusterStr)
|
cls, err = etcdserver.NewClusterFromString(cfg.durl, clusterStr)
|
||||||
case cfg.dnsCluster != "":
|
case cfg.dnsCluster != "":
|
||||||
clusterStr, clusterToken, err := genDNSClusterString(cfg.name, cfg.dnsCluster, cfg.initialClusterToken, cfg.apurls)
|
clusterStr, clusterToken, err := discovery.SRVGetCluster(cfg.name, cfg.dnsCluster, cfg.initialClusterToken, cfg.apurls)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -279,67 +274,3 @@ func genClusterString(name string, urls types.URLs) string {
|
||||||
}
|
}
|
||||||
return strings.Join(addrs, ",")
|
return strings.Join(addrs, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(barakmich): Currently ignores priority and weight (as they don't make as much sense for a bootstrap)
|
|
||||||
// Also doesn't do any lookups for the token (though it could)
|
|
||||||
// Also sees each entry as a separate instance.
|
|
||||||
func genDNSClusterString(name, dns string, defaultToken string, apurls types.URLs) (string, string, error) {
|
|
||||||
stringParts := make([]string, 0)
|
|
||||||
tempName := int(0)
|
|
||||||
tcpAPUrls := make([]string, 0)
|
|
||||||
|
|
||||||
// First, resolve the apurls
|
|
||||||
for _, url := range apurls {
|
|
||||||
tcpAddr, err := net.ResolveTCPAddr("tcp", url.Host)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("etcd: Couldn't resolve host %s during SRV discovery", url.Host)
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
tcpAPUrls = append(tcpAPUrls, tcpAddr.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
updateNodeMap := func(service, prefix string) error {
|
|
||||||
_, addrs, err := lookupSRV(service, "tcp", dns)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, srv := range addrs {
|
|
||||||
host := net.JoinHostPort(srv.Target, fmt.Sprintf("%d", srv.Port))
|
|
||||||
tcpAddr, err := net.ResolveTCPAddr("tcp", host)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("etcd: Couldn't resolve host %s during SRV discovery", host)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
n := ""
|
|
||||||
for _, url := range tcpAPUrls {
|
|
||||||
if url == tcpAddr.String() {
|
|
||||||
n = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if n == "" {
|
|
||||||
n = fmt.Sprintf("%d", tempName)
|
|
||||||
tempName += 1
|
|
||||||
}
|
|
||||||
stringParts = append(stringParts, fmt.Sprintf("%s=%s%s", n, prefix, tcpAddr.String()))
|
|
||||||
log.Printf("etcd: Got bootstrap from DNS for %s at host %s to %s%s", service, host, prefix, tcpAddr.String())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
failCount := 0
|
|
||||||
err := updateNodeMap("etcd-server-ssl", "https://")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("etcd: Error querying DNS SRV records for _etcd-server-ssl. Error: %s.", err)
|
|
||||||
failCount += 1
|
|
||||||
}
|
|
||||||
err = updateNodeMap("etcd-server", "http://")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("etcd: Error querying DNS SRV records for _etcd-server. Error: %s.", err)
|
|
||||||
failCount += 1
|
|
||||||
}
|
|
||||||
if failCount == 2 {
|
|
||||||
log.Printf("etcd: Too many errors querying DNS SRV records. Failing discovery.")
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
return strings.Join(stringParts, ","), defaultToken, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,25 +17,11 @@
|
||||||
package etcdmain
|
package etcdmain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/coreos/etcd/pkg/types"
|
"github.com/coreos/etcd/pkg/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func mustNewURLs(t *testing.T, urls []string) []url.URL {
|
|
||||||
if urls == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
u, err := types.NewURLs(urls)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected new urls error: %v", err)
|
|
||||||
}
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGenClusterString(t *testing.T) {
|
func TestGenClusterString(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
token string
|
token string
|
||||||
|
@ -52,85 +38,10 @@ func TestGenClusterString(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
urls := mustNewURLs(t, tt.urls)
|
urls := testutil.MustNewURLs(t, tt.urls)
|
||||||
str := genClusterString(tt.token, urls)
|
str := genClusterString(tt.token, urls)
|
||||||
if str != tt.wstr {
|
if str != tt.wstr {
|
||||||
t.Errorf("#%d: cluster = %s, want %s", i, str, tt.wstr)
|
t.Errorf("#%d: cluster = %s, want %s", i, str, tt.wstr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenDNSClusterString(t *testing.T) {
|
|
||||||
name := "dnsClusterTest"
|
|
||||||
tests := []struct {
|
|
||||||
withSSL []*net.SRV
|
|
||||||
withoutSSL []*net.SRV
|
|
||||||
urls []string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
[]*net.SRV{},
|
|
||||||
[]*net.SRV{},
|
|
||||||
nil,
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]*net.SRV{
|
|
||||||
&net.SRV{Target: "10.0.0.1", Port: 2480},
|
|
||||||
&net.SRV{Target: "10.0.0.2", Port: 2480},
|
|
||||||
&net.SRV{Target: "10.0.0.3", Port: 2480},
|
|
||||||
},
|
|
||||||
[]*net.SRV{},
|
|
||||||
nil,
|
|
||||||
"0=https://10.0.0.1:2480,1=https://10.0.0.2:2480,2=https://10.0.0.3:2480",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]*net.SRV{
|
|
||||||
&net.SRV{Target: "10.0.0.1", Port: 2480},
|
|
||||||
&net.SRV{Target: "10.0.0.2", Port: 2480},
|
|
||||||
&net.SRV{Target: "10.0.0.3", Port: 2480},
|
|
||||||
},
|
|
||||||
[]*net.SRV{
|
|
||||||
&net.SRV{Target: "10.0.0.1", Port: 7001},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"0=https://10.0.0.1:2480,1=https://10.0.0.2:2480,2=https://10.0.0.3:2480,3=http://10.0.0.1:7001",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]*net.SRV{
|
|
||||||
&net.SRV{Target: "10.0.0.1", Port: 2480},
|
|
||||||
&net.SRV{Target: "10.0.0.2", Port: 2480},
|
|
||||||
&net.SRV{Target: "10.0.0.3", Port: 2480},
|
|
||||||
},
|
|
||||||
[]*net.SRV{
|
|
||||||
&net.SRV{Target: "10.0.0.1", Port: 7001},
|
|
||||||
},
|
|
||||||
[]string{"https://10.0.0.1:2480"},
|
|
||||||
"dnsClusterTest=https://10.0.0.1:2480,0=https://10.0.0.2:2480,1=https://10.0.0.3:2480,2=http://10.0.0.1:7001",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
lookupSRV = func(service string, proto string, domain string) (string, []*net.SRV, error) {
|
|
||||||
if service == "etcd-server-ssl" {
|
|
||||||
return "", tt.withSSL, nil
|
|
||||||
}
|
|
||||||
if service == "etcd-server" {
|
|
||||||
return "", tt.withoutSSL, nil
|
|
||||||
}
|
|
||||||
return "", nil, errors.New("Unkown service in mock")
|
|
||||||
}
|
|
||||||
defer func() { lookupSRV = net.LookupSRV }()
|
|
||||||
urls := mustNewURLs(t, tt.urls)
|
|
||||||
str, token, err := genDNSClusterString(name, "example.com", "token", urls)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d: err: %#v", i, err)
|
|
||||||
}
|
|
||||||
if token != "token" {
|
|
||||||
t.Errorf("%d: token: %s", i, token)
|
|
||||||
}
|
|
||||||
if str != tt.expected {
|
|
||||||
t.Errorf("#%d: cluster = %s, want %s", i, str, tt.expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,7 +17,11 @@
|
||||||
package testutil
|
package testutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WARNING: This is a hack.
|
// WARNING: This is a hack.
|
||||||
|
@ -28,3 +32,14 @@ func ForceGosched() {
|
||||||
runtime.Gosched()
|
runtime.Gosched()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MustNewURLs(t *testing.T, urls []string) []url.URL {
|
||||||
|
if urls == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
u, err := types.NewURLs(urls)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected new urls error: %v", err)
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue