gitee.com/zhaochuninhefei/gmgo@v0.0.31-0.20240209061119-069254a02979/grpc/xds/internal/resolver/watch_service_test.go (about)

     1  /*
     2   *
     3   * Copyright 2020 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 resolver
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"testing"
    25  	"time"
    26  
    27  	"gitee.com/zhaochuninhefei/gmgo/grpc/internal/testutils"
    28  	"gitee.com/zhaochuninhefei/gmgo/grpc/xds/internal/testutils/fakeclient"
    29  	"gitee.com/zhaochuninhefei/gmgo/grpc/xds/internal/xdsclient/xdsresource"
    30  	"github.com/golang/protobuf/proto"
    31  	"github.com/google/go-cmp/cmp"
    32  	"github.com/google/go-cmp/cmp/cmpopts"
    33  )
    34  
    35  func (s) TestFindBestMatchingVirtualHost(t *testing.T) {
    36  	var (
    37  		oneExactMatch = &xdsresource.VirtualHost{
    38  			Domains: []string{"foo.bar.com"},
    39  		}
    40  		oneSuffixMatch = &xdsresource.VirtualHost{
    41  			Domains: []string{"*.bar.com"},
    42  		}
    43  		onePrefixMatch = &xdsresource.VirtualHost{
    44  			Domains: []string{"foo.bar.*"},
    45  		}
    46  		oneUniversalMatch = &xdsresource.VirtualHost{
    47  			Domains: []string{"*"},
    48  		}
    49  		longExactMatch = &xdsresource.VirtualHost{
    50  			Domains: []string{"v2.foo.bar.com"},
    51  		}
    52  		multipleMatch = &xdsresource.VirtualHost{
    53  			Domains: []string{"pi.foo.bar.com", "314.*", "*.159"},
    54  		}
    55  		vhs = []*xdsresource.VirtualHost{oneExactMatch, oneSuffixMatch, onePrefixMatch, oneUniversalMatch, longExactMatch, multipleMatch}
    56  	)
    57  
    58  	tests := []struct {
    59  		name   string
    60  		host   string
    61  		vHosts []*xdsresource.VirtualHost
    62  		want   *xdsresource.VirtualHost
    63  	}{
    64  		{name: "exact-match", host: "foo.bar.com", vHosts: vhs, want: oneExactMatch},
    65  		{name: "suffix-match", host: "123.bar.com", vHosts: vhs, want: oneSuffixMatch},
    66  		{name: "prefix-match", host: "foo.bar.org", vHosts: vhs, want: onePrefixMatch},
    67  		{name: "universal-match", host: "abc.123", vHosts: vhs, want: oneUniversalMatch},
    68  		{name: "long-exact-match", host: "v2.foo.bar.com", vHosts: vhs, want: longExactMatch},
    69  		// Matches suffix "*.bar.com" and exact "pi.foo.bar.com". Takes exact.
    70  		{name: "multiple-match-exact", host: "pi.foo.bar.com", vHosts: vhs, want: multipleMatch},
    71  		// Matches suffix "*.159" and prefix "foo.bar.*". Takes suffix.
    72  		{name: "multiple-match-suffix", host: "foo.bar.159", vHosts: vhs, want: multipleMatch},
    73  		// Matches suffix "*.bar.com" and prefix "314.*". Takes suffix.
    74  		{name: "multiple-match-prefix", host: "314.bar.com", vHosts: vhs, want: oneSuffixMatch},
    75  	}
    76  	for _, tt := range tests {
    77  		t.Run(tt.name, func(t *testing.T) {
    78  			if got := xdsresource.FindBestMatchingVirtualHost(tt.host, tt.vHosts); !cmp.Equal(got, tt.want, cmp.Comparer(proto.Equal)) {
    79  				t.Errorf("findBestMatchingxdsclient.VirtualHost() = %v, want %v", got, tt.want)
    80  			}
    81  		})
    82  	}
    83  }
    84  
    85  type serviceUpdateErr struct {
    86  	u   serviceUpdate
    87  	err error
    88  }
    89  
    90  func verifyServiceUpdate(ctx context.Context, updateCh *testutils.Channel, wantUpdate serviceUpdate) error {
    91  	u, err := updateCh.Receive(ctx)
    92  	if err != nil {
    93  		return fmt.Errorf("timeout when waiting for service update: %v", err)
    94  	}
    95  	gotUpdate := u.(serviceUpdateErr)
    96  	if gotUpdate.err != nil || !cmp.Equal(gotUpdate.u, wantUpdate, cmpopts.EquateEmpty(), cmp.AllowUnexported(serviceUpdate{}, ldsConfig{})) {
    97  		return fmt.Errorf("unexpected service update: (%v, %v), want: (%v, nil),  diff (-want +got):\n%s", gotUpdate.u, gotUpdate.err, wantUpdate, cmp.Diff(gotUpdate.u, wantUpdate, cmpopts.EquateEmpty(), cmp.AllowUnexported(serviceUpdate{}, ldsConfig{})))
    98  	}
    99  	return nil
   100  }
   101  
   102  func newStringP(s string) *string {
   103  	return &s
   104  }
   105  
   106  // TestServiceWatch covers the cases:
   107  // - an update is received after a watch()
   108  // - an update with routes received
   109  func (s) TestServiceWatch(t *testing.T) {
   110  	serviceUpdateCh := testutils.NewChannel()
   111  	xdsC := fakeclient.NewClient()
   112  	cancelWatch := watchService(xdsC, targetStr, func(update serviceUpdate, err error) {
   113  		serviceUpdateCh.Send(serviceUpdateErr{u: update, err: err})
   114  	}, nil)
   115  	defer cancelWatch()
   116  
   117  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   118  	defer cancel()
   119  	waitForWatchListener(ctx, t, xdsC, targetStr)
   120  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr}, nil)
   121  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   122  
   123  	wantUpdate := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}}}
   124  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   125  		VirtualHosts: []*xdsresource.VirtualHost{
   126  			{
   127  				Domains: []string{targetStr},
   128  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}},
   129  			},
   130  		},
   131  	}, nil)
   132  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil {
   133  		t.Fatal(err)
   134  	}
   135  
   136  	wantUpdate2 := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"},
   137  		Routes: []*xdsresource.Route{{
   138  			Path:             newStringP(""),
   139  			WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}},
   140  		}},
   141  	}}
   142  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   143  		VirtualHosts: []*xdsresource.VirtualHost{
   144  			{
   145  				Domains: []string{targetStr},
   146  				Routes:  []*xdsresource.Route{{Path: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}},
   147  			},
   148  			{
   149  				// Another virtual host, with different domains.
   150  				Domains: []string{"random"},
   151  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}},
   152  			},
   153  		},
   154  	}, nil)
   155  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate2); err != nil {
   156  		t.Fatal(err)
   157  	}
   158  }
   159  
   160  // TestServiceWatchLDSUpdate covers the case that after first LDS and first RDS
   161  // response, the second LDS response trigger an new RDS watch, and an update of
   162  // the old RDS watch doesn't trigger update to service callback.
   163  func (s) TestServiceWatchLDSUpdate(t *testing.T) {
   164  	serviceUpdateCh := testutils.NewChannel()
   165  	xdsC := fakeclient.NewClient()
   166  	cancelWatch := watchService(xdsC, targetStr, func(update serviceUpdate, err error) {
   167  		serviceUpdateCh.Send(serviceUpdateErr{u: update, err: err})
   168  	}, nil)
   169  	defer cancelWatch()
   170  
   171  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   172  	defer cancel()
   173  	waitForWatchListener(ctx, t, xdsC, targetStr)
   174  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr}, nil)
   175  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   176  
   177  	wantUpdate := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}}}
   178  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   179  		VirtualHosts: []*xdsresource.VirtualHost{
   180  			{
   181  				Domains: []string{targetStr},
   182  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}},
   183  			},
   184  		},
   185  	}, nil)
   186  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil {
   187  		t.Fatal(err)
   188  	}
   189  
   190  	// Another LDS update with a different RDS_name.
   191  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr + "2"}, nil)
   192  	if _, err := xdsC.WaitForCancelRouteConfigWatch(ctx); err != nil {
   193  		t.Fatalf("wait for cancel route watch failed: %v, want nil", err)
   194  	}
   195  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr+"2")
   196  
   197  	// RDS update for the new name.
   198  	wantUpdate2 := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster + "2": {Weight: 1}}}}}}
   199  	xdsC.InvokeWatchRouteConfigCallback(routeStr+"2", xdsresource.RouteConfigUpdate{
   200  		VirtualHosts: []*xdsresource.VirtualHost{
   201  			{
   202  				Domains: []string{targetStr},
   203  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster + "2": {Weight: 1}}}},
   204  			},
   205  		},
   206  	}, nil)
   207  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate2); err != nil {
   208  		t.Fatal(err)
   209  	}
   210  }
   211  
   212  // TestServiceWatchLDSUpdate covers the case that after first LDS and first RDS
   213  // response, the second LDS response includes a new MaxStreamDuration.  It also
   214  // verifies this is reported in subsequent RDS updates.
   215  func (s) TestServiceWatchLDSUpdateMaxStreamDuration(t *testing.T) {
   216  	serviceUpdateCh := testutils.NewChannel()
   217  	xdsC := fakeclient.NewClient()
   218  	cancelWatch := watchService(xdsC, targetStr, func(update serviceUpdate, err error) {
   219  		serviceUpdateCh.Send(serviceUpdateErr{u: update, err: err})
   220  	}, nil)
   221  	defer cancelWatch()
   222  
   223  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   224  	defer cancel()
   225  	waitForWatchListener(ctx, t, xdsC, targetStr)
   226  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr, MaxStreamDuration: time.Second}, nil)
   227  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   228  
   229  	wantUpdate := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, Routes: []*xdsresource.Route{{
   230  		Prefix:           newStringP(""),
   231  		WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}},
   232  		ldsConfig: ldsConfig{maxStreamDuration: time.Second},
   233  	}
   234  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   235  		VirtualHosts: []*xdsresource.VirtualHost{
   236  			{
   237  				Domains: []string{targetStr},
   238  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}},
   239  			},
   240  		},
   241  	}, nil)
   242  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil {
   243  		t.Fatal(err)
   244  	}
   245  
   246  	// Another LDS update with the same RDS_name but different MaxStreamDuration (zero in this case).
   247  	wantUpdate2 := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}}}
   248  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr}, nil)
   249  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate2); err != nil {
   250  		t.Fatal(err)
   251  	}
   252  
   253  	// RDS update.
   254  	wantUpdate3 := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, Routes: []*xdsresource.Route{{
   255  		Prefix:           newStringP(""),
   256  		WeightedClusters: map[string]xdsresource.WeightedCluster{cluster + "2": {Weight: 1}}}},
   257  	}}
   258  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   259  		VirtualHosts: []*xdsresource.VirtualHost{
   260  			{
   261  				Domains: []string{targetStr},
   262  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster + "2": {Weight: 1}}}},
   263  			},
   264  		},
   265  	}, nil)
   266  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate3); err != nil {
   267  		t.Fatal(err)
   268  	}
   269  }
   270  
   271  // TestServiceNotCancelRDSOnSameLDSUpdate covers the case that if the second LDS
   272  // update contains the same RDS name as the previous, the RDS watch isn't
   273  // canceled and restarted.
   274  func (s) TestServiceNotCancelRDSOnSameLDSUpdate(t *testing.T) {
   275  	serviceUpdateCh := testutils.NewChannel()
   276  	xdsC := fakeclient.NewClient()
   277  	cancelWatch := watchService(xdsC, targetStr, func(update serviceUpdate, err error) {
   278  		serviceUpdateCh.Send(serviceUpdateErr{u: update, err: err})
   279  	}, nil)
   280  	defer cancelWatch()
   281  
   282  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   283  	defer cancel()
   284  	waitForWatchListener(ctx, t, xdsC, targetStr)
   285  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr}, nil)
   286  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   287  
   288  	wantUpdate := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, Routes: []*xdsresource.Route{{
   289  		Prefix:           newStringP(""),
   290  		WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}},
   291  	}}
   292  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   293  		VirtualHosts: []*xdsresource.VirtualHost{
   294  			{
   295  				Domains: []string{targetStr},
   296  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}},
   297  			},
   298  		},
   299  	}, nil)
   300  
   301  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil {
   302  		t.Fatal(err)
   303  	}
   304  
   305  	// Another LDS update with a the same RDS_name.
   306  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr}, nil)
   307  	sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   308  	defer sCancel()
   309  	if _, err := xdsC.WaitForCancelRouteConfigWatch(sCtx); err != context.DeadlineExceeded {
   310  		t.Fatalf("wait for cancel route watch failed: %v, want nil", err)
   311  	}
   312  }
   313  
   314  // TestServiceWatchInlineRDS covers the cases switching between:
   315  // - LDS update contains RDS name to watch
   316  // - LDS update contains inline RDS resource
   317  func (s) TestServiceWatchInlineRDS(t *testing.T) {
   318  	serviceUpdateCh := testutils.NewChannel()
   319  	xdsC := fakeclient.NewClient()
   320  	cancelWatch := watchService(xdsC, targetStr, func(update serviceUpdate, err error) {
   321  		serviceUpdateCh.Send(serviceUpdateErr{u: update, err: err})
   322  	}, nil)
   323  	defer cancelWatch()
   324  
   325  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   326  	defer cancel()
   327  
   328  	// First LDS update is LDS with RDS name to watch.
   329  	waitForWatchListener(ctx, t, xdsC, targetStr)
   330  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr}, nil)
   331  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   332  	wantUpdate := serviceUpdate{virtualHost: &xdsresource.VirtualHost{Domains: []string{"target"}, Routes: []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}}}}
   333  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   334  		VirtualHosts: []*xdsresource.VirtualHost{
   335  			{
   336  				Domains: []string{targetStr},
   337  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}},
   338  			},
   339  		},
   340  	}, nil)
   341  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil {
   342  		t.Fatal(err)
   343  	}
   344  
   345  	// Switch LDS resp to a LDS with inline RDS resource
   346  	wantVirtualHosts2 := &xdsresource.VirtualHost{Domains: []string{"target"},
   347  		Routes: []*xdsresource.Route{{
   348  			Path:             newStringP(""),
   349  			WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}},
   350  		}},
   351  	}
   352  	wantUpdate2 := serviceUpdate{virtualHost: wantVirtualHosts2}
   353  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{InlineRouteConfig: &xdsresource.RouteConfigUpdate{
   354  		VirtualHosts: []*xdsresource.VirtualHost{wantVirtualHosts2},
   355  	}}, nil)
   356  	// This inline RDS resource should cause the RDS watch to be canceled.
   357  	if _, err := xdsC.WaitForCancelRouteConfigWatch(ctx); err != nil {
   358  		t.Fatalf("wait for cancel route watch failed: %v, want nil", err)
   359  	}
   360  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate2); err != nil {
   361  		t.Fatal(err)
   362  	}
   363  
   364  	// Switch LDS update back to LDS with RDS name to watch.
   365  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{RouteConfigName: routeStr}, nil)
   366  	waitForWatchRouteConfig(ctx, t, xdsC, routeStr)
   367  	xdsC.InvokeWatchRouteConfigCallback("", xdsresource.RouteConfigUpdate{
   368  		VirtualHosts: []*xdsresource.VirtualHost{
   369  			{
   370  				Domains: []string{targetStr},
   371  				Routes:  []*xdsresource.Route{{Prefix: newStringP(""), WeightedClusters: map[string]xdsresource.WeightedCluster{cluster: {Weight: 1}}}},
   372  			},
   373  		},
   374  	}, nil)
   375  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate); err != nil {
   376  		t.Fatal(err)
   377  	}
   378  
   379  	// Switch LDS resp to a LDS with inline RDS resource again.
   380  	xdsC.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{InlineRouteConfig: &xdsresource.RouteConfigUpdate{
   381  		VirtualHosts: []*xdsresource.VirtualHost{wantVirtualHosts2},
   382  	}}, nil)
   383  	// This inline RDS resource should cause the RDS watch to be canceled.
   384  	if _, err := xdsC.WaitForCancelRouteConfigWatch(ctx); err != nil {
   385  		t.Fatalf("wait for cancel route watch failed: %v, want nil", err)
   386  	}
   387  	if err := verifyServiceUpdate(ctx, serviceUpdateCh, wantUpdate2); err != nil {
   388  		t.Fatal(err)
   389  	}
   390  }