google.golang.org/grpc@v1.62.1/xds/internal/xdsclient/tests/lds_watchers_test.go (about)

     1  /*
     2   *
     3   * Copyright 2022 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 xdsclient_test
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/envoyproxy/go-control-plane/pkg/wellknown"
    29  	"github.com/google/go-cmp/cmp"
    30  	"github.com/google/go-cmp/cmp/cmpopts"
    31  	"github.com/google/uuid"
    32  	"google.golang.org/grpc/internal/grpcsync"
    33  	"google.golang.org/grpc/internal/grpctest"
    34  	"google.golang.org/grpc/internal/testutils"
    35  	"google.golang.org/grpc/internal/testutils/xds/e2e"
    36  	xdstestutils "google.golang.org/grpc/xds/internal/testutils"
    37  	"google.golang.org/grpc/xds/internal/xdsclient"
    38  	"google.golang.org/grpc/xds/internal/xdsclient/bootstrap"
    39  	"google.golang.org/grpc/xds/internal/xdsclient/xdsresource"
    40  
    41  	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    42  	v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
    43  	v3routerpb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3"
    44  	v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
    45  	v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
    46  
    47  	_ "google.golang.org/grpc/xds"                            // To ensure internal.NewXDSResolverWithConfigForTesting is set.
    48  	_ "google.golang.org/grpc/xds/internal/httpfilter/router" // Register the router filter.
    49  )
    50  
    51  type s struct {
    52  	grpctest.Tester
    53  }
    54  
    55  func Test(t *testing.T) {
    56  	grpctest.RunSubTests(t, s{})
    57  }
    58  
    59  const (
    60  	defaultTestWatchExpiryTimeout   = 500 * time.Millisecond
    61  	defaultTestIdleAuthorityTimeout = 50 * time.Millisecond
    62  	defaultTestTimeout              = 5 * time.Second
    63  	defaultTestShortTimeout         = 10 * time.Millisecond // For events expected to *not* happen.
    64  
    65  	ldsName         = "xdsclient-test-lds-resource"
    66  	rdsName         = "xdsclient-test-rds-resource"
    67  	cdsName         = "xdsclient-test-cds-resource"
    68  	edsName         = "xdsclient-test-eds-resource"
    69  	ldsNameNewStyle = "xdstp:///envoy.config.listener.v3.Listener/xdsclient-test-lds-resource"
    70  	rdsNameNewStyle = "xdstp:///envoy.config.route.v3.RouteConfiguration/xdsclient-test-rds-resource"
    71  	cdsNameNewStyle = "xdstp:///envoy.config.cluster.v3.Cluster/xdsclient-test-cds-resource"
    72  	edsNameNewStyle = "xdstp:///envoy.config.endpoint.v3.ClusterLoadAssignment/xdsclient-test-eds-resource"
    73  )
    74  
    75  type noopListenerWatcher struct{}
    76  
    77  func (noopListenerWatcher) OnUpdate(update *xdsresource.ListenerResourceData) {}
    78  func (noopListenerWatcher) OnError(err error)                                 {}
    79  func (noopListenerWatcher) OnResourceDoesNotExist()                           {}
    80  
    81  type listenerUpdateErrTuple struct {
    82  	update xdsresource.ListenerUpdate
    83  	err    error
    84  }
    85  
    86  type listenerWatcher struct {
    87  	updateCh *testutils.Channel
    88  }
    89  
    90  func newListenerWatcher() *listenerWatcher {
    91  	return &listenerWatcher{updateCh: testutils.NewChannel()}
    92  }
    93  
    94  func (cw *listenerWatcher) OnUpdate(update *xdsresource.ListenerResourceData) {
    95  	cw.updateCh.Send(listenerUpdateErrTuple{update: update.Resource})
    96  }
    97  
    98  func (cw *listenerWatcher) OnError(err error) {
    99  	// When used with a go-control-plane management server that continuously
   100  	// resends resources which are NACKed by the xDS client, using a `Replace()`
   101  	// here and in OnResourceDoesNotExist() simplifies tests which will have
   102  	// access to the most recently received error.
   103  	cw.updateCh.Replace(listenerUpdateErrTuple{err: err})
   104  }
   105  
   106  func (cw *listenerWatcher) OnResourceDoesNotExist() {
   107  	cw.updateCh.Replace(listenerUpdateErrTuple{err: xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "Listener not found in received response")})
   108  }
   109  
   110  // badListenerResource returns a listener resource for the given name which does
   111  // not contain the `RouteSpecifier` field in the HTTPConnectionManager, and
   112  // hence is expected to be NACKed by the client.
   113  func badListenerResource(t *testing.T, name string) *v3listenerpb.Listener {
   114  	hcm := testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   115  		HttpFilters: []*v3httppb.HttpFilter{e2e.HTTPFilter("router", &v3routerpb.Router{})},
   116  	})
   117  	return &v3listenerpb.Listener{
   118  		Name:        name,
   119  		ApiListener: &v3listenerpb.ApiListener{ApiListener: hcm},
   120  		FilterChains: []*v3listenerpb.FilterChain{{
   121  			Name: "filter-chain-name",
   122  			Filters: []*v3listenerpb.Filter{{
   123  				Name:       wellknown.HTTPConnectionManager,
   124  				ConfigType: &v3listenerpb.Filter_TypedConfig{TypedConfig: hcm},
   125  			}},
   126  		}},
   127  	}
   128  }
   129  
   130  // xdsClient is expected to produce an error containing this string when an
   131  // update is received containing a listener created using `badListenerResource`.
   132  const wantListenerNACKErr = "no RouteSpecifier"
   133  
   134  // verifyNoListenerUpdate verifies that no listener update is received on the
   135  // provided update channel, and returns an error if an update is received.
   136  //
   137  // A very short deadline is used while waiting for the update, as this function
   138  // is intended to be used when an update is not expected.
   139  func verifyNoListenerUpdate(ctx context.Context, updateCh *testutils.Channel) error {
   140  	sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   141  	defer sCancel()
   142  	if u, err := updateCh.Receive(sCtx); err != context.DeadlineExceeded {
   143  		return fmt.Errorf("unexpected ListenerUpdate: %v", u)
   144  	}
   145  	return nil
   146  }
   147  
   148  // verifyListenerUpdate waits for an update to be received on the provided
   149  // update channel and verifies that it matches the expected update.
   150  //
   151  // Returns an error if no update is received before the context deadline expires
   152  // or the received update does not match the expected one.
   153  func verifyListenerUpdate(ctx context.Context, updateCh *testutils.Channel, wantUpdate listenerUpdateErrTuple) error {
   154  	u, err := updateCh.Receive(ctx)
   155  	if err != nil {
   156  		return fmt.Errorf("timeout when waiting for a listener resource from the management server: %v", err)
   157  	}
   158  	got := u.(listenerUpdateErrTuple)
   159  	if wantUpdate.err != nil {
   160  		if gotType, wantType := xdsresource.ErrType(got.err), xdsresource.ErrType(wantUpdate.err); gotType != wantType {
   161  			return fmt.Errorf("received update with error type %v, want %v", gotType, wantType)
   162  		}
   163  	}
   164  	cmpOpts := []cmp.Option{
   165  		cmpopts.EquateEmpty(),
   166  		cmpopts.IgnoreFields(xdsresource.HTTPFilter{}, "Filter", "Config"),
   167  		cmpopts.IgnoreFields(xdsresource.ListenerUpdate{}, "Raw"),
   168  	}
   169  	if diff := cmp.Diff(wantUpdate.update, got.update, cmpOpts...); diff != "" {
   170  		return fmt.Errorf("received unepected diff in the listener resource update: (-want, got):\n%s", diff)
   171  	}
   172  	return nil
   173  }
   174  
   175  // TestLDSWatch covers the case where a single watcher exists for a single
   176  // listener resource. The test verifies the following scenarios:
   177  //  1. An update from the management server containing the resource being
   178  //     watched should result in the invocation of the watch callback.
   179  //  2. An update from the management server containing a resource *not* being
   180  //     watched should not result in the invocation of the watch callback.
   181  //  3. After the watch is cancelled, an update from the management server
   182  //     containing the resource that was being watched should not result in the
   183  //     invocation of the watch callback.
   184  //
   185  // The test is run for old and new style names.
   186  func (s) TestLDSWatch(t *testing.T) {
   187  	tests := []struct {
   188  		desc                   string
   189  		resourceName           string
   190  		watchedResource        *v3listenerpb.Listener // The resource being watched.
   191  		updatedWatchedResource *v3listenerpb.Listener // The watched resource after an update.
   192  		notWatchedResource     *v3listenerpb.Listener // A resource which is not being watched.
   193  		wantUpdate             listenerUpdateErrTuple
   194  	}{
   195  		{
   196  			desc:                   "old style resource",
   197  			resourceName:           ldsName,
   198  			watchedResource:        e2e.DefaultClientListener(ldsName, rdsName),
   199  			updatedWatchedResource: e2e.DefaultClientListener(ldsName, "new-rds-resource"),
   200  			notWatchedResource:     e2e.DefaultClientListener("unsubscribed-lds-resource", rdsName),
   201  			wantUpdate: listenerUpdateErrTuple{
   202  				update: xdsresource.ListenerUpdate{
   203  					RouteConfigName: rdsName,
   204  					HTTPFilters:     []xdsresource.HTTPFilter{{Name: "router"}},
   205  				},
   206  			},
   207  		},
   208  		{
   209  			desc:                   "new style resource",
   210  			resourceName:           ldsNameNewStyle,
   211  			watchedResource:        e2e.DefaultClientListener(ldsNameNewStyle, rdsNameNewStyle),
   212  			updatedWatchedResource: e2e.DefaultClientListener(ldsNameNewStyle, "new-rds-resource"),
   213  			notWatchedResource:     e2e.DefaultClientListener("unsubscribed-lds-resource", rdsNameNewStyle),
   214  			wantUpdate: listenerUpdateErrTuple{
   215  				update: xdsresource.ListenerUpdate{
   216  					RouteConfigName: rdsNameNewStyle,
   217  					HTTPFilters:     []xdsresource.HTTPFilter{{Name: "router"}},
   218  				},
   219  			},
   220  		},
   221  	}
   222  
   223  	for _, test := range tests {
   224  		t.Run(test.desc, func(t *testing.T) {
   225  			mgmtServer, nodeID, bootstrapContents, _, cleanup := e2e.SetupManagementServer(t, e2e.ManagementServerOptions{})
   226  			defer cleanup()
   227  
   228  			// Create an xDS client with the above bootstrap contents.
   229  			client, close, err := xdsclient.NewWithBootstrapContentsForTesting(bootstrapContents)
   230  			if err != nil {
   231  				t.Fatalf("Failed to create xDS client: %v", err)
   232  			}
   233  			defer close()
   234  
   235  			// Register a watch for a listener resource and have the watch
   236  			// callback push the received update on to a channel.
   237  			lw := newListenerWatcher()
   238  			ldsCancel := xdsresource.WatchListener(client, test.resourceName, lw)
   239  
   240  			// Configure the management server to return a single listener
   241  			// resource, corresponding to the one we registered a watch for.
   242  			resources := e2e.UpdateOptions{
   243  				NodeID:         nodeID,
   244  				Listeners:      []*v3listenerpb.Listener{test.watchedResource},
   245  				SkipValidation: true,
   246  			}
   247  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   248  			defer cancel()
   249  			if err := mgmtServer.Update(ctx, resources); err != nil {
   250  				t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   251  			}
   252  
   253  			// Verify the contents of the received update.
   254  			if err := verifyListenerUpdate(ctx, lw.updateCh, test.wantUpdate); err != nil {
   255  				t.Fatal(err)
   256  			}
   257  
   258  			// Configure the management server to return an additional listener
   259  			// resource, one that we are not interested in.
   260  			resources = e2e.UpdateOptions{
   261  				NodeID:         nodeID,
   262  				Listeners:      []*v3listenerpb.Listener{test.watchedResource, test.notWatchedResource},
   263  				SkipValidation: true,
   264  			}
   265  			if err := mgmtServer.Update(ctx, resources); err != nil {
   266  				t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   267  			}
   268  			if err := verifyNoListenerUpdate(ctx, lw.updateCh); err != nil {
   269  				t.Fatal(err)
   270  			}
   271  
   272  			// Cancel the watch and update the resource corresponding to the original
   273  			// watch.  Ensure that the cancelled watch callback is not invoked.
   274  			ldsCancel()
   275  			resources = e2e.UpdateOptions{
   276  				NodeID:         nodeID,
   277  				Listeners:      []*v3listenerpb.Listener{test.updatedWatchedResource, test.notWatchedResource},
   278  				SkipValidation: true,
   279  			}
   280  			if err := mgmtServer.Update(ctx, resources); err != nil {
   281  				t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   282  			}
   283  			if err := verifyNoListenerUpdate(ctx, lw.updateCh); err != nil {
   284  				t.Fatal(err)
   285  			}
   286  		})
   287  	}
   288  }
   289  
   290  // TestLDSWatch_TwoWatchesForSameResourceName covers the case where two watchers
   291  // exist for a single listener resource.  The test verifies the following
   292  // scenarios:
   293  //  1. An update from the management server containing the resource being
   294  //     watched should result in the invocation of both watch callbacks.
   295  //  2. After one of the watches is cancelled, a redundant update from the
   296  //     management server should not result in the invocation of either of the
   297  //     watch callbacks.
   298  //  3. An update from the management server containing the resource being
   299  //     watched should result in the invocation of the un-cancelled watch
   300  //     callback.
   301  //
   302  // The test is run for old and new style names.
   303  func (s) TestLDSWatch_TwoWatchesForSameResourceName(t *testing.T) {
   304  	tests := []struct {
   305  		desc                   string
   306  		resourceName           string
   307  		watchedResource        *v3listenerpb.Listener // The resource being watched.
   308  		updatedWatchedResource *v3listenerpb.Listener // The watched resource after an update.
   309  		wantUpdateV1           listenerUpdateErrTuple
   310  		wantUpdateV2           listenerUpdateErrTuple
   311  	}{
   312  		{
   313  			desc:                   "old style resource",
   314  			resourceName:           ldsName,
   315  			watchedResource:        e2e.DefaultClientListener(ldsName, rdsName),
   316  			updatedWatchedResource: e2e.DefaultClientListener(ldsName, "new-rds-resource"),
   317  			wantUpdateV1: listenerUpdateErrTuple{
   318  				update: xdsresource.ListenerUpdate{
   319  					RouteConfigName: rdsName,
   320  					HTTPFilters:     []xdsresource.HTTPFilter{{Name: "router"}},
   321  				},
   322  			},
   323  			wantUpdateV2: listenerUpdateErrTuple{
   324  				update: xdsresource.ListenerUpdate{
   325  					RouteConfigName: "new-rds-resource",
   326  					HTTPFilters:     []xdsresource.HTTPFilter{{Name: "router"}},
   327  				},
   328  			},
   329  		},
   330  		{
   331  			desc:                   "new style resource",
   332  			resourceName:           ldsNameNewStyle,
   333  			watchedResource:        e2e.DefaultClientListener(ldsNameNewStyle, rdsNameNewStyle),
   334  			updatedWatchedResource: e2e.DefaultClientListener(ldsNameNewStyle, "new-rds-resource"),
   335  			wantUpdateV1: listenerUpdateErrTuple{
   336  				update: xdsresource.ListenerUpdate{
   337  					RouteConfigName: rdsNameNewStyle,
   338  					HTTPFilters:     []xdsresource.HTTPFilter{{Name: "router"}},
   339  				},
   340  			},
   341  			wantUpdateV2: listenerUpdateErrTuple{
   342  				update: xdsresource.ListenerUpdate{
   343  					RouteConfigName: "new-rds-resource",
   344  					HTTPFilters:     []xdsresource.HTTPFilter{{Name: "router"}},
   345  				},
   346  			},
   347  		},
   348  	}
   349  
   350  	for _, test := range tests {
   351  		t.Run(test.desc, func(t *testing.T) {
   352  			mgmtServer, nodeID, bootstrapContents, _, cleanup := e2e.SetupManagementServer(t, e2e.ManagementServerOptions{})
   353  			defer cleanup()
   354  
   355  			// Create an xDS client with the above bootstrap contents.
   356  			client, close, err := xdsclient.NewWithBootstrapContentsForTesting(bootstrapContents)
   357  			if err != nil {
   358  				t.Fatalf("Failed to create xDS client: %v", err)
   359  			}
   360  			defer close()
   361  
   362  			// Register two watches for the same listener resource and have the
   363  			// callbacks push the received updates on to a channel.
   364  			lw1 := newListenerWatcher()
   365  			ldsCancel1 := xdsresource.WatchListener(client, test.resourceName, lw1)
   366  			defer ldsCancel1()
   367  			lw2 := newListenerWatcher()
   368  			ldsCancel2 := xdsresource.WatchListener(client, test.resourceName, lw2)
   369  
   370  			// Configure the management server to return a single listener
   371  			// resource, corresponding to the one we registered watches for.
   372  			resources := e2e.UpdateOptions{
   373  				NodeID:         nodeID,
   374  				Listeners:      []*v3listenerpb.Listener{test.watchedResource},
   375  				SkipValidation: true,
   376  			}
   377  			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   378  			defer cancel()
   379  			if err := mgmtServer.Update(ctx, resources); err != nil {
   380  				t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   381  			}
   382  
   383  			// Verify the contents of the received update.
   384  			if err := verifyListenerUpdate(ctx, lw1.updateCh, test.wantUpdateV1); err != nil {
   385  				t.Fatal(err)
   386  			}
   387  			if err := verifyListenerUpdate(ctx, lw2.updateCh, test.wantUpdateV1); err != nil {
   388  				t.Fatal(err)
   389  			}
   390  
   391  			// Cancel the second watch and force the management server to push a
   392  			// redundant update for the resource being watched. Neither of the
   393  			// two watch callbacks should be invoked.
   394  			ldsCancel2()
   395  			if err := mgmtServer.Update(ctx, resources); err != nil {
   396  				t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   397  			}
   398  			if err := verifyNoListenerUpdate(ctx, lw1.updateCh); err != nil {
   399  				t.Fatal(err)
   400  			}
   401  			if err := verifyNoListenerUpdate(ctx, lw2.updateCh); err != nil {
   402  				t.Fatal(err)
   403  			}
   404  
   405  			// Update to the resource being watched. The un-cancelled callback
   406  			// should be invoked while the cancelled one should not be.
   407  			resources = e2e.UpdateOptions{
   408  				NodeID:         nodeID,
   409  				Listeners:      []*v3listenerpb.Listener{test.updatedWatchedResource},
   410  				SkipValidation: true,
   411  			}
   412  			if err := mgmtServer.Update(ctx, resources); err != nil {
   413  				t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   414  			}
   415  			if err := verifyListenerUpdate(ctx, lw1.updateCh, test.wantUpdateV2); err != nil {
   416  				t.Fatal(err)
   417  			}
   418  			if err := verifyNoListenerUpdate(ctx, lw2.updateCh); err != nil {
   419  				t.Fatal(err)
   420  			}
   421  		})
   422  	}
   423  }
   424  
   425  // TestLDSWatch_ThreeWatchesForDifferentResourceNames covers the case with three
   426  // watchers (two watchers for one resource, and the third watcher for another
   427  // resource), exist across two listener resources.  The test verifies that an
   428  // update from the management server containing both resources results in the
   429  // invocation of all watch callbacks.
   430  //
   431  // The test is run with both old and new style names.
   432  func (s) TestLDSWatch_ThreeWatchesForDifferentResourceNames(t *testing.T) {
   433  	mgmtServer, nodeID, bootstrapContents, _, cleanup := e2e.SetupManagementServer(t, e2e.ManagementServerOptions{})
   434  	defer cleanup()
   435  
   436  	// Create an xDS client with the above bootstrap contents.
   437  	client, close, err := xdsclient.NewWithBootstrapContentsForTesting(bootstrapContents)
   438  	if err != nil {
   439  		t.Fatalf("Failed to create xDS client: %v", err)
   440  	}
   441  	defer close()
   442  
   443  	// Register two watches for the same listener resource and have the
   444  	// callbacks push the received updates on to a channel.
   445  	lw1 := newListenerWatcher()
   446  	ldsCancel1 := xdsresource.WatchListener(client, ldsName, lw1)
   447  	defer ldsCancel1()
   448  	lw2 := newListenerWatcher()
   449  	ldsCancel2 := xdsresource.WatchListener(client, ldsName, lw2)
   450  	defer ldsCancel2()
   451  
   452  	// Register the third watch for a different listener resource.
   453  	lw3 := newListenerWatcher()
   454  	ldsCancel3 := xdsresource.WatchListener(client, ldsNameNewStyle, lw3)
   455  	defer ldsCancel3()
   456  
   457  	// Configure the management server to return two listener resources,
   458  	// corresponding to the registered watches.
   459  	resources := e2e.UpdateOptions{
   460  		NodeID: nodeID,
   461  		Listeners: []*v3listenerpb.Listener{
   462  			e2e.DefaultClientListener(ldsName, rdsName),
   463  			e2e.DefaultClientListener(ldsNameNewStyle, rdsName),
   464  		},
   465  		SkipValidation: true,
   466  	}
   467  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   468  	defer cancel()
   469  	if err := mgmtServer.Update(ctx, resources); err != nil {
   470  		t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   471  	}
   472  
   473  	// Verify the contents of the received update for the all watchers. The two
   474  	// resources returned differ only in the resource name. Therefore the
   475  	// expected update is the same for all the watchers.
   476  	wantUpdate := listenerUpdateErrTuple{
   477  		update: xdsresource.ListenerUpdate{
   478  			RouteConfigName: rdsName,
   479  			HTTPFilters:     []xdsresource.HTTPFilter{{Name: "router"}},
   480  		},
   481  	}
   482  	if err := verifyListenerUpdate(ctx, lw1.updateCh, wantUpdate); err != nil {
   483  		t.Fatal(err)
   484  	}
   485  	if err := verifyListenerUpdate(ctx, lw2.updateCh, wantUpdate); err != nil {
   486  		t.Fatal(err)
   487  	}
   488  	if err := verifyListenerUpdate(ctx, lw3.updateCh, wantUpdate); err != nil {
   489  		t.Fatal(err)
   490  	}
   491  }
   492  
   493  // TestLDSWatch_ResourceCaching covers the case where a watch is registered for
   494  // a resource which is already present in the cache.  The test verifies that the
   495  // watch callback is invoked with the contents from the cache, instead of a
   496  // request being sent to the management server.
   497  func (s) TestLDSWatch_ResourceCaching(t *testing.T) {
   498  	firstRequestReceived := false
   499  	firstAckReceived := grpcsync.NewEvent()
   500  	secondRequestReceived := grpcsync.NewEvent()
   501  
   502  	mgmtServer, nodeID, bootstrapContents, _, cleanup := e2e.SetupManagementServer(t, e2e.ManagementServerOptions{
   503  		OnStreamRequest: func(id int64, req *v3discoverypb.DiscoveryRequest) error {
   504  			// The first request has an empty version string.
   505  			if !firstRequestReceived && req.GetVersionInfo() == "" {
   506  				firstRequestReceived = true
   507  				return nil
   508  			}
   509  			// The first ack has a non-empty version string.
   510  			if !firstAckReceived.HasFired() && req.GetVersionInfo() != "" {
   511  				firstAckReceived.Fire()
   512  				return nil
   513  			}
   514  			// Any requests after the first request and ack, are not expected.
   515  			secondRequestReceived.Fire()
   516  			return nil
   517  		},
   518  	})
   519  	defer cleanup()
   520  
   521  	// Create an xDS client with the above bootstrap contents.
   522  	client, close, err := xdsclient.NewWithBootstrapContentsForTesting(bootstrapContents)
   523  	if err != nil {
   524  		t.Fatalf("Failed to create xDS client: %v", err)
   525  	}
   526  	defer close()
   527  
   528  	// Register a watch for a listener resource and have the watch
   529  	// callback push the received update on to a channel.
   530  	lw1 := newListenerWatcher()
   531  	ldsCancel1 := xdsresource.WatchListener(client, ldsName, lw1)
   532  	defer ldsCancel1()
   533  
   534  	// Configure the management server to return a single listener
   535  	// resource, corresponding to the one we registered a watch for.
   536  	resources := e2e.UpdateOptions{
   537  		NodeID:         nodeID,
   538  		Listeners:      []*v3listenerpb.Listener{e2e.DefaultClientListener(ldsName, rdsName)},
   539  		SkipValidation: true,
   540  	}
   541  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   542  	defer cancel()
   543  	if err := mgmtServer.Update(ctx, resources); err != nil {
   544  		t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   545  	}
   546  
   547  	// Verify the contents of the received update.
   548  	wantUpdate := listenerUpdateErrTuple{
   549  		update: xdsresource.ListenerUpdate{
   550  			RouteConfigName: rdsName,
   551  			HTTPFilters:     []xdsresource.HTTPFilter{{Name: "router"}},
   552  		},
   553  	}
   554  	if err := verifyListenerUpdate(ctx, lw1.updateCh, wantUpdate); err != nil {
   555  		t.Fatal(err)
   556  	}
   557  	select {
   558  	case <-ctx.Done():
   559  		t.Fatal("timeout when waiting for receipt of ACK at the management server")
   560  	case <-firstAckReceived.Done():
   561  	}
   562  
   563  	// Register another watch for the same resource. This should get the update
   564  	// from the cache.
   565  	lw2 := newListenerWatcher()
   566  	ldsCancel2 := xdsresource.WatchListener(client, ldsName, lw2)
   567  	defer ldsCancel2()
   568  	if err := verifyListenerUpdate(ctx, lw2.updateCh, wantUpdate); err != nil {
   569  		t.Fatal(err)
   570  	}
   571  	// No request should get sent out as part of this watch.
   572  	sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   573  	defer sCancel()
   574  	select {
   575  	case <-sCtx.Done():
   576  	case <-secondRequestReceived.Done():
   577  		t.Fatal("xdsClient sent out request instead of using update from cache")
   578  	}
   579  }
   580  
   581  // TestLDSWatch_ExpiryTimerFiresBeforeResponse tests the case where the client
   582  // does not receive an LDS response for the request that it sends. The test
   583  // verifies that the watch callback is invoked with an error once the
   584  // watchExpiryTimer fires.
   585  func (s) TestLDSWatch_ExpiryTimerFiresBeforeResponse(t *testing.T) {
   586  	mgmtServer, err := e2e.StartManagementServer(e2e.ManagementServerOptions{})
   587  	if err != nil {
   588  		t.Fatalf("Failed to spin up the xDS management server: %v", err)
   589  	}
   590  	defer mgmtServer.Stop()
   591  
   592  	client, close, err := xdsclient.NewWithConfigForTesting(&bootstrap.Config{
   593  		XDSServer: xdstestutils.ServerConfigForAddress(t, mgmtServer.Address),
   594  		NodeProto: &v3corepb.Node{},
   595  	}, defaultTestWatchExpiryTimeout, time.Duration(0))
   596  	if err != nil {
   597  		t.Fatalf("failed to create xds client: %v", err)
   598  	}
   599  	defer close()
   600  
   601  	// Register a watch for a resource which is expected to fail with an error
   602  	// after the watch expiry timer fires.
   603  	lw := newListenerWatcher()
   604  	ldsCancel := xdsresource.WatchListener(client, ldsName, lw)
   605  	defer ldsCancel()
   606  
   607  	// Wait for the watch expiry timer to fire.
   608  	<-time.After(defaultTestWatchExpiryTimeout)
   609  
   610  	// Verify that an empty update with the expected error is received.
   611  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   612  	defer cancel()
   613  	wantErr := xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "")
   614  	if err := verifyListenerUpdate(ctx, lw.updateCh, listenerUpdateErrTuple{err: wantErr}); err != nil {
   615  		t.Fatal(err)
   616  	}
   617  }
   618  
   619  // TestLDSWatch_ValidResponseCancelsExpiryTimerBehavior tests the case where the
   620  // client receives a valid LDS response for the request that it sends. The test
   621  // verifies that the behavior associated with the expiry timer (i.e, callback
   622  // invocation with error) does not take place.
   623  func (s) TestLDSWatch_ValidResponseCancelsExpiryTimerBehavior(t *testing.T) {
   624  	mgmtServer, err := e2e.StartManagementServer(e2e.ManagementServerOptions{})
   625  	if err != nil {
   626  		t.Fatalf("Failed to spin up the xDS management server: %v", err)
   627  	}
   628  	defer mgmtServer.Stop()
   629  
   630  	// Create an xDS client talking to the above management server.
   631  	nodeID := uuid.New().String()
   632  	client, close, err := xdsclient.NewWithConfigForTesting(&bootstrap.Config{
   633  		XDSServer: xdstestutils.ServerConfigForAddress(t, mgmtServer.Address),
   634  		NodeProto: &v3corepb.Node{Id: nodeID},
   635  	}, defaultTestWatchExpiryTimeout, time.Duration(0))
   636  	if err != nil {
   637  		t.Fatalf("failed to create xds client: %v", err)
   638  	}
   639  	defer close()
   640  
   641  	// Register a watch for a listener resource and have the watch
   642  	// callback push the received update on to a channel.
   643  	lw := newListenerWatcher()
   644  	ldsCancel := xdsresource.WatchListener(client, ldsName, lw)
   645  	defer ldsCancel()
   646  
   647  	// Configure the management server to return a single listener
   648  	// resource, corresponding to the one we registered a watch for.
   649  	resources := e2e.UpdateOptions{
   650  		NodeID:         nodeID,
   651  		Listeners:      []*v3listenerpb.Listener{e2e.DefaultClientListener(ldsName, rdsName)},
   652  		SkipValidation: true,
   653  	}
   654  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   655  	defer cancel()
   656  	if err := mgmtServer.Update(ctx, resources); err != nil {
   657  		t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   658  	}
   659  
   660  	// Verify the contents of the received update.
   661  	wantUpdate := listenerUpdateErrTuple{
   662  		update: xdsresource.ListenerUpdate{
   663  			RouteConfigName: rdsName,
   664  			HTTPFilters:     []xdsresource.HTTPFilter{{Name: "router"}},
   665  		},
   666  	}
   667  	if err := verifyListenerUpdate(ctx, lw.updateCh, wantUpdate); err != nil {
   668  		t.Fatal(err)
   669  	}
   670  
   671  	// Wait for the watch expiry timer to fire, and verify that the callback is
   672  	// not invoked.
   673  	<-time.After(defaultTestWatchExpiryTimeout)
   674  	if err := verifyNoListenerUpdate(ctx, lw.updateCh); err != nil {
   675  		t.Fatal(err)
   676  	}
   677  }
   678  
   679  // TestLDSWatch_ResourceRemoved covers the cases where a resource being watched
   680  // is removed from the management server. The test verifies the following
   681  // scenarios:
   682  //  1. Removing a resource should trigger the watch callback with a resource
   683  //     removed error. It should not trigger the watch callback for an unrelated
   684  //     resource.
   685  //  2. An update to another resource should result in the invocation of the watch
   686  //     callback associated with that resource.  It should not result in the
   687  //     invocation of the watch callback associated with the deleted resource.
   688  //
   689  // The test is run with both old and new style names.
   690  func (s) TestLDSWatch_ResourceRemoved(t *testing.T) {
   691  	mgmtServer, nodeID, bootstrapContents, _, cleanup := e2e.SetupManagementServer(t, e2e.ManagementServerOptions{})
   692  	defer cleanup()
   693  
   694  	// Create an xDS client with the above bootstrap contents.
   695  	client, close, err := xdsclient.NewWithBootstrapContentsForTesting(bootstrapContents)
   696  	if err != nil {
   697  		t.Fatalf("Failed to create xDS client: %v", err)
   698  	}
   699  	defer close()
   700  
   701  	// Register two watches for two listener resources and have the
   702  	// callbacks push the received updates on to a channel.
   703  	resourceName1 := ldsName
   704  	lw1 := newListenerWatcher()
   705  	ldsCancel1 := xdsresource.WatchListener(client, resourceName1, lw1)
   706  	defer ldsCancel1()
   707  
   708  	resourceName2 := ldsNameNewStyle
   709  	lw2 := newListenerWatcher()
   710  	ldsCancel2 := xdsresource.WatchListener(client, resourceName2, lw2)
   711  	defer ldsCancel2()
   712  
   713  	// Configure the management server to return two listener resources,
   714  	// corresponding to the registered watches.
   715  	resources := e2e.UpdateOptions{
   716  		NodeID: nodeID,
   717  		Listeners: []*v3listenerpb.Listener{
   718  			e2e.DefaultClientListener(resourceName1, rdsName),
   719  			e2e.DefaultClientListener(resourceName2, rdsName),
   720  		},
   721  		SkipValidation: true,
   722  	}
   723  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   724  	defer cancel()
   725  	if err := mgmtServer.Update(ctx, resources); err != nil {
   726  		t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   727  	}
   728  
   729  	// Verify the contents of the received update for both watchers. The two
   730  	// resources returned differ only in the resource name. Therefore the
   731  	// expected update is the same for both watchers.
   732  	wantUpdate := listenerUpdateErrTuple{
   733  		update: xdsresource.ListenerUpdate{
   734  			RouteConfigName: rdsName,
   735  			HTTPFilters:     []xdsresource.HTTPFilter{{Name: "router"}},
   736  		},
   737  	}
   738  	if err := verifyListenerUpdate(ctx, lw1.updateCh, wantUpdate); err != nil {
   739  		t.Fatal(err)
   740  	}
   741  	if err := verifyListenerUpdate(ctx, lw2.updateCh, wantUpdate); err != nil {
   742  		t.Fatal(err)
   743  	}
   744  
   745  	// Remove the first listener resource on the management server.
   746  	resources = e2e.UpdateOptions{
   747  		NodeID:         nodeID,
   748  		Listeners:      []*v3listenerpb.Listener{e2e.DefaultClientListener(resourceName2, rdsName)},
   749  		SkipValidation: true,
   750  	}
   751  	if err := mgmtServer.Update(ctx, resources); err != nil {
   752  		t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   753  	}
   754  
   755  	// The first watcher should receive a resource removed error, while the
   756  	// second watcher should not see an update.
   757  	if err := verifyListenerUpdate(ctx, lw1.updateCh, listenerUpdateErrTuple{
   758  		err: xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, ""),
   759  	}); err != nil {
   760  		t.Fatal(err)
   761  	}
   762  	if err := verifyNoListenerUpdate(ctx, lw2.updateCh); err != nil {
   763  		t.Fatal(err)
   764  	}
   765  
   766  	// Update the second listener resource on the management server. The first
   767  	// watcher should not see an update, while the second watcher should.
   768  	resources = e2e.UpdateOptions{
   769  		NodeID:         nodeID,
   770  		Listeners:      []*v3listenerpb.Listener{e2e.DefaultClientListener(resourceName2, "new-rds-resource")},
   771  		SkipValidation: true,
   772  	}
   773  	if err := mgmtServer.Update(ctx, resources); err != nil {
   774  		t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   775  	}
   776  	if err := verifyNoListenerUpdate(ctx, lw1.updateCh); err != nil {
   777  		t.Fatal(err)
   778  	}
   779  	wantUpdate = listenerUpdateErrTuple{
   780  		update: xdsresource.ListenerUpdate{
   781  			RouteConfigName: "new-rds-resource",
   782  			HTTPFilters:     []xdsresource.HTTPFilter{{Name: "router"}},
   783  		},
   784  	}
   785  	if err := verifyListenerUpdate(ctx, lw2.updateCh, wantUpdate); err != nil {
   786  		t.Fatal(err)
   787  	}
   788  }
   789  
   790  // TestLDSWatch_NACKError covers the case where an update from the management
   791  // server is NACK'ed by the xdsclient. The test verifies that the error is
   792  // propagated to the watcher.
   793  func (s) TestLDSWatch_NACKError(t *testing.T) {
   794  	mgmtServer, nodeID, bootstrapContents, _, cleanup := e2e.SetupManagementServer(t, e2e.ManagementServerOptions{})
   795  	defer cleanup()
   796  
   797  	// Create an xDS client with the above bootstrap contents.
   798  	client, close, err := xdsclient.NewWithBootstrapContentsForTesting(bootstrapContents)
   799  	if err != nil {
   800  		t.Fatalf("Failed to create xDS client: %v", err)
   801  	}
   802  	defer close()
   803  
   804  	// Register a watch for a listener resource and have the watch
   805  	// callback push the received update on to a channel.
   806  	lw := newListenerWatcher()
   807  	ldsCancel := xdsresource.WatchListener(client, ldsName, lw)
   808  	defer ldsCancel()
   809  
   810  	// Configure the management server to return a single listener resource
   811  	// which is expected to be NACKed by the client.
   812  	resources := e2e.UpdateOptions{
   813  		NodeID:         nodeID,
   814  		Listeners:      []*v3listenerpb.Listener{badListenerResource(t, ldsName)},
   815  		SkipValidation: true,
   816  	}
   817  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   818  	defer cancel()
   819  	if err := mgmtServer.Update(ctx, resources); err != nil {
   820  		t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   821  	}
   822  
   823  	// Verify that the expected error is propagated to the watcher.
   824  	u, err := lw.updateCh.Receive(ctx)
   825  	if err != nil {
   826  		t.Fatalf("timeout when waiting for a listener resource from the management server: %v", err)
   827  	}
   828  	gotErr := u.(listenerUpdateErrTuple).err
   829  	if gotErr == nil || !strings.Contains(gotErr.Error(), wantListenerNACKErr) {
   830  		t.Fatalf("update received with error: %v, want %q", gotErr, wantListenerNACKErr)
   831  	}
   832  }
   833  
   834  // TestLDSWatch_PartialValid covers the case where a response from the
   835  // management server contains both valid and invalid resources and is expected
   836  // to be NACK'ed by the xdsclient. The test verifies that watchers corresponding
   837  // to the valid resource receive the update, while watchers corresponding to the
   838  // invalid resource receive an error.
   839  func (s) TestLDSWatch_PartialValid(t *testing.T) {
   840  	mgmtServer, nodeID, bootstrapContents, _, cleanup := e2e.SetupManagementServer(t, e2e.ManagementServerOptions{})
   841  	defer cleanup()
   842  
   843  	// Create an xDS client with the above bootstrap contents.
   844  	client, close, err := xdsclient.NewWithBootstrapContentsForTesting(bootstrapContents)
   845  	if err != nil {
   846  		t.Fatalf("Failed to create xDS client: %v", err)
   847  	}
   848  	defer close()
   849  
   850  	// Register two watches for listener resources. The first watch is expected
   851  	// to receive an error because the received resource is NACKed. The second
   852  	// watch is expected to get a good update.
   853  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   854  	defer cancel()
   855  	badResourceName := ldsName
   856  	lw1 := newListenerWatcher()
   857  	ldsCancel1 := xdsresource.WatchListener(client, badResourceName, lw1)
   858  	defer ldsCancel1()
   859  	goodResourceName := ldsNameNewStyle
   860  	lw2 := newListenerWatcher()
   861  	ldsCancel2 := xdsresource.WatchListener(client, goodResourceName, lw2)
   862  	defer ldsCancel2()
   863  
   864  	// Configure the management server with two listener resources. One of these
   865  	// is a bad resource causing the update to be NACKed.
   866  	resources := e2e.UpdateOptions{
   867  		NodeID: nodeID,
   868  		Listeners: []*v3listenerpb.Listener{
   869  			badListenerResource(t, badResourceName),
   870  			e2e.DefaultClientListener(goodResourceName, rdsName),
   871  		},
   872  		SkipValidation: true,
   873  	}
   874  	if err := mgmtServer.Update(ctx, resources); err != nil {
   875  		t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   876  	}
   877  
   878  	// Verify that the expected error is propagated to the watcher which
   879  	// requested for the bad resource.
   880  	u, err := lw1.updateCh.Receive(ctx)
   881  	if err != nil {
   882  		t.Fatalf("timeout when waiting for a listener resource from the management server: %v", err)
   883  	}
   884  	gotErr := u.(listenerUpdateErrTuple).err
   885  	if gotErr == nil || !strings.Contains(gotErr.Error(), wantListenerNACKErr) {
   886  		t.Fatalf("update received with error: %v, want %q", gotErr, wantListenerNACKErr)
   887  	}
   888  
   889  	// Verify that the watcher watching the good resource receives a good
   890  	// update.
   891  	wantUpdate := listenerUpdateErrTuple{
   892  		update: xdsresource.ListenerUpdate{
   893  			RouteConfigName: rdsName,
   894  			HTTPFilters:     []xdsresource.HTTPFilter{{Name: "router"}},
   895  		},
   896  	}
   897  	if err := verifyListenerUpdate(ctx, lw2.updateCh, wantUpdate); err != nil {
   898  		t.Fatal(err)
   899  	}
   900  }
   901  
   902  // TestLDSWatch_PartialResponse covers the case where a response from the
   903  // management server does not contain all requested resources. LDS responses are
   904  // supposed to contain all requested resources, and the absence of one usually
   905  // indicates that the management server does not know about it. In cases where
   906  // the server has never responded with this resource before, the xDS client is
   907  // expected to wait for the watch timeout to expire before concluding that the
   908  // resource does not exist on the server
   909  func (s) TestLDSWatch_PartialResponse(t *testing.T) {
   910  	mgmtServer, nodeID, bootstrapContents, _, cleanup := e2e.SetupManagementServer(t, e2e.ManagementServerOptions{})
   911  	defer cleanup()
   912  
   913  	// Create an xDS client with the above bootstrap contents.
   914  	client, close, err := xdsclient.NewWithBootstrapContentsForTesting(bootstrapContents)
   915  	if err != nil {
   916  		t.Fatalf("Failed to create xDS client: %v", err)
   917  	}
   918  	defer close()
   919  
   920  	// Register two watches for two listener resources and have the
   921  	// callbacks push the received updates on to a channel.
   922  	resourceName1 := ldsName
   923  	lw1 := newListenerWatcher()
   924  	ldsCancel1 := xdsresource.WatchListener(client, resourceName1, lw1)
   925  	defer ldsCancel1()
   926  
   927  	resourceName2 := ldsNameNewStyle
   928  	lw2 := newListenerWatcher()
   929  	ldsCancel2 := xdsresource.WatchListener(client, resourceName2, lw2)
   930  	defer ldsCancel2()
   931  
   932  	// Configure the management server to return only one of the two listener
   933  	// resources, corresponding to the registered watches.
   934  	resources := e2e.UpdateOptions{
   935  		NodeID: nodeID,
   936  		Listeners: []*v3listenerpb.Listener{
   937  			e2e.DefaultClientListener(resourceName1, rdsName),
   938  		},
   939  		SkipValidation: true,
   940  	}
   941  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   942  	defer cancel()
   943  	if err := mgmtServer.Update(ctx, resources); err != nil {
   944  		t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   945  	}
   946  
   947  	// Verify the contents of the received update for first watcher.
   948  	wantUpdate1 := listenerUpdateErrTuple{
   949  		update: xdsresource.ListenerUpdate{
   950  			RouteConfigName: rdsName,
   951  			HTTPFilters:     []xdsresource.HTTPFilter{{Name: "router"}},
   952  		},
   953  	}
   954  	if err := verifyListenerUpdate(ctx, lw1.updateCh, wantUpdate1); err != nil {
   955  		t.Fatal(err)
   956  	}
   957  
   958  	// Verify that the second watcher does not get an update with an error.
   959  	if err := verifyNoListenerUpdate(ctx, lw2.updateCh); err != nil {
   960  		t.Fatal(err)
   961  	}
   962  
   963  	// Configure the management server to return two listener resources,
   964  	// corresponding to the registered watches.
   965  	resources = e2e.UpdateOptions{
   966  		NodeID: nodeID,
   967  		Listeners: []*v3listenerpb.Listener{
   968  			e2e.DefaultClientListener(resourceName1, rdsName),
   969  			e2e.DefaultClientListener(resourceName2, rdsName),
   970  		},
   971  		SkipValidation: true,
   972  	}
   973  	if err := mgmtServer.Update(ctx, resources); err != nil {
   974  		t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   975  	}
   976  
   977  	// Verify the contents of the received update for the second watcher.
   978  	wantUpdate2 := listenerUpdateErrTuple{
   979  		update: xdsresource.ListenerUpdate{
   980  			RouteConfigName: rdsName,
   981  			HTTPFilters:     []xdsresource.HTTPFilter{{Name: "router"}},
   982  		},
   983  	}
   984  	if err := verifyListenerUpdate(ctx, lw2.updateCh, wantUpdate2); err != nil {
   985  		t.Fatal(err)
   986  	}
   987  
   988  	// Verify that the first watcher gets no update, as the first resource did
   989  	// not change.
   990  	if err := verifyNoListenerUpdate(ctx, lw1.updateCh); err != nil {
   991  		t.Fatal(err)
   992  	}
   993  }