gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/xds/internal/xdsclient/controller/v2_ack_test.go (about) 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 controller 19 20 import ( 21 "context" 22 "fmt" 23 "strconv" 24 "testing" 25 "time" 26 27 xdspb "gitee.com/ks-custle/core-gm/go-control-plane/envoy/api/v2" 28 "gitee.com/ks-custle/core-gm/grpc/codes" 29 "gitee.com/ks-custle/core-gm/grpc/internal/testutils" 30 "gitee.com/ks-custle/core-gm/grpc/xds/internal/testutils/fakeserver" 31 "gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient/xdsresource" 32 "gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient/xdsresource/version" 33 "github.com/golang/protobuf/proto" 34 anypb "github.com/golang/protobuf/ptypes/any" 35 "github.com/google/go-cmp/cmp" 36 ) 37 38 const ( 39 defaultTestTimeout = 5 * time.Second 40 defaultTestShortTimeout = 10 * time.Millisecond 41 ) 42 43 func startXDSV2Client(t *testing.T, controlPlaneAddr string) (v2c *Controller, cbLDS, cbRDS, cbCDS, cbEDS *testutils.Channel, cleanup func()) { 44 cbLDS = testutils.NewChannel() 45 cbRDS = testutils.NewChannel() 46 cbCDS = testutils.NewChannel() 47 cbEDS = testutils.NewChannel() 48 v2c, err := newTestController(&testUpdateReceiver{ 49 f: func(rType xdsresource.ResourceType, d map[string]interface{}, md xdsresource.UpdateMetadata) { 50 t.Logf("Received %v callback with {%+v}", rType, d) 51 switch rType { 52 case xdsresource.ListenerResource: 53 if _, ok := d[goodLDSTarget1]; ok { 54 cbLDS.Send(struct{}{}) 55 } 56 case xdsresource.RouteConfigResource: 57 if _, ok := d[goodRouteName1]; ok { 58 cbRDS.Send(struct{}{}) 59 } 60 case xdsresource.ClusterResource: 61 if _, ok := d[goodClusterName1]; ok { 62 cbCDS.Send(struct{}{}) 63 } 64 case xdsresource.EndpointsResource: 65 if _, ok := d[goodEDSName]; ok { 66 cbEDS.Send(struct{}{}) 67 } 68 } 69 }, 70 }, controlPlaneAddr, goodNodeProto, func(int) time.Duration { return 0 }, nil) 71 if err != nil { 72 t.Fatal(err) 73 } 74 t.Log("Started xds client...") 75 return v2c, cbLDS, cbRDS, cbCDS, cbEDS, v2c.Close 76 } 77 78 // compareXDSRequest reads requests from channel, compare it with want. 79 func compareXDSRequest(ctx context.Context, ch *testutils.Channel, want *xdspb.DiscoveryRequest, ver, nonce string, wantErr bool) error { 80 val, err := ch.Receive(ctx) 81 if err != nil { 82 return err 83 } 84 req := val.(*fakeserver.Request) 85 if req.Err != nil { 86 return fmt.Errorf("unexpected error from request: %v", req.Err) 87 } 88 89 xdsReq := req.Req.(*xdspb.DiscoveryRequest) 90 if (xdsReq.ErrorDetail != nil) != wantErr { 91 return fmt.Errorf("received request with error details: %v, wantErr: %v", xdsReq.ErrorDetail, wantErr) 92 } 93 // All NACK request.ErrorDetails have hardcoded status code InvalidArguments. 94 if xdsReq.ErrorDetail != nil && xdsReq.ErrorDetail.Code != int32(codes.InvalidArgument) { 95 return fmt.Errorf("received request with error details: %v, want status with code: %v", xdsReq.ErrorDetail, codes.InvalidArgument) 96 } 97 98 xdsReq.ErrorDetail = nil // Clear the error details field before comparing. 99 wantClone := proto.Clone(want).(*xdspb.DiscoveryRequest) 100 wantClone.VersionInfo = ver 101 wantClone.ResponseNonce = nonce 102 if !cmp.Equal(xdsReq, wantClone, cmp.Comparer(proto.Equal)) { 103 return fmt.Errorf("received request different from want, diff: %s", cmp.Diff(req.Req, wantClone, cmp.Comparer(proto.Equal))) 104 } 105 return nil 106 } 107 108 func sendXDSRespWithVersion(ch chan<- *fakeserver.Response, respWithoutVersion *xdspb.DiscoveryResponse, ver int) (nonce string) { 109 respToSend := proto.Clone(respWithoutVersion).(*xdspb.DiscoveryResponse) 110 respToSend.VersionInfo = strconv.Itoa(ver) 111 nonce = strconv.Itoa(int(time.Now().UnixNano())) 112 respToSend.Nonce = nonce 113 ch <- &fakeserver.Response{Resp: respToSend} 114 return 115 } 116 117 // startXDS calls watch to send the first request. It then sends a good response 118 // and checks for ack. 119 func startXDS(ctx context.Context, t *testing.T, rType xdsresource.ResourceType, v2c *Controller, reqChan *testutils.Channel, req *xdspb.DiscoveryRequest, preVersion string, preNonce string) { 120 nameToWatch := "" 121 switch rType { 122 case xdsresource.ListenerResource: 123 nameToWatch = goodLDSTarget1 124 case xdsresource.RouteConfigResource: 125 nameToWatch = goodRouteName1 126 case xdsresource.ClusterResource: 127 nameToWatch = goodClusterName1 128 case xdsresource.EndpointsResource: 129 nameToWatch = goodEDSName 130 } 131 v2c.AddWatch(rType, nameToWatch) 132 133 if err := compareXDSRequest(ctx, reqChan, req, preVersion, preNonce, false); err != nil { 134 t.Fatalf("Failed to receive %v request: %v", rType, err) 135 } 136 t.Logf("FakeServer received %v request...", rType) 137 } 138 139 // sendGoodResp sends the good response, with the given version, and a random 140 // nonce. 141 // 142 // It also waits and checks that the ack request contains the given version, and 143 // the generated nonce. 144 func sendGoodResp(ctx context.Context, t *testing.T, rType xdsresource.ResourceType, fakeServer *fakeserver.Server, ver int, goodResp *xdspb.DiscoveryResponse, wantReq *xdspb.DiscoveryRequest, callbackCh *testutils.Channel) (string, error) { 145 nonce := sendXDSRespWithVersion(fakeServer.XDSResponseChan, goodResp, ver) 146 t.Logf("Good %v response pushed to fakeServer...", rType) 147 148 if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, wantReq, strconv.Itoa(ver), nonce, false); err != nil { 149 return "", fmt.Errorf("failed to receive %v request: %v", rType, err) 150 } 151 t.Logf("Good %v response acked", rType) 152 153 if _, err := callbackCh.Receive(ctx); err != nil { 154 return "", fmt.Errorf("timeout when expecting %v update", rType) 155 } 156 t.Logf("Good %v response callback executed", rType) 157 return nonce, nil 158 } 159 160 // sendBadResp sends a bad response with the given version. This response will 161 // be nacked, so we expect a request with the previous version (version-1). 162 // 163 // But the nonce in request should be the new nonce. 164 func sendBadResp(ctx context.Context, t *testing.T, rType xdsresource.ResourceType, fakeServer *fakeserver.Server, ver int, wantReq *xdspb.DiscoveryRequest) error { 165 var typeURL string 166 switch rType { 167 case xdsresource.ListenerResource: 168 typeURL = version.V2ListenerURL 169 case xdsresource.RouteConfigResource: 170 typeURL = version.V2RouteConfigURL 171 case xdsresource.ClusterResource: 172 typeURL = version.V2ClusterURL 173 case xdsresource.EndpointsResource: 174 typeURL = version.V2EndpointsURL 175 } 176 nonce := sendXDSRespWithVersion(fakeServer.XDSResponseChan, &xdspb.DiscoveryResponse{ 177 Resources: []*anypb.Any{{}}, 178 TypeUrl: typeURL, 179 }, ver) 180 t.Logf("Bad %v response pushed to fakeServer...", rType) 181 if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, wantReq, strconv.Itoa(ver-1), nonce, true); err != nil { 182 return fmt.Errorf("failed to receive %v request: %v", rType, err) 183 } 184 t.Logf("Bad %v response nacked", rType) 185 return nil 186 } 187 188 // TestV2ClientAck verifies that valid responses are acked, and invalid ones 189 // are nacked. 190 // 191 // This test also verifies the version for different types are independent. 192 func (s) TestV2ClientAck(t *testing.T) { 193 var ( 194 versionLDS = 1000 195 versionRDS = 2000 196 versionCDS = 3000 197 versionEDS = 4000 198 ) 199 200 fakeServer, cleanup := startServer(t) 201 defer cleanup() 202 203 v2c, cbLDS, cbRDS, cbCDS, cbEDS, v2cCleanup := startXDSV2Client(t, fakeServer.Address) 204 defer v2cCleanup() 205 206 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 207 defer cancel() 208 209 // Start the watch, send a good response, and check for ack. 210 startXDS(ctx, t, xdsresource.ListenerResource, v2c, fakeServer.XDSRequestChan, goodLDSRequest, "", "") 211 if _, err := sendGoodResp(ctx, t, xdsresource.ListenerResource, fakeServer, versionLDS, goodLDSResponse1, goodLDSRequest, cbLDS); err != nil { 212 t.Fatal(err) 213 } 214 versionLDS++ 215 startXDS(ctx, t, xdsresource.RouteConfigResource, v2c, fakeServer.XDSRequestChan, goodRDSRequest, "", "") 216 if _, err := sendGoodResp(ctx, t, xdsresource.RouteConfigResource, fakeServer, versionRDS, goodRDSResponse1, goodRDSRequest, cbRDS); err != nil { 217 t.Fatal(err) 218 } 219 versionRDS++ 220 startXDS(ctx, t, xdsresource.ClusterResource, v2c, fakeServer.XDSRequestChan, goodCDSRequest, "", "") 221 if _, err := sendGoodResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSResponse1, goodCDSRequest, cbCDS); err != nil { 222 t.Fatal(err) 223 } 224 versionCDS++ 225 startXDS(ctx, t, xdsresource.EndpointsResource, v2c, fakeServer.XDSRequestChan, goodEDSRequest, "", "") 226 if _, err := sendGoodResp(ctx, t, xdsresource.EndpointsResource, fakeServer, versionEDS, goodEDSResponse1, goodEDSRequest, cbEDS); err != nil { 227 t.Fatal(err) 228 } 229 versionEDS++ 230 231 // Send a bad response, and check for nack. 232 if err := sendBadResp(ctx, t, xdsresource.ListenerResource, fakeServer, versionLDS, goodLDSRequest); err != nil { 233 t.Fatal(err) 234 } 235 versionLDS++ 236 if err := sendBadResp(ctx, t, xdsresource.RouteConfigResource, fakeServer, versionRDS, goodRDSRequest); err != nil { 237 t.Fatal(err) 238 } 239 versionRDS++ 240 if err := sendBadResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSRequest); err != nil { 241 t.Fatal(err) 242 } 243 versionCDS++ 244 if err := sendBadResp(ctx, t, xdsresource.EndpointsResource, fakeServer, versionEDS, goodEDSRequest); err != nil { 245 t.Fatal(err) 246 } 247 versionEDS++ 248 249 // send another good response, and check for ack, with the new version. 250 if _, err := sendGoodResp(ctx, t, xdsresource.ListenerResource, fakeServer, versionLDS, goodLDSResponse1, goodLDSRequest, cbLDS); err != nil { 251 t.Fatal(err) 252 } 253 versionLDS++ 254 if _, err := sendGoodResp(ctx, t, xdsresource.RouteConfigResource, fakeServer, versionRDS, goodRDSResponse1, goodRDSRequest, cbRDS); err != nil { 255 t.Fatal(err) 256 } 257 versionRDS++ 258 if _, err := sendGoodResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSResponse1, goodCDSRequest, cbCDS); err != nil { 259 t.Fatal(err) 260 } 261 versionCDS++ 262 if _, err := sendGoodResp(ctx, t, xdsresource.EndpointsResource, fakeServer, versionEDS, goodEDSResponse1, goodEDSRequest, cbEDS); err != nil { 263 t.Fatal(err) 264 } 265 versionEDS++ 266 } 267 268 // Test when the first response is invalid, and is nacked, the nack requests 269 // should have an empty version string. 270 func (s) TestV2ClientAckFirstIsNack(t *testing.T) { 271 var versionLDS = 1000 272 273 fakeServer, cleanup := startServer(t) 274 defer cleanup() 275 276 v2c, cbLDS, _, _, _, v2cCleanup := startXDSV2Client(t, fakeServer.Address) 277 defer v2cCleanup() 278 279 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 280 defer cancel() 281 282 // Start the watch, send a good response, and check for ack. 283 startXDS(ctx, t, xdsresource.ListenerResource, v2c, fakeServer.XDSRequestChan, goodLDSRequest, "", "") 284 285 nonce := sendXDSRespWithVersion(fakeServer.XDSResponseChan, &xdspb.DiscoveryResponse{ 286 Resources: []*anypb.Any{{}}, 287 TypeUrl: version.V2ListenerURL, 288 }, versionLDS) 289 t.Logf("Bad response pushed to fakeServer...") 290 291 // The expected version string is an empty string, because this is the first 292 // response, and it's nacked (so there's no previous ack version). 293 if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, goodLDSRequest, "", nonce, true); err != nil { 294 t.Errorf("Failed to receive request: %v", err) 295 } 296 t.Logf("Bad response nacked") 297 versionLDS++ 298 299 sendGoodResp(ctx, t, xdsresource.ListenerResource, fakeServer, versionLDS, goodLDSResponse1, goodLDSRequest, cbLDS) 300 versionLDS++ 301 } 302 303 // Test when a nack is sent after a new watch, we nack with the previous acked 304 // version (instead of resetting to empty string). 305 func (s) TestV2ClientAckNackAfterNewWatch(t *testing.T) { 306 var versionLDS = 1000 307 308 fakeServer, cleanup := startServer(t) 309 defer cleanup() 310 311 v2c, cbLDS, _, _, _, v2cCleanup := startXDSV2Client(t, fakeServer.Address) 312 defer v2cCleanup() 313 314 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 315 defer cancel() 316 317 // Start the watch, send a good response, and check for ack. 318 startXDS(ctx, t, xdsresource.ListenerResource, v2c, fakeServer.XDSRequestChan, goodLDSRequest, "", "") 319 nonce, err := sendGoodResp(ctx, t, xdsresource.ListenerResource, fakeServer, versionLDS, goodLDSResponse1, goodLDSRequest, cbLDS) 320 if err != nil { 321 t.Fatal(err) 322 } 323 // Start a new watch. The version in the new request should be the version 324 // from the previous response, thus versionLDS before ++. 325 startXDS(ctx, t, xdsresource.ListenerResource, v2c, fakeServer.XDSRequestChan, goodLDSRequest, strconv.Itoa(versionLDS), nonce) 326 versionLDS++ 327 328 // This is an invalid response after the new watch. 329 nonce = sendXDSRespWithVersion(fakeServer.XDSResponseChan, &xdspb.DiscoveryResponse{ 330 Resources: []*anypb.Any{{}}, 331 TypeUrl: version.V2ListenerURL, 332 }, versionLDS) 333 t.Logf("Bad response pushed to fakeServer...") 334 335 // The expected version string is the previous acked version. 336 if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, goodLDSRequest, strconv.Itoa(versionLDS-1), nonce, true); err != nil { 337 t.Errorf("Failed to receive request: %v", err) 338 } 339 t.Logf("Bad response nacked") 340 versionLDS++ 341 342 if _, err := sendGoodResp(ctx, t, xdsresource.ListenerResource, fakeServer, versionLDS, goodLDSResponse1, goodLDSRequest, cbLDS); err != nil { 343 t.Fatal(err) 344 } 345 versionLDS++ 346 } 347 348 // TestV2ClientAckNewWatchAfterCancel verifies the new request for a new watch 349 // after the previous watch is canceled, has the right version. 350 func (s) TestV2ClientAckNewWatchAfterCancel(t *testing.T) { 351 var versionCDS = 3000 352 353 fakeServer, cleanup := startServer(t) 354 defer cleanup() 355 356 v2c, _, _, cbCDS, _, v2cCleanup := startXDSV2Client(t, fakeServer.Address) 357 defer v2cCleanup() 358 359 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 360 defer cancel() 361 362 // Start a CDS watch. 363 v2c.AddWatch(xdsresource.ClusterResource, goodClusterName1) 364 if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, goodCDSRequest, "", "", false); err != nil { 365 t.Fatal(err) 366 } 367 t.Logf("FakeServer received %v request...", xdsresource.ClusterResource) 368 369 // Send a good CDS response, this function waits for the ACK with the right 370 // version. 371 nonce, err := sendGoodResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSResponse1, goodCDSRequest, cbCDS) 372 if err != nil { 373 t.Fatal(err) 374 } 375 // Cancel the CDS watch, and start a new one. The new watch should have the 376 // version from the response above. 377 v2c.RemoveWatch(xdsresource.ClusterResource, goodClusterName1) 378 // Wait for a request with no resource names, because the only watch was 379 // removed. 380 emptyReq := &xdspb.DiscoveryRequest{Node: goodNodeProto, TypeUrl: version.V2ClusterURL} 381 if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, emptyReq, strconv.Itoa(versionCDS), nonce, false); err != nil { 382 t.Fatalf("Failed to receive %v request: %v", xdsresource.ClusterResource, err) 383 } 384 v2c.AddWatch(xdsresource.ClusterResource, goodClusterName1) 385 // Wait for a request with correct resource names and version. 386 if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, goodCDSRequest, strconv.Itoa(versionCDS), nonce, false); err != nil { 387 t.Fatalf("Failed to receive %v request: %v", xdsresource.ClusterResource, err) 388 } 389 versionCDS++ 390 391 // Send a bad response with the next version. 392 if err := sendBadResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSRequest); err != nil { 393 t.Fatal(err) 394 } 395 versionCDS++ 396 397 // send another good response, and check for ack, with the new version. 398 if _, err := sendGoodResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSResponse1, goodCDSRequest, cbCDS); err != nil { 399 t.Fatal(err) 400 } 401 versionCDS++ 402 } 403 404 // TestV2ClientAckCancelResponseRace verifies if the response and ACK request 405 // race with cancel (which means the ACK request will not be sent on wire, 406 // because there's no active watch), the nonce will still be updated, and the 407 // new request with the new watch will have the correct nonce. 408 func (s) TestV2ClientAckCancelResponseRace(t *testing.T) { 409 var versionCDS = 3000 410 411 fakeServer, cleanup := startServer(t) 412 defer cleanup() 413 414 v2c, _, _, cbCDS, _, v2cCleanup := startXDSV2Client(t, fakeServer.Address) 415 defer v2cCleanup() 416 417 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 418 defer cancel() 419 420 // Start a CDS watch. 421 v2c.AddWatch(xdsresource.ClusterResource, goodClusterName1) 422 if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, goodCDSRequest, "", "", false); err != nil { 423 t.Fatalf("Failed to receive %v request: %v", xdsresource.ClusterResource, err) 424 } 425 t.Logf("FakeServer received %v request...", xdsresource.ClusterResource) 426 427 // send a good response, and check for ack, with the new version. 428 nonce, err := sendGoodResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSResponse1, goodCDSRequest, cbCDS) 429 if err != nil { 430 t.Fatal(err) 431 } 432 // Cancel the watch before the next response is sent. This mimics the case 433 // watch is canceled while response is on wire. 434 v2c.RemoveWatch(xdsresource.ClusterResource, goodClusterName1) 435 // Wait for a request with no resource names, because the only watch was 436 // removed. 437 emptyReq := &xdspb.DiscoveryRequest{Node: goodNodeProto, TypeUrl: version.V2ClusterURL} 438 if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, emptyReq, strconv.Itoa(versionCDS), nonce, false); err != nil { 439 t.Fatalf("Failed to receive %v request: %v", xdsresource.ClusterResource, err) 440 } 441 versionCDS++ 442 443 sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 444 defer sCancel() 445 if req, err := fakeServer.XDSRequestChan.Receive(sCtx); err != context.DeadlineExceeded { 446 t.Fatalf("Got unexpected xds request after watch is canceled: %v", req) 447 } 448 449 // Send a good response. 450 nonce = sendXDSRespWithVersion(fakeServer.XDSResponseChan, goodCDSResponse1, versionCDS) 451 t.Logf("Good %v response pushed to fakeServer...", xdsresource.ClusterResource) 452 453 // Expect no ACK because watch was canceled. 454 sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout) 455 defer sCancel() 456 if req, err := fakeServer.XDSRequestChan.Receive(sCtx); err != context.DeadlineExceeded { 457 t.Fatalf("Got unexpected xds request after watch is canceled: %v", req) 458 } 459 460 // Still expected an callback update, because response was good. 461 if _, err := cbCDS.Receive(ctx); err != nil { 462 t.Fatalf("Timeout when expecting %v update", xdsresource.ClusterResource) 463 } 464 465 // Start a new watch. The new watch should have the nonce from the response 466 // above, and version from the first good response. 467 v2c.AddWatch(xdsresource.ClusterResource, goodClusterName1) 468 if err := compareXDSRequest(ctx, fakeServer.XDSRequestChan, goodCDSRequest, strconv.Itoa(versionCDS-1), nonce, false); err != nil { 469 t.Fatalf("Failed to receive %v request: %v", xdsresource.ClusterResource, err) 470 } 471 472 // Send a bad response with the next version. 473 if err := sendBadResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSRequest); err != nil { 474 t.Fatal(err) 475 } 476 versionCDS++ 477 478 // send another good response, and check for ack, with the new version. 479 if _, err := sendGoodResp(ctx, t, xdsresource.ClusterResource, fakeServer, versionCDS, goodCDSResponse1, goodCDSRequest, cbCDS); err != nil { 480 t.Fatal(err) 481 } 482 versionCDS++ 483 }