You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

466 lines
12 KiB

  1. /*
  2. *
  3. * Copyright 2019 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 xds
  19. import (
  20. "context"
  21. "errors"
  22. "io"
  23. "net"
  24. "testing"
  25. "time"
  26. xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2"
  27. xdscorepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
  28. xdsendpointpb "github.com/envoyproxy/go-control-plane/envoy/api/v2/endpoint"
  29. xdsdiscoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v2"
  30. "github.com/gogo/protobuf/proto"
  31. "github.com/gogo/protobuf/types"
  32. "google.golang.org/grpc"
  33. "google.golang.org/grpc/balancer"
  34. "google.golang.org/grpc/codes"
  35. "google.golang.org/grpc/status"
  36. )
  37. var (
  38. testServiceName = "test/foo"
  39. testCDSReq = &xdspb.DiscoveryRequest{
  40. Node: &xdscorepb.Node{
  41. Metadata: &types.Struct{
  42. Fields: map[string]*types.Value{
  43. grpcHostname: {
  44. Kind: &types.Value_StringValue{StringValue: testServiceName},
  45. },
  46. },
  47. },
  48. },
  49. TypeUrl: cdsType,
  50. }
  51. testEDSReq = &xdspb.DiscoveryRequest{
  52. Node: &xdscorepb.Node{
  53. Metadata: &types.Struct{
  54. Fields: map[string]*types.Value{
  55. endpointRequired: {
  56. Kind: &types.Value_BoolValue{BoolValue: true},
  57. },
  58. },
  59. },
  60. },
  61. ResourceNames: []string{testServiceName},
  62. TypeUrl: edsType,
  63. }
  64. testEDSReqWithoutEndpoints = &xdspb.DiscoveryRequest{
  65. Node: &xdscorepb.Node{
  66. Metadata: &types.Struct{
  67. Fields: map[string]*types.Value{
  68. endpointRequired: {
  69. Kind: &types.Value_BoolValue{BoolValue: false},
  70. },
  71. },
  72. },
  73. },
  74. ResourceNames: []string{testServiceName},
  75. TypeUrl: edsType,
  76. }
  77. testCluster = &xdspb.Cluster{
  78. Name: testServiceName,
  79. ClusterDiscoveryType: &xdspb.Cluster_Type{Type: xdspb.Cluster_EDS},
  80. LbPolicy: xdspb.Cluster_ROUND_ROBIN,
  81. }
  82. marshaledCluster, _ = proto.Marshal(testCluster)
  83. testCDSResp = &xdspb.DiscoveryResponse{
  84. Resources: []types.Any{
  85. {
  86. TypeUrl: cdsType,
  87. Value: marshaledCluster,
  88. },
  89. },
  90. TypeUrl: cdsType,
  91. }
  92. testClusterLoadAssignment = &xdspb.ClusterLoadAssignment{
  93. ClusterName: testServiceName,
  94. Endpoints: []xdsendpointpb.LocalityLbEndpoints{
  95. {
  96. Locality: &xdscorepb.Locality{
  97. Region: "asia-east1",
  98. Zone: "1",
  99. SubZone: "sa",
  100. },
  101. LbEndpoints: []xdsendpointpb.LbEndpoint{
  102. {
  103. HostIdentifier: &xdsendpointpb.LbEndpoint_Endpoint{
  104. Endpoint: &xdsendpointpb.Endpoint{
  105. Address: &xdscorepb.Address{
  106. Address: &xdscorepb.Address_SocketAddress{
  107. SocketAddress: &xdscorepb.SocketAddress{
  108. Address: "1.1.1.1",
  109. PortSpecifier: &xdscorepb.SocketAddress_PortValue{
  110. PortValue: 10001,
  111. },
  112. ResolverName: "dns",
  113. },
  114. },
  115. },
  116. HealthCheckConfig: nil,
  117. },
  118. },
  119. Metadata: &xdscorepb.Metadata{
  120. FilterMetadata: map[string]*types.Struct{
  121. "xx.lb": {
  122. Fields: map[string]*types.Value{
  123. "endpoint_name": {
  124. Kind: &types.Value_StringValue{
  125. StringValue: "some.endpoint.name",
  126. },
  127. },
  128. },
  129. },
  130. },
  131. },
  132. },
  133. },
  134. LoadBalancingWeight: &types.UInt32Value{
  135. Value: 1,
  136. },
  137. Priority: 0,
  138. },
  139. },
  140. }
  141. marshaledClusterLoadAssignment, _ = proto.Marshal(testClusterLoadAssignment)
  142. testEDSResp = &xdspb.DiscoveryResponse{
  143. Resources: []types.Any{
  144. {
  145. TypeUrl: edsType,
  146. Value: marshaledClusterLoadAssignment,
  147. },
  148. },
  149. TypeUrl: edsType,
  150. }
  151. testClusterLoadAssignmentWithoutEndpoints = &xdspb.ClusterLoadAssignment{
  152. ClusterName: testServiceName,
  153. Endpoints: []xdsendpointpb.LocalityLbEndpoints{
  154. {
  155. Locality: &xdscorepb.Locality{
  156. SubZone: "sa",
  157. },
  158. LoadBalancingWeight: &types.UInt32Value{
  159. Value: 128,
  160. },
  161. Priority: 0,
  162. },
  163. },
  164. Policy: nil,
  165. }
  166. marshaledClusterLoadAssignmentWithoutEndpoints, _ = proto.Marshal(testClusterLoadAssignmentWithoutEndpoints)
  167. testEDSRespWithoutEndpoints = &xdspb.DiscoveryResponse{
  168. Resources: []types.Any{
  169. {
  170. TypeUrl: edsType,
  171. Value: marshaledClusterLoadAssignmentWithoutEndpoints,
  172. },
  173. },
  174. TypeUrl: edsType,
  175. }
  176. )
  177. type testTrafficDirector struct {
  178. reqChan chan *request
  179. respChan chan *response
  180. }
  181. type request struct {
  182. req *xdspb.DiscoveryRequest
  183. err error
  184. }
  185. type response struct {
  186. resp *xdspb.DiscoveryResponse
  187. err error
  188. }
  189. func (ttd *testTrafficDirector) StreamAggregatedResources(s xdsdiscoverypb.AggregatedDiscoveryService_StreamAggregatedResourcesServer) error {
  190. for {
  191. req, err := s.Recv()
  192. if err != nil {
  193. ttd.reqChan <- &request{
  194. req: nil,
  195. err: err,
  196. }
  197. if err == io.EOF {
  198. return nil
  199. }
  200. return err
  201. }
  202. ttd.reqChan <- &request{
  203. req: req,
  204. err: nil,
  205. }
  206. if req.TypeUrl == edsType {
  207. break
  208. }
  209. }
  210. for {
  211. select {
  212. case resp := <-ttd.respChan:
  213. if resp.err != nil {
  214. return resp.err
  215. }
  216. if err := s.Send(resp.resp); err != nil {
  217. return err
  218. }
  219. case <-s.Context().Done():
  220. return s.Context().Err()
  221. }
  222. }
  223. }
  224. func (ttd *testTrafficDirector) DeltaAggregatedResources(xdsdiscoverypb.AggregatedDiscoveryService_DeltaAggregatedResourcesServer) error {
  225. return status.Error(codes.Unimplemented, "")
  226. }
  227. func (ttd *testTrafficDirector) sendResp(resp *response) {
  228. ttd.respChan <- resp
  229. }
  230. func (ttd *testTrafficDirector) getReq() *request {
  231. return <-ttd.reqChan
  232. }
  233. func newTestTrafficDirector() *testTrafficDirector {
  234. return &testTrafficDirector{
  235. reqChan: make(chan *request, 10),
  236. respChan: make(chan *response, 10),
  237. }
  238. }
  239. type testConfig struct {
  240. doCDS bool
  241. expectedRequests []*xdspb.DiscoveryRequest
  242. responsesToSend []*xdspb.DiscoveryResponse
  243. expectedADSResponses []proto.Message
  244. adsErr error
  245. svrErr error
  246. }
  247. func setupServer(t *testing.T) (addr string, td *testTrafficDirector, cleanup func()) {
  248. lis, err := net.Listen("tcp", "localhost:0")
  249. if err != nil {
  250. t.Fatalf("listen failed due to: %v", err)
  251. }
  252. svr := grpc.NewServer()
  253. td = newTestTrafficDirector()
  254. xdsdiscoverypb.RegisterAggregatedDiscoveryServiceServer(svr, td)
  255. go svr.Serve(lis)
  256. return lis.Addr().String(), td, func() {
  257. svr.Stop()
  258. lis.Close()
  259. }
  260. }
  261. func (s) TestXdsClientResponseHandling(t *testing.T) {
  262. for _, test := range []*testConfig{
  263. {
  264. doCDS: true,
  265. expectedRequests: []*xdspb.DiscoveryRequest{testCDSReq, testEDSReq},
  266. responsesToSend: []*xdspb.DiscoveryResponse{testCDSResp, testEDSResp},
  267. expectedADSResponses: []proto.Message{testCluster, testClusterLoadAssignment},
  268. },
  269. {
  270. doCDS: false,
  271. expectedRequests: []*xdspb.DiscoveryRequest{testEDSReqWithoutEndpoints},
  272. responsesToSend: []*xdspb.DiscoveryResponse{testEDSRespWithoutEndpoints},
  273. expectedADSResponses: []proto.Message{testClusterLoadAssignmentWithoutEndpoints},
  274. },
  275. } {
  276. testXdsClientResponseHandling(t, test)
  277. }
  278. }
  279. func testXdsClientResponseHandling(t *testing.T, test *testConfig) {
  280. addr, td, cleanup := setupServer(t)
  281. defer cleanup()
  282. adsChan := make(chan proto.Message, 10)
  283. newADS := func(ctx context.Context, i proto.Message) error {
  284. adsChan <- i
  285. return nil
  286. }
  287. client := newXDSClient(addr, testServiceName, test.doCDS, balancer.BuildOptions{}, newADS, func(context.Context) {}, func() {})
  288. defer client.close()
  289. go client.run()
  290. for _, expectedReq := range test.expectedRequests {
  291. req := td.getReq()
  292. if req.err != nil {
  293. t.Fatalf("ads RPC failed with err: %v", req.err)
  294. }
  295. if !proto.Equal(req.req, expectedReq) {
  296. t.Fatalf("got ADS request %T %v, expected: %T %v", req.req, req.req, expectedReq, expectedReq)
  297. }
  298. }
  299. for i, resp := range test.responsesToSend {
  300. td.sendResp(&response{resp: resp})
  301. ads := <-adsChan
  302. if !proto.Equal(ads, test.expectedADSResponses[i]) {
  303. t.Fatalf("received unexpected ads response, got %v, want %v", ads, test.expectedADSResponses[i])
  304. }
  305. }
  306. }
  307. func (s) TestXdsClientLoseContact(t *testing.T) {
  308. for _, test := range []*testConfig{
  309. {
  310. doCDS: true,
  311. responsesToSend: []*xdspb.DiscoveryResponse{},
  312. },
  313. {
  314. doCDS: false,
  315. responsesToSend: []*xdspb.DiscoveryResponse{testEDSRespWithoutEndpoints},
  316. },
  317. } {
  318. testXdsClientLoseContactRemoteClose(t, test)
  319. }
  320. for _, test := range []*testConfig{
  321. {
  322. doCDS: false,
  323. responsesToSend: []*xdspb.DiscoveryResponse{testCDSResp}, // CDS response when in custom mode.
  324. },
  325. {
  326. doCDS: true,
  327. responsesToSend: []*xdspb.DiscoveryResponse{{}}, // response with 0 resources is an error case.
  328. },
  329. {
  330. doCDS: true,
  331. responsesToSend: []*xdspb.DiscoveryResponse{testCDSResp},
  332. adsErr: errors.New("some ads parsing error from xdsBalancer"),
  333. },
  334. } {
  335. testXdsClientLoseContactADSRelatedErrorOccur(t, test)
  336. }
  337. }
  338. func testXdsClientLoseContactRemoteClose(t *testing.T, test *testConfig) {
  339. addr, td, cleanup := setupServer(t)
  340. defer cleanup()
  341. adsChan := make(chan proto.Message, 10)
  342. newADS := func(ctx context.Context, i proto.Message) error {
  343. adsChan <- i
  344. return nil
  345. }
  346. contactChan := make(chan *loseContact, 10)
  347. loseContactFunc := func(context.Context) {
  348. contactChan <- &loseContact{}
  349. }
  350. client := newXDSClient(addr, testServiceName, test.doCDS, balancer.BuildOptions{}, newADS, loseContactFunc, func() {})
  351. defer client.close()
  352. go client.run()
  353. // make sure server side get the request (i.e stream created successfully on client side)
  354. td.getReq()
  355. for _, resp := range test.responsesToSend {
  356. td.sendResp(&response{resp: resp})
  357. // make sure client side receives it
  358. <-adsChan
  359. }
  360. cleanup()
  361. select {
  362. case <-contactChan:
  363. case <-time.After(2 * time.Second):
  364. t.Fatal("time out when expecting lost contact signal")
  365. }
  366. }
  367. func testXdsClientLoseContactADSRelatedErrorOccur(t *testing.T, test *testConfig) {
  368. addr, td, cleanup := setupServer(t)
  369. defer cleanup()
  370. adsChan := make(chan proto.Message, 10)
  371. newADS := func(ctx context.Context, i proto.Message) error {
  372. adsChan <- i
  373. return test.adsErr
  374. }
  375. contactChan := make(chan *loseContact, 10)
  376. loseContactFunc := func(context.Context) {
  377. contactChan <- &loseContact{}
  378. }
  379. client := newXDSClient(addr, testServiceName, test.doCDS, balancer.BuildOptions{}, newADS, loseContactFunc, func() {})
  380. defer client.close()
  381. go client.run()
  382. // make sure server side get the request (i.e stream created successfully on client side)
  383. td.getReq()
  384. for _, resp := range test.responsesToSend {
  385. td.sendResp(&response{resp: resp})
  386. }
  387. select {
  388. case <-contactChan:
  389. case <-time.After(2 * time.Second):
  390. t.Fatal("time out when expecting lost contact signal")
  391. }
  392. }
  393. func (s) TestXdsClientExponentialRetry(t *testing.T) {
  394. cfg := &testConfig{
  395. svrErr: status.Errorf(codes.Aborted, "abort the stream to trigger retry"),
  396. }
  397. addr, td, cleanup := setupServer(t)
  398. defer cleanup()
  399. adsChan := make(chan proto.Message, 10)
  400. newADS := func(ctx context.Context, i proto.Message) error {
  401. adsChan <- i
  402. return nil
  403. }
  404. contactChan := make(chan *loseContact, 10)
  405. loseContactFunc := func(context.Context) {
  406. contactChan <- &loseContact{}
  407. }
  408. client := newXDSClient(addr, testServiceName, cfg.doCDS, balancer.BuildOptions{}, newADS, loseContactFunc, func() {})
  409. defer client.close()
  410. go client.run()
  411. var secondRetry, thirdRetry time.Time
  412. for i := 0; i < 3; i++ {
  413. // make sure server side get the request (i.e stream created successfully on client side)
  414. td.getReq()
  415. td.sendResp(&response{err: cfg.svrErr})
  416. select {
  417. case <-contactChan:
  418. if i == 1 {
  419. secondRetry = time.Now()
  420. }
  421. if i == 2 {
  422. thirdRetry = time.Now()
  423. }
  424. case <-time.After(2 * time.Second):
  425. t.Fatal("time out when expecting lost contact signal")
  426. }
  427. }
  428. if thirdRetry.Sub(secondRetry) < 1*time.Second {
  429. t.Fatalf("interval between second and third retry is %v, expected > 1s", thirdRetry.Sub(secondRetry))
  430. }
  431. }