1// +build go1.12 2 3/* 4 * 5 * Copyright 2019 gRPC authors. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 */ 20 21package v2 22 23import ( 24 "testing" 25 "time" 26 27 v2xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2" 28 29 xdsclient "google.golang.org/grpc/xds/internal/client" 30) 31 32// TestLDSHandleResponse starts a fake xDS server, makes a ClientConn to it, 33// and creates a client using it. Then, it registers a watchLDS and tests 34// different LDS responses. 35func (s) TestLDSHandleResponse(t *testing.T) { 36 tests := []struct { 37 name string 38 ldsResponse *v2xdspb.DiscoveryResponse 39 wantErr bool 40 wantUpdate map[string]xdsclient.ListenerUpdate 41 wantUpdateMD xdsclient.UpdateMetadata 42 wantUpdateErr bool 43 }{ 44 // Badly marshaled LDS response. 45 { 46 name: "badly-marshaled-response", 47 ldsResponse: badlyMarshaledLDSResponse, 48 wantErr: true, 49 wantUpdate: nil, 50 wantUpdateMD: xdsclient.UpdateMetadata{ 51 Status: xdsclient.ServiceStatusNACKed, 52 ErrState: &xdsclient.UpdateErrorMetadata{ 53 Err: errPlaceHolder, 54 }, 55 }, 56 wantUpdateErr: false, 57 }, 58 // Response does not contain Listener proto. 59 { 60 name: "no-listener-proto-in-response", 61 ldsResponse: badResourceTypeInLDSResponse, 62 wantErr: true, 63 wantUpdate: nil, 64 wantUpdateMD: xdsclient.UpdateMetadata{ 65 Status: xdsclient.ServiceStatusNACKed, 66 ErrState: &xdsclient.UpdateErrorMetadata{ 67 Err: errPlaceHolder, 68 }, 69 }, 70 wantUpdateErr: false, 71 }, 72 // No APIListener in the response. Just one test case here for a bad 73 // ApiListener, since the others are covered in 74 // TestGetRouteConfigNameFromListener. 75 { 76 name: "no-apiListener-in-response", 77 ldsResponse: noAPIListenerLDSResponse, 78 wantErr: true, 79 wantUpdate: map[string]xdsclient.ListenerUpdate{ 80 goodLDSTarget1: {}, 81 }, 82 wantUpdateMD: xdsclient.UpdateMetadata{ 83 Status: xdsclient.ServiceStatusNACKed, 84 ErrState: &xdsclient.UpdateErrorMetadata{ 85 Err: errPlaceHolder, 86 }, 87 }, 88 wantUpdateErr: false, 89 }, 90 // Response contains one listener and it is good. 91 { 92 name: "one-good-listener", 93 ldsResponse: goodLDSResponse1, 94 wantErr: false, 95 wantUpdate: map[string]xdsclient.ListenerUpdate{ 96 goodLDSTarget1: {RouteConfigName: goodRouteName1, Raw: marshaledListener1}, 97 }, 98 wantUpdateMD: xdsclient.UpdateMetadata{ 99 Status: xdsclient.ServiceStatusACKed, 100 }, 101 wantUpdateErr: false, 102 }, 103 // Response contains multiple good listeners, including the one we are 104 // interested in. 105 { 106 name: "multiple-good-listener", 107 ldsResponse: ldsResponseWithMultipleResources, 108 wantErr: false, 109 wantUpdate: map[string]xdsclient.ListenerUpdate{ 110 goodLDSTarget1: {RouteConfigName: goodRouteName1, Raw: marshaledListener1}, 111 goodLDSTarget2: {RouteConfigName: goodRouteName1, Raw: marshaledListener2}, 112 }, 113 wantUpdateMD: xdsclient.UpdateMetadata{ 114 Status: xdsclient.ServiceStatusACKed, 115 }, 116 wantUpdateErr: false, 117 }, 118 // Response contains two good listeners (one interesting and one 119 // uninteresting), and one badly marshaled listener. This will cause a 120 // nack because the uninteresting listener will still be parsed. 121 { 122 name: "good-bad-ugly-listeners", 123 ldsResponse: goodBadUglyLDSResponse, 124 wantErr: true, 125 wantUpdate: map[string]xdsclient.ListenerUpdate{ 126 goodLDSTarget1: {RouteConfigName: goodRouteName1, Raw: marshaledListener1}, 127 goodLDSTarget2: {}, 128 }, 129 wantUpdateMD: xdsclient.UpdateMetadata{ 130 Status: xdsclient.ServiceStatusNACKed, 131 ErrState: &xdsclient.UpdateErrorMetadata{ 132 Err: errPlaceHolder, 133 }, 134 }, 135 wantUpdateErr: false, 136 }, 137 // Response contains one listener, but we are not interested in it. 138 { 139 name: "one-uninteresting-listener", 140 ldsResponse: goodLDSResponse2, 141 wantErr: false, 142 wantUpdate: map[string]xdsclient.ListenerUpdate{ 143 goodLDSTarget2: {RouteConfigName: goodRouteName1, Raw: marshaledListener2}, 144 }, 145 wantUpdateMD: xdsclient.UpdateMetadata{ 146 Status: xdsclient.ServiceStatusACKed, 147 }, 148 wantUpdateErr: false, 149 }, 150 // Response constains no resources. This is the case where the server 151 // does not know about the target we are interested in. 152 { 153 name: "empty-response", 154 ldsResponse: emptyLDSResponse, 155 wantErr: false, 156 wantUpdate: nil, 157 wantUpdateMD: xdsclient.UpdateMetadata{ 158 Status: xdsclient.ServiceStatusACKed, 159 }, 160 wantUpdateErr: false, 161 }, 162 } 163 164 for _, test := range tests { 165 t.Run(test.name, func(t *testing.T) { 166 testWatchHandle(t, &watchHandleTestcase{ 167 rType: xdsclient.ListenerResource, 168 resourceName: goodLDSTarget1, 169 responseToHandle: test.ldsResponse, 170 wantHandleErr: test.wantErr, 171 wantUpdate: test.wantUpdate, 172 wantUpdateMD: test.wantUpdateMD, 173 wantUpdateErr: test.wantUpdateErr, 174 }) 175 }) 176 } 177} 178 179// TestLDSHandleResponseWithoutWatch tests the case where the client receives 180// an LDS response without a registered watcher. 181func (s) TestLDSHandleResponseWithoutWatch(t *testing.T) { 182 _, cc, cleanup := startServerAndGetCC(t) 183 defer cleanup() 184 185 v2c, err := newV2Client(&testUpdateReceiver{ 186 f: func(xdsclient.ResourceType, map[string]interface{}, xdsclient.UpdateMetadata) {}, 187 }, cc, goodNodeProto, func(int) time.Duration { return 0 }, nil) 188 if err != nil { 189 t.Fatal(err) 190 } 191 defer v2c.Close() 192 193 if v2c.handleLDSResponse(badResourceTypeInLDSResponse) == nil { 194 t.Fatal("v2c.handleLDSResponse() succeeded, should have failed") 195 } 196 197 if v2c.handleLDSResponse(goodLDSResponse1) != nil { 198 t.Fatal("v2c.handleLDSResponse() succeeded, should have failed") 199 } 200} 201