gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/xds/internal/server/rds_handler_test.go (about)

     1  /*
     2   *
     3   * Copyright 2021 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  
    19  package server
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"fmt"
    25  	"testing"
    26  
    27  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/testutils/fakeclient"
    28  	"gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient/xdsresource"
    29  	"github.com/google/go-cmp/cmp"
    30  )
    31  
    32  const (
    33  	route1 = "route1"
    34  	route2 = "route2"
    35  	route3 = "route3"
    36  )
    37  
    38  // setupTests creates a rds handler with a fake xds client for control over the
    39  // xds client.
    40  func setupTests() (*rdsHandler, *fakeclient.Client, chan rdsHandlerUpdate) {
    41  	xdsC := fakeclient.NewClient()
    42  	ch := make(chan rdsHandlerUpdate, 1)
    43  	rh := newRDSHandler(xdsC, ch)
    44  	return rh, xdsC, ch
    45  }
    46  
    47  // waitForFuncWithNames makes sure that a blocking function returns the correct
    48  // set of names, where order doesn't matter. This takes away nondeterminism from
    49  // ranging through a map.
    50  func waitForFuncWithNames(ctx context.Context, f func(context.Context) (string, error), names ...string) error {
    51  	wantNames := make(map[string]bool, len(names))
    52  	for _, name := range names {
    53  		wantNames[name] = true
    54  	}
    55  	gotNames := make(map[string]bool, len(names))
    56  	for range wantNames {
    57  		name, err := f(ctx)
    58  		if err != nil {
    59  			return err
    60  		}
    61  		gotNames[name] = true
    62  	}
    63  	if !cmp.Equal(gotNames, wantNames) {
    64  		return fmt.Errorf("got routeNames %v, want %v", gotNames, wantNames)
    65  	}
    66  	return nil
    67  }
    68  
    69  // TestSuccessCaseOneRDSWatch tests the simplest scenario: the rds handler
    70  // receives a single route name, starts a watch for that route name, gets a
    71  // successful update, and then writes an update to the update channel for
    72  // listener to pick up.
    73  func (s) TestSuccessCaseOneRDSWatch(t *testing.T) {
    74  	rh, fakeClient, ch := setupTests()
    75  	// When you first update the rds handler with a list of a single Route names
    76  	// that needs dynamic RDS Configuration, this Route name has not been seen
    77  	// before, so the RDS Handler should start a watch on that RouteName.
    78  	rh.updateRouteNamesToWatch(map[string]bool{route1: true})
    79  	// The RDS Handler should start a watch for that routeName.
    80  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
    81  	defer cancel()
    82  	gotRoute, err := fakeClient.WaitForWatchRouteConfig(ctx)
    83  	if err != nil {
    84  		t.Fatalf("xdsClient.WatchRDS failed with error: %v", err)
    85  	}
    86  	if gotRoute != route1 {
    87  		t.Fatalf("xdsClient.WatchRDS called for route: %v, want %v", gotRoute, route1)
    88  	}
    89  	rdsUpdate := xdsresource.RouteConfigUpdate{}
    90  	// Invoke callback with the xds client with a certain route update. Due to
    91  	// this route update updating every route name that rds handler handles,
    92  	// this should write to the update channel to send to the listener.
    93  	fakeClient.InvokeWatchRouteConfigCallback(route1, rdsUpdate, nil)
    94  	rhuWant := map[string]xdsresource.RouteConfigUpdate{route1: rdsUpdate}
    95  	select {
    96  	case rhu := <-ch:
    97  		if diff := cmp.Diff(rhu.updates, rhuWant); diff != "" {
    98  			t.Fatalf("got unexpected route update, diff (-got, +want): %v", diff)
    99  		}
   100  	case <-ctx.Done():
   101  		t.Fatal("Timed out waiting for update from update channel.")
   102  	}
   103  	// Close the rds handler. This is meant to be called when the lis wrapper is
   104  	// closed, and the call should cancel all the watches present (for this
   105  	// test, a single watch).
   106  	rh.close()
   107  	routeNameDeleted, err := fakeClient.WaitForCancelRouteConfigWatch(ctx)
   108  	if err != nil {
   109  		t.Fatalf("xdsClient.CancelRDS failed with error: %v", err)
   110  	}
   111  	if routeNameDeleted != route1 {
   112  		t.Fatalf("xdsClient.CancelRDS called for route %v, want %v", routeNameDeleted, route1)
   113  	}
   114  }
   115  
   116  // TestSuccessCaseTwoUpdates tests the case where the rds handler receives an
   117  // update with a single Route, then receives a second update with two routes.
   118  // The handler should start a watch for the added route, and if received a RDS
   119  // update for that route it should send an update with both RDS updates present.
   120  func (s) TestSuccessCaseTwoUpdates(t *testing.T) {
   121  	rh, fakeClient, ch := setupTests()
   122  
   123  	rh.updateRouteNamesToWatch(map[string]bool{route1: true})
   124  
   125  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   126  	defer cancel()
   127  	gotRoute, err := fakeClient.WaitForWatchRouteConfig(ctx)
   128  	if err != nil {
   129  		t.Fatalf("xdsClient.WatchRDS failed with error: %v", err)
   130  	}
   131  	if gotRoute != route1 {
   132  		t.Fatalf("xdsClient.WatchRDS called for route: %v, want %v", gotRoute, route1)
   133  	}
   134  
   135  	// Update the RDSHandler with route names which adds a route name to watch.
   136  	// This should trigger the RDSHandler to start a watch for the added route
   137  	// name to watch.
   138  	rh.updateRouteNamesToWatch(map[string]bool{route1: true, route2: true})
   139  	gotRoute, err = fakeClient.WaitForWatchRouteConfig(ctx)
   140  	if err != nil {
   141  		t.Fatalf("xdsClient.WatchRDS failed with error: %v", err)
   142  	}
   143  	if gotRoute != route2 {
   144  		t.Fatalf("xdsClient.WatchRDS called for route: %v, want %v", gotRoute, route2)
   145  	}
   146  
   147  	// Invoke the callback with an update for route 1. This shouldn't cause the
   148  	// handler to write an update, as it has not received RouteConfigurations
   149  	// for every RouteName.
   150  	rdsUpdate1 := xdsresource.RouteConfigUpdate{}
   151  	fakeClient.InvokeWatchRouteConfigCallback(route1, rdsUpdate1, nil)
   152  
   153  	// The RDS Handler should not send an update.
   154  	sCtx, sCtxCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
   155  	defer sCtxCancel()
   156  	select {
   157  	case <-ch:
   158  		t.Fatal("RDS Handler wrote an update to updateChannel when it shouldn't have, as each route name has not received an update yet")
   159  	case <-sCtx.Done():
   160  	}
   161  
   162  	// Invoke the callback with an update for route 2. This should cause the
   163  	// handler to write an update, as it has received RouteConfigurations for
   164  	// every RouteName.
   165  	rdsUpdate2 := xdsresource.RouteConfigUpdate{}
   166  	fakeClient.InvokeWatchRouteConfigCallback(route2, rdsUpdate2, nil)
   167  	// The RDS Handler should then update the listener wrapper with an update
   168  	// with two route configurations, as both route names the RDS Handler handles
   169  	// have received an update.
   170  	rhuWant := map[string]xdsresource.RouteConfigUpdate{route1: rdsUpdate1, route2: rdsUpdate2}
   171  	select {
   172  	case rhu := <-ch:
   173  		if diff := cmp.Diff(rhu.updates, rhuWant); diff != "" {
   174  			t.Fatalf("got unexpected route update, diff (-got, +want): %v", diff)
   175  		}
   176  	case <-ctx.Done():
   177  		t.Fatal("Timed out waiting for the rds handler update to be written to the update buffer.")
   178  	}
   179  
   180  	// Close the rds handler. This is meant to be called when the lis wrapper is
   181  	// closed, and the call should cancel all the watches present (for this
   182  	// test, two watches on route1 and route2).
   183  	rh.close()
   184  	if err = waitForFuncWithNames(ctx, fakeClient.WaitForCancelRouteConfigWatch, route1, route2); err != nil {
   185  		t.Fatalf("Error while waiting for names: %v", err)
   186  	}
   187  }
   188  
   189  // TestSuccessCaseDeletedRoute tests the case where the rds handler receives an
   190  // update with two routes, then receives an update with only one route. The RDS
   191  // Handler is expected to cancel the watch for the route no longer present.
   192  func (s) TestSuccessCaseDeletedRoute(t *testing.T) {
   193  	rh, fakeClient, ch := setupTests()
   194  
   195  	rh.updateRouteNamesToWatch(map[string]bool{route1: true, route2: true})
   196  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   197  	defer cancel()
   198  	// Will start two watches.
   199  	if err := waitForFuncWithNames(ctx, fakeClient.WaitForWatchRouteConfig, route1, route2); err != nil {
   200  		t.Fatalf("Error while waiting for names: %v", err)
   201  	}
   202  
   203  	// Update the RDSHandler with route names which deletes a route name to
   204  	// watch. This should trigger the RDSHandler to cancel the watch for the
   205  	// deleted route name to watch.
   206  	rh.updateRouteNamesToWatch(map[string]bool{route1: true})
   207  	// This should delete the watch for route2.
   208  	routeNameDeleted, err := fakeClient.WaitForCancelRouteConfigWatch(ctx)
   209  	if err != nil {
   210  		t.Fatalf("xdsClient.CancelRDS failed with error %v", err)
   211  	}
   212  	if routeNameDeleted != route2 {
   213  		t.Fatalf("xdsClient.CancelRDS called for route %v, want %v", routeNameDeleted, route2)
   214  	}
   215  
   216  	rdsUpdate := xdsresource.RouteConfigUpdate{}
   217  	// Invoke callback with the xds client with a certain route update. Due to
   218  	// this route update updating every route name that rds handler handles,
   219  	// this should write to the update channel to send to the listener.
   220  	fakeClient.InvokeWatchRouteConfigCallback(route1, rdsUpdate, nil)
   221  	rhuWant := map[string]xdsresource.RouteConfigUpdate{route1: rdsUpdate}
   222  	select {
   223  	case rhu := <-ch:
   224  		if diff := cmp.Diff(rhu.updates, rhuWant); diff != "" {
   225  			t.Fatalf("got unexpected route update, diff (-got, +want): %v", diff)
   226  		}
   227  	case <-ctx.Done():
   228  		t.Fatal("Timed out waiting for update from update channel.")
   229  	}
   230  
   231  	rh.close()
   232  	routeNameDeleted, err = fakeClient.WaitForCancelRouteConfigWatch(ctx)
   233  	if err != nil {
   234  		t.Fatalf("xdsClient.CancelRDS failed with error: %v", err)
   235  	}
   236  	if routeNameDeleted != route1 {
   237  		t.Fatalf("xdsClient.CancelRDS called for route %v, want %v", routeNameDeleted, route1)
   238  	}
   239  }
   240  
   241  // TestSuccessCaseTwoUpdatesAddAndDeleteRoute tests the case where the rds
   242  // handler receives an update with two routes, and then receives an update with
   243  // two routes, one previously there and one added (i.e. 12 -> 23). This should
   244  // cause the route that is no longer there to be deleted and cancelled, and the
   245  // route that was added should have a watch started for it.
   246  func (s) TestSuccessCaseTwoUpdatesAddAndDeleteRoute(t *testing.T) {
   247  	rh, fakeClient, ch := setupTests()
   248  
   249  	rh.updateRouteNamesToWatch(map[string]bool{route1: true, route2: true})
   250  
   251  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   252  	defer cancel()
   253  	if err := waitForFuncWithNames(ctx, fakeClient.WaitForWatchRouteConfig, route1, route2); err != nil {
   254  		t.Fatalf("Error while waiting for names: %v", err)
   255  	}
   256  
   257  	// Update the rds handler with two routes, one which was already there and a new route.
   258  	// This should cause the rds handler to delete/cancel watch for route 1 and start a watch
   259  	// for route 3.
   260  	rh.updateRouteNamesToWatch(map[string]bool{route2: true, route3: true})
   261  
   262  	// Start watch comes first, which should be for route3 as was just added.
   263  	gotRoute, err := fakeClient.WaitForWatchRouteConfig(ctx)
   264  	if err != nil {
   265  		t.Fatalf("xdsClient.WatchRDS failed with error: %v", err)
   266  	}
   267  	if gotRoute != route3 {
   268  		t.Fatalf("xdsClient.WatchRDS called for route: %v, want %v", gotRoute, route3)
   269  	}
   270  
   271  	// Then route 1 should be deleted/cancelled watch for, as it is no longer present
   272  	// in the new RouteName to watch map.
   273  	routeNameDeleted, err := fakeClient.WaitForCancelRouteConfigWatch(ctx)
   274  	if err != nil {
   275  		t.Fatalf("xdsClient.CancelRDS failed with error: %v", err)
   276  	}
   277  	if routeNameDeleted != route1 {
   278  		t.Fatalf("xdsClient.CancelRDS called for route %v, want %v", routeNameDeleted, route1)
   279  	}
   280  
   281  	// Invoke the callback with an update for route 2. This shouldn't cause the
   282  	// handler to write an update, as it has not received RouteConfigurations
   283  	// for every RouteName.
   284  	rdsUpdate2 := xdsresource.RouteConfigUpdate{}
   285  	fakeClient.InvokeWatchRouteConfigCallback(route2, rdsUpdate2, nil)
   286  
   287  	// The RDS Handler should not send an update.
   288  	sCtx, sCtxCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
   289  	defer sCtxCancel()
   290  	select {
   291  	case <-ch:
   292  		t.Fatalf("RDS Handler wrote an update to updateChannel when it shouldn't have, as each route name has not received an update yet")
   293  	case <-sCtx.Done():
   294  	}
   295  
   296  	// Invoke the callback with an update for route 3. This should cause the
   297  	// handler to write an update, as it has received RouteConfigurations for
   298  	// every RouteName.
   299  	rdsUpdate3 := xdsresource.RouteConfigUpdate{}
   300  	fakeClient.InvokeWatchRouteConfigCallback(route3, rdsUpdate3, nil)
   301  	// The RDS Handler should then update the listener wrapper with an update
   302  	// with two route configurations, as both route names the RDS Handler handles
   303  	// have received an update.
   304  	rhuWant := map[string]xdsresource.RouteConfigUpdate{route2: rdsUpdate2, route3: rdsUpdate3}
   305  	select {
   306  	case rhu := <-rh.updateChannel:
   307  		if diff := cmp.Diff(rhu.updates, rhuWant); diff != "" {
   308  			t.Fatalf("got unexpected route update, diff (-got, +want): %v", diff)
   309  		}
   310  	case <-ctx.Done():
   311  		t.Fatal("Timed out waiting for the rds handler update to be written to the update buffer.")
   312  	}
   313  	// Close the rds handler. This is meant to be called when the lis wrapper is
   314  	// closed, and the call should cancel all the watches present (for this
   315  	// test, two watches on route2 and route3).
   316  	rh.close()
   317  	if err = waitForFuncWithNames(ctx, fakeClient.WaitForCancelRouteConfigWatch, route2, route3); err != nil {
   318  		t.Fatalf("Error while waiting for names: %v", err)
   319  	}
   320  }
   321  
   322  // TestSuccessCaseSecondUpdateMakesRouteFull tests the scenario where the rds handler gets
   323  // told to watch three rds configurations, gets two successful updates, then gets told to watch
   324  // only those two. The rds handler should then write an update to update buffer.
   325  func (s) TestSuccessCaseSecondUpdateMakesRouteFull(t *testing.T) {
   326  	rh, fakeClient, ch := setupTests()
   327  
   328  	rh.updateRouteNamesToWatch(map[string]bool{route1: true, route2: true, route3: true})
   329  
   330  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   331  	defer cancel()
   332  	if err := waitForFuncWithNames(ctx, fakeClient.WaitForWatchRouteConfig, route1, route2, route3); err != nil {
   333  		t.Fatalf("Error while waiting for names: %v", err)
   334  	}
   335  
   336  	// Invoke the callbacks for two of the three watches. Since RDS is not full,
   337  	// this shouldn't trigger rds handler to write an update to update buffer.
   338  	fakeClient.InvokeWatchRouteConfigCallback(route1, xdsresource.RouteConfigUpdate{}, nil)
   339  	fakeClient.InvokeWatchRouteConfigCallback(route2, xdsresource.RouteConfigUpdate{}, nil)
   340  
   341  	// The RDS Handler should not send an update.
   342  	sCtx, sCtxCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
   343  	defer sCtxCancel()
   344  	select {
   345  	case <-rh.updateChannel:
   346  		t.Fatalf("RDS Handler wrote an update to updateChannel when it shouldn't have, as each route name has not received an update yet")
   347  	case <-sCtx.Done():
   348  	}
   349  
   350  	// Tell the rds handler to now only watch Route 1 and Route 2. This should
   351  	// trigger the rds handler to write an update to the update buffer as it now
   352  	// has full rds configuration.
   353  	rh.updateRouteNamesToWatch(map[string]bool{route1: true, route2: true})
   354  	// Route 3 should be deleted/cancelled watch for, as it is no longer present
   355  	// in the new RouteName to watch map.
   356  	routeNameDeleted, err := fakeClient.WaitForCancelRouteConfigWatch(ctx)
   357  	if err != nil {
   358  		t.Fatalf("xdsClient.CancelRDS failed with error: %v", err)
   359  	}
   360  	if routeNameDeleted != route3 {
   361  		t.Fatalf("xdsClient.CancelRDS called for route %v, want %v", routeNameDeleted, route1)
   362  	}
   363  	rhuWant := map[string]xdsresource.RouteConfigUpdate{route1: {}, route2: {}}
   364  	select {
   365  	case rhu := <-ch:
   366  		if diff := cmp.Diff(rhu.updates, rhuWant); diff != "" {
   367  			t.Fatalf("got unexpected route update, diff (-got, +want): %v", diff)
   368  		}
   369  	case <-ctx.Done():
   370  		t.Fatal("Timed out waiting for the rds handler update to be written to the update buffer.")
   371  	}
   372  }
   373  
   374  // TestErrorReceived tests the case where the rds handler receives a route name
   375  // to watch, then receives an update with an error. This error should be then
   376  // written to the update channel.
   377  func (s) TestErrorReceived(t *testing.T) {
   378  	rh, fakeClient, ch := setupTests()
   379  
   380  	rh.updateRouteNamesToWatch(map[string]bool{route1: true})
   381  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   382  	defer cancel()
   383  	gotRoute, err := fakeClient.WaitForWatchRouteConfig(ctx)
   384  	if err != nil {
   385  		t.Fatalf("xdsClient.WatchRDS failed with error %v", err)
   386  	}
   387  	if gotRoute != route1 {
   388  		t.Fatalf("xdsClient.WatchRDS called for route: %v, want %v", gotRoute, route1)
   389  	}
   390  
   391  	rdsErr := errors.New("some error")
   392  	fakeClient.InvokeWatchRouteConfigCallback(route1, xdsresource.RouteConfigUpdate{}, rdsErr)
   393  	select {
   394  	case rhu := <-ch:
   395  		if rhu.err.Error() != "some error" {
   396  			t.Fatalf("Did not receive the expected error, instead received: %v", rhu.err.Error())
   397  		}
   398  	case <-ctx.Done():
   399  		t.Fatal("Timed out waiting for update from update channel")
   400  	}
   401  }