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