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  }