1/* 2 * 3 * Copyright 2017 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 19package grpc 20 21import ( 22 "context" 23 "errors" 24 "fmt" 25 "net" 26 "strings" 27 "testing" 28 "time" 29 30 "google.golang.org/grpc/balancer" 31 "google.golang.org/grpc/codes" 32 "google.golang.org/grpc/internal/balancer/stub" 33 "google.golang.org/grpc/resolver" 34 "google.golang.org/grpc/resolver/manual" 35 "google.golang.org/grpc/serviceconfig" 36 "google.golang.org/grpc/status" 37) 38 39// The target string with unknown scheme should be kept unchanged and passed to 40// the dialer. 41func (s) TestDialParseTargetUnknownScheme(t *testing.T) { 42 for _, test := range []struct { 43 targetStr string 44 want string 45 }{ 46 {"/unix/socket/address", "/unix/socket/address"}, 47 48 // For known scheme. 49 {"passthrough://a.server.com/google.com", "google.com"}, 50 } { 51 dialStrCh := make(chan string, 1) 52 cc, err := Dial(test.targetStr, WithInsecure(), WithDialer(func(addr string, _ time.Duration) (net.Conn, error) { 53 select { 54 case dialStrCh <- addr: 55 default: 56 } 57 return nil, fmt.Errorf("test dialer, always error") 58 })) 59 if err != nil { 60 t.Fatalf("Failed to create ClientConn: %v", err) 61 } 62 got := <-dialStrCh 63 cc.Close() 64 if got != test.want { 65 t.Errorf("Dial(%q), dialer got %q, want %q", test.targetStr, got, test.want) 66 } 67 } 68} 69 70func testResolverErrorPolling(t *testing.T, badUpdate func(*manual.Resolver), goodUpdate func(*manual.Resolver), dopts ...DialOption) { 71 boIter := make(chan int) 72 resolverBackoff := func(v int) time.Duration { 73 boIter <- v 74 return 0 75 } 76 77 r := manual.NewBuilderWithScheme("whatever") 78 rn := make(chan struct{}) 79 defer func() { close(rn) }() 80 r.ResolveNowCallback = func(resolver.ResolveNowOptions) { rn <- struct{}{} } 81 82 defaultDialOptions := []DialOption{ 83 WithInsecure(), 84 WithResolvers(r), 85 withResolveNowBackoff(resolverBackoff), 86 } 87 cc, err := Dial(r.Scheme()+":///test.server", append(defaultDialOptions, dopts...)...) 88 if err != nil { 89 t.Fatalf("Dial(_, _) = _, %v; want _, nil", err) 90 } 91 defer cc.Close() 92 badUpdate(r) 93 94 panicAfter := time.AfterFunc(5*time.Second, func() { panic("timed out polling resolver") }) 95 defer panicAfter.Stop() 96 97 // Ensure ResolveNow is called, then Backoff with the right parameter, several times 98 for i := 0; i < 7; i++ { 99 <-rn 100 if v := <-boIter; v != i { 101 t.Errorf("Backoff call %v uses value %v", i, v) 102 } 103 } 104 105 // UpdateState will block if ResolveNow is being called (which blocks on 106 // rn), so call it in a goroutine. 107 goodUpdate(r) 108 109 // Wait awhile to ensure ResolveNow and Backoff stop being called when the 110 // state is OK (i.e. polling was cancelled). 111 for { 112 t := time.NewTimer(50 * time.Millisecond) 113 select { 114 case <-rn: 115 // ClientConn is still calling ResolveNow 116 <-boIter 117 time.Sleep(5 * time.Millisecond) 118 continue 119 case <-t.C: 120 // ClientConn stopped calling ResolveNow; success 121 } 122 break 123 } 124} 125 126const happyBalancerName = "happy balancer" 127 128func init() { 129 // Register a balancer that never returns an error from 130 // UpdateClientConnState, and doesn't do anything else either. 131 bf := stub.BalancerFuncs{ 132 UpdateClientConnState: func(*stub.BalancerData, balancer.ClientConnState) error { 133 return nil 134 }, 135 } 136 stub.Register(happyBalancerName, bf) 137} 138 139// TestResolverErrorPolling injects resolver errors and verifies ResolveNow is 140// called with the appropriate backoff strategy being consulted between 141// ResolveNow calls. 142func (s) TestResolverErrorPolling(t *testing.T) { 143 testResolverErrorPolling(t, func(r *manual.Resolver) { 144 r.CC.ReportError(errors.New("res err")) 145 }, func(r *manual.Resolver) { 146 // UpdateState will block if ResolveNow is being called (which blocks on 147 // rn), so call it in a goroutine. 148 go r.CC.UpdateState(resolver.State{}) 149 }, 150 WithDefaultServiceConfig(fmt.Sprintf(`{ "loadBalancingConfig": [{"%v": {}}] }`, happyBalancerName))) 151} 152 153// TestServiceConfigErrorPolling injects a service config error and verifies 154// ResolveNow is called with the appropriate backoff strategy being consulted 155// between ResolveNow calls. 156func (s) TestServiceConfigErrorPolling(t *testing.T) { 157 testResolverErrorPolling(t, func(r *manual.Resolver) { 158 badsc := r.CC.ParseServiceConfig("bad config") 159 r.UpdateState(resolver.State{ServiceConfig: badsc}) 160 }, func(r *manual.Resolver) { 161 // UpdateState will block if ResolveNow is being called (which blocks on 162 // rn), so call it in a goroutine. 163 go r.CC.UpdateState(resolver.State{}) 164 }, 165 WithDefaultServiceConfig(fmt.Sprintf(`{ "loadBalancingConfig": [{"%v": {}}] }`, happyBalancerName))) 166} 167 168// TestResolverErrorInBuild makes the resolver.Builder call into the ClientConn 169// during the Build call. We use two separate mutexes in the code which make 170// sure there is no data race in this code path, and also that there is no 171// deadlock. 172func (s) TestResolverErrorInBuild(t *testing.T) { 173 r := manual.NewBuilderWithScheme("whatever") 174 r.InitialState(resolver.State{ServiceConfig: &serviceconfig.ParseResult{Err: errors.New("resolver build err")}}) 175 176 cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithResolvers(r)) 177 if err != nil { 178 t.Fatalf("Dial(_, _) = _, %v; want _, nil", err) 179 } 180 defer cc.Close() 181 182 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 183 defer cancel() 184 var dummy int 185 const wantMsg = "error parsing service config" 186 const wantCode = codes.Unavailable 187 if err := cc.Invoke(ctx, "/foo/bar", &dummy, &dummy); status.Code(err) != wantCode || !strings.Contains(status.Convert(err).Message(), wantMsg) { 188 t.Fatalf("cc.Invoke(_, _, _, _) = %v; want status.Code()==%v, status.Message() contains %q", err, wantCode, wantMsg) 189 } 190} 191 192func (s) TestServiceConfigErrorRPC(t *testing.T) { 193 r := manual.NewBuilderWithScheme("whatever") 194 195 cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithResolvers(r)) 196 if err != nil { 197 t.Fatalf("Dial(_, _) = _, %v; want _, nil", err) 198 } 199 defer cc.Close() 200 badsc := r.CC.ParseServiceConfig("bad config") 201 r.UpdateState(resolver.State{ServiceConfig: badsc}) 202 203 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 204 defer cancel() 205 var dummy int 206 const wantMsg = "error parsing service config" 207 const wantCode = codes.Unavailable 208 if err := cc.Invoke(ctx, "/foo/bar", &dummy, &dummy); status.Code(err) != wantCode || !strings.Contains(status.Convert(err).Message(), wantMsg) { 209 t.Fatalf("cc.Invoke(_, _, _, _) = %v; want status.Code()==%v, status.Message() contains %q", err, wantCode, wantMsg) 210 } 211} 212