...

Source file src/google.golang.org/grpc/resolver_test.go

Documentation: google.golang.org/grpc

     1  /*
     2   *
     3   * Copyright 2023 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  package grpc
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"net"
    25  	"testing"
    26  
    27  	"github.com/google/go-cmp/cmp"
    28  	"google.golang.org/grpc/attributes"
    29  	"google.golang.org/grpc/balancer"
    30  	"google.golang.org/grpc/credentials/insecure"
    31  	"google.golang.org/grpc/internal/balancer/stub"
    32  	"google.golang.org/grpc/resolver"
    33  	"google.golang.org/grpc/resolver/manual"
    34  )
    35  
    36  type wrapResolverBuilder struct {
    37  	resolver.Builder
    38  	scheme string
    39  }
    40  
    41  func (w *wrapResolverBuilder) Scheme() string {
    42  	return w.scheme
    43  }
    44  
    45  func init() {
    46  	resolver.Register(&wrapResolverBuilder{Builder: resolver.Get("passthrough"), scheme: "casetest"})
    47  	resolver.Register(&wrapResolverBuilder{Builder: resolver.Get("dns"), scheme: "caseTest"})
    48  }
    49  
    50  func (s) TestResolverCaseSensitivity(t *testing.T) {
    51  	// This should find the "casetest" resolver instead of the "caseTest"
    52  	// resolver, even though the latter was registered later.  "casetest" is
    53  	// "passthrough" and "caseTest" is "dns".  With "passthrough" the dialer
    54  	// should see the target's address directly, but "dns" would be converted
    55  	// into a loopback IP (v4 or v6) address.
    56  	target := "caseTest:///localhost:1234"
    57  	addrCh := make(chan string, 1)
    58  	customDialer := func(ctx context.Context, addr string) (net.Conn, error) {
    59  		select {
    60  		case addrCh <- addr:
    61  		default:
    62  		}
    63  		return nil, fmt.Errorf("not dialing with custom dialer")
    64  	}
    65  
    66  	cc, err := Dial(target, WithContextDialer(customDialer), WithTransportCredentials(insecure.NewCredentials()))
    67  	if err != nil {
    68  		t.Fatalf("Unexpected Dial(%q) error: %v", target, err)
    69  	}
    70  	cc.Connect()
    71  	if got, want := <-addrCh, "localhost:1234"; got != want {
    72  		cc.Close()
    73  		t.Fatalf("Dialer got address %q; wanted %q", got, want)
    74  	}
    75  	cc.Close()
    76  
    77  	// Clear addrCh for future use.
    78  	select {
    79  	case <-addrCh:
    80  	default:
    81  	}
    82  
    83  	res := &wrapResolverBuilder{Builder: resolver.Get("dns"), scheme: "caseTest2"}
    84  	// This should not find the injected resolver due to the case not matching.
    85  	// This results in "passthrough" being used with the address as the whole
    86  	// target.
    87  	target = "caseTest2:///localhost:1234"
    88  	cc, err = Dial(target, WithContextDialer(customDialer), WithResolvers(res), WithTransportCredentials(insecure.NewCredentials()))
    89  	if err != nil {
    90  		t.Fatalf("Unexpected Dial(%q) error: %v", target, err)
    91  	}
    92  	cc.Connect()
    93  	if got, want := <-addrCh, target; got != want {
    94  		cc.Close()
    95  		t.Fatalf("Dialer got address %q; wanted %q", got, want)
    96  	}
    97  	cc.Close()
    98  }
    99  
   100  // TestResolverAddressesToEndpoints ensures one Endpoint is created for each
   101  // entry in resolver.State.Addresses automatically.
   102  func (s) TestResolverAddressesToEndpoints(t *testing.T) {
   103  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   104  	defer cancel()
   105  
   106  	const scheme = "testresolveraddressestoendpoints"
   107  	r := manual.NewBuilderWithScheme(scheme)
   108  
   109  	stateCh := make(chan balancer.ClientConnState, 1)
   110  	bf := stub.BalancerFuncs{
   111  		UpdateClientConnState: func(_ *stub.BalancerData, ccs balancer.ClientConnState) error {
   112  			stateCh <- ccs
   113  			return nil
   114  		},
   115  	}
   116  	balancerName := "stub-balancer-" + scheme
   117  	stub.Register(balancerName, bf)
   118  
   119  	a1 := attributes.New("x", "y")
   120  	a2 := attributes.New("a", "b")
   121  	r.InitialState(resolver.State{Addresses: []resolver.Address{{Addr: "addr1", BalancerAttributes: a1}, {Addr: "addr2", BalancerAttributes: a2}}})
   122  
   123  	cc, err := Dial(r.Scheme()+":///",
   124  		WithTransportCredentials(insecure.NewCredentials()),
   125  		WithResolvers(r),
   126  		WithDefaultServiceConfig(fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, balancerName)))
   127  	if err != nil {
   128  		t.Fatalf("Unexpected error dialing: %v", err)
   129  	}
   130  	defer cc.Close()
   131  
   132  	select {
   133  	case got := <-stateCh:
   134  		want := []resolver.Endpoint{
   135  			{Addresses: []resolver.Address{{Addr: "addr1"}}, Attributes: a1},
   136  			{Addresses: []resolver.Address{{Addr: "addr2"}}, Attributes: a2},
   137  		}
   138  		if diff := cmp.Diff(got.ResolverState.Endpoints, want); diff != "" {
   139  			t.Errorf("Did not receive expected endpoints.  Diff (-got +want):\n%v", diff)
   140  		}
   141  	case <-ctx.Done():
   142  		t.Fatalf("timed out waiting for endpoints")
   143  	}
   144  }
   145  
   146  // Test ensures that there is no panic if the attributes within
   147  // resolver.State.Addresses contains a typed-nil value.
   148  func (s) TestResolverAddressesWithTypedNilAttribute(t *testing.T) {
   149  	r := manual.NewBuilderWithScheme(t.Name())
   150  	resolver.Register(r)
   151  
   152  	addrAttr := attributes.New("typed_nil", (*stringerVal)(nil))
   153  	r.InitialState(resolver.State{Addresses: []resolver.Address{{Addr: "addr1", Attributes: addrAttr}}})
   154  
   155  	cc, err := Dial(r.Scheme()+":///", WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r))
   156  	if err != nil {
   157  		t.Fatalf("Unexpected error dialing: %v", err)
   158  	}
   159  	defer cc.Close()
   160  }
   161  
   162  type stringerVal struct{ s string }
   163  
   164  func (s stringerVal) String() string { return s.s }
   165  

View as plain text