google.golang.org/grpc@v1.62.1/xds/internal/xdsclient/tests/federation_watchers_test.go (about) 1 /* 2 * 3 * Copyright 2021 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 package xdsclient_test 19 20 import ( 21 "context" 22 "fmt" 23 "testing" 24 25 "github.com/google/uuid" 26 "google.golang.org/grpc/internal/testutils/xds/bootstrap" 27 "google.golang.org/grpc/internal/testutils/xds/e2e" 28 "google.golang.org/grpc/xds/internal" 29 "google.golang.org/grpc/xds/internal/xdsclient" 30 "google.golang.org/grpc/xds/internal/xdsclient/xdsresource" 31 32 v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" 33 v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" 34 v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" 35 v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" 36 ) 37 38 const testNonDefaultAuthority = "non-default-authority" 39 40 // setupForFederationWatchersTest spins up two management servers, one for the 41 // default (empty) authority and another for a non-default authority. 42 // 43 // Returns the management server associated with the non-default authority, the 44 // nodeID to use, and the xDS client. 45 func setupForFederationWatchersTest(t *testing.T) (*e2e.ManagementServer, string, xdsclient.XDSClient) { 46 // Start a management server as the default authority. 47 serverDefaultAuthority, err := e2e.StartManagementServer(e2e.ManagementServerOptions{}) 48 if err != nil { 49 t.Fatalf("Failed to spin up the xDS management server: %v", err) 50 } 51 t.Cleanup(serverDefaultAuthority.Stop) 52 53 // Start another management server as the other authority. 54 serverNonDefaultAuthority, err := e2e.StartManagementServer(e2e.ManagementServerOptions{}) 55 if err != nil { 56 t.Fatalf("Failed to spin up the xDS management server: %v", err) 57 } 58 t.Cleanup(serverNonDefaultAuthority.Stop) 59 60 nodeID := uuid.New().String() 61 bootstrapContents, err := bootstrap.Contents(bootstrap.Options{ 62 NodeID: nodeID, 63 ServerURI: serverDefaultAuthority.Address, 64 ServerListenerResourceNameTemplate: e2e.ServerListenerResourceNameTemplate, 65 // Specify the address of the non-default authority. 66 Authorities: map[string]string{testNonDefaultAuthority: serverNonDefaultAuthority.Address}, 67 }) 68 if err != nil { 69 t.Fatalf("Failed to create bootstrap file: %v", err) 70 } 71 // Create an xDS client with the above bootstrap contents. 72 client, close, err := xdsclient.NewWithBootstrapContentsForTesting(bootstrapContents) 73 if err != nil { 74 t.Fatalf("Failed to create xDS client: %v", err) 75 } 76 t.Cleanup(close) 77 return serverNonDefaultAuthority, nodeID, client 78 } 79 80 // TestFederation_ListenerResourceContextParamOrder covers the case of watching 81 // a Listener resource with the new style resource name and context parameters. 82 // The test registers watches for two resources which differ only in the order 83 // of context parameters in their URI. The server is configured to respond with 84 // a single resource with canonicalized context parameters. The test verifies 85 // that both watchers are notified. 86 func (s) TestFederation_ListenerResourceContextParamOrder(t *testing.T) { 87 serverNonDefaultAuthority, nodeID, client := setupForFederationWatchersTest(t) 88 89 var ( 90 // Two resource names only differ in context parameter order. 91 resourceName1 = fmt.Sprintf("xdstp://%s/envoy.config.listener.v3.Listener/xdsclient-test-lds-resource?a=1&b=2", testNonDefaultAuthority) 92 resourceName2 = fmt.Sprintf("xdstp://%s/envoy.config.listener.v3.Listener/xdsclient-test-lds-resource?b=2&a=1", testNonDefaultAuthority) 93 ) 94 95 // Register two watches for listener resources with the same query string, 96 // but context parameters in different order. 97 lw1 := newListenerWatcher() 98 ldsCancel1 := xdsresource.WatchListener(client, resourceName1, lw1) 99 defer ldsCancel1() 100 lw2 := newListenerWatcher() 101 ldsCancel2 := xdsresource.WatchListener(client, resourceName2, lw2) 102 defer ldsCancel2() 103 104 // Configure the management server for the non-default authority to return a 105 // single listener resource, corresponding to the watches registered above. 106 resources := e2e.UpdateOptions{ 107 NodeID: nodeID, 108 Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(resourceName1, "rds-resource")}, 109 SkipValidation: true, 110 } 111 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 112 defer cancel() 113 if err := serverNonDefaultAuthority.Update(ctx, resources); err != nil { 114 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 115 } 116 117 wantUpdate := listenerUpdateErrTuple{ 118 update: xdsresource.ListenerUpdate{ 119 RouteConfigName: "rds-resource", 120 HTTPFilters: []xdsresource.HTTPFilter{{Name: "router"}}, 121 }, 122 } 123 // Verify the contents of the received update. 124 if err := verifyListenerUpdate(ctx, lw1.updateCh, wantUpdate); err != nil { 125 t.Fatal(err) 126 } 127 if err := verifyListenerUpdate(ctx, lw2.updateCh, wantUpdate); err != nil { 128 t.Fatal(err) 129 } 130 } 131 132 // TestFederation_RouteConfigResourceContextParamOrder covers the case of 133 // watching a RouteConfiguration resource with the new style resource name and 134 // context parameters. The test registers watches for two resources which 135 // differ only in the order of context parameters in their URI. The server is 136 // configured to respond with a single resource with canonicalized context 137 // parameters. The test verifies that both watchers are notified. 138 func (s) TestFederation_RouteConfigResourceContextParamOrder(t *testing.T) { 139 serverNonDefaultAuthority, nodeID, client := setupForFederationWatchersTest(t) 140 141 var ( 142 // Two resource names only differ in context parameter order. 143 resourceName1 = fmt.Sprintf("xdstp://%s/envoy.config.route.v3.RouteConfiguration/xdsclient-test-rds-resource?a=1&b=2", testNonDefaultAuthority) 144 resourceName2 = fmt.Sprintf("xdstp://%s/envoy.config.route.v3.RouteConfiguration/xdsclient-test-rds-resource?b=2&a=1", testNonDefaultAuthority) 145 ) 146 147 // Register two watches for route configuration resources with the same 148 // query string, but context parameters in different order. 149 rw1 := newRouteConfigWatcher() 150 rdsCancel1 := xdsresource.WatchRouteConfig(client, resourceName1, rw1) 151 defer rdsCancel1() 152 rw2 := newRouteConfigWatcher() 153 rdsCancel2 := xdsresource.WatchRouteConfig(client, resourceName2, rw2) 154 defer rdsCancel2() 155 156 // Configure the management server for the non-default authority to return a 157 // single route config resource, corresponding to the watches registered. 158 resources := e2e.UpdateOptions{ 159 NodeID: nodeID, 160 Routes: []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(resourceName1, "listener-resource", "cluster-resource")}, 161 SkipValidation: true, 162 } 163 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 164 defer cancel() 165 if err := serverNonDefaultAuthority.Update(ctx, resources); err != nil { 166 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 167 } 168 169 wantUpdate := routeConfigUpdateErrTuple{ 170 update: xdsresource.RouteConfigUpdate{ 171 VirtualHosts: []*xdsresource.VirtualHost{ 172 { 173 Domains: []string{"listener-resource"}, 174 Routes: []*xdsresource.Route{ 175 { 176 Prefix: newStringP("/"), 177 ActionType: xdsresource.RouteActionRoute, 178 WeightedClusters: map[string]xdsresource.WeightedCluster{"cluster-resource": {Weight: 100}}, 179 }, 180 }, 181 }, 182 }, 183 }, 184 } 185 // Verify the contents of the received update. 186 if err := verifyRouteConfigUpdate(ctx, rw1.updateCh, wantUpdate); err != nil { 187 t.Fatal(err) 188 } 189 if err := verifyRouteConfigUpdate(ctx, rw2.updateCh, wantUpdate); err != nil { 190 t.Fatal(err) 191 } 192 } 193 194 // TestFederation_ClusterResourceContextParamOrder covers the case of watching a 195 // Cluster resource with the new style resource name and context parameters. 196 // The test registers watches for two resources which differ only in the order 197 // of context parameters in their URI. The server is configured to respond with 198 // a single resource with canonicalized context parameters. The test verifies 199 // that both watchers are notified. 200 func (s) TestFederation_ClusterResourceContextParamOrder(t *testing.T) { 201 serverNonDefaultAuthority, nodeID, client := setupForFederationWatchersTest(t) 202 203 var ( 204 // Two resource names only differ in context parameter order. 205 resourceName1 = fmt.Sprintf("xdstp://%s/envoy.config.cluster.v3.Cluster/xdsclient-test-cds-resource?a=1&b=2", testNonDefaultAuthority) 206 resourceName2 = fmt.Sprintf("xdstp://%s/envoy.config.cluster.v3.Cluster/xdsclient-test-cds-resource?b=2&a=1", testNonDefaultAuthority) 207 ) 208 209 // Register two watches for cluster resources with the same query string, 210 // but context parameters in different order. 211 cw1 := newClusterWatcher() 212 cdsCancel1 := xdsresource.WatchCluster(client, resourceName1, cw1) 213 defer cdsCancel1() 214 cw2 := newClusterWatcher() 215 cdsCancel2 := xdsresource.WatchCluster(client, resourceName2, cw2) 216 defer cdsCancel2() 217 218 // Configure the management server for the non-default authority to return a 219 // single cluster resource, corresponding to the watches registered. 220 resources := e2e.UpdateOptions{ 221 NodeID: nodeID, 222 Clusters: []*v3clusterpb.Cluster{e2e.DefaultCluster(resourceName1, "eds-service-name", e2e.SecurityLevelNone)}, 223 SkipValidation: true, 224 } 225 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 226 defer cancel() 227 if err := serverNonDefaultAuthority.Update(ctx, resources); err != nil { 228 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 229 } 230 231 wantUpdate := clusterUpdateErrTuple{ 232 update: xdsresource.ClusterUpdate{ 233 ClusterName: "xdstp://non-default-authority/envoy.config.cluster.v3.Cluster/xdsclient-test-cds-resource?a=1&b=2", 234 EDSServiceName: "eds-service-name", 235 }, 236 } 237 // Verify the contents of the received update. 238 if err := verifyClusterUpdate(ctx, cw1.updateCh, wantUpdate); err != nil { 239 t.Fatal(err) 240 } 241 if err := verifyClusterUpdate(ctx, cw2.updateCh, wantUpdate); err != nil { 242 t.Fatal(err) 243 } 244 } 245 246 // TestFederation_EndpointsResourceContextParamOrder covers the case of watching 247 // an Endpoints resource with the new style resource name and context parameters. 248 // The test registers watches for two resources which differ only in the order 249 // of context parameters in their URI. The server is configured to respond with 250 // a single resource with canonicalized context parameters. The test verifies 251 // that both watchers are notified. 252 func (s) TestFederation_EndpointsResourceContextParamOrder(t *testing.T) { 253 serverNonDefaultAuthority, nodeID, client := setupForFederationWatchersTest(t) 254 255 var ( 256 // Two resource names only differ in context parameter order. 257 resourceName1 = fmt.Sprintf("xdstp://%s/envoy.config.endpoint.v3.ClusterLoadAssignment/xdsclient-test-eds-resource?a=1&b=2", testNonDefaultAuthority) 258 resourceName2 = fmt.Sprintf("xdstp://%s/envoy.config.endpoint.v3.ClusterLoadAssignment/xdsclient-test-eds-resource?b=2&a=1", testNonDefaultAuthority) 259 ) 260 261 // Register two watches for endpoint resources with the same query string, 262 // but context parameters in different order. 263 ew1 := newEndpointsWatcher() 264 edsCancel1 := xdsresource.WatchEndpoints(client, resourceName1, ew1) 265 defer edsCancel1() 266 ew2 := newEndpointsWatcher() 267 edsCancel2 := xdsresource.WatchEndpoints(client, resourceName2, ew2) 268 defer edsCancel2() 269 270 // Configure the management server for the non-default authority to return a 271 // single endpoints resource, corresponding to the watches registered. 272 resources := e2e.UpdateOptions{ 273 NodeID: nodeID, 274 Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(resourceName1, "localhost", []uint32{666})}, 275 SkipValidation: true, 276 } 277 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 278 defer cancel() 279 if err := serverNonDefaultAuthority.Update(ctx, resources); err != nil { 280 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 281 } 282 283 wantUpdate := endpointsUpdateErrTuple{ 284 update: xdsresource.EndpointsUpdate{ 285 Localities: []xdsresource.Locality{ 286 { 287 Endpoints: []xdsresource.Endpoint{{Address: "localhost:666", Weight: 1}}, 288 Weight: 1, 289 ID: internal.LocalityID{ 290 Region: "region-1", 291 Zone: "zone-1", 292 SubZone: "subzone-1", 293 }, 294 }, 295 }, 296 }, 297 } 298 // Verify the contents of the received update. 299 if err := verifyEndpointsUpdate(ctx, ew1.updateCh, wantUpdate); err != nil { 300 t.Fatal(err) 301 } 302 if err := verifyEndpointsUpdate(ctx, ew2.updateCh, wantUpdate); err != nil { 303 t.Fatal(err) 304 } 305 } 306 307 func newStringP(s string) *string { 308 return &s 309 }