resolver_conn_wrapper_test.go 6.9 KB

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