google.golang.org/grpc@v1.72.2/xds/internal/xdsclient/tests/dump_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 "encoding/json" 24 "fmt" 25 "slices" 26 "strings" 27 "testing" 28 "time" 29 30 "github.com/google/go-cmp/cmp" 31 "github.com/google/uuid" 32 "google.golang.org/grpc" 33 "google.golang.org/grpc/internal/pretty" 34 "google.golang.org/grpc/internal/testutils" 35 "google.golang.org/grpc/internal/testutils/xds/e2e" 36 "google.golang.org/grpc/internal/xds/bootstrap" 37 "google.golang.org/grpc/xds/internal/xdsclient" 38 "google.golang.org/grpc/xds/internal/xdsclient/xdsresource" 39 "google.golang.org/protobuf/testing/protocmp" 40 "google.golang.org/protobuf/types/known/anypb" 41 42 v3adminpb "github.com/envoyproxy/go-control-plane/envoy/admin/v3" 43 v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" 44 v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 45 v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" 46 v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" 47 v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" 48 v3routerpb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" 49 v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" 50 v3statuspb "github.com/envoyproxy/go-control-plane/envoy/service/status/v3" 51 "github.com/envoyproxy/go-control-plane/pkg/wellknown" 52 ) 53 54 func makeGenericXdsConfig(typeURL, name, version string, status v3adminpb.ClientResourceStatus, config *anypb.Any, failure *v3adminpb.UpdateFailureState) *v3statuspb.ClientConfig_GenericXdsConfig { 55 return &v3statuspb.ClientConfig_GenericXdsConfig{ 56 TypeUrl: typeURL, 57 Name: name, 58 VersionInfo: version, 59 ClientStatus: status, 60 XdsConfig: config, 61 ErrorState: failure, 62 } 63 } 64 65 func checkResourceDump(ctx context.Context, want *v3statuspb.ClientStatusResponse, pool *xdsclient.Pool) error { 66 var cmpOpts = cmp.Options{ 67 protocmp.Transform(), 68 protocmp.IgnoreFields((*v3statuspb.ClientConfig_GenericXdsConfig)(nil), "last_updated"), 69 protocmp.IgnoreFields((*v3adminpb.UpdateFailureState)(nil), "last_update_attempt", "details"), 70 } 71 72 var lastErr error 73 for ; ctx.Err() == nil; <-time.After(defaultTestShortTimeout) { 74 got := pool.DumpResources() 75 // Sort the client configs based on the `client_scope` field. 76 slices.SortFunc(got.GetConfig(), func(a, b *v3statuspb.ClientConfig) int { 77 return strings.Compare(a.ClientScope, b.ClientScope) 78 }) 79 // Sort the resource configs based on the type_url and name fields. 80 for _, cc := range got.GetConfig() { 81 slices.SortFunc(cc.GetGenericXdsConfigs(), func(a, b *v3statuspb.ClientConfig_GenericXdsConfig) int { 82 if strings.Compare(a.TypeUrl, b.TypeUrl) == 0 { 83 return strings.Compare(a.Name, b.Name) 84 } 85 return strings.Compare(a.TypeUrl, b.TypeUrl) 86 }) 87 } 88 diff := cmp.Diff(want, got, cmpOpts) 89 if diff == "" { 90 return nil 91 } 92 lastErr = fmt.Errorf("received unexpected resource dump, diff (-got, +want):\n%s, got: %s\n want:%s", diff, pretty.ToJSON(got), pretty.ToJSON(want)) 93 } 94 return fmt.Errorf("timeout when waiting for resource dump to reach expected state: %v", lastErr) 95 } 96 97 // Tests the scenario where there are multiple xDS clients talking to the same 98 // management server, and requesting the same set of resources. Verifies that 99 // under all circumstances, both xDS clients receive the same configuration from 100 // the server. 101 func (s) TestDumpResources_ManyToOne(t *testing.T) { 102 // Initialize the xDS resources to be used in this test. 103 ldsTargets := []string{"lds.target.good:0000", "lds.target.good:1111"} 104 rdsTargets := []string{"route-config-0", "route-config-1"} 105 cdsTargets := []string{"cluster-0", "cluster-1"} 106 edsTargets := []string{"endpoints-0", "endpoints-1"} 107 listeners := make([]*v3listenerpb.Listener, len(ldsTargets)) 108 listenerAnys := make([]*anypb.Any, len(ldsTargets)) 109 for i := range ldsTargets { 110 listeners[i] = e2e.DefaultClientListener(ldsTargets[i], rdsTargets[i]) 111 listenerAnys[i] = testutils.MarshalAny(t, listeners[i]) 112 } 113 routes := make([]*v3routepb.RouteConfiguration, len(rdsTargets)) 114 routeAnys := make([]*anypb.Any, len(rdsTargets)) 115 for i := range rdsTargets { 116 routes[i] = e2e.DefaultRouteConfig(rdsTargets[i], ldsTargets[i], cdsTargets[i]) 117 routeAnys[i] = testutils.MarshalAny(t, routes[i]) 118 } 119 clusters := make([]*v3clusterpb.Cluster, len(cdsTargets)) 120 clusterAnys := make([]*anypb.Any, len(cdsTargets)) 121 for i := range cdsTargets { 122 clusters[i] = e2e.DefaultCluster(cdsTargets[i], edsTargets[i], e2e.SecurityLevelNone) 123 clusterAnys[i] = testutils.MarshalAny(t, clusters[i]) 124 } 125 endpoints := make([]*v3endpointpb.ClusterLoadAssignment, len(edsTargets)) 126 endpointAnys := make([]*anypb.Any, len(edsTargets)) 127 ips := []string{"0.0.0.0", "1.1.1.1"} 128 ports := []uint32{123, 456} 129 for i := range edsTargets { 130 endpoints[i] = e2e.DefaultEndpoint(edsTargets[i], ips[i], ports[i:i+1]) 131 endpointAnys[i] = testutils.MarshalAny(t, endpoints[i]) 132 } 133 134 // Spin up an xDS management server on a local port. 135 mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 136 137 nodeID := uuid.New().String() 138 bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address) 139 config, err := bootstrap.NewConfigFromContents(bc) 140 if err != nil { 141 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err) 142 } 143 pool := xdsclient.NewPool(config) 144 // Create two xDS clients with the above bootstrap contents. 145 client1Name := t.Name() + "-1" 146 client1, close1, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 147 Name: client1Name, 148 }) 149 if err != nil { 150 t.Fatalf("Failed to create xDS client: %v", err) 151 } 152 defer close1() 153 client2Name := t.Name() + "-2" 154 client2, close2, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 155 Name: client2Name, 156 }) 157 if err != nil { 158 t.Fatalf("Failed to create xDS client: %v", err) 159 } 160 defer close2() 161 162 // Dump resources and expect empty configs. 163 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 164 defer cancel() 165 wantNode := &v3corepb.Node{ 166 Id: nodeID, 167 UserAgentName: "gRPC Go", 168 UserAgentVersionType: &v3corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version}, 169 ClientFeatures: []string{"envoy.lb.does_not_support_overprovisioning", "xds.config.resource-in-sotw"}, 170 } 171 wantResp := &v3statuspb.ClientStatusResponse{ 172 Config: []*v3statuspb.ClientConfig{ 173 { 174 Node: wantNode, 175 ClientScope: client1Name, 176 }, 177 { 178 Node: wantNode, 179 ClientScope: client2Name, 180 }, 181 }, 182 } 183 if err := checkResourceDump(ctx, wantResp, pool); err != nil { 184 t.Fatal(err) 185 } 186 187 // Register watches, dump resources and expect configs in requested state. 188 for _, xdsC := range []xdsclient.XDSClient{client1, client2} { 189 for _, target := range ldsTargets { 190 xdsresource.WatchListener(xdsC, target, noopListenerWatcher{}) 191 } 192 for _, target := range rdsTargets { 193 xdsresource.WatchRouteConfig(xdsC, target, noopRouteConfigWatcher{}) 194 } 195 for _, target := range cdsTargets { 196 xdsresource.WatchCluster(xdsC, target, noopClusterWatcher{}) 197 } 198 for _, target := range edsTargets { 199 xdsresource.WatchEndpoints(xdsC, target, noopEndpointsWatcher{}) 200 } 201 } 202 wantConfigs := []*v3statuspb.ClientConfig_GenericXdsConfig{ 203 makeGenericXdsConfig("type.googleapis.com/envoy.config.cluster.v3.Cluster", cdsTargets[0], "", v3adminpb.ClientResourceStatus_REQUESTED, nil, nil), 204 makeGenericXdsConfig("type.googleapis.com/envoy.config.cluster.v3.Cluster", cdsTargets[1], "", v3adminpb.ClientResourceStatus_REQUESTED, nil, nil), 205 makeGenericXdsConfig("type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", edsTargets[0], "", v3adminpb.ClientResourceStatus_REQUESTED, nil, nil), 206 makeGenericXdsConfig("type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", edsTargets[1], "", v3adminpb.ClientResourceStatus_REQUESTED, nil, nil), 207 makeGenericXdsConfig("type.googleapis.com/envoy.config.listener.v3.Listener", ldsTargets[0], "", v3adminpb.ClientResourceStatus_REQUESTED, nil, nil), 208 makeGenericXdsConfig("type.googleapis.com/envoy.config.listener.v3.Listener", ldsTargets[1], "", v3adminpb.ClientResourceStatus_REQUESTED, nil, nil), 209 makeGenericXdsConfig("type.googleapis.com/envoy.config.route.v3.RouteConfiguration", rdsTargets[0], "", v3adminpb.ClientResourceStatus_REQUESTED, nil, nil), 210 makeGenericXdsConfig("type.googleapis.com/envoy.config.route.v3.RouteConfiguration", rdsTargets[1], "", v3adminpb.ClientResourceStatus_REQUESTED, nil, nil), 211 } 212 wantResp = &v3statuspb.ClientStatusResponse{ 213 Config: []*v3statuspb.ClientConfig{ 214 { 215 Node: wantNode, 216 GenericXdsConfigs: wantConfigs, 217 ClientScope: client1Name, 218 }, 219 { 220 Node: wantNode, 221 GenericXdsConfigs: wantConfigs, 222 ClientScope: client2Name, 223 }, 224 }, 225 } 226 if err := checkResourceDump(ctx, wantResp, pool); err != nil { 227 t.Fatal(err) 228 } 229 230 // Configure the resources on the management server. 231 if err := mgmtServer.Update(ctx, e2e.UpdateOptions{ 232 NodeID: nodeID, 233 Listeners: listeners, 234 Routes: routes, 235 Clusters: clusters, 236 Endpoints: endpoints, 237 }); err != nil { 238 t.Fatal(err) 239 } 240 241 // Dump resources and expect ACK configs. 242 wantConfigs = []*v3statuspb.ClientConfig_GenericXdsConfig{ 243 makeGenericXdsConfig("type.googleapis.com/envoy.config.cluster.v3.Cluster", cdsTargets[0], "1", v3adminpb.ClientResourceStatus_ACKED, clusterAnys[0], nil), 244 makeGenericXdsConfig("type.googleapis.com/envoy.config.cluster.v3.Cluster", cdsTargets[1], "1", v3adminpb.ClientResourceStatus_ACKED, clusterAnys[1], nil), 245 makeGenericXdsConfig("type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", edsTargets[0], "1", v3adminpb.ClientResourceStatus_ACKED, endpointAnys[0], nil), 246 makeGenericXdsConfig("type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", edsTargets[1], "1", v3adminpb.ClientResourceStatus_ACKED, endpointAnys[1], nil), 247 makeGenericXdsConfig("type.googleapis.com/envoy.config.listener.v3.Listener", ldsTargets[0], "1", v3adminpb.ClientResourceStatus_ACKED, listenerAnys[0], nil), 248 makeGenericXdsConfig("type.googleapis.com/envoy.config.listener.v3.Listener", ldsTargets[1], "1", v3adminpb.ClientResourceStatus_ACKED, listenerAnys[1], nil), 249 makeGenericXdsConfig("type.googleapis.com/envoy.config.route.v3.RouteConfiguration", rdsTargets[0], "1", v3adminpb.ClientResourceStatus_ACKED, routeAnys[0], nil), 250 makeGenericXdsConfig("type.googleapis.com/envoy.config.route.v3.RouteConfiguration", rdsTargets[1], "1", v3adminpb.ClientResourceStatus_ACKED, routeAnys[1], nil), 251 } 252 wantResp = &v3statuspb.ClientStatusResponse{ 253 Config: []*v3statuspb.ClientConfig{ 254 { 255 Node: wantNode, 256 GenericXdsConfigs: wantConfigs, 257 ClientScope: client1Name, 258 }, 259 { 260 Node: wantNode, 261 GenericXdsConfigs: wantConfigs, 262 ClientScope: client2Name, 263 }, 264 }, 265 } 266 if err := checkResourceDump(ctx, wantResp, pool); err != nil { 267 t.Fatal(err) 268 } 269 270 // Update the first resource of each type in the management server to a 271 // value which is expected to be NACK'ed by the xDS client. 272 listeners[0] = func() *v3listenerpb.Listener { 273 hcm := testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{ 274 HttpFilters: []*v3httppb.HttpFilter{e2e.HTTPFilter("router", &v3routerpb.Router{})}, 275 }) 276 return &v3listenerpb.Listener{ 277 Name: ldsTargets[0], 278 ApiListener: &v3listenerpb.ApiListener{ApiListener: hcm}, 279 FilterChains: []*v3listenerpb.FilterChain{{ 280 Name: "filter-chain-name", 281 Filters: []*v3listenerpb.Filter{{ 282 Name: wellknown.HTTPConnectionManager, 283 ConfigType: &v3listenerpb.Filter_TypedConfig{TypedConfig: hcm}, 284 }}, 285 }}, 286 } 287 }() 288 routes[0].VirtualHosts = []*v3routepb.VirtualHost{{Routes: []*v3routepb.Route{{}}}} 289 clusters[0].ClusterDiscoveryType = &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_STATIC} 290 endpoints[0].Endpoints = []*v3endpointpb.LocalityLbEndpoints{{}} 291 if err := mgmtServer.Update(ctx, e2e.UpdateOptions{ 292 NodeID: nodeID, 293 Listeners: listeners, 294 Routes: routes, 295 Clusters: clusters, 296 Endpoints: endpoints, 297 SkipValidation: true, 298 }); err != nil { 299 t.Fatal(err) 300 } 301 302 wantConfigs = []*v3statuspb.ClientConfig_GenericXdsConfig{ 303 makeGenericXdsConfig("type.googleapis.com/envoy.config.cluster.v3.Cluster", cdsTargets[0], "1", v3adminpb.ClientResourceStatus_NACKED, clusterAnys[0], &v3adminpb.UpdateFailureState{VersionInfo: "2"}), 304 makeGenericXdsConfig("type.googleapis.com/envoy.config.cluster.v3.Cluster", cdsTargets[1], "2", v3adminpb.ClientResourceStatus_ACKED, clusterAnys[1], nil), 305 makeGenericXdsConfig("type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", edsTargets[0], "1", v3adminpb.ClientResourceStatus_NACKED, endpointAnys[0], &v3adminpb.UpdateFailureState{VersionInfo: "2"}), 306 makeGenericXdsConfig("type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", edsTargets[1], "2", v3adminpb.ClientResourceStatus_ACKED, endpointAnys[1], nil), 307 makeGenericXdsConfig("type.googleapis.com/envoy.config.listener.v3.Listener", ldsTargets[0], "1", v3adminpb.ClientResourceStatus_NACKED, listenerAnys[0], &v3adminpb.UpdateFailureState{VersionInfo: "2"}), 308 makeGenericXdsConfig("type.googleapis.com/envoy.config.listener.v3.Listener", ldsTargets[1], "2", v3adminpb.ClientResourceStatus_ACKED, listenerAnys[1], nil), 309 makeGenericXdsConfig("type.googleapis.com/envoy.config.route.v3.RouteConfiguration", rdsTargets[0], "1", v3adminpb.ClientResourceStatus_NACKED, routeAnys[0], &v3adminpb.UpdateFailureState{VersionInfo: "2"}), 310 makeGenericXdsConfig("type.googleapis.com/envoy.config.route.v3.RouteConfiguration", rdsTargets[1], "2", v3adminpb.ClientResourceStatus_ACKED, routeAnys[1], nil), 311 } 312 wantResp = &v3statuspb.ClientStatusResponse{ 313 Config: []*v3statuspb.ClientConfig{ 314 { 315 Node: wantNode, 316 GenericXdsConfigs: wantConfigs, 317 ClientScope: client1Name, 318 }, 319 { 320 Node: wantNode, 321 GenericXdsConfigs: wantConfigs, 322 ClientScope: client2Name, 323 }, 324 }, 325 } 326 if err := checkResourceDump(ctx, wantResp, pool); err != nil { 327 t.Fatal(err) 328 } 329 } 330 331 // Tests the scenario where there are multiple xDS client talking to different 332 // management server, and requesting different set of resources. 333 func (s) TestDumpResources_ManyToMany(t *testing.T) { 334 // Initialize the xDS resources to be used in this test: 335 // - The first xDS client watches old style resource names, and thereby 336 // requests these resources from the top-level xDS server. 337 // - The second xDS client watches new style resource names with a non-empty 338 // authority, and thereby requests these resources from the server 339 // configuration for that authority. 340 authority := strings.Join(strings.Split(t.Name(), "/"), "") 341 ldsTargets := []string{ 342 "lds.target.good:0000", 343 fmt.Sprintf("xdstp://%s/envoy.config.listener.v3.Listener/lds.targer.good:1111", authority), 344 } 345 rdsTargets := []string{ 346 "route-config-0", 347 fmt.Sprintf("xdstp://%s/envoy.config.route.v3.RouteConfiguration/route-config-1", authority), 348 } 349 cdsTargets := []string{ 350 "cluster-0", 351 fmt.Sprintf("xdstp://%s/envoy.config.cluster.v3.Cluster/cluster-1", authority), 352 } 353 edsTargets := []string{ 354 "endpoints-0", 355 fmt.Sprintf("xdstp://%s/envoy.config.endpoint.v3.ClusterLoadAssignment/endpoints-1", authority), 356 } 357 listeners := make([]*v3listenerpb.Listener, len(ldsTargets)) 358 listenerAnys := make([]*anypb.Any, len(ldsTargets)) 359 for i := range ldsTargets { 360 listeners[i] = e2e.DefaultClientListener(ldsTargets[i], rdsTargets[i]) 361 listenerAnys[i] = testutils.MarshalAny(t, listeners[i]) 362 } 363 routes := make([]*v3routepb.RouteConfiguration, len(rdsTargets)) 364 routeAnys := make([]*anypb.Any, len(rdsTargets)) 365 for i := range rdsTargets { 366 routes[i] = e2e.DefaultRouteConfig(rdsTargets[i], ldsTargets[i], cdsTargets[i]) 367 routeAnys[i] = testutils.MarshalAny(t, routes[i]) 368 } 369 clusters := make([]*v3clusterpb.Cluster, len(cdsTargets)) 370 clusterAnys := make([]*anypb.Any, len(cdsTargets)) 371 for i := range cdsTargets { 372 clusters[i] = e2e.DefaultCluster(cdsTargets[i], edsTargets[i], e2e.SecurityLevelNone) 373 clusterAnys[i] = testutils.MarshalAny(t, clusters[i]) 374 } 375 endpoints := make([]*v3endpointpb.ClusterLoadAssignment, len(edsTargets)) 376 endpointAnys := make([]*anypb.Any, len(edsTargets)) 377 ips := []string{"0.0.0.0", "1.1.1.1"} 378 ports := []uint32{123, 456} 379 for i := range edsTargets { 380 endpoints[i] = e2e.DefaultEndpoint(edsTargets[i], ips[i], ports[i:i+1]) 381 endpointAnys[i] = testutils.MarshalAny(t, endpoints[i]) 382 } 383 384 // Start two management servers. 385 mgmtServer1 := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 386 mgmtServer2 := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 387 388 // The first of the above management servers will be the top-level xDS 389 // server in the bootstrap configuration, and the second will be the xDS 390 // server corresponding to the test authority. 391 nodeID := uuid.New().String() 392 bc, err := bootstrap.NewContentsForTesting(bootstrap.ConfigOptionsForTesting{ 393 Servers: []byte(fmt.Sprintf(`[{ 394 "server_uri": %q, 395 "channel_creds": [{"type": "insecure"}] 396 }]`, mgmtServer1.Address)), 397 Node: []byte(fmt.Sprintf(`{"id": "%s"}`, nodeID)), 398 Authorities: map[string]json.RawMessage{ 399 authority: []byte(fmt.Sprintf(`{ 400 "xds_servers": [{ 401 "server_uri": %q, 402 "channel_creds": [{"type": "insecure"}] 403 }]}`, mgmtServer2.Address)), 404 }, 405 }) 406 if err != nil { 407 t.Fatalf("Failed to create bootstrap configuration: %v", err) 408 } 409 config, err := bootstrap.NewConfigFromContents(bc) 410 if err != nil { 411 t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bc), err) 412 } 413 pool := xdsclient.NewPool(config) 414 415 // Create two xDS clients with the above bootstrap contents. 416 client1Name := t.Name() + "-1" 417 client1, close1, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 418 Name: client1Name, 419 }) 420 if err != nil { 421 t.Fatalf("Failed to create xDS client: %v", err) 422 } 423 defer close1() 424 client2Name := t.Name() + "-2" 425 client2, close2, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{ 426 Name: client2Name, 427 }) 428 if err != nil { 429 t.Fatalf("Failed to create xDS client: %v", err) 430 } 431 defer close2() 432 433 // Check the resource dump before configuring resources on the management server. 434 // Dump resources and expect empty configs. 435 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 436 defer cancel() 437 wantNode := &v3corepb.Node{ 438 Id: nodeID, 439 UserAgentName: "gRPC Go", 440 UserAgentVersionType: &v3corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version}, 441 ClientFeatures: []string{"envoy.lb.does_not_support_overprovisioning", "xds.config.resource-in-sotw"}, 442 } 443 wantResp := &v3statuspb.ClientStatusResponse{ 444 Config: []*v3statuspb.ClientConfig{ 445 { 446 Node: wantNode, 447 ClientScope: client1Name, 448 }, 449 { 450 Node: wantNode, 451 ClientScope: client2Name, 452 }, 453 }, 454 } 455 if err := checkResourceDump(ctx, wantResp, pool); err != nil { 456 t.Fatal(err) 457 } 458 459 // Register watches, the first xDS client watches old style resource names, 460 // while the second xDS client watches new style resource names. 461 xdsresource.WatchListener(client1, ldsTargets[0], noopListenerWatcher{}) 462 xdsresource.WatchRouteConfig(client1, rdsTargets[0], noopRouteConfigWatcher{}) 463 xdsresource.WatchCluster(client1, cdsTargets[0], noopClusterWatcher{}) 464 xdsresource.WatchEndpoints(client1, edsTargets[0], noopEndpointsWatcher{}) 465 xdsresource.WatchListener(client2, ldsTargets[1], noopListenerWatcher{}) 466 xdsresource.WatchRouteConfig(client2, rdsTargets[1], noopRouteConfigWatcher{}) 467 xdsresource.WatchCluster(client2, cdsTargets[1], noopClusterWatcher{}) 468 xdsresource.WatchEndpoints(client2, edsTargets[1], noopEndpointsWatcher{}) 469 470 // Check the resource dump. Both clients should have all resources in 471 // REQUESTED state. 472 wantConfigs1 := []*v3statuspb.ClientConfig_GenericXdsConfig{ 473 makeGenericXdsConfig("type.googleapis.com/envoy.config.cluster.v3.Cluster", cdsTargets[0], "", v3adminpb.ClientResourceStatus_REQUESTED, nil, nil), 474 makeGenericXdsConfig("type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", edsTargets[0], "", v3adminpb.ClientResourceStatus_REQUESTED, nil, nil), 475 makeGenericXdsConfig("type.googleapis.com/envoy.config.listener.v3.Listener", ldsTargets[0], "", v3adminpb.ClientResourceStatus_REQUESTED, nil, nil), 476 makeGenericXdsConfig("type.googleapis.com/envoy.config.route.v3.RouteConfiguration", rdsTargets[0], "", v3adminpb.ClientResourceStatus_REQUESTED, nil, nil), 477 } 478 wantConfigs2 := []*v3statuspb.ClientConfig_GenericXdsConfig{ 479 makeGenericXdsConfig("type.googleapis.com/envoy.config.cluster.v3.Cluster", cdsTargets[1], "", v3adminpb.ClientResourceStatus_REQUESTED, nil, nil), 480 makeGenericXdsConfig("type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", edsTargets[1], "", v3adminpb.ClientResourceStatus_REQUESTED, nil, nil), 481 makeGenericXdsConfig("type.googleapis.com/envoy.config.listener.v3.Listener", ldsTargets[1], "", v3adminpb.ClientResourceStatus_REQUESTED, nil, nil), 482 makeGenericXdsConfig("type.googleapis.com/envoy.config.route.v3.RouteConfiguration", rdsTargets[1], "", v3adminpb.ClientResourceStatus_REQUESTED, nil, nil), 483 } 484 wantResp = &v3statuspb.ClientStatusResponse{ 485 Config: []*v3statuspb.ClientConfig{ 486 { 487 Node: wantNode, 488 GenericXdsConfigs: wantConfigs1, 489 ClientScope: client1Name, 490 }, 491 { 492 Node: wantNode, 493 GenericXdsConfigs: wantConfigs2, 494 ClientScope: client2Name, 495 }, 496 }, 497 } 498 if err := checkResourceDump(ctx, wantResp, pool); err != nil { 499 t.Fatal(err) 500 } 501 502 // Configure resources on the first management server. 503 if err := mgmtServer1.Update(ctx, e2e.UpdateOptions{ 504 NodeID: nodeID, 505 Listeners: listeners[:1], 506 Routes: routes[:1], 507 Clusters: clusters[:1], 508 Endpoints: endpoints[:1], 509 }); err != nil { 510 t.Fatal(err) 511 } 512 513 // Check the resource dump. One client should have resources in ACKED state, 514 // while the other should still have resources in REQUESTED state. 515 wantConfigs1 = []*v3statuspb.ClientConfig_GenericXdsConfig{ 516 makeGenericXdsConfig("type.googleapis.com/envoy.config.cluster.v3.Cluster", cdsTargets[0], "1", v3adminpb.ClientResourceStatus_ACKED, clusterAnys[0], nil), 517 makeGenericXdsConfig("type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", edsTargets[0], "1", v3adminpb.ClientResourceStatus_ACKED, endpointAnys[0], nil), 518 makeGenericXdsConfig("type.googleapis.com/envoy.config.listener.v3.Listener", ldsTargets[0], "1", v3adminpb.ClientResourceStatus_ACKED, listenerAnys[0], nil), 519 makeGenericXdsConfig("type.googleapis.com/envoy.config.route.v3.RouteConfiguration", rdsTargets[0], "1", v3adminpb.ClientResourceStatus_ACKED, routeAnys[0], nil), 520 } 521 wantResp = &v3statuspb.ClientStatusResponse{ 522 Config: []*v3statuspb.ClientConfig{ 523 { 524 Node: wantNode, 525 GenericXdsConfigs: wantConfigs1, 526 ClientScope: client1Name, 527 }, 528 { 529 Node: wantNode, 530 GenericXdsConfigs: wantConfigs2, 531 ClientScope: client2Name, 532 }, 533 }, 534 } 535 if err := checkResourceDump(ctx, wantResp, pool); err != nil { 536 t.Fatal(err) 537 } 538 539 // Configure resources on the second management server. 540 if err := mgmtServer2.Update(ctx, e2e.UpdateOptions{ 541 NodeID: nodeID, 542 Listeners: listeners[1:], 543 Routes: routes[1:], 544 Clusters: clusters[1:], 545 Endpoints: endpoints[1:], 546 }); err != nil { 547 t.Fatal(err) 548 } 549 550 // Check the resource dump. Both clients should have appropriate resources 551 // in REQUESTED state. 552 wantConfigs2 = []*v3statuspb.ClientConfig_GenericXdsConfig{ 553 makeGenericXdsConfig("type.googleapis.com/envoy.config.cluster.v3.Cluster", cdsTargets[1], "1", v3adminpb.ClientResourceStatus_ACKED, clusterAnys[1], nil), 554 makeGenericXdsConfig("type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", edsTargets[1], "1", v3adminpb.ClientResourceStatus_ACKED, endpointAnys[1], nil), 555 makeGenericXdsConfig("type.googleapis.com/envoy.config.listener.v3.Listener", ldsTargets[1], "1", v3adminpb.ClientResourceStatus_ACKED, listenerAnys[1], nil), 556 makeGenericXdsConfig("type.googleapis.com/envoy.config.route.v3.RouteConfiguration", rdsTargets[1], "1", v3adminpb.ClientResourceStatus_ACKED, routeAnys[1], nil), 557 } 558 wantResp = &v3statuspb.ClientStatusResponse{ 559 Config: []*v3statuspb.ClientConfig{ 560 { 561 Node: wantNode, 562 GenericXdsConfigs: wantConfigs1, 563 ClientScope: client1Name, 564 }, 565 { 566 Node: wantNode, 567 GenericXdsConfigs: wantConfigs2, 568 ClientScope: client2Name, 569 }, 570 }, 571 } 572 if err := checkResourceDump(ctx, wantResp, pool); err != nil { 573 t.Fatal(err) 574 } 575 }