...

Source file src/go.etcd.io/etcd/client/pkg/v3/srv/srv_test.go

Documentation: go.etcd.io/etcd/client/pkg/v3/srv

     1  // 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
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"net"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  
    25  	"go.etcd.io/etcd/client/pkg/v3/testutil"
    26  )
    27  
    28  func notFoundErr(service, proto, domain string) error {
    29  	name := fmt.Sprintf("_%s._%s.%s", service, proto, domain)
    30  	return &net.DNSError{Err: "no such host", Name: name, Server: "10.0.0.53:53", IsTimeout: false, IsTemporary: false, IsNotFound: true}
    31  }
    32  
    33  func TestSRVGetCluster(t *testing.T) {
    34  	defer func() {
    35  		lookupSRV = net.LookupSRV
    36  		resolveTCPAddr = net.ResolveTCPAddr
    37  	}()
    38  
    39  	hasErr := func(err error) bool {
    40  		return err != nil
    41  	}
    42  
    43  	name := "dnsClusterTest"
    44  	dns := map[string]string{
    45  		"1.example.com.:2480": "10.0.0.1:2480",
    46  		"2.example.com.:2480": "10.0.0.2:2480",
    47  		"3.example.com.:2480": "10.0.0.3:2480",
    48  		"4.example.com.:2380": "10.0.0.3:2380",
    49  	}
    50  	srvAll := []*net.SRV{
    51  		{Target: "1.example.com.", Port: 2480},
    52  		{Target: "2.example.com.", Port: 2480},
    53  		{Target: "3.example.com.", Port: 2480},
    54  	}
    55  	srvNone := []*net.SRV{}
    56  
    57  	tests := []struct {
    58  		service    string
    59  		scheme     string
    60  		withSSL    []*net.SRV
    61  		withoutSSL []*net.SRV
    62  		urls       []string
    63  		expected   string
    64  		werr       bool
    65  	}{
    66  		{
    67  			"etcd-server-ssl",
    68  			"https",
    69  			srvNone,
    70  			srvNone,
    71  			nil,
    72  			"",
    73  			true,
    74  		},
    75  		{
    76  			"etcd-server-ssl",
    77  			"https",
    78  			srvAll,
    79  			srvNone,
    80  			nil,
    81  			"0=https://1.example.com:2480,1=https://2.example.com:2480,2=https://3.example.com:2480",
    82  			false,
    83  		},
    84  		{
    85  			"etcd-server",
    86  			"http",
    87  			srvNone,
    88  			srvAll,
    89  			nil,
    90  			"0=http://1.example.com:2480,1=http://2.example.com:2480,2=http://3.example.com:2480",
    91  			false,
    92  		},
    93  		{
    94  			"etcd-server-ssl",
    95  			"https",
    96  			srvAll,
    97  			srvNone,
    98  			[]string{"https://10.0.0.1:2480"},
    99  			"dnsClusterTest=https://1.example.com:2480,0=https://2.example.com:2480,1=https://3.example.com:2480",
   100  			false,
   101  		},
   102  		// matching local member with resolved addr and return unresolved hostnames
   103  		{
   104  			"etcd-server-ssl",
   105  			"https",
   106  			srvAll,
   107  			srvNone,
   108  			[]string{"https://10.0.0.1:2480"},
   109  			"dnsClusterTest=https://1.example.com:2480,0=https://2.example.com:2480,1=https://3.example.com:2480",
   110  			false,
   111  		},
   112  		// reject if apurls are TLS but SRV is only http
   113  		{
   114  			"etcd-server",
   115  			"http",
   116  			srvNone,
   117  			srvAll,
   118  			[]string{"https://10.0.0.1:2480"},
   119  			"0=http://2.example.com:2480,1=http://3.example.com:2480",
   120  			false,
   121  		},
   122  	}
   123  
   124  	resolveTCPAddr = func(network, addr string) (*net.TCPAddr, error) {
   125  		if strings.Contains(addr, "10.0.0.") {
   126  			// accept IP addresses when resolving apurls
   127  			return net.ResolveTCPAddr(network, addr)
   128  		}
   129  		if dns[addr] == "" {
   130  			return nil, errors.New("missing dns record")
   131  		}
   132  		return net.ResolveTCPAddr(network, dns[addr])
   133  	}
   134  
   135  	for i, tt := range tests {
   136  		lookupSRV = func(service string, proto string, domain string) (string, []*net.SRV, error) {
   137  			if service == "etcd-server-ssl" {
   138  				if len(tt.withSSL) > 0 {
   139  					return "", tt.withSSL, nil
   140  				}
   141  				return "", nil, notFoundErr(service, proto, domain)
   142  			}
   143  			if service == "etcd-server" {
   144  				if len(tt.withoutSSL) > 0 {
   145  					return "", tt.withoutSSL, nil
   146  				}
   147  				return "", nil, notFoundErr(service, proto, domain)
   148  			}
   149  			return "", nil, errors.New("unknown service in mock")
   150  		}
   151  
   152  		urls := testutil.MustNewURLs(t, tt.urls)
   153  		str, err := GetCluster(tt.scheme, tt.service, name, "example.com", urls)
   154  
   155  		if hasErr(err) != tt.werr {
   156  			t.Fatalf("%d: err = %#v, want = %#v", i, err, tt.werr)
   157  		}
   158  		if strings.Join(str, ",") != tt.expected {
   159  			t.Errorf("#%d: cluster = %s, want %s", i, str, tt.expected)
   160  		}
   161  	}
   162  }
   163  
   164  func TestSRVDiscover(t *testing.T) {
   165  	defer func() { lookupSRV = net.LookupSRV }()
   166  
   167  	hasErr := func(err error) bool {
   168  		return err != nil
   169  	}
   170  
   171  	tests := []struct {
   172  		withSSL    []*net.SRV
   173  		withoutSSL []*net.SRV
   174  		expected   []string
   175  		werr       bool
   176  	}{
   177  		{
   178  			[]*net.SRV{},
   179  			[]*net.SRV{},
   180  			[]string{},
   181  			true,
   182  		},
   183  		{
   184  			[]*net.SRV{},
   185  			[]*net.SRV{
   186  				{Target: "10.0.0.1", Port: 2480},
   187  				{Target: "10.0.0.2", Port: 2480},
   188  				{Target: "10.0.0.3", Port: 2480},
   189  			},
   190  			[]string{"http://10.0.0.1:2480", "http://10.0.0.2:2480", "http://10.0.0.3:2480"},
   191  			false,
   192  		},
   193  		{
   194  			[]*net.SRV{
   195  				{Target: "10.0.0.1", Port: 2480},
   196  				{Target: "10.0.0.2", Port: 2480},
   197  				{Target: "10.0.0.3", Port: 2480},
   198  			},
   199  			[]*net.SRV{},
   200  			[]string{"https://10.0.0.1:2480", "https://10.0.0.2:2480", "https://10.0.0.3:2480"},
   201  			false,
   202  		},
   203  		{
   204  			[]*net.SRV{
   205  				{Target: "10.0.0.1", Port: 2480},
   206  				{Target: "10.0.0.2", Port: 2480},
   207  				{Target: "10.0.0.3", Port: 2480},
   208  			},
   209  			[]*net.SRV{
   210  				{Target: "10.0.0.1", Port: 7001},
   211  			},
   212  			[]string{"https://10.0.0.1:2480", "https://10.0.0.2:2480", "https://10.0.0.3:2480", "http://10.0.0.1:7001"},
   213  			false,
   214  		},
   215  		{
   216  			[]*net.SRV{
   217  				{Target: "10.0.0.1", Port: 2480},
   218  				{Target: "10.0.0.2", Port: 2480},
   219  				{Target: "10.0.0.3", Port: 2480},
   220  			},
   221  			[]*net.SRV{
   222  				{Target: "10.0.0.1", Port: 7001},
   223  			},
   224  			[]string{"https://10.0.0.1:2480", "https://10.0.0.2:2480", "https://10.0.0.3:2480", "http://10.0.0.1:7001"},
   225  			false,
   226  		},
   227  		{
   228  			[]*net.SRV{
   229  				{Target: "a.example.com", Port: 2480},
   230  				{Target: "b.example.com", Port: 2480},
   231  				{Target: "c.example.com.", Port: 2480},
   232  			},
   233  			[]*net.SRV{},
   234  			[]string{"https://a.example.com:2480", "https://b.example.com:2480", "https://c.example.com.:2480"},
   235  			false,
   236  		},
   237  	}
   238  
   239  	for i, tt := range tests {
   240  		lookupSRV = func(service string, proto string, domain string) (string, []*net.SRV, error) {
   241  			if service == "etcd-client-ssl" {
   242  				if len(tt.withSSL) > 0 {
   243  					return "", tt.withSSL, nil
   244  				}
   245  				return "", nil, notFoundErr(service, proto, domain)
   246  			}
   247  			if service == "etcd-client" {
   248  				if len(tt.withoutSSL) > 0 {
   249  					return "", tt.withoutSSL, nil
   250  				}
   251  				return "", nil, notFoundErr(service, proto, domain)
   252  			}
   253  			return "", nil, errors.New("unknown service in mock")
   254  		}
   255  
   256  		srvs, err := GetClient("etcd-client", "example.com", "")
   257  
   258  		if hasErr(err) != tt.werr {
   259  			t.Fatalf("%d: err = %#v, want = %#v", i, err, tt.werr)
   260  		}
   261  		if srvs == nil {
   262  			if len(tt.expected) > 0 {
   263  				t.Errorf("#%d: srvs = nil, want non-nil", i)
   264  			}
   265  		} else {
   266  			if !reflect.DeepEqual(srvs.Endpoints, tt.expected) {
   267  				t.Errorf("#%d: endpoints = %v, want = %v", i, srvs.Endpoints, tt.expected)
   268  			}
   269  		}
   270  	}
   271  }
   272  
   273  func TestGetSRVService(t *testing.T) {
   274  	tests := []struct {
   275  		scheme      string
   276  		serviceName string
   277  
   278  		expected string
   279  	}{
   280  		{
   281  			"https",
   282  			"",
   283  			"etcd-client-ssl",
   284  		},
   285  		{
   286  			"http",
   287  			"",
   288  			"etcd-client",
   289  		},
   290  		{
   291  			"https",
   292  			"foo",
   293  			"etcd-client-ssl-foo",
   294  		},
   295  		{
   296  			"http",
   297  			"bar",
   298  			"etcd-client-bar",
   299  		},
   300  	}
   301  
   302  	for i, tt := range tests {
   303  		service := GetSRVService("etcd-client", tt.serviceName, tt.scheme)
   304  		if strings.Compare(service, tt.expected) != 0 {
   305  			t.Errorf("#%d: service = %s, want %s", i, service, tt.expected)
   306  		}
   307  	}
   308  }
   309  

View as plain text