github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/balancer/cdsbalancer/cluster_handler_test.go (about) 1 /* 2 * Copyright 2021 gRPC authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cdsbalancer 18 19 import ( 20 "context" 21 "errors" 22 "testing" 23 24 "github.com/google/go-cmp/cmp" 25 "github.com/hxx258456/ccgo/grpc/xds/internal/testutils/fakeclient" 26 "github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource" 27 ) 28 29 const ( 30 edsService = "EDS Service" 31 logicalDNSService = "Logical DNS Service" 32 edsService2 = "EDS Service 2" 33 logicalDNSService2 = "Logical DNS Service 2" 34 aggregateClusterService = "Aggregate Cluster Service" 35 ) 36 37 // setupTests creates a clusterHandler with a fake xds client for control over 38 // xds client. 39 func setupTests() (*clusterHandler, *fakeclient.Client) { 40 xdsC := fakeclient.NewClient() 41 ch := newClusterHandler(&cdsBalancer{xdsClient: xdsC}) 42 return ch, xdsC 43 } 44 45 // Simplest case: the cluster handler receives a cluster name, handler starts a 46 // watch for that cluster, xds client returns that it is a Leaf Node (EDS or 47 // LogicalDNS), not a tree, so expectation that update is written to buffer 48 // which will be read by CDS LB. 49 func (s) TestSuccessCaseLeafNode(t *testing.T) { 50 tests := []struct { 51 name string 52 clusterName string 53 clusterUpdate xdsresource.ClusterUpdate 54 lbPolicy *xdsresource.ClusterLBPolicyRingHash 55 }{ 56 { 57 name: "test-update-root-cluster-EDS-success", 58 clusterName: edsService, 59 clusterUpdate: xdsresource.ClusterUpdate{ 60 ClusterType: xdsresource.ClusterTypeEDS, 61 ClusterName: edsService, 62 }, 63 }, 64 { 65 name: "test-update-root-cluster-EDS-with-ring-hash", 66 clusterName: logicalDNSService, 67 clusterUpdate: xdsresource.ClusterUpdate{ 68 ClusterType: xdsresource.ClusterTypeLogicalDNS, 69 ClusterName: logicalDNSService, 70 LBPolicy: &xdsresource.ClusterLBPolicyRingHash{MinimumRingSize: 10, MaximumRingSize: 100}, 71 }, 72 lbPolicy: &xdsresource.ClusterLBPolicyRingHash{MinimumRingSize: 10, MaximumRingSize: 100}, 73 }, 74 { 75 name: "test-update-root-cluster-Logical-DNS-success", 76 clusterName: logicalDNSService, 77 clusterUpdate: xdsresource.ClusterUpdate{ 78 ClusterType: xdsresource.ClusterTypeLogicalDNS, 79 ClusterName: logicalDNSService, 80 }, 81 }, 82 } 83 84 for _, test := range tests { 85 t.Run(test.name, func(t *testing.T) { 86 ch, fakeClient := setupTests() 87 // When you first update the root cluster, it should hit the code 88 // path which will start a cluster node for that root. Updating the 89 // root cluster logically represents a ping from a ClientConn. 90 ch.updateRootCluster(test.clusterName) 91 // Starting a cluster node involves communicating with the 92 // xdsClient, telling it to watch a cluster. 93 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 94 defer ctxCancel() 95 gotCluster, err := fakeClient.WaitForWatchCluster(ctx) 96 if err != nil { 97 t.Fatalf("xdsClient.WatchCDS failed with error: %v", err) 98 } 99 if gotCluster != test.clusterName { 100 t.Fatalf("xdsClient.WatchCDS called for cluster: %v, want: %v", gotCluster, test.clusterName) 101 } 102 // Invoke callback with xds client with a certain clusterUpdate. Due 103 // to this cluster update filling out the whole cluster tree, as the 104 // cluster is of a root type (EDS or Logical DNS) and not an 105 // aggregate cluster, this should trigger the ClusterHandler to 106 // write to the update buffer to update the CDS policy. 107 fakeClient.InvokeWatchClusterCallback(test.clusterUpdate, nil) 108 select { 109 case chu := <-ch.updateChannel: 110 if diff := cmp.Diff(chu.updates, []xdsresource.ClusterUpdate{test.clusterUpdate}); diff != "" { 111 t.Fatalf("got unexpected cluster update, diff (-got, +want): %v", diff) 112 } 113 if diff := cmp.Diff(chu.lbPolicy, test.lbPolicy); diff != "" { 114 t.Fatalf("got unexpected lb policy in cluster update, diff (-got, +want): %v", diff) 115 } 116 case <-ctx.Done(): 117 t.Fatal("Timed out waiting for update from update channel.") 118 } 119 // Close the clusterHandler. This is meant to be called when the CDS 120 // Balancer is closed, and the call should cancel the watch for this 121 // cluster. 122 ch.close() 123 clusterNameDeleted, err := fakeClient.WaitForCancelClusterWatch(ctx) 124 if err != nil { 125 t.Fatalf("xdsClient.CancelCDS failed with error: %v", err) 126 } 127 if clusterNameDeleted != test.clusterName { 128 t.Fatalf("xdsClient.CancelCDS called for cluster %v, want: %v", clusterNameDeleted, logicalDNSService) 129 } 130 }) 131 } 132 } 133 134 // The cluster handler receives a cluster name, handler starts a watch for that 135 // cluster, xds client returns that it is a Leaf Node (EDS or LogicalDNS), not a 136 // tree, so expectation that first update is written to buffer which will be 137 // read by CDS LB. Then, send a new cluster update that is different, with the 138 // expectation that it is also written to the update buffer to send back to CDS. 139 func (s) TestSuccessCaseLeafNodeThenNewUpdate(t *testing.T) { 140 tests := []struct { 141 name string 142 clusterName string 143 clusterUpdate xdsresource.ClusterUpdate 144 newClusterUpdate xdsresource.ClusterUpdate 145 }{ 146 {name: "test-update-root-cluster-then-new-update-EDS-success", 147 clusterName: edsService, 148 clusterUpdate: xdsresource.ClusterUpdate{ 149 ClusterType: xdsresource.ClusterTypeEDS, 150 ClusterName: edsService, 151 }, 152 newClusterUpdate: xdsresource.ClusterUpdate{ 153 ClusterType: xdsresource.ClusterTypeEDS, 154 ClusterName: edsService2, 155 }, 156 }, 157 { 158 name: "test-update-root-cluster-then-new-update-Logical-DNS-success", 159 clusterName: logicalDNSService, 160 clusterUpdate: xdsresource.ClusterUpdate{ 161 ClusterType: xdsresource.ClusterTypeLogicalDNS, 162 ClusterName: logicalDNSService, 163 }, 164 newClusterUpdate: xdsresource.ClusterUpdate{ 165 ClusterType: xdsresource.ClusterTypeLogicalDNS, 166 ClusterName: logicalDNSService2, 167 }, 168 }, 169 } 170 171 for _, test := range tests { 172 t.Run(test.name, func(t *testing.T) { 173 ch, fakeClient := setupTests() 174 ch.updateRootCluster(test.clusterName) 175 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 176 defer ctxCancel() 177 _, err := fakeClient.WaitForWatchCluster(ctx) 178 if err != nil { 179 t.Fatalf("xdsClient.WatchCDS failed with error: %v", err) 180 } 181 fakeClient.InvokeWatchClusterCallback(test.clusterUpdate, nil) 182 select { 183 case <-ch.updateChannel: 184 case <-ctx.Done(): 185 t.Fatal("Timed out waiting for update from updateChannel.") 186 } 187 188 // Check that sending the same cluster update also induces an update 189 // to be written to update buffer. 190 fakeClient.InvokeWatchClusterCallback(test.clusterUpdate, nil) 191 shouldNotHappenCtx, shouldNotHappenCtxCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 192 defer shouldNotHappenCtxCancel() 193 select { 194 case <-ch.updateChannel: 195 case <-shouldNotHappenCtx.Done(): 196 t.Fatal("Timed out waiting for update from updateChannel.") 197 } 198 199 // Above represents same thing as the simple 200 // TestSuccessCaseLeafNode, extra behavior + validation (clusterNode 201 // which is a leaf receives a changed clusterUpdate, which should 202 // ping clusterHandler, which should then write to the update 203 // buffer). 204 fakeClient.InvokeWatchClusterCallback(test.newClusterUpdate, nil) 205 select { 206 case chu := <-ch.updateChannel: 207 if diff := cmp.Diff(chu.updates, []xdsresource.ClusterUpdate{test.newClusterUpdate}); diff != "" { 208 t.Fatalf("got unexpected cluster update, diff (-got, +want): %v", diff) 209 } 210 case <-ctx.Done(): 211 t.Fatal("Timed out waiting for update from updateChannel.") 212 } 213 }) 214 } 215 } 216 217 // TestUpdateRootClusterAggregateSuccess tests the case where an aggregate 218 // cluster is a root pointing to two child clusters one of type EDS and the 219 // other of type LogicalDNS. This test will then send cluster updates for both 220 // the children, and at the end there should be a successful clusterUpdate 221 // written to the update buffer to send back to CDS. 222 func (s) TestUpdateRootClusterAggregateSuccess(t *testing.T) { 223 ch, fakeClient := setupTests() 224 ch.updateRootCluster(aggregateClusterService) 225 226 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 227 defer ctxCancel() 228 gotCluster, err := fakeClient.WaitForWatchCluster(ctx) 229 if err != nil { 230 t.Fatalf("xdsClient.WatchCDS failed with error: %v", err) 231 } 232 if gotCluster != aggregateClusterService { 233 t.Fatalf("xdsClient.WatchCDS called for cluster: %v, want: %v", gotCluster, aggregateClusterService) 234 } 235 236 // The xdsClient telling the clusterNode that the cluster type is an 237 // aggregate cluster which will cause a lot of downstream behavior. For a 238 // cluster type that isn't an aggregate, the behavior is simple. The 239 // clusterNode will simply get a successful update, which will then ping the 240 // clusterHandler which will successfully build an update to send to the CDS 241 // policy. In the aggregate cluster case, the handleResp callback must also 242 // start watches for the aggregate cluster's children. The ping to the 243 // clusterHandler at the end of handleResp should be a no-op, as neither the 244 // EDS or LogicalDNS child clusters have received an update yet. 245 fakeClient.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{ 246 ClusterType: xdsresource.ClusterTypeAggregate, 247 ClusterName: aggregateClusterService, 248 PrioritizedClusterNames: []string{edsService, logicalDNSService}, 249 }, nil) 250 251 // xds client should be called to start a watch for one of the child 252 // clusters of the aggregate. The order of the children in the update 253 // written to the buffer to send to CDS matters, however there is no 254 // guarantee on the order it will start the watches of the children. 255 gotCluster, err = fakeClient.WaitForWatchCluster(ctx) 256 if err != nil { 257 t.Fatalf("xdsClient.WatchCDS failed with error: %v", err) 258 } 259 if gotCluster != edsService { 260 if gotCluster != logicalDNSService { 261 t.Fatalf("xdsClient.WatchCDS called for cluster: %v, want: %v", gotCluster, edsService) 262 } 263 } 264 265 // xds client should then be called to start a watch for the second child 266 // cluster. 267 gotCluster, err = fakeClient.WaitForWatchCluster(ctx) 268 if err != nil { 269 t.Fatalf("xdsClient.WatchCDS failed with error: %v", err) 270 } 271 if gotCluster != edsService { 272 if gotCluster != logicalDNSService { 273 t.Fatalf("xdsClient.WatchCDS called for cluster: %v, want: %v", gotCluster, logicalDNSService) 274 } 275 } 276 277 // The handleResp() call on the root aggregate cluster should not ping the 278 // cluster handler to try and construct an update, as the handleResp() 279 // callback knows that when a child is created, it cannot possibly build a 280 // successful update yet. Thus, there should be nothing in the update 281 // channel. 282 283 shouldNotHappenCtx, shouldNotHappenCtxCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 284 defer shouldNotHappenCtxCancel() 285 286 select { 287 case <-ch.updateChannel: 288 t.Fatal("Cluster Handler wrote an update to updateChannel when it shouldn't have, as each node in the full cluster tree has not yet received an update") 289 case <-shouldNotHappenCtx.Done(): 290 } 291 292 // Send callback for the EDS child cluster. 293 fakeClient.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{ 294 ClusterType: xdsresource.ClusterTypeEDS, 295 ClusterName: edsService, 296 }, nil) 297 298 // EDS child cluster will ping the Cluster Handler, to try an update, which 299 // still won't successfully build as the LogicalDNS child of the root 300 // aggregate cluster has not yet received and handled an update. 301 select { 302 case <-ch.updateChannel: 303 t.Fatal("Cluster Handler wrote an update to updateChannel when it shouldn't have, as each node in the full cluster tree has not yet received an update") 304 case <-shouldNotHappenCtx.Done(): 305 } 306 307 // Invoke callback for Logical DNS child cluster. 308 309 fakeClient.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{ 310 ClusterType: xdsresource.ClusterTypeLogicalDNS, 311 ClusterName: logicalDNSService, 312 }, nil) 313 314 // Will Ping Cluster Handler, which will finally successfully build an 315 // update as all nodes in the tree of clusters have received an update. 316 // Since this cluster is an aggregate cluster comprised of two children, the 317 // returned update should be length 2, as the xds cluster resolver LB policy 318 // only cares about the full list of LogicalDNS and EDS clusters 319 // representing the base nodes of the tree of clusters. This list should be 320 // ordered as per the cluster update. 321 select { 322 case chu := <-ch.updateChannel: 323 if diff := cmp.Diff(chu.updates, []xdsresource.ClusterUpdate{{ 324 ClusterType: xdsresource.ClusterTypeEDS, 325 ClusterName: edsService, 326 }, { 327 ClusterType: xdsresource.ClusterTypeLogicalDNS, 328 ClusterName: logicalDNSService, 329 }}); diff != "" { 330 t.Fatalf("got unexpected cluster update, diff (-got, +want): %v", diff) 331 } 332 case <-ctx.Done(): 333 t.Fatal("Timed out waiting for the cluster update to be written to the update buffer.") 334 } 335 } 336 337 // TestUpdateRootClusterAggregateThenChangeChild tests the scenario where you 338 // have an aggregate cluster with an EDS child and a LogicalDNS child, then you 339 // change one of the children and send an update for the changed child. This 340 // should write a new update to the update buffer to send back to CDS. 341 func (s) TestUpdateRootClusterAggregateThenChangeChild(t *testing.T) { 342 // This initial code is the same as the test for the aggregate success case, 343 // except without validations. This will get this test to the point where it 344 // can change one of the children. 345 ch, fakeClient := setupTests() 346 ch.updateRootCluster(aggregateClusterService) 347 348 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 349 defer ctxCancel() 350 _, err := fakeClient.WaitForWatchCluster(ctx) 351 if err != nil { 352 t.Fatalf("xdsClient.WatchCDS failed with error: %v", err) 353 } 354 355 fakeClient.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{ 356 ClusterType: xdsresource.ClusterTypeAggregate, 357 ClusterName: aggregateClusterService, 358 PrioritizedClusterNames: []string{edsService, logicalDNSService}, 359 }, nil) 360 fakeClient.WaitForWatchCluster(ctx) 361 fakeClient.WaitForWatchCluster(ctx) 362 fakeClient.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{ 363 ClusterType: xdsresource.ClusterTypeEDS, 364 ClusterName: edsService, 365 }, nil) 366 fakeClient.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{ 367 ClusterType: xdsresource.ClusterTypeLogicalDNS, 368 ClusterName: logicalDNSService, 369 }, nil) 370 371 select { 372 case <-ch.updateChannel: 373 case <-ctx.Done(): 374 t.Fatal("Timed out waiting for the cluster update to be written to the update buffer.") 375 } 376 377 fakeClient.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{ 378 ClusterType: xdsresource.ClusterTypeAggregate, 379 ClusterName: aggregateClusterService, 380 PrioritizedClusterNames: []string{edsService, logicalDNSService2}, 381 }, nil) 382 383 // The cluster update let's the aggregate cluster know that it's children 384 // are now edsService and logicalDNSService2, which implies that the 385 // aggregateCluster lost it's old logicalDNSService child. Thus, the 386 // logicalDNSService child should be deleted. 387 clusterNameDeleted, err := fakeClient.WaitForCancelClusterWatch(ctx) 388 if err != nil { 389 t.Fatalf("xdsClient.CancelCDS failed with error: %v", err) 390 } 391 if clusterNameDeleted != logicalDNSService { 392 t.Fatalf("xdsClient.CancelCDS called for cluster %v, want: %v", clusterNameDeleted, logicalDNSService) 393 } 394 395 // The handleResp() callback should then start a watch for 396 // logicalDNSService2. 397 clusterNameCreated, err := fakeClient.WaitForWatchCluster(ctx) 398 if err != nil { 399 t.Fatalf("xdsClient.WatchCDS failed with error: %v", err) 400 } 401 if clusterNameCreated != logicalDNSService2 { 402 t.Fatalf("xdsClient.WatchCDS called for cluster %v, want: %v", clusterNameCreated, logicalDNSService2) 403 } 404 405 // handleResp() should try and send an update here, but it will fail as 406 // logicalDNSService2 has not yet received an update. 407 shouldNotHappenCtx, shouldNotHappenCtxCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 408 defer shouldNotHappenCtxCancel() 409 select { 410 case <-ch.updateChannel: 411 t.Fatal("Cluster Handler wrote an update to updateChannel when it shouldn't have, as each node in the full cluster tree has not yet received an update") 412 case <-shouldNotHappenCtx.Done(): 413 } 414 415 // Invoke a callback for the new logicalDNSService2 - this will fill out the 416 // tree with successful updates. 417 fakeClient.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{ 418 ClusterType: xdsresource.ClusterTypeLogicalDNS, 419 ClusterName: logicalDNSService2, 420 }, nil) 421 422 // Behavior: This update make every node in the tree of cluster have 423 // received an update. Thus, at the end of this callback, when you ping the 424 // clusterHandler to try and construct an update, the update should now 425 // successfully be written to update buffer to send back to CDS. This new 426 // update should contain the new child of LogicalDNS2. 427 428 select { 429 case chu := <-ch.updateChannel: 430 if diff := cmp.Diff(chu.updates, []xdsresource.ClusterUpdate{{ 431 ClusterType: xdsresource.ClusterTypeEDS, 432 ClusterName: edsService, 433 }, { 434 ClusterType: xdsresource.ClusterTypeLogicalDNS, 435 ClusterName: logicalDNSService2, 436 }}); diff != "" { 437 t.Fatalf("got unexpected cluster update, diff (-got, +want): %v", diff) 438 } 439 case <-ctx.Done(): 440 t.Fatal("Timed out waiting for the cluster update to be written to the update buffer.") 441 } 442 } 443 444 // TestUpdateRootClusterAggregateThenChangeRootToEDS tests the situation where 445 // you have a fully updated aggregate cluster (where AggregateCluster success 446 // test gets you) as the root cluster, then you update that root cluster to a 447 // cluster of type EDS. 448 func (s) TestUpdateRootClusterAggregateThenChangeRootToEDS(t *testing.T) { 449 // This initial code is the same as the test for the aggregate success case, 450 // except without validations. This will get this test to the point where it 451 // can update the root cluster to one of type EDS. 452 ch, fakeClient := setupTests() 453 ch.updateRootCluster(aggregateClusterService) 454 455 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 456 defer ctxCancel() 457 _, err := fakeClient.WaitForWatchCluster(ctx) 458 if err != nil { 459 t.Fatalf("xdsClient.WatchCDS failed with error: %v", err) 460 } 461 462 fakeClient.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{ 463 ClusterType: xdsresource.ClusterTypeAggregate, 464 ClusterName: aggregateClusterService, 465 PrioritizedClusterNames: []string{edsService, logicalDNSService}, 466 }, nil) 467 fakeClient.WaitForWatchCluster(ctx) 468 fakeClient.WaitForWatchCluster(ctx) 469 fakeClient.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{ 470 ClusterType: xdsresource.ClusterTypeEDS, 471 ClusterName: edsService, 472 }, nil) 473 fakeClient.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{ 474 ClusterType: xdsresource.ClusterTypeLogicalDNS, 475 ClusterName: logicalDNSService, 476 }, nil) 477 478 select { 479 case <-ch.updateChannel: 480 case <-ctx.Done(): 481 t.Fatal("Timed out waiting for the cluster update to be written to the update buffer.") 482 } 483 484 // Changes the root aggregate cluster to a EDS cluster. This should delete 485 // the root aggregate cluster and all of it's children by successfully 486 // canceling the watches for them. 487 ch.updateRootCluster(edsService2) 488 489 // Reads from the cancel channel, should first be type Aggregate, then EDS 490 // then Logical DNS. 491 clusterNameDeleted, err := fakeClient.WaitForCancelClusterWatch(ctx) 492 if err != nil { 493 t.Fatalf("xdsClient.CancelCDS failed with error: %v", err) 494 } 495 if clusterNameDeleted != aggregateClusterService { 496 t.Fatalf("xdsClient.CancelCDS called for cluster %v, want: %v", clusterNameDeleted, logicalDNSService) 497 } 498 499 clusterNameDeleted, err = fakeClient.WaitForCancelClusterWatch(ctx) 500 if err != nil { 501 t.Fatalf("xdsClient.CancelCDS failed with error: %v", err) 502 } 503 if clusterNameDeleted != edsService { 504 t.Fatalf("xdsClient.CancelCDS called for cluster %v, want: %v", clusterNameDeleted, logicalDNSService) 505 } 506 507 clusterNameDeleted, err = fakeClient.WaitForCancelClusterWatch(ctx) 508 if err != nil { 509 t.Fatalf("xdsClient.CancelCDS failed with error: %v", err) 510 } 511 if clusterNameDeleted != logicalDNSService { 512 t.Fatalf("xdsClient.CancelCDS called for cluster %v, want: %v", clusterNameDeleted, logicalDNSService) 513 } 514 515 // After deletion, it should start a watch for the EDS Cluster. The behavior 516 // for this EDS Cluster receiving an update from xds client and then 517 // successfully writing an update to send back to CDS is already tested in 518 // the updateEDS success case. 519 gotCluster, err := fakeClient.WaitForWatchCluster(ctx) 520 if err != nil { 521 t.Fatalf("xdsClient.WatchCDS failed with error: %v", err) 522 } 523 if gotCluster != edsService2 { 524 t.Fatalf("xdsClient.WatchCDS called for cluster: %v, want: %v", gotCluster, edsService2) 525 } 526 } 527 528 // TestHandleRespInvokedWithError tests that when handleResp is invoked with an 529 // error, that the error is successfully written to the update buffer. 530 func (s) TestHandleRespInvokedWithError(t *testing.T) { 531 ch, fakeClient := setupTests() 532 ch.updateRootCluster(edsService) 533 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 534 defer ctxCancel() 535 _, err := fakeClient.WaitForWatchCluster(ctx) 536 if err != nil { 537 t.Fatalf("xdsClient.WatchCDS failed with error: %v", err) 538 } 539 fakeClient.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{}, errors.New("some error")) 540 select { 541 case chu := <-ch.updateChannel: 542 if chu.err.Error() != "some error" { 543 t.Fatalf("Did not receive the expected error, instead received: %v", chu.err.Error()) 544 } 545 case <-ctx.Done(): 546 t.Fatal("Timed out waiting for update from update channel.") 547 } 548 } 549 550 // TestSwitchClusterNodeBetweenLeafAndAggregated tests having an existing 551 // cluster node switch between a leaf and an aggregated cluster. When the 552 // cluster switches from a leaf to an aggregated cluster, it should add 553 // children, and when it switches back to a leaf, it should delete those new 554 // children and also successfully write a cluster update to the update buffer. 555 func (s) TestSwitchClusterNodeBetweenLeafAndAggregated(t *testing.T) { 556 // Getting the test to the point where there's a root cluster which is a eds 557 // leaf. 558 ch, fakeClient := setupTests() 559 ch.updateRootCluster(edsService2) 560 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 561 defer ctxCancel() 562 _, err := fakeClient.WaitForWatchCluster(ctx) 563 if err != nil { 564 t.Fatalf("xdsClient.WatchCDS failed with error: %v", err) 565 } 566 fakeClient.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{ 567 ClusterType: xdsresource.ClusterTypeEDS, 568 ClusterName: edsService2, 569 }, nil) 570 select { 571 case <-ch.updateChannel: 572 case <-ctx.Done(): 573 t.Fatal("Timed out waiting for update from update channel.") 574 } 575 // Switch the cluster to an aggregate cluster, this should cause two new 576 // child watches to be created. 577 fakeClient.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{ 578 ClusterType: xdsresource.ClusterTypeAggregate, 579 ClusterName: edsService2, 580 PrioritizedClusterNames: []string{edsService, logicalDNSService}, 581 }, nil) 582 583 // xds client should be called to start a watch for one of the child 584 // clusters of the aggregate. The order of the children in the update 585 // written to the buffer to send to CDS matters, however there is no 586 // guarantee on the order it will start the watches of the children. 587 gotCluster, err := fakeClient.WaitForWatchCluster(ctx) 588 if err != nil { 589 t.Fatalf("xdsClient.WatchCDS failed with error: %v", err) 590 } 591 if gotCluster != edsService { 592 if gotCluster != logicalDNSService { 593 t.Fatalf("xdsClient.WatchCDS called for cluster: %v, want: %v", gotCluster, edsService) 594 } 595 } 596 597 // xds client should then be called to start a watch for the second child 598 // cluster. 599 gotCluster, err = fakeClient.WaitForWatchCluster(ctx) 600 if err != nil { 601 t.Fatalf("xdsClient.WatchCDS failed with error: %v", err) 602 } 603 if gotCluster != edsService { 604 if gotCluster != logicalDNSService { 605 t.Fatalf("xdsClient.WatchCDS called for cluster: %v, want: %v", gotCluster, logicalDNSService) 606 } 607 } 608 609 // After starting a watch for the second child cluster, there should be no 610 // more watches started on the xds client. 611 shouldNotHappenCtx, shouldNotHappenCtxCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 612 defer shouldNotHappenCtxCancel() 613 gotCluster, err = fakeClient.WaitForWatchCluster(shouldNotHappenCtx) 614 if err == nil { 615 t.Fatalf("xdsClient.WatchCDS called for cluster: %v, no more watches should be started.", gotCluster) 616 } 617 618 // The handleResp() call on the root aggregate cluster should not ping the 619 // cluster handler to try and construct an update, as the handleResp() 620 // callback knows that when a child is created, it cannot possibly build a 621 // successful update yet. Thus, there should be nothing in the update 622 // channel. 623 624 shouldNotHappenCtx, shouldNotHappenCtxCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout) 625 defer shouldNotHappenCtxCancel() 626 627 select { 628 case <-ch.updateChannel: 629 t.Fatal("Cluster Handler wrote an update to updateChannel when it shouldn't have, as each node in the full cluster tree has not yet received an update") 630 case <-shouldNotHappenCtx.Done(): 631 } 632 633 // Switch the cluster back to an EDS Cluster. This should cause the two 634 // children to be deleted. 635 fakeClient.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{ 636 ClusterType: xdsresource.ClusterTypeEDS, 637 ClusterName: edsService2, 638 }, nil) 639 640 // Should delete the two children (no guarantee of ordering deleted, which 641 // is ok), then successfully write an update to the update buffer as the 642 // full cluster tree has received updates. 643 clusterNameDeleted, err := fakeClient.WaitForCancelClusterWatch(ctx) 644 if err != nil { 645 t.Fatalf("xdsClient.CancelCDS failed with error: %v", err) 646 } 647 // No guarantee of ordering, so one of the children should be deleted first. 648 if clusterNameDeleted != edsService { 649 if clusterNameDeleted != logicalDNSService { 650 t.Fatalf("xdsClient.CancelCDS called for cluster %v, want either: %v or: %v", clusterNameDeleted, edsService, logicalDNSService) 651 } 652 } 653 // Then the other child should be deleted. 654 clusterNameDeleted, err = fakeClient.WaitForCancelClusterWatch(ctx) 655 if err != nil { 656 t.Fatalf("xdsClient.CancelCDS failed with error: %v", err) 657 } 658 if clusterNameDeleted != edsService { 659 if clusterNameDeleted != logicalDNSService { 660 t.Fatalf("xdsClient.CancelCDS called for cluster %v, want either: %v or: %v", clusterNameDeleted, edsService, logicalDNSService) 661 } 662 } 663 664 // After cancelling a watch for the second child cluster, there should be no 665 // more watches cancelled on the xds client. 666 shouldNotHappenCtx, shouldNotHappenCtxCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout) 667 defer shouldNotHappenCtxCancel() 668 gotCluster, err = fakeClient.WaitForCancelClusterWatch(shouldNotHappenCtx) 669 if err == nil { 670 t.Fatalf("xdsClient.WatchCDS called for cluster: %v, no more watches should be cancelled.", gotCluster) 671 } 672 673 // Then an update should successfully be written to the update buffer. 674 select { 675 case chu := <-ch.updateChannel: 676 if diff := cmp.Diff(chu.updates, []xdsresource.ClusterUpdate{{ 677 ClusterType: xdsresource.ClusterTypeEDS, 678 ClusterName: edsService2, 679 }}); diff != "" { 680 t.Fatalf("got unexpected cluster update, diff (-got, +want): %v", diff) 681 } 682 case <-ctx.Done(): 683 t.Fatal("Timed out waiting for update from update channel.") 684 } 685 }