resolver_conn_wrapper_test.go 6.9 KB

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