1// +build go1.12 2 3/* 4 * 5 * Copyright 2021 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 tests_test 22 23import ( 24 "fmt" 25 "testing" 26 "time" 27 28 v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" 29 v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 30 v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" 31 v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" 32 v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" 33 "github.com/google/go-cmp/cmp" 34 "github.com/google/go-cmp/cmp/cmpopts" 35 "google.golang.org/protobuf/testing/protocmp" 36 "google.golang.org/protobuf/types/known/anypb" 37 "google.golang.org/protobuf/types/known/durationpb" 38 39 "google.golang.org/grpc" 40 "google.golang.org/grpc/credentials/insecure" 41 "google.golang.org/grpc/internal/testutils" 42 xdsclient "google.golang.org/grpc/xds/internal/client" 43 "google.golang.org/grpc/xds/internal/client/bootstrap" 44 xdstestutils "google.golang.org/grpc/xds/internal/testutils" 45) 46 47const defaultTestWatchExpiryTimeout = 500 * time.Millisecond 48 49func (s) TestLDSConfigDump(t *testing.T) { 50 const testVersion = "test-version-lds" 51 var ( 52 ldsTargets = []string{"lds.target.good:0000", "lds.target.good:1111"} 53 routeConfigNames = []string{"route-config-0", "route-config-1"} 54 listenerRaws = make(map[string]*anypb.Any, len(ldsTargets)) 55 ) 56 57 for i := range ldsTargets { 58 listenersT := &v3listenerpb.Listener{ 59 Name: ldsTargets[i], 60 ApiListener: &v3listenerpb.ApiListener{ 61 ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{ 62 RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{ 63 Rds: &v3httppb.Rds{ 64 ConfigSource: &v3corepb.ConfigSource{ 65 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}}, 66 }, 67 RouteConfigName: routeConfigNames[i], 68 }, 69 }, 70 CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{ 71 MaxStreamDuration: durationpb.New(time.Second), 72 }, 73 }), 74 }, 75 } 76 listenerRaws[ldsTargets[i]] = testutils.MarshalAny(listenersT) 77 } 78 79 client, err := xdsclient.NewWithConfigForTesting(&bootstrap.Config{ 80 BalancerName: testXDSServer, 81 Creds: grpc.WithTransportCredentials(insecure.NewCredentials()), 82 NodeProto: xdstestutils.EmptyNodeProtoV2, 83 }, defaultTestWatchExpiryTimeout) 84 if err != nil { 85 t.Fatalf("failed to create client: %v", err) 86 } 87 defer client.Close() 88 89 // Expected unknown. 90 if err := compareDump(client.DumpLDS, "", map[string]xdsclient.UpdateWithMD{}); err != nil { 91 t.Fatalf(err.Error()) 92 } 93 94 wantRequested := make(map[string]xdsclient.UpdateWithMD) 95 for _, n := range ldsTargets { 96 cancel := client.WatchListener(n, func(update xdsclient.ListenerUpdate, err error) {}) 97 defer cancel() 98 wantRequested[n] = xdsclient.UpdateWithMD{MD: xdsclient.UpdateMetadata{Status: xdsclient.ServiceStatusRequested}} 99 } 100 // Expected requested. 101 if err := compareDump(client.DumpLDS, "", wantRequested); err != nil { 102 t.Fatalf(err.Error()) 103 } 104 105 update0 := make(map[string]xdsclient.ListenerUpdate) 106 want0 := make(map[string]xdsclient.UpdateWithMD) 107 for n, r := range listenerRaws { 108 update0[n] = xdsclient.ListenerUpdate{Raw: r} 109 want0[n] = xdsclient.UpdateWithMD{ 110 MD: xdsclient.UpdateMetadata{Version: testVersion}, 111 Raw: r, 112 } 113 } 114 client.NewListeners(update0, xdsclient.UpdateMetadata{Version: testVersion}) 115 116 // Expect ACK. 117 if err := compareDump(client.DumpLDS, testVersion, want0); err != nil { 118 t.Fatalf(err.Error()) 119 } 120 121 const nackVersion = "lds-version-nack" 122 var nackErr = fmt.Errorf("lds nack error") 123 client.NewListeners( 124 map[string]xdsclient.ListenerUpdate{ 125 ldsTargets[0]: {}, 126 }, 127 xdsclient.UpdateMetadata{ 128 ErrState: &xdsclient.UpdateErrorMetadata{ 129 Version: nackVersion, 130 Err: nackErr, 131 }, 132 }, 133 ) 134 135 // Expect NACK for [0], but old ACK for [1]. 136 wantDump := make(map[string]xdsclient.UpdateWithMD) 137 // Though resource 0 was NACKed, the dump should show the previous ACKed raw 138 // message, as well as the NACK error. 139 wantDump[ldsTargets[0]] = xdsclient.UpdateWithMD{ 140 MD: xdsclient.UpdateMetadata{ 141 Version: testVersion, 142 ErrState: &xdsclient.UpdateErrorMetadata{ 143 Version: nackVersion, 144 Err: nackErr, 145 }, 146 }, 147 Raw: listenerRaws[ldsTargets[0]], 148 } 149 150 wantDump[ldsTargets[1]] = xdsclient.UpdateWithMD{ 151 MD: xdsclient.UpdateMetadata{Version: testVersion}, 152 Raw: listenerRaws[ldsTargets[1]], 153 } 154 if err := compareDump(client.DumpLDS, nackVersion, wantDump); err != nil { 155 t.Fatalf(err.Error()) 156 } 157} 158 159func (s) TestRDSConfigDump(t *testing.T) { 160 const testVersion = "test-version-rds" 161 var ( 162 listenerNames = []string{"lds.target.good:0000", "lds.target.good:1111"} 163 rdsTargets = []string{"route-config-0", "route-config-1"} 164 clusterNames = []string{"cluster-0", "cluster-1"} 165 routeRaws = make(map[string]*anypb.Any, len(rdsTargets)) 166 ) 167 168 for i := range rdsTargets { 169 routeConfigT := &v3routepb.RouteConfiguration{ 170 Name: rdsTargets[i], 171 VirtualHosts: []*v3routepb.VirtualHost{ 172 { 173 Domains: []string{listenerNames[i]}, 174 Routes: []*v3routepb.Route{{ 175 Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: ""}}, 176 Action: &v3routepb.Route_Route{ 177 Route: &v3routepb.RouteAction{ 178 ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: clusterNames[i]}, 179 }, 180 }, 181 }}, 182 }, 183 }, 184 } 185 186 routeRaws[rdsTargets[i]] = testutils.MarshalAny(routeConfigT) 187 } 188 189 client, err := xdsclient.NewWithConfigForTesting(&bootstrap.Config{ 190 BalancerName: testXDSServer, 191 Creds: grpc.WithTransportCredentials(insecure.NewCredentials()), 192 NodeProto: xdstestutils.EmptyNodeProtoV2, 193 }, defaultTestWatchExpiryTimeout) 194 if err != nil { 195 t.Fatalf("failed to create client: %v", err) 196 } 197 defer client.Close() 198 199 // Expected unknown. 200 if err := compareDump(client.DumpRDS, "", map[string]xdsclient.UpdateWithMD{}); err != nil { 201 t.Fatalf(err.Error()) 202 } 203 204 wantRequested := make(map[string]xdsclient.UpdateWithMD) 205 for _, n := range rdsTargets { 206 cancel := client.WatchRouteConfig(n, func(update xdsclient.RouteConfigUpdate, err error) {}) 207 defer cancel() 208 wantRequested[n] = xdsclient.UpdateWithMD{MD: xdsclient.UpdateMetadata{Status: xdsclient.ServiceStatusRequested}} 209 } 210 // Expected requested. 211 if err := compareDump(client.DumpRDS, "", wantRequested); err != nil { 212 t.Fatalf(err.Error()) 213 } 214 215 update0 := make(map[string]xdsclient.RouteConfigUpdate) 216 want0 := make(map[string]xdsclient.UpdateWithMD) 217 for n, r := range routeRaws { 218 update0[n] = xdsclient.RouteConfigUpdate{Raw: r} 219 want0[n] = xdsclient.UpdateWithMD{ 220 MD: xdsclient.UpdateMetadata{Version: testVersion}, 221 Raw: r, 222 } 223 } 224 client.NewRouteConfigs(update0, xdsclient.UpdateMetadata{Version: testVersion}) 225 226 // Expect ACK. 227 if err := compareDump(client.DumpRDS, testVersion, want0); err != nil { 228 t.Fatalf(err.Error()) 229 } 230 231 const nackVersion = "rds-version-nack" 232 var nackErr = fmt.Errorf("rds nack error") 233 client.NewRouteConfigs( 234 map[string]xdsclient.RouteConfigUpdate{ 235 rdsTargets[0]: {}, 236 }, 237 xdsclient.UpdateMetadata{ 238 ErrState: &xdsclient.UpdateErrorMetadata{ 239 Version: nackVersion, 240 Err: nackErr, 241 }, 242 }, 243 ) 244 245 // Expect NACK for [0], but old ACK for [1]. 246 wantDump := make(map[string]xdsclient.UpdateWithMD) 247 // Though resource 0 was NACKed, the dump should show the previous ACKed raw 248 // message, as well as the NACK error. 249 wantDump[rdsTargets[0]] = xdsclient.UpdateWithMD{ 250 MD: xdsclient.UpdateMetadata{ 251 Version: testVersion, 252 ErrState: &xdsclient.UpdateErrorMetadata{ 253 Version: nackVersion, 254 Err: nackErr, 255 }, 256 }, 257 Raw: routeRaws[rdsTargets[0]], 258 } 259 wantDump[rdsTargets[1]] = xdsclient.UpdateWithMD{ 260 MD: xdsclient.UpdateMetadata{Version: testVersion}, 261 Raw: routeRaws[rdsTargets[1]], 262 } 263 if err := compareDump(client.DumpRDS, nackVersion, wantDump); err != nil { 264 t.Fatalf(err.Error()) 265 } 266} 267 268func (s) TestCDSConfigDump(t *testing.T) { 269 const testVersion = "test-version-cds" 270 var ( 271 cdsTargets = []string{"cluster-0", "cluster-1"} 272 serviceNames = []string{"service-0", "service-1"} 273 clusterRaws = make(map[string]*anypb.Any, len(cdsTargets)) 274 ) 275 276 for i := range cdsTargets { 277 clusterT := &v3clusterpb.Cluster{ 278 Name: cdsTargets[i], 279 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 280 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 281 EdsConfig: &v3corepb.ConfigSource{ 282 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 283 Ads: &v3corepb.AggregatedConfigSource{}, 284 }, 285 }, 286 ServiceName: serviceNames[i], 287 }, 288 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 289 LrsServer: &v3corepb.ConfigSource{ 290 ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ 291 Self: &v3corepb.SelfConfigSource{}, 292 }, 293 }, 294 } 295 296 clusterRaws[cdsTargets[i]] = testutils.MarshalAny(clusterT) 297 } 298 299 client, err := xdsclient.NewWithConfigForTesting(&bootstrap.Config{ 300 BalancerName: testXDSServer, 301 Creds: grpc.WithTransportCredentials(insecure.NewCredentials()), 302 NodeProto: xdstestutils.EmptyNodeProtoV2, 303 }, defaultTestWatchExpiryTimeout) 304 if err != nil { 305 t.Fatalf("failed to create client: %v", err) 306 } 307 defer client.Close() 308 309 // Expected unknown. 310 if err := compareDump(client.DumpCDS, "", map[string]xdsclient.UpdateWithMD{}); err != nil { 311 t.Fatalf(err.Error()) 312 } 313 314 wantRequested := make(map[string]xdsclient.UpdateWithMD) 315 for _, n := range cdsTargets { 316 cancel := client.WatchCluster(n, func(update xdsclient.ClusterUpdate, err error) {}) 317 defer cancel() 318 wantRequested[n] = xdsclient.UpdateWithMD{MD: xdsclient.UpdateMetadata{Status: xdsclient.ServiceStatusRequested}} 319 } 320 // Expected requested. 321 if err := compareDump(client.DumpCDS, "", wantRequested); err != nil { 322 t.Fatalf(err.Error()) 323 } 324 325 update0 := make(map[string]xdsclient.ClusterUpdate) 326 want0 := make(map[string]xdsclient.UpdateWithMD) 327 for n, r := range clusterRaws { 328 update0[n] = xdsclient.ClusterUpdate{Raw: r} 329 want0[n] = xdsclient.UpdateWithMD{ 330 MD: xdsclient.UpdateMetadata{Version: testVersion}, 331 Raw: r, 332 } 333 } 334 client.NewClusters(update0, xdsclient.UpdateMetadata{Version: testVersion}) 335 336 // Expect ACK. 337 if err := compareDump(client.DumpCDS, testVersion, want0); err != nil { 338 t.Fatalf(err.Error()) 339 } 340 341 const nackVersion = "cds-version-nack" 342 var nackErr = fmt.Errorf("cds nack error") 343 client.NewClusters( 344 map[string]xdsclient.ClusterUpdate{ 345 cdsTargets[0]: {}, 346 }, 347 xdsclient.UpdateMetadata{ 348 ErrState: &xdsclient.UpdateErrorMetadata{ 349 Version: nackVersion, 350 Err: nackErr, 351 }, 352 }, 353 ) 354 355 // Expect NACK for [0], but old ACK for [1]. 356 wantDump := make(map[string]xdsclient.UpdateWithMD) 357 // Though resource 0 was NACKed, the dump should show the previous ACKed raw 358 // message, as well as the NACK error. 359 wantDump[cdsTargets[0]] = xdsclient.UpdateWithMD{ 360 MD: xdsclient.UpdateMetadata{ 361 Version: testVersion, 362 ErrState: &xdsclient.UpdateErrorMetadata{ 363 Version: nackVersion, 364 Err: nackErr, 365 }, 366 }, 367 Raw: clusterRaws[cdsTargets[0]], 368 } 369 wantDump[cdsTargets[1]] = xdsclient.UpdateWithMD{ 370 MD: xdsclient.UpdateMetadata{Version: testVersion}, 371 Raw: clusterRaws[cdsTargets[1]], 372 } 373 if err := compareDump(client.DumpCDS, nackVersion, wantDump); err != nil { 374 t.Fatalf(err.Error()) 375 } 376} 377 378func (s) TestEDSConfigDump(t *testing.T) { 379 const testVersion = "test-version-cds" 380 var ( 381 edsTargets = []string{"cluster-0", "cluster-1"} 382 localityNames = []string{"locality-0", "locality-1"} 383 addrs = []string{"addr0:123", "addr1:456"} 384 endpointRaws = make(map[string]*anypb.Any, len(edsTargets)) 385 ) 386 387 for i := range edsTargets { 388 clab0 := xdstestutils.NewClusterLoadAssignmentBuilder(edsTargets[i], nil) 389 clab0.AddLocality(localityNames[i], 1, 1, []string{addrs[i]}, nil) 390 claT := clab0.Build() 391 392 endpointRaws[edsTargets[i]] = testutils.MarshalAny(claT) 393 } 394 395 client, err := xdsclient.NewWithConfigForTesting(&bootstrap.Config{ 396 BalancerName: testXDSServer, 397 Creds: grpc.WithTransportCredentials(insecure.NewCredentials()), 398 NodeProto: xdstestutils.EmptyNodeProtoV2, 399 }, defaultTestWatchExpiryTimeout) 400 if err != nil { 401 t.Fatalf("failed to create client: %v", err) 402 } 403 defer client.Close() 404 405 // Expected unknown. 406 if err := compareDump(client.DumpEDS, "", map[string]xdsclient.UpdateWithMD{}); err != nil { 407 t.Fatalf(err.Error()) 408 } 409 410 wantRequested := make(map[string]xdsclient.UpdateWithMD) 411 for _, n := range edsTargets { 412 cancel := client.WatchEndpoints(n, func(update xdsclient.EndpointsUpdate, err error) {}) 413 defer cancel() 414 wantRequested[n] = xdsclient.UpdateWithMD{MD: xdsclient.UpdateMetadata{Status: xdsclient.ServiceStatusRequested}} 415 } 416 // Expected requested. 417 if err := compareDump(client.DumpEDS, "", wantRequested); err != nil { 418 t.Fatalf(err.Error()) 419 } 420 421 update0 := make(map[string]xdsclient.EndpointsUpdate) 422 want0 := make(map[string]xdsclient.UpdateWithMD) 423 for n, r := range endpointRaws { 424 update0[n] = xdsclient.EndpointsUpdate{Raw: r} 425 want0[n] = xdsclient.UpdateWithMD{ 426 MD: xdsclient.UpdateMetadata{Version: testVersion}, 427 Raw: r, 428 } 429 } 430 client.NewEndpoints(update0, xdsclient.UpdateMetadata{Version: testVersion}) 431 432 // Expect ACK. 433 if err := compareDump(client.DumpEDS, testVersion, want0); err != nil { 434 t.Fatalf(err.Error()) 435 } 436 437 const nackVersion = "eds-version-nack" 438 var nackErr = fmt.Errorf("eds nack error") 439 client.NewEndpoints( 440 map[string]xdsclient.EndpointsUpdate{ 441 edsTargets[0]: {}, 442 }, 443 xdsclient.UpdateMetadata{ 444 ErrState: &xdsclient.UpdateErrorMetadata{ 445 Version: nackVersion, 446 Err: nackErr, 447 }, 448 }, 449 ) 450 451 // Expect NACK for [0], but old ACK for [1]. 452 wantDump := make(map[string]xdsclient.UpdateWithMD) 453 // Though resource 0 was NACKed, the dump should show the previous ACKed raw 454 // message, as well as the NACK error. 455 wantDump[edsTargets[0]] = xdsclient.UpdateWithMD{ 456 MD: xdsclient.UpdateMetadata{ 457 Version: testVersion, 458 ErrState: &xdsclient.UpdateErrorMetadata{ 459 Version: nackVersion, 460 Err: nackErr, 461 }, 462 }, 463 Raw: endpointRaws[edsTargets[0]], 464 } 465 wantDump[edsTargets[1]] = xdsclient.UpdateWithMD{ 466 MD: xdsclient.UpdateMetadata{Version: testVersion}, 467 Raw: endpointRaws[edsTargets[1]], 468 } 469 if err := compareDump(client.DumpEDS, nackVersion, wantDump); err != nil { 470 t.Fatalf(err.Error()) 471 } 472} 473 474func compareDump(dumpFunc func() (string, map[string]xdsclient.UpdateWithMD), wantVersion string, wantDump interface{}) error { 475 v, dump := dumpFunc() 476 if v != wantVersion { 477 return fmt.Errorf("Dump() returned version %q, want %q", v, wantVersion) 478 } 479 cmpOpts := cmp.Options{ 480 cmpopts.EquateEmpty(), 481 cmp.Comparer(func(a, b time.Time) bool { return true }), 482 cmp.Comparer(func(x, y error) bool { 483 if x == nil || y == nil { 484 return x == nil && y == nil 485 } 486 return x.Error() == y.Error() 487 }), 488 protocmp.Transform(), 489 } 490 if diff := cmp.Diff(dump, wantDump, cmpOpts); diff != "" { 491 return fmt.Errorf("Dump() returned unexpected dump, diff (-got +want): %s", diff) 492 } 493 return nil 494} 495