github.com/imran-kn/cilium-fork@v1.6.9/pkg/envoy/xds/server_e2e_test.go (about)

     1  // Copyright 2018 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build !privileged_tests
    16  
    17  package xds
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"reflect"
    24  	"sort"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/cilium/cilium/pkg/checker"
    29  	"github.com/cilium/cilium/pkg/completion"
    30  	envoy_api_v2 "github.com/cilium/proxy/go/envoy/api/v2"
    31  	envoy_api_v2_core "github.com/cilium/proxy/go/envoy/api/v2/core"
    32  
    33  	"github.com/golang/protobuf/proto"
    34  	"github.com/golang/protobuf/ptypes/any"
    35  	"google.golang.org/genproto/googleapis/rpc/status"
    36  	. "gopkg.in/check.v1"
    37  )
    38  
    39  // Hook up gocheck into the "go test" runner.
    40  func Test(t *testing.T) {
    41  	// logging.ToggleDebugLogs(true)
    42  	TestingT(t)
    43  }
    44  
    45  type ServerSuite struct{}
    46  
    47  var _ = Suite(&ServerSuite{})
    48  
    49  const (
    50  	TestTimeout      = 10 * time.Second
    51  	StreamTimeout    = 2 * time.Second
    52  	CacheUpdateDelay = 250 * time.Millisecond
    53  )
    54  
    55  var (
    56  	DeferredCompletion error = errors.New("Deferred completion")
    57  	nodes                    = map[string]*envoy_api_v2_core.Node{
    58  		node0: {Id: "sidecar~10.0.0.0~node0~bar"},
    59  		node1: {Id: "sidecar~10.0.0.1~node1~bar"},
    60  		node2: {Id: "sidecar~10.0.0.2~node2~bar"},
    61  	}
    62  )
    63  
    64  // ResponseMatchesChecker checks that a DiscoveryResponse's fields match the given
    65  // parameters.
    66  type ResponseMatchesChecker struct {
    67  	*CheckerInfo
    68  }
    69  
    70  func (c *ResponseMatchesChecker) Check(params []interface{}, names []string) (result bool, error string) {
    71  	response, ok := params[0].(*envoy_api_v2.DiscoveryResponse)
    72  	if !ok {
    73  		return false, "response must be an *envoy_api_v2.DiscoveryResponse"
    74  	}
    75  	if response == nil {
    76  		return false, "response is nil"
    77  	}
    78  
    79  	versionInfo, ok := params[1].(string)
    80  	if !ok {
    81  		return false, "VersionInfo must be a string"
    82  	}
    83  	resources, ok := params[2].([]proto.Message)
    84  	if params[2] != nil && !ok {
    85  		return false, "Resources must be a []proto.Message"
    86  	}
    87  	canary, ok := params[3].(bool)
    88  	if !ok {
    89  		return false, "Canary must be a bool"
    90  	}
    91  	typeURL, ok := params[4].(string)
    92  	if !ok {
    93  		return false, "TypeURL must be a string"
    94  	}
    95  
    96  	error = ""
    97  
    98  	result = response.VersionInfo == versionInfo &&
    99  		len(response.Resources) == len(resources) &&
   100  		response.Canary == canary &&
   101  		response.TypeUrl == typeURL
   102  
   103  	if result && len(resources) > 0 {
   104  		// Convert the resources into Any protocol buffer messages, which is
   105  		// the type of Resources in the response, so that we can compare them.
   106  		resourcesAny := make([]*any.Any, 0, len(resources))
   107  		for _, res := range resources {
   108  			data, err := proto.Marshal(res)
   109  			if err != nil {
   110  				return false, fmt.Sprintf("error marshalling protocol buffer %v", res)
   111  			}
   112  			resourcesAny = append(resourcesAny,
   113  				&any.Any{
   114  					TypeUrl: typeURL,
   115  					Value:   data,
   116  				})
   117  		}
   118  		// Sort both lists.
   119  		sort.Slice(response.Resources, func(i, j int) bool {
   120  			return response.Resources[i].String() < response.Resources[j].String()
   121  		})
   122  		sort.Slice(resourcesAny, func(i, j int) bool {
   123  			return resourcesAny[i].String() < resourcesAny[j].String()
   124  		})
   125  		result = reflect.DeepEqual(response.Resources, resourcesAny)
   126  	}
   127  
   128  	return
   129  }
   130  
   131  // ResponseMatches checks that a DiscoveryResponse's fields match the given
   132  // parameters.
   133  var ResponseMatches Checker = &ResponseMatchesChecker{
   134  	&CheckerInfo{Name: "ResponseMatches", Params: []string{
   135  		"response", "VersionInfo", "Resources", "Canary", "TypeUrl"}},
   136  }
   137  
   138  var resources = []*envoy_api_v2.RouteConfiguration{
   139  	{Name: "resource0"},
   140  	{Name: "resource1"},
   141  	{Name: "resource2"},
   142  }
   143  
   144  func (s *ServerSuite) TestRequestAllResources(c *C) {
   145  	typeURL := "type.googleapis.com/envoy.api.v2.DummyConfiguration"
   146  
   147  	var err error
   148  	var req *envoy_api_v2.DiscoveryRequest
   149  	var resp *envoy_api_v2.DiscoveryResponse
   150  	var v uint64
   151  	var mod bool
   152  
   153  	ctx, cancel := context.WithTimeout(context.Background(), TestTimeout)
   154  	defer cancel()
   155  
   156  	cache := NewCache()
   157  	mutator := NewAckingResourceMutatorWrapper(cache)
   158  
   159  	streamCtx, closeStream := context.WithCancel(ctx)
   160  	stream := NewMockStream(streamCtx, 1, 1, StreamTimeout, StreamTimeout)
   161  	defer stream.Close()
   162  
   163  	server := NewServer(map[string]*ResourceTypeConfiguration{typeURL: {Source: cache, AckObserver: mutator}},
   164  		TestTimeout)
   165  
   166  	streamDone := make(chan struct{})
   167  
   168  	// Run the server's stream handler concurrently.
   169  	go func() {
   170  		err := server.HandleRequestStream(ctx, stream, AnyTypeURL)
   171  		close(streamDone)
   172  		c.Check(err, IsNil)
   173  	}()
   174  
   175  	// Request all resources.
   176  	req = &envoy_api_v2.DiscoveryRequest{
   177  		TypeUrl:       typeURL,
   178  		VersionInfo:   "",
   179  		Node:          nodes[node0],
   180  		ResourceNames: nil,
   181  		ResponseNonce: "",
   182  	}
   183  	err = stream.SendRequest(req)
   184  	c.Assert(err, IsNil)
   185  
   186  	// Expecting an empty response.
   187  	resp, err = stream.RecvResponse()
   188  	c.Assert(err, IsNil)
   189  	c.Assert(resp, ResponseMatches, "1", nil, false, typeURL)
   190  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   191  
   192  	// Request the next version of resources.
   193  	req = &envoy_api_v2.DiscoveryRequest{
   194  		TypeUrl:       typeURL,
   195  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   196  		Node:          nodes[node0],
   197  		ResourceNames: nil,
   198  		ResponseNonce: resp.Nonce,
   199  	}
   200  	err = stream.SendRequest(req)
   201  	c.Assert(err, IsNil)
   202  
   203  	// Create version 2 with resource 0.
   204  	time.Sleep(CacheUpdateDelay)
   205  	v, mod, _ = cache.Upsert(typeURL, resources[0].Name, resources[0])
   206  	c.Assert(v, Equals, uint64(2))
   207  	c.Assert(mod, Equals, true)
   208  
   209  	// Expecting a response with that resource.
   210  	resp, err = stream.RecvResponse()
   211  	c.Assert(err, IsNil)
   212  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   213  	c.Assert(resp, ResponseMatches, "2", []proto.Message{resources[0]}, false, typeURL)
   214  
   215  	// Create version 3 with resources 0 and 1.
   216  	// This time, update the cache before sending the request.
   217  	v, mod, _ = cache.Upsert(typeURL, resources[1].Name, resources[1])
   218  	c.Assert(v, Equals, uint64(3))
   219  	c.Assert(mod, Equals, true)
   220  
   221  	// Request the next version of resources.
   222  	req = &envoy_api_v2.DiscoveryRequest{
   223  		TypeUrl:       typeURL,
   224  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   225  		Node:          nodes[node0],
   226  		ResourceNames: nil,
   227  		ResponseNonce: resp.Nonce,
   228  	}
   229  	err = stream.SendRequest(req)
   230  	c.Assert(err, IsNil)
   231  
   232  	// Expecting a response with both resources.
   233  	resp, err = stream.RecvResponse()
   234  	c.Assert(err, IsNil)
   235  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   236  	c.Assert(resp, ResponseMatches, "3", []proto.Message{resources[0], resources[1]}, false, typeURL)
   237  
   238  	// Request the next version of resources.
   239  	req = &envoy_api_v2.DiscoveryRequest{
   240  		TypeUrl:       typeURL,
   241  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   242  		Node:          nodes[node0],
   243  		ResourceNames: nil,
   244  		ResponseNonce: resp.Nonce,
   245  	}
   246  	err = stream.SendRequest(req)
   247  	c.Assert(err, IsNil)
   248  
   249  	// Create version 4 with resource 1.
   250  	time.Sleep(CacheUpdateDelay)
   251  	v, mod, _ = cache.Delete(typeURL, resources[0].Name)
   252  	c.Assert(v, Equals, uint64(4))
   253  	c.Assert(mod, Equals, true)
   254  
   255  	// Expecting a response with that resource.
   256  	resp, err = stream.RecvResponse()
   257  	c.Assert(err, IsNil)
   258  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   259  	c.Assert(resp, ResponseMatches, "4", []proto.Message{resources[1]}, false, typeURL)
   260  
   261  	// Close the stream.
   262  	closeStream()
   263  
   264  	select {
   265  	case <-ctx.Done():
   266  		c.Errorf("HandleRequestStream(%v, %v, %v) took too long to return after stream was closed", "ctx", "stream", AnyTypeURL)
   267  	case <-streamDone:
   268  	}
   269  }
   270  
   271  func (s *ServerSuite) TestAck(c *C) {
   272  	typeURL := "type.googleapis.com/envoy.api.v2.DummyConfiguration"
   273  
   274  	var err error
   275  	var req *envoy_api_v2.DiscoveryRequest
   276  	var resp *envoy_api_v2.DiscoveryResponse
   277  
   278  	ctx, cancel := context.WithTimeout(context.Background(), TestTimeout)
   279  	defer cancel()
   280  	wg := completion.NewWaitGroup(ctx)
   281  
   282  	cache := NewCache()
   283  	mutator := NewAckingResourceMutatorWrapper(cache)
   284  
   285  	streamCtx, closeStream := context.WithCancel(ctx)
   286  	stream := NewMockStream(streamCtx, 1, 1, StreamTimeout, StreamTimeout)
   287  	defer stream.Close()
   288  
   289  	server := NewServer(map[string]*ResourceTypeConfiguration{typeURL: {Source: cache, AckObserver: mutator}},
   290  		TestTimeout)
   291  
   292  	streamDone := make(chan struct{})
   293  
   294  	// Run the server's stream handler concurrently.
   295  	go func() {
   296  		err := server.HandleRequestStream(ctx, stream, AnyTypeURL)
   297  		close(streamDone)
   298  		c.Check(err, IsNil)
   299  	}()
   300  
   301  	// Request all resources.
   302  	req = &envoy_api_v2.DiscoveryRequest{
   303  		TypeUrl:       typeURL,
   304  		VersionInfo:   "",
   305  		Node:          nodes[node0],
   306  		ResourceNames: nil,
   307  		ResponseNonce: "",
   308  	}
   309  	err = stream.SendRequest(req)
   310  	c.Assert(err, IsNil)
   311  
   312  	// Expecting an empty response.
   313  	resp, err = stream.RecvResponse()
   314  	c.Assert(err, IsNil)
   315  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   316  	c.Assert(resp, ResponseMatches, "1", nil, false, typeURL)
   317  
   318  	// Request the next version of resources.
   319  	req = &envoy_api_v2.DiscoveryRequest{
   320  		TypeUrl:       typeURL,
   321  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   322  		Node:          nodes[node0],
   323  		ResourceNames: nil,
   324  		ResponseNonce: resp.Nonce,
   325  	}
   326  	err = stream.SendRequest(req)
   327  	c.Assert(err, IsNil)
   328  
   329  	// Create version 2 with resource 0.
   330  	time.Sleep(CacheUpdateDelay)
   331  	callback1, comp1 := newCompCallback()
   332  	mutator.Upsert(typeURL, resources[0].Name, resources[0], []string{node0}, wg, callback1)
   333  	c.Assert(comp1, Not(IsCompleted))
   334  
   335  	// Expecting a response with that resource.
   336  	resp, err = stream.RecvResponse()
   337  	c.Assert(err, IsNil)
   338  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   339  	c.Assert(resp, ResponseMatches, "2", []proto.Message{resources[0]}, false, typeURL)
   340  
   341  	// Create version 3 with resources 0 and 1.
   342  	// This time, update the cache before sending the request.
   343  	callback2, comp2 := newCompCallback()
   344  	mutator.Upsert(typeURL, resources[1].Name, resources[1], []string{node0}, wg, callback2)
   345  	c.Assert(comp2, Not(IsCompleted))
   346  
   347  	// Request the next version of resources.
   348  	req = &envoy_api_v2.DiscoveryRequest{
   349  		TypeUrl:       typeURL,
   350  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   351  		Node:          nodes[node0],
   352  		ResourceNames: nil,
   353  		ResponseNonce: resp.Nonce,
   354  	}
   355  	err = stream.SendRequest(req)
   356  	c.Assert(err, IsNil)
   357  
   358  	// Expecting a response with both resources.
   359  	resp, err = stream.RecvResponse()
   360  	c.Assert(err, IsNil)
   361  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   362  	c.Assert(resp, ResponseMatches, "3", []proto.Message{resources[0], resources[1]}, false, typeURL)
   363  
   364  	// Version 2 was ACKed by the last request.
   365  	c.Assert(comp1, IsCompleted)
   366  	c.Assert(comp2, Not(IsCompleted))
   367  
   368  	// Request the next version of resources.
   369  	req = &envoy_api_v2.DiscoveryRequest{
   370  		TypeUrl:       typeURL,
   371  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   372  		Node:          nodes[node0],
   373  		ResourceNames: nil,
   374  		ResponseNonce: resp.Nonce,
   375  	}
   376  	err = stream.SendRequest(req)
   377  	c.Assert(err, IsNil)
   378  
   379  	// Expecting no response.
   380  
   381  	time.Sleep(CacheUpdateDelay)
   382  
   383  	// Version 3 was ACKed by the last request.
   384  	c.Assert(comp2, IsCompleted)
   385  
   386  	// Close the stream.
   387  	closeStream()
   388  
   389  	select {
   390  	case <-ctx.Done():
   391  		c.Errorf("HandleRequestStream(%v, %v, %v) took too long to return after stream was closed", "ctx", "stream", AnyTypeURL)
   392  	case <-streamDone:
   393  	}
   394  }
   395  
   396  func (s *ServerSuite) TestRequestSomeResources(c *C) {
   397  	typeURL := "type.googleapis.com/envoy.api.v2.DummyConfiguration"
   398  
   399  	var err error
   400  	var req *envoy_api_v2.DiscoveryRequest
   401  	var resp *envoy_api_v2.DiscoveryResponse
   402  	var v uint64
   403  	var mod bool
   404  
   405  	ctx, cancel := context.WithTimeout(context.Background(), TestTimeout)
   406  	defer cancel()
   407  
   408  	cache := NewCache()
   409  	mutator := NewAckingResourceMutatorWrapper(cache)
   410  
   411  	streamCtx, closeStream := context.WithCancel(ctx)
   412  	stream := NewMockStream(streamCtx, 1, 1, StreamTimeout, StreamTimeout)
   413  	defer stream.Close()
   414  
   415  	server := NewServer(map[string]*ResourceTypeConfiguration{typeURL: {Source: cache, AckObserver: mutator}},
   416  		TestTimeout)
   417  
   418  	streamDone := make(chan struct{})
   419  
   420  	// Run the server's stream handler concurrently.
   421  	go func() {
   422  		err := server.HandleRequestStream(ctx, stream, AnyTypeURL)
   423  		close(streamDone)
   424  		c.Check(err, IsNil)
   425  	}()
   426  
   427  	// Request resources 1 and 2 (not 0).
   428  	req = &envoy_api_v2.DiscoveryRequest{
   429  		TypeUrl:       typeURL,
   430  		VersionInfo:   "",
   431  		Node:          nodes[node0],
   432  		ResourceNames: []string{resources[1].Name, resources[2].Name},
   433  		ResponseNonce: "",
   434  	}
   435  	err = stream.SendRequest(req)
   436  	c.Assert(err, IsNil)
   437  
   438  	// Expecting an empty response.
   439  	resp, err = stream.RecvResponse()
   440  	c.Assert(err, IsNil)
   441  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   442  	c.Assert(resp, ResponseMatches, "1", nil, false, typeURL)
   443  
   444  	// Request the next version of resources.
   445  	req = &envoy_api_v2.DiscoveryRequest{
   446  		TypeUrl:       typeURL,
   447  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   448  		Node:          nodes[node0],
   449  		ResourceNames: []string{resources[1].Name, resources[2].Name},
   450  		ResponseNonce: resp.Nonce,
   451  	}
   452  	err = stream.SendRequest(req)
   453  	c.Assert(err, IsNil)
   454  
   455  	// Create version 2 with resource 0.
   456  	time.Sleep(CacheUpdateDelay)
   457  	v, mod, _ = cache.Upsert(typeURL, resources[0].Name, resources[0])
   458  	c.Assert(v, Equals, uint64(2))
   459  	c.Assert(mod, Equals, true)
   460  
   461  	// There should be a response with no resources.
   462  	resp, err = stream.RecvResponse()
   463  	c.Assert(err, IsNil)
   464  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   465  	c.Assert(resp, ResponseMatches, "2", nil, false, typeURL)
   466  
   467  	// Create version 3 with resource 0 and 1.
   468  	// This time, update the cache before sending the request.
   469  	v, mod, _ = cache.Upsert(typeURL, resources[1].Name, resources[1])
   470  	c.Assert(v, Equals, uint64(3))
   471  	c.Assert(mod, Equals, true)
   472  
   473  	// Request the next version of resources.
   474  	req = &envoy_api_v2.DiscoveryRequest{
   475  		TypeUrl:       typeURL,
   476  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   477  		Node:          nodes[node0],
   478  		ResourceNames: []string{resources[1].Name, resources[2].Name},
   479  		ResponseNonce: resp.Nonce,
   480  	}
   481  	err = stream.SendRequest(req)
   482  	c.Assert(err, IsNil)
   483  
   484  	// Expecting a response with one resource.
   485  	resp, err = stream.RecvResponse()
   486  	c.Assert(err, IsNil)
   487  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   488  	c.Assert(resp, ResponseMatches, "3", []proto.Message{resources[1]}, false, typeURL)
   489  
   490  	// Request the next version of resources.
   491  	req = &envoy_api_v2.DiscoveryRequest{
   492  		TypeUrl:       typeURL,
   493  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   494  		Node:          nodes[node0],
   495  		ResourceNames: []string{resources[1].Name, resources[2].Name},
   496  		ResponseNonce: resp.Nonce,
   497  	}
   498  	err = stream.SendRequest(req)
   499  	c.Assert(err, IsNil)
   500  
   501  	// Create version 4 with resources 0, 1 and 2.
   502  	time.Sleep(CacheUpdateDelay)
   503  	v, mod, _ = cache.Upsert(typeURL, resources[2].Name, resources[2])
   504  	c.Assert(v, Equals, uint64(4))
   505  	c.Assert(mod, Equals, true)
   506  
   507  	// Expecting a response with resources 1 and 2.
   508  	resp, err = stream.RecvResponse()
   509  	c.Assert(err, IsNil)
   510  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   511  	c.Assert(resp, ResponseMatches, "4", []proto.Message{resources[1], resources[2]}, false, typeURL)
   512  
   513  	// Request the next version of resources.
   514  	req = &envoy_api_v2.DiscoveryRequest{
   515  		TypeUrl:       typeURL,
   516  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   517  		Node:          nodes[node0],
   518  		ResourceNames: []string{resources[1].Name, resources[2].Name},
   519  		ResponseNonce: resp.Nonce,
   520  	}
   521  	err = stream.SendRequest(req)
   522  	c.Assert(err, IsNil)
   523  
   524  	// Create version 5 with resources 1 and 2.
   525  	time.Sleep(CacheUpdateDelay)
   526  	v, mod, _ = cache.Delete(typeURL, resources[0].Name)
   527  	c.Assert(v, Equals, uint64(5))
   528  	c.Assert(mod, Equals, true)
   529  
   530  	// Expecting no response for version 5, since neither resources 1 and 2
   531  	// have changed.
   532  
   533  	// Updating resource 2 with the exact same value won't increase the version
   534  	// number. Remain at version 5.
   535  	v, mod, _ = cache.Upsert(typeURL, resources[2].Name, resources[2])
   536  	c.Assert(v, Equals, uint64(5))
   537  	c.Assert(mod, Equals, false)
   538  
   539  	// Create version 6 with resource 1.
   540  	v, mod, _ = cache.Delete(typeURL, resources[1].Name)
   541  	c.Assert(v, Equals, uint64(6))
   542  	c.Assert(mod, Equals, true)
   543  
   544  	// Expecting a response with resource 2.
   545  	resp, err = stream.RecvResponse()
   546  	c.Assert(err, IsNil)
   547  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   548  	c.Assert(resp, ResponseMatches, "6", []proto.Message{resources[2]}, false, typeURL)
   549  
   550  	// Resource 1 has been deleted; Resource 2 exists. Confirm using Lookup().
   551  	rsrc, err := cache.Lookup(typeURL, resources[1].Name)
   552  	c.Assert(err, IsNil)
   553  	c.Assert(rsrc, IsNil)
   554  
   555  	rsrc, err = cache.Lookup(typeURL, resources[2].Name)
   556  	c.Assert(err, IsNil)
   557  	c.Assert(rsrc, Not(IsNil))
   558  	c.Assert(rsrc.(*envoy_api_v2.RouteConfiguration), checker.DeepEquals, resources[2])
   559  
   560  	// Close the stream.
   561  	closeStream()
   562  
   563  	select {
   564  	case <-ctx.Done():
   565  		c.Errorf("HandleRequestStream(%v, %v, %v) took too long to return after stream was closed", "ctx", "stream", AnyTypeURL)
   566  	case <-streamDone:
   567  	}
   568  }
   569  
   570  func (s *ServerSuite) TestUpdateRequestResources(c *C) {
   571  	typeURL := "type.googleapis.com/envoy.api.v2.DummyConfiguration"
   572  
   573  	var err error
   574  	var req *envoy_api_v2.DiscoveryRequest
   575  	var resp *envoy_api_v2.DiscoveryResponse
   576  	var v uint64
   577  	var mod bool
   578  
   579  	ctx, cancel := context.WithTimeout(context.Background(), TestTimeout)
   580  	defer cancel()
   581  
   582  	cache := NewCache()
   583  	mutator := NewAckingResourceMutatorWrapper(cache)
   584  
   585  	streamCtx, closeStream := context.WithCancel(ctx)
   586  	stream := NewMockStream(streamCtx, 1, 1, StreamTimeout, StreamTimeout)
   587  	defer stream.Close()
   588  
   589  	server := NewServer(map[string]*ResourceTypeConfiguration{typeURL: {Source: cache, AckObserver: mutator}},
   590  		TestTimeout)
   591  
   592  	streamDone := make(chan struct{})
   593  
   594  	// Run the server's stream handler concurrently.
   595  	go func() {
   596  		err := server.HandleRequestStream(ctx, stream, AnyTypeURL)
   597  		close(streamDone)
   598  		c.Check(err, IsNil)
   599  	}()
   600  
   601  	// Create version 2 with resources 0 and 1.
   602  	time.Sleep(CacheUpdateDelay)
   603  	v, mod, _ = cache.tx(typeURL, map[string]proto.Message{
   604  		resources[0].Name: resources[0],
   605  		resources[1].Name: resources[1],
   606  	}, nil)
   607  	c.Assert(v, Equals, uint64(2))
   608  	c.Assert(mod, Equals, true)
   609  
   610  	// Request resource 1.
   611  	req = &envoy_api_v2.DiscoveryRequest{
   612  		TypeUrl:       typeURL,
   613  		VersionInfo:   "",
   614  		Node:          nodes[node0],
   615  		ResourceNames: []string{resources[1].Name},
   616  		ResponseNonce: "",
   617  	}
   618  	err = stream.SendRequest(req)
   619  	c.Assert(err, IsNil)
   620  
   621  	// Expecting a response with resource 1.
   622  	resp, err = stream.RecvResponse()
   623  	c.Assert(err, IsNil)
   624  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   625  	c.Assert(resp, ResponseMatches, "2", []proto.Message{resources[1]}, false, typeURL)
   626  
   627  	// Request the next version of resource 1.
   628  	req = &envoy_api_v2.DiscoveryRequest{
   629  		TypeUrl:       typeURL,
   630  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   631  		Node:          nodes[node0],
   632  		ResourceNames: []string{resources[1].Name},
   633  		ResponseNonce: resp.Nonce,
   634  	}
   635  	err = stream.SendRequest(req)
   636  	c.Assert(err, IsNil)
   637  
   638  	// Create version 3 with resource 0, 1 and 2.
   639  	time.Sleep(CacheUpdateDelay)
   640  	v, mod, _ = cache.Upsert(typeURL, resources[2].Name, resources[2])
   641  	c.Assert(v, Equals, uint64(3))
   642  	c.Assert(mod, Equals, true)
   643  
   644  	// Not expecting any response since resource 1 didn't change in version 3.
   645  
   646  	// Send an updated request for both resource 1 and 2.
   647  	req = &envoy_api_v2.DiscoveryRequest{
   648  		TypeUrl:       typeURL,
   649  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   650  		Node:          nodes[node0],
   651  		ResourceNames: []string{resources[1].Name, resources[2].Name},
   652  		ResponseNonce: resp.Nonce,
   653  	}
   654  	err = stream.SendRequest(req)
   655  	c.Assert(err, IsNil)
   656  
   657  	// Expecting a response with resources 1 and 2.
   658  	resp, err = stream.RecvResponse()
   659  	c.Assert(err, IsNil)
   660  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   661  	c.Assert(resp, ResponseMatches, "3", []proto.Message{resources[1], resources[2]}, false, typeURL)
   662  
   663  	// Close the stream.
   664  	closeStream()
   665  
   666  	select {
   667  	case <-ctx.Done():
   668  		c.Errorf("HandleRequestStream(%v, %v, %v) took too long to return after stream was closed", "ctx", "stream", AnyTypeURL)
   669  	case <-streamDone:
   670  	}
   671  }
   672  
   673  func (s *ServerSuite) TestRequestStaleNonce(c *C) {
   674  	typeURL := "type.googleapis.com/envoy.api.v2.DummyConfiguration"
   675  
   676  	var err error
   677  	var req *envoy_api_v2.DiscoveryRequest
   678  	var resp *envoy_api_v2.DiscoveryResponse
   679  	var v uint64
   680  	var mod bool
   681  
   682  	ctx, cancel := context.WithTimeout(context.Background(), TestTimeout)
   683  	defer cancel()
   684  
   685  	cache := NewCache()
   686  	mutator := NewAckingResourceMutatorWrapper(cache)
   687  
   688  	streamCtx, closeStream := context.WithCancel(ctx)
   689  	stream := NewMockStream(streamCtx, 1, 1, StreamTimeout, StreamTimeout)
   690  	defer stream.Close()
   691  
   692  	server := NewServer(map[string]*ResourceTypeConfiguration{typeURL: {Source: cache, AckObserver: mutator}},
   693  		TestTimeout)
   694  
   695  	streamDone := make(chan struct{})
   696  
   697  	// Run the server's stream handler concurrently.
   698  	go func() {
   699  		err := server.HandleRequestStream(ctx, stream, AnyTypeURL)
   700  		close(streamDone)
   701  		c.Check(err, IsNil)
   702  	}()
   703  
   704  	// Request all resources.
   705  	req = &envoy_api_v2.DiscoveryRequest{
   706  		TypeUrl:       typeURL,
   707  		VersionInfo:   "",
   708  		Node:          nodes[node0],
   709  		ResourceNames: nil,
   710  		ResponseNonce: "",
   711  	}
   712  	err = stream.SendRequest(req)
   713  	c.Assert(err, IsNil)
   714  
   715  	// Expecting an empty response.
   716  	resp, err = stream.RecvResponse()
   717  	c.Assert(err, IsNil)
   718  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   719  	c.Assert(resp, ResponseMatches, "1", nil, false, typeURL)
   720  
   721  	// Request the next version of resources.
   722  	req = &envoy_api_v2.DiscoveryRequest{
   723  		TypeUrl:       typeURL,
   724  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   725  		Node:          nodes[node0],
   726  		ResourceNames: nil,
   727  		ResponseNonce: resp.Nonce,
   728  	}
   729  	err = stream.SendRequest(req)
   730  	c.Assert(err, IsNil)
   731  
   732  	// Create version 2 with resource 0.
   733  	time.Sleep(CacheUpdateDelay)
   734  	v, mod, _ = cache.Upsert(typeURL, resources[0].Name, resources[0])
   735  	c.Assert(v, Equals, uint64(2))
   736  	c.Assert(mod, Equals, true)
   737  
   738  	// Expecting a response with that resource.
   739  	resp, err = stream.RecvResponse()
   740  	c.Assert(err, IsNil)
   741  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   742  	c.Assert(resp, ResponseMatches, "2", []proto.Message{resources[0]}, false, typeURL)
   743  
   744  	// Create version 3 with resources 0 and 1.
   745  	// This time, update the cache before sending the request.
   746  	v, mod, _ = cache.Upsert(typeURL, resources[1].Name, resources[1])
   747  	c.Assert(v, Equals, uint64(3))
   748  	c.Assert(mod, Equals, true)
   749  
   750  	// Request the next version of resources, with a stale nonce.
   751  	req = &envoy_api_v2.DiscoveryRequest{
   752  		TypeUrl:       typeURL,
   753  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   754  		Node:          nodes[node0],
   755  		ResourceNames: nil,
   756  		ResponseNonce: "0",
   757  	}
   758  	// Do not update the nonce.
   759  	err = stream.SendRequest(req)
   760  	c.Assert(err, IsNil)
   761  
   762  	// Expecting no response from the server.
   763  
   764  	// Resend the request with the correct nonce.
   765  	req = &envoy_api_v2.DiscoveryRequest{
   766  		TypeUrl:       typeURL,
   767  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   768  		Node:          nodes[node0],
   769  		ResourceNames: nil,
   770  		ResponseNonce: resp.Nonce,
   771  	}
   772  	err = stream.SendRequest(req)
   773  	c.Assert(err, IsNil)
   774  
   775  	// Expecting a response with both resources.
   776  	resp, err = stream.RecvResponse()
   777  	c.Assert(err, IsNil)
   778  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   779  	c.Assert(resp, ResponseMatches, "3", []proto.Message{resources[0], resources[1]}, false, typeURL)
   780  
   781  	// Request the next version of resources.
   782  	req = &envoy_api_v2.DiscoveryRequest{
   783  		TypeUrl:       typeURL,
   784  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   785  		Node:          nodes[node0],
   786  		ResourceNames: nil,
   787  		ResponseNonce: resp.Nonce,
   788  	}
   789  	err = stream.SendRequest(req)
   790  	c.Assert(err, IsNil)
   791  
   792  	// Create version 4 with resource 1.
   793  	time.Sleep(CacheUpdateDelay)
   794  	v, mod, _ = cache.Delete(typeURL, resources[0].Name)
   795  	c.Assert(v, Equals, uint64(4))
   796  	c.Assert(mod, Equals, true)
   797  
   798  	// Expecting a response with that resource.
   799  	resp, err = stream.RecvResponse()
   800  	c.Assert(err, IsNil)
   801  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   802  	c.Assert(resp, ResponseMatches, "4", []proto.Message{resources[1]}, false, typeURL)
   803  
   804  	// Close the stream.
   805  	closeStream()
   806  
   807  	select {
   808  	case <-ctx.Done():
   809  		c.Errorf("HandleRequestStream(%v, %v, %v) took too long to return after stream was closed", "ctx", "stream", AnyTypeURL)
   810  	case <-streamDone:
   811  	}
   812  }
   813  
   814  func (s *ServerSuite) TestNAck(c *C) {
   815  	typeURL := "type.googleapis.com/envoy.api.v2.DummyConfiguration"
   816  
   817  	var err error
   818  	var req *envoy_api_v2.DiscoveryRequest
   819  	var resp *envoy_api_v2.DiscoveryResponse
   820  
   821  	ctx, cancel := context.WithTimeout(context.Background(), TestTimeout)
   822  	defer cancel()
   823  	wg := completion.NewWaitGroup(ctx)
   824  
   825  	cache := NewCache()
   826  	mutator := NewAckingResourceMutatorWrapper(cache)
   827  
   828  	streamCtx, closeStream := context.WithCancel(ctx)
   829  	stream := NewMockStream(streamCtx, 1, 1, StreamTimeout, StreamTimeout)
   830  	defer stream.Close()
   831  
   832  	server := NewServer(map[string]*ResourceTypeConfiguration{typeURL: {Source: cache, AckObserver: mutator}},
   833  		TestTimeout)
   834  
   835  	streamDone := make(chan struct{})
   836  
   837  	// Run the server's stream handler concurrently.
   838  	go func() {
   839  		err := server.HandleRequestStream(ctx, stream, AnyTypeURL)
   840  		close(streamDone)
   841  		c.Check(err, IsNil)
   842  	}()
   843  
   844  	// Request all resources.
   845  	req = &envoy_api_v2.DiscoveryRequest{
   846  		TypeUrl:       typeURL,
   847  		VersionInfo:   "",
   848  		Node:          nodes[node0],
   849  		ResourceNames: nil,
   850  		ResponseNonce: "",
   851  	}
   852  	err = stream.SendRequest(req)
   853  	c.Assert(err, IsNil)
   854  
   855  	// Expecting an empty response.
   856  	resp, err = stream.RecvResponse()
   857  	c.Assert(err, IsNil)
   858  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   859  	c.Assert(resp, ResponseMatches, "1", nil, false, typeURL)
   860  
   861  	// Request the next version of resources.
   862  	req = &envoy_api_v2.DiscoveryRequest{
   863  		TypeUrl:       typeURL,
   864  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   865  		Node:          nodes[node0],
   866  		ResourceNames: nil,
   867  		ResponseNonce: resp.Nonce,
   868  	}
   869  	ackedVersion := resp.VersionInfo
   870  	err = stream.SendRequest(req)
   871  	c.Assert(err, IsNil)
   872  
   873  	// Create version 2 with resource 0.
   874  	time.Sleep(CacheUpdateDelay)
   875  	callback1, comp1 := newCompCallback()
   876  	mutator.Upsert(typeURL, resources[0].Name, resources[0], []string{node0}, wg, callback1)
   877  	c.Assert(comp1, Not(IsCompleted))
   878  
   879  	// Expecting a response with that resource.
   880  	resp, err = stream.RecvResponse()
   881  	c.Assert(err, IsNil)
   882  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   883  	c.Assert(resp, ResponseMatches, "2", []proto.Message{resources[0]}, false, typeURL)
   884  
   885  	// NACK the received version of resources.
   886  	req = &envoy_api_v2.DiscoveryRequest{
   887  		TypeUrl:       typeURL,
   888  		VersionInfo:   ackedVersion, // NACK the received version.
   889  		Node:          nodes[node0],
   890  		ResourceNames: nil,
   891  		ResponseNonce: resp.Nonce,
   892  		ErrorDetail:   &status.Status{Message: "FAILFAIL"},
   893  	}
   894  	err = stream.SendRequest(req)
   895  	c.Assert(err, IsNil)
   896  
   897  	// Create version 3 with resources 0 and 1.
   898  	time.Sleep(CacheUpdateDelay)
   899  
   900  	// NACK cancelled the wg, create a new one
   901  	wg = completion.NewWaitGroup(ctx)
   902  	callback2, comp2 := newCompCallback()
   903  	mutator.Upsert(typeURL, resources[1].Name, resources[1], []string{node0}, wg, callback2)
   904  	c.Assert(comp2, Not(IsCompleted))
   905  
   906  	// Version 2 was NACKed by the last request, so comp1 must NOT be completed ever.
   907  	c.Assert(comp1, Not(IsCompleted))
   908  	c.Assert(comp1.Err(), checker.DeepEquals, &ProxyError{Err: ErrNackReceived, Detail: "FAILFAIL"})
   909  
   910  	// Expecting a response with both resources.
   911  	// Note that the stream should not have a message that repeats the previous one!
   912  	resp, err = stream.RecvResponse()
   913  	c.Assert(err, IsNil)
   914  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   915  	c.Assert(resp, ResponseMatches, "3", []proto.Message{resources[0], resources[1]}, false, typeURL)
   916  
   917  	c.Assert(comp1, Not(IsCompleted))
   918  	c.Assert(comp2, Not(IsCompleted))
   919  
   920  	// Request the next version of resources.
   921  	req = &envoy_api_v2.DiscoveryRequest{
   922  		TypeUrl:       typeURL,
   923  		VersionInfo:   resp.VersionInfo, // ACK the received version.
   924  		Node:          nodes[node0],
   925  		ResourceNames: nil,
   926  		ResponseNonce: resp.Nonce,
   927  	}
   928  	err = stream.SendRequest(req)
   929  	c.Assert(err, IsNil)
   930  
   931  	// Expecting no response.
   932  
   933  	time.Sleep(CacheUpdateDelay)
   934  
   935  	// comp2 was ACKed by the last request.
   936  	c.Assert(comp1, Not(IsCompleted))
   937  	c.Assert(comp2, IsCompleted)
   938  
   939  	// Close the stream.
   940  	closeStream()
   941  
   942  	select {
   943  	case <-ctx.Done():
   944  		c.Errorf("HandleRequestStream(%v, %v, %v) took too long to return after stream was closed", "ctx", "stream", AnyTypeURL)
   945  	case <-streamDone:
   946  	}
   947  }
   948  
   949  func (s *ServerSuite) TestNAckFromTheStart(c *C) {
   950  	typeURL := "type.googleapis.com/envoy.api.v2.DummyConfiguration"
   951  
   952  	var err error
   953  	var req *envoy_api_v2.DiscoveryRequest
   954  	var resp *envoy_api_v2.DiscoveryResponse
   955  
   956  	ctx, cancel := context.WithTimeout(context.Background(), TestTimeout)
   957  	defer cancel()
   958  	wg := completion.NewWaitGroup(ctx)
   959  
   960  	cache := NewCache()
   961  	mutator := NewAckingResourceMutatorWrapper(cache)
   962  
   963  	streamCtx, closeStream := context.WithCancel(ctx)
   964  	stream := NewMockStream(streamCtx, 1, 1, StreamTimeout, StreamTimeout)
   965  	defer stream.Close()
   966  
   967  	server := NewServer(map[string]*ResourceTypeConfiguration{typeURL: {Source: cache, AckObserver: mutator}},
   968  		TestTimeout)
   969  
   970  	streamDone := make(chan struct{})
   971  
   972  	// Run the server's stream handler concurrently.
   973  	go func() {
   974  		err := server.HandleRequestStream(ctx, stream, AnyTypeURL)
   975  		close(streamDone)
   976  		c.Check(err, IsNil)
   977  	}()
   978  
   979  	// Request all resources.
   980  	req = &envoy_api_v2.DiscoveryRequest{
   981  		TypeUrl:       typeURL,
   982  		VersionInfo:   "",
   983  		Node:          nodes[node0],
   984  		ResourceNames: nil,
   985  		ResponseNonce: "",
   986  	}
   987  	err = stream.SendRequest(req)
   988  	c.Assert(err, IsNil)
   989  
   990  	// Expecting an empty response.
   991  	resp, err = stream.RecvResponse()
   992  	c.Assert(err, IsNil)
   993  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
   994  	c.Assert(resp, ResponseMatches, "1", nil, false, typeURL)
   995  
   996  	// Create version 2 with resource 0.
   997  	time.Sleep(CacheUpdateDelay)
   998  	callback1, comp1 := newCompCallback()
   999  	mutator.Upsert(typeURL, resources[0].Name, resources[0], []string{node0}, wg, callback1)
  1000  	c.Assert(comp1, Not(IsCompleted))
  1001  
  1002  	// Request the next version of resources.
  1003  	req = &envoy_api_v2.DiscoveryRequest{
  1004  		TypeUrl:       typeURL,
  1005  		VersionInfo:   "", // NACK all received versions.
  1006  		Node:          nodes[node0],
  1007  		ResourceNames: nil,
  1008  		ResponseNonce: resp.Nonce,
  1009  	}
  1010  	err = stream.SendRequest(req)
  1011  	c.Assert(err, IsNil)
  1012  
  1013  	// Expecting a response with that resource.
  1014  	resp, err = stream.RecvResponse()
  1015  	c.Assert(err, IsNil)
  1016  	c.Assert(resp.Nonce, Equals, resp.VersionInfo)
  1017  	c.Assert(resp, ResponseMatches, "2", []proto.Message{resources[0]}, false, typeURL)
  1018  
  1019  	// NACK the received version of resources.
  1020  	req = &envoy_api_v2.DiscoveryRequest{
  1021  		TypeUrl:       typeURL,
  1022  		VersionInfo:   "", // NACK all received versions.
  1023  		Node:          nodes[node0],
  1024  		ResourceNames: nil,
  1025  		ResponseNonce: resp.Nonce,
  1026  	}
  1027  	err = stream.SendRequest(req)
  1028  	c.Assert(err, IsNil)
  1029  
  1030  	time.Sleep(CacheUpdateDelay)
  1031  
  1032  	// Version 2 was NACKed by the last request, so it must NOT be completed successfully.
  1033  	c.Assert(comp1, Not(IsCompleted))
  1034  	// Version 2 did not have a callback, so the completion was completed with an error
  1035  	c.Assert(comp1.Err(), Not(IsNil))
  1036  	c.Assert(comp1.Err(), checker.DeepEquals, &ProxyError{Err: ErrNackReceived})
  1037  
  1038  	// NACK canceled the WaitGroup, create new one
  1039  	wg = completion.NewWaitGroup(ctx)
  1040  
  1041  	// Create version 3 with resources 0 and 1.
  1042  	callback2, comp2 := newCompCallback()
  1043  	mutator.Upsert(typeURL, resources[1].Name, resources[1], []string{node0}, wg, callback2)
  1044  	c.Assert(comp2, Not(IsCompleted))
  1045  
  1046  	// Expecting a response with both resources.
  1047  	// Note that the stream should not have a message that repeats the previous one!
  1048  	resp, err = stream.RecvResponse()
  1049  	c.Assert(err, IsNil)
  1050  	c.Assert(resp, ResponseMatches, "3", []proto.Message{resources[0], resources[1]}, false, typeURL)
  1051  	c.Assert(resp.Nonce, Not(Equals), "")
  1052  
  1053  	c.Assert(comp2, Not(IsCompleted))
  1054  
  1055  	// Request the next version of resources.
  1056  	req = &envoy_api_v2.DiscoveryRequest{
  1057  		TypeUrl:       typeURL,
  1058  		VersionInfo:   resp.VersionInfo, // ACK the received version.
  1059  		Node:          nodes[node0],
  1060  		ResourceNames: nil,
  1061  		ResponseNonce: resp.Nonce,
  1062  	}
  1063  	err = stream.SendRequest(req)
  1064  	c.Assert(err, IsNil)
  1065  
  1066  	// Expecting no response.
  1067  
  1068  	time.Sleep(CacheUpdateDelay)
  1069  
  1070  	// Version 3 was ACKed by the last request.
  1071  	c.Assert(comp2, IsCompleted)
  1072  
  1073  	// Close the stream.
  1074  	closeStream()
  1075  
  1076  	select {
  1077  	case <-ctx.Done():
  1078  		c.Errorf("HandleRequestStream(%v, %v, %v) took too long to return after stream was closed", "ctx", "stream", AnyTypeURL)
  1079  	case <-streamDone:
  1080  	}
  1081  }
  1082  
  1083  func (s *ServerSuite) TestRequestHighVersionFromTheStart(c *C) {
  1084  	typeURL := "type.googleapis.com/envoy.api.v2.DummyConfiguration"
  1085  
  1086  	var err error
  1087  	var req *envoy_api_v2.DiscoveryRequest
  1088  	var resp *envoy_api_v2.DiscoveryResponse
  1089  
  1090  	ctx, cancel := context.WithTimeout(context.Background(), TestTimeout)
  1091  	defer cancel()
  1092  	wg := completion.NewWaitGroup(ctx)
  1093  
  1094  	cache := NewCache()
  1095  	mutator := NewAckingResourceMutatorWrapper(cache)
  1096  
  1097  	streamCtx, closeStream := context.WithCancel(ctx)
  1098  	stream := NewMockStream(streamCtx, 1, 1, StreamTimeout, StreamTimeout)
  1099  	defer stream.Close()
  1100  
  1101  	server := NewServer(map[string]*ResourceTypeConfiguration{typeURL: {Source: cache, AckObserver: mutator}},
  1102  		TestTimeout)
  1103  
  1104  	streamDone := make(chan struct{})
  1105  
  1106  	// Run the server's stream handler concurrently.
  1107  	go func() {
  1108  		err := server.HandleRequestStream(ctx, stream, AnyTypeURL)
  1109  		close(streamDone)
  1110  		c.Check(err, IsNil)
  1111  	}()
  1112  
  1113  	// Create version 2 with resource 0.
  1114  	time.Sleep(CacheUpdateDelay)
  1115  	callback1, comp1 := newCompCallback()
  1116  	mutator.Upsert(typeURL, resources[0].Name, resources[0], []string{node0}, wg, callback1)
  1117  	c.Assert(comp1, Not(IsCompleted))
  1118  
  1119  	// Request all resources, with a version higher than the version currently
  1120  	// in Cilium's cache. This happens after the server restarts but the
  1121  	// xDS client survives and continues to request the same version.
  1122  	req = &envoy_api_v2.DiscoveryRequest{
  1123  		TypeUrl:       typeURL,
  1124  		VersionInfo:   "64",
  1125  		Node:          nodes[node0],
  1126  		ResourceNames: nil,
  1127  		ResponseNonce: "64",
  1128  	}
  1129  	err = stream.SendRequest(req)
  1130  	c.Assert(err, IsNil)
  1131  
  1132  	// Expecting a response with that resource, and an updated version.
  1133  	resp, err = stream.RecvResponse()
  1134  	c.Assert(err, IsNil)
  1135  	c.Assert(resp, ResponseMatches, "65", []proto.Message{resources[0]}, false, typeURL)
  1136  	c.Assert(resp.Nonce, Not(Equals), "")
  1137  
  1138  	// Close the stream.
  1139  	closeStream()
  1140  
  1141  	select {
  1142  	case <-ctx.Done():
  1143  		c.Errorf("HandleRequestStream(%v, %v, %v) took too long to return after stream was closed", "ctx", "stream", AnyTypeURL)
  1144  	case <-streamDone:
  1145  	}
  1146  }