@@ -21,6 +21,7 @@ package transport_test
2121
2222import (
2323 "context"
24+ "errors"
2425 "testing"
2526 "time"
2627
@@ -217,3 +218,193 @@ func (s) TestHandleResponseFromManagementServer(t *testing.T) {
217218 })
218219 }
219220}
221+
222+ func (s ) TestEmptyListenerResourceOnStreamRestart (t * testing.T ) {
223+ ctx , cancel := context .WithTimeout (context .Background (), defaultTestTimeout )
224+ defer cancel ()
225+
226+ mgmtServer , cleanup := startFakeManagementServer (t )
227+ defer cleanup ()
228+ t .Logf ("Started xDS management server on %s" , mgmtServer .Address )
229+ nodeProto := & v3corepb.Node {Id : uuid .New ().String ()}
230+ tr , err := transport .New (transport.Options {
231+ ServerCfg : * xdstestutils .ServerConfigForAddress (t , mgmtServer .Address ),
232+ OnRecvHandler : func (update transport.ResourceUpdate ) error {
233+ return nil
234+ },
235+ OnSendHandler : func (* transport.ResourceSendInfo ) {}, // No onSend handling.
236+ OnErrorHandler : func (error ) {}, // No stream error handling.
237+ Backoff : func (int ) time.Duration { return time .Duration (0 ) }, // No backoff.
238+ NodeProto : nodeProto ,
239+ })
240+ if err != nil {
241+ t .Fatalf ("Failed to create xDS transport: %v" , err )
242+ }
243+ defer tr .Close ()
244+
245+ // Send a request for a listener resource.
246+ const resource = "some-resource"
247+ tr .SendRequest (version .V3ListenerURL , []string {resource })
248+
249+ // Ensure the proper request was sent.
250+ val , err := mgmtServer .XDSRequestChan .Receive (ctx )
251+ if err != nil {
252+ t .Fatalf ("Error waiting for mgmt server response: %v" , err )
253+ }
254+ wantReq := & fakeserver.Request {Req : & v3discoverypb.DiscoveryRequest {
255+ Node : nodeProto ,
256+ ResourceNames : []string {resource },
257+ TypeUrl : "type.googleapis.com/envoy.config.listener.v3.Listener" ,
258+ }}
259+ gotReq := val .(* fakeserver.Request )
260+ if diff := cmp .Diff (gotReq , wantReq , protocmp .Transform ()); diff != "" {
261+ t .Fatalf ("Discovery request received at management server is %+v, want %+v" , gotReq , wantReq )
262+ }
263+
264+ // Remove the subscription by requesting an empty list.
265+ tr .SendRequest (version .V3ListenerURL , []string {})
266+
267+ // Ensure the proper request was sent.
268+ val , err = mgmtServer .XDSRequestChan .Receive (ctx )
269+ if err != nil {
270+ t .Fatalf ("Error waiting for mgmt server response: %v" , err )
271+ }
272+ wantReq = & fakeserver.Request {Req : & v3discoverypb.DiscoveryRequest {
273+ ResourceNames : []string {},
274+ TypeUrl : "type.googleapis.com/envoy.config.listener.v3.Listener" ,
275+ }}
276+ gotReq = val .(* fakeserver.Request )
277+ if diff := cmp .Diff (gotReq , wantReq , protocmp .Transform ()); diff != "" {
278+ t .Fatalf ("Discovery request received at management server is %+v, want %+v" , gotReq , wantReq )
279+ }
280+
281+ // Cause the stream to restart.
282+ mgmtServer .XDSResponseChan <- & fakeserver.Response {Err : errors .New ("go away" )}
283+
284+ // Ensure no request is sent since there are no resources.
285+ ctxShort , cancel := context .WithTimeout (ctx , defaultTestShortTimeout )
286+ defer cancel ()
287+ if got , err := mgmtServer .XDSRequestChan .Receive (ctxShort ); ! errors .Is (err , context .DeadlineExceeded ) {
288+ t .Fatalf ("mgmt server received request: %v; wanted DeadlineExceeded error" , got )
289+ }
290+
291+ tr .SendRequest (version .V3ListenerURL , []string {resource })
292+
293+ // Ensure the proper request was sent with the node proto.
294+ val , err = mgmtServer .XDSRequestChan .Receive (ctx )
295+ if err != nil {
296+ t .Fatalf ("Error waiting for mgmt server response: %v" , err )
297+ }
298+ wantReq = & fakeserver.Request {Req : & v3discoverypb.DiscoveryRequest {
299+ Node : nodeProto ,
300+ ResourceNames : []string {resource },
301+ TypeUrl : "type.googleapis.com/envoy.config.listener.v3.Listener" ,
302+ }}
303+ gotReq = val .(* fakeserver.Request )
304+ if diff := cmp .Diff (gotReq , wantReq , protocmp .Transform ()); diff != "" {
305+ t .Fatalf ("Discovery request received at management server is %+v, want %+v" , gotReq , wantReq )
306+ }
307+
308+ }
309+
310+ func (s ) TestEmptyClusterResourceOnStreamRestartWithListener (t * testing.T ) {
311+ ctx , cancel := context .WithTimeout (context .Background (), defaultTestTimeout )
312+ defer cancel ()
313+
314+ mgmtServer , cleanup := startFakeManagementServer (t )
315+ defer cleanup ()
316+ t .Logf ("Started xDS management server on %s" , mgmtServer .Address )
317+ nodeProto := & v3corepb.Node {Id : uuid .New ().String ()}
318+ tr , err := transport .New (transport.Options {
319+ ServerCfg : * xdstestutils .ServerConfigForAddress (t , mgmtServer .Address ),
320+ OnRecvHandler : func (update transport.ResourceUpdate ) error {
321+ return nil
322+ },
323+ OnSendHandler : func (* transport.ResourceSendInfo ) {}, // No onSend handling.
324+ OnErrorHandler : func (error ) {}, // No stream error handling.
325+ Backoff : func (int ) time.Duration { return time .Duration (0 ) }, // No backoff.
326+ NodeProto : nodeProto ,
327+ })
328+ if err != nil {
329+ t .Fatalf ("Failed to create xDS transport: %v" , err )
330+ }
331+ defer tr .Close ()
332+
333+ // Send a request for a listener resource.
334+ const resource = "some-resource"
335+ tr .SendRequest (version .V3ListenerURL , []string {resource })
336+
337+ // Ensure the proper request was sent.
338+ val , err := mgmtServer .XDSRequestChan .Receive (ctx )
339+ if err != nil {
340+ t .Fatalf ("Error waiting for mgmt server response: %v" , err )
341+ }
342+ wantReq := & fakeserver.Request {Req : & v3discoverypb.DiscoveryRequest {
343+ Node : nodeProto ,
344+ ResourceNames : []string {resource },
345+ TypeUrl : "type.googleapis.com/envoy.config.listener.v3.Listener" ,
346+ }}
347+ gotReq := val .(* fakeserver.Request )
348+ if diff := cmp .Diff (gotReq , wantReq , protocmp .Transform ()); diff != "" {
349+ t .Fatalf ("Discovery request received at management server is %+v, want %+v" , gotReq , wantReq )
350+ }
351+
352+ // Send a request for a cluster resource.
353+ tr .SendRequest (version .V3ClusterURL , []string {resource })
354+
355+ // Ensure the proper request was sent.
356+ val , err = mgmtServer .XDSRequestChan .Receive (ctx )
357+ if err != nil {
358+ t .Fatalf ("Error waiting for mgmt server response: %v" , err )
359+ }
360+ wantReq = & fakeserver.Request {Req : & v3discoverypb.DiscoveryRequest {
361+ ResourceNames : []string {resource },
362+ TypeUrl : "type.googleapis.com/envoy.config.cluster.v3.Cluster" ,
363+ }}
364+ gotReq = val .(* fakeserver.Request )
365+ if diff := cmp .Diff (gotReq , wantReq , protocmp .Transform ()); diff != "" {
366+ t .Fatalf ("Discovery request received at management server is %+v, want %+v" , gotReq , wantReq )
367+ }
368+
369+ // Remove the cluster subscription by requesting an empty list.
370+ tr .SendRequest (version .V3ClusterURL , []string {})
371+
372+ // Ensure the proper request was sent.
373+ val , err = mgmtServer .XDSRequestChan .Receive (ctx )
374+ if err != nil {
375+ t .Fatalf ("Error waiting for mgmt server response: %v" , err )
376+ }
377+ wantReq = & fakeserver.Request {Req : & v3discoverypb.DiscoveryRequest {
378+ ResourceNames : []string {},
379+ TypeUrl : "type.googleapis.com/envoy.config.cluster.v3.Cluster" ,
380+ }}
381+ gotReq = val .(* fakeserver.Request )
382+ if diff := cmp .Diff (gotReq , wantReq , protocmp .Transform ()); diff != "" {
383+ t .Fatalf ("Discovery request received at management server is %+v, want %+v" , gotReq , wantReq )
384+ }
385+
386+ // Cause the stream to restart.
387+ mgmtServer .XDSResponseChan <- & fakeserver.Response {Err : errors .New ("go away" )}
388+
389+ // Ensure the proper LDS request was sent.
390+ val , err = mgmtServer .XDSRequestChan .Receive (ctx )
391+ if err != nil {
392+ t .Fatalf ("Error waiting for mgmt server response: %v" , err )
393+ }
394+ wantReq = & fakeserver.Request {Req : & v3discoverypb.DiscoveryRequest {
395+ Node : nodeProto ,
396+ ResourceNames : []string {resource },
397+ TypeUrl : "type.googleapis.com/envoy.config.listener.v3.Listener" ,
398+ }}
399+ gotReq = val .(* fakeserver.Request )
400+ if diff := cmp .Diff (gotReq , wantReq , protocmp .Transform ()); diff != "" {
401+ t .Fatalf ("Discovery request received at management server is %+v, want %+v" , gotReq , wantReq )
402+ }
403+
404+ // Ensure no cluster request is sent since there are no cluster resources.
405+ ctxShort , cancel := context .WithTimeout (ctx , defaultTestShortTimeout )
406+ defer cancel ()
407+ if got , err := mgmtServer .XDSRequestChan .Receive (ctxShort ); ! errors .Is (err , context .DeadlineExceeded ) {
408+ t .Fatalf ("mgmt server received request: %v; wanted DeadlineExceeded error" , got )
409+ }
410+ }
0 commit comments