google.golang.org/grpc@v1.74.2/xds/internal/clients/xdsclient/test/authority_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  	"net"
    24  	"testing"
    25  
    26  	"github.com/google/uuid"
    27  	"google.golang.org/grpc/credentials/insecure"
    28  	"google.golang.org/grpc/xds/internal/clients"
    29  	"google.golang.org/grpc/xds/internal/clients/grpctransport"
    30  	"google.golang.org/grpc/xds/internal/clients/internal/testutils"
    31  	"google.golang.org/grpc/xds/internal/clients/internal/testutils/e2e"
    32  	"google.golang.org/grpc/xds/internal/clients/xdsclient"
    33  	"google.golang.org/grpc/xds/internal/clients/xdsclient/internal/xdsresource"
    34  
    35  	v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
    36  )
    37  
    38  const (
    39  	testAuthority1 = "test-authority1"
    40  	testAuthority2 = "test-authority2"
    41  	testAuthority3 = "test-authority3"
    42  )
    43  
    44  var (
    45  	// These two resources use `testAuthority1`, which contains an empty server
    46  	// config and therefore will use the default management server.
    47  	authorityTestResourceName11 = buildResourceName(listenerResourceTypeName, testAuthority1, ldsName, nil)
    48  	authorityTestResourceName12 = buildResourceName(listenerResourceTypeName, testAuthority1, ldsName+"2", nil)
    49  	// This resource uses `testAuthority2`, which contains an empty server
    50  	// config and therefore will use the default management server.
    51  	authorityTestResourceName2 = buildResourceName(listenerResourceTypeName, testAuthority2, ldsName+"3", nil)
    52  	// This resource uses `testAuthority3`, which contains a non-empty server
    53  	// config, and therefore will use the non-default management server.
    54  	authorityTestResourceName3 = buildResourceName(listenerResourceTypeName, testAuthority3, ldsName+"3", nil)
    55  )
    56  
    57  // setupForAuthorityTests spins up two management servers, one to act as the
    58  // default and the other to act as the non-default. It also creates a
    59  // xDS client configuration with three authorities (the first two pointing to
    60  // the default and the third one pointing to the non-default).
    61  //
    62  // Returns two listeners used by the default and non-default management servers
    63  // respectively, and the xDS client.
    64  func setupForAuthorityTests(ctx context.Context, t *testing.T) (*testutils.ListenerWrapper, *testutils.ListenerWrapper, *xdsclient.XDSClient) {
    65  	// Create listener wrappers which notify on to a channel whenever a new
    66  	// connection is accepted. We use this to track the number of transports
    67  	// used by the xDS client.
    68  	lisDefault := testutils.NewListenerWrapper(t, nil)
    69  	lisNonDefault := testutils.NewListenerWrapper(t, nil)
    70  
    71  	// Start a management server to act as the default authority.
    72  	defaultAuthorityServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{Listener: lisDefault})
    73  
    74  	// Start a management server to act as the non-default authority.
    75  	nonDefaultAuthorityServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{Listener: lisNonDefault})
    76  
    77  	// Create a bootstrap configuration with two non-default authorities which
    78  	// have empty server configs, and therefore end up using the default server
    79  	// config, which points to the above management server.
    80  	nodeID := uuid.New().String()
    81  
    82  	resourceTypes := map[string]xdsclient.ResourceType{xdsresource.V3ListenerURL: listenerType}
    83  	ext := grpctransport.ServerIdentifierExtension{ConfigName: "insecure"}
    84  	siDefault := clients.ServerIdentifier{
    85  		ServerURI:  defaultAuthorityServer.Address,
    86  		Extensions: ext,
    87  	}
    88  	siNonDefault := clients.ServerIdentifier{
    89  		ServerURI:  nonDefaultAuthorityServer.Address,
    90  		Extensions: ext,
    91  	}
    92  
    93  	configs := map[string]grpctransport.Config{"insecure": {Credentials: insecure.NewBundle()}}
    94  	xdsClientConfig := xdsclient.Config{
    95  		Servers:          []xdsclient.ServerConfig{{ServerIdentifier: siDefault}},
    96  		Node:             clients.Node{ID: nodeID},
    97  		TransportBuilder: grpctransport.NewBuilder(configs),
    98  		ResourceTypes:    resourceTypes,
    99  		// Xdstp style resource names used in this test use a slash removed
   100  		// version of t.Name as their authority, and the empty config
   101  		// results in the top-level xds server configuration being used for
   102  		// this authority.
   103  		Authorities: map[string]xdsclient.Authority{
   104  			testAuthority1: {XDSServers: []xdsclient.ServerConfig{}},
   105  			testAuthority2: {XDSServers: []xdsclient.ServerConfig{}},
   106  			testAuthority3: {XDSServers: []xdsclient.ServerConfig{{ServerIdentifier: siNonDefault}}},
   107  		},
   108  	}
   109  
   110  	// Create an xDS client with the above config.
   111  	overrideWatchExpiryTimeout(t, defaultTestWatchExpiryTimeout)
   112  	client, err := xdsclient.New(xdsClientConfig)
   113  	if err != nil {
   114  		t.Fatalf("Failed to create xDS client: %v", err)
   115  	}
   116  
   117  	resources := e2e.UpdateOptions{
   118  		NodeID: nodeID,
   119  		Listeners: []*v3listenerpb.Listener{
   120  			e2e.DefaultClientListener(authorityTestResourceName11, rdsName),
   121  			e2e.DefaultClientListener(authorityTestResourceName12, rdsName),
   122  			e2e.DefaultClientListener(authorityTestResourceName2, rdsName),
   123  			e2e.DefaultClientListener(authorityTestResourceName3, rdsName),
   124  		},
   125  		SkipValidation: true,
   126  	}
   127  	if err := defaultAuthorityServer.Update(ctx, resources); err != nil {
   128  		t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err)
   129  	}
   130  	return lisDefault, lisNonDefault, client
   131  }
   132  
   133  // Tests the xdsChannel sharing logic among authorities. The test verifies the
   134  // following scenarios:
   135  //   - A watch for a resource name with an authority matching an existing watch
   136  //     should not result in a new transport being created.
   137  //   - A watch for a resource name with different authority name but same
   138  //     authority config as an existing watch should not result in a new transport
   139  //     being created.
   140  func (s) TestAuthority_XDSChannelSharing(t *testing.T) {
   141  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   142  	defer cancel()
   143  	lis, _, client := setupForAuthorityTests(ctx, t)
   144  	defer client.Close()
   145  
   146  	// Verify that no connection is established to the management server at this
   147  	// point. A transport is created only when a resource (which belongs to that
   148  	// authority) is requested.
   149  	sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   150  	defer sCancel()
   151  	if _, err := lis.NewConnCh.Receive(sCtx); err != context.DeadlineExceeded {
   152  		t.Fatal("Unexpected new transport created to management server")
   153  	}
   154  
   155  	// Request the first resource. Verify that a new transport is created.
   156  	watcher := noopListenerWatcher{}
   157  	ldsCancel1 := client.WatchResource(xdsresource.V3ListenerURL, authorityTestResourceName11, watcher)
   158  	defer ldsCancel1()
   159  	if _, err := lis.NewConnCh.Receive(ctx); err != nil {
   160  		t.Fatalf("Timed out when waiting for a new transport to be created to the management server: %v", err)
   161  	}
   162  
   163  	// Request the second resource. Verify that no new transport is created.
   164  	ldsCancel2 := client.WatchResource(xdsresource.V3ListenerURL, authorityTestResourceName12, watcher)
   165  	defer ldsCancel2()
   166  	sCtx, sCancel = context.WithTimeout(ctx, defaultTestShortTimeout)
   167  	defer sCancel()
   168  	if _, err := lis.NewConnCh.Receive(sCtx); err != context.DeadlineExceeded {
   169  		t.Fatal("Unexpected new transport created to management server")
   170  	}
   171  
   172  	// Request the third resource. Verify that no new transport is created.
   173  	ldsCancel3 := client.WatchResource(xdsresource.V3ListenerURL, authorityTestResourceName2, watcher)
   174  	defer ldsCancel3()
   175  	sCtx, sCancel = context.WithTimeout(ctx, defaultTestShortTimeout)
   176  	defer sCancel()
   177  	if _, err := lis.NewConnCh.Receive(sCtx); err != context.DeadlineExceeded {
   178  		t.Fatal("Unexpected new transport created to management server")
   179  	}
   180  }
   181  
   182  // Test the xdsChannel close logic. The test verifies that the xDS client
   183  // closes an xdsChannel immediately after the last watch is canceled.
   184  func (s) TestAuthority_XDSChannelClose(t *testing.T) {
   185  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   186  	defer cancel()
   187  	lis, _, client := setupForAuthorityTests(ctx, t)
   188  	defer client.Close()
   189  
   190  	// Request the first resource. Verify that a new transport is created.
   191  	watcher := noopListenerWatcher{}
   192  	ldsCancel1 := client.WatchResource(xdsresource.V3ListenerURL, authorityTestResourceName11, watcher)
   193  	val, err := lis.NewConnCh.Receive(ctx)
   194  	if err != nil {
   195  		t.Fatalf("Timed out when waiting for a new transport to be created to the management server: %v", err)
   196  	}
   197  	conn := val.(*testutils.ConnWrapper)
   198  
   199  	// Request the second resource. Verify that no new transport is created.
   200  	ldsCancel2 := client.WatchResource(xdsresource.V3ListenerURL, authorityTestResourceName12, watcher)
   201  	sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   202  	defer sCancel()
   203  	if _, err := lis.NewConnCh.Receive(sCtx); err != context.DeadlineExceeded {
   204  		t.Fatal("Unexpected new transport created to management server")
   205  	}
   206  
   207  	// Cancel both watches, and verify that the connection to the management
   208  	// server is closed.
   209  	ldsCancel1()
   210  	ldsCancel2()
   211  	if _, err := conn.CloseCh.Receive(ctx); err != nil {
   212  		t.Fatal("Timeout when waiting for connection to management server to be closed")
   213  	}
   214  }
   215  
   216  // Tests the scenario where the primary management server is unavailable at
   217  // startup and the xDS client falls back to the secondary.  The test verifies
   218  // that the resource watcher is not notified of the connectivity failure until
   219  // all servers have failed.
   220  func (s) TestAuthority_Fallback(t *testing.T) {
   221  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   222  	defer cancel()
   223  
   224  	// Create primary and secondary management servers with restartable
   225  	// listeners.
   226  	l, err := net.Listen("tcp", "localhost:0")
   227  	if err != nil {
   228  		t.Fatalf("net.Listen() failed: %v", err)
   229  	}
   230  	primaryLis := testutils.NewRestartableListener(l)
   231  	primaryMgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{Listener: primaryLis})
   232  	l, err = net.Listen("tcp", "localhost:0")
   233  	if err != nil {
   234  		t.Fatalf("net.Listen() failed: %v", err)
   235  	}
   236  	secondaryLis := testutils.NewRestartableListener(l)
   237  	secondaryMgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{Listener: secondaryLis})
   238  
   239  	nodeID := uuid.New().String()
   240  
   241  	resourceTypes := map[string]xdsclient.ResourceType{xdsresource.V3ListenerURL: listenerType}
   242  	psi := clients.ServerIdentifier{
   243  		ServerURI:  primaryMgmtServer.Address,
   244  		Extensions: grpctransport.ServerIdentifierExtension{ConfigName: "insecure"},
   245  	}
   246  	ssi := clients.ServerIdentifier{
   247  		ServerURI:  secondaryMgmtServer.Address,
   248  		Extensions: grpctransport.ServerIdentifierExtension{ConfigName: "insecure"},
   249  	}
   250  
   251  	// Create config with the above primary and fallback management servers,
   252  	// and an xDS client with that configuration.
   253  	configs := map[string]grpctransport.Config{"insecure": {Credentials: insecure.NewBundle()}}
   254  	xdsClientConfig := xdsclient.Config{
   255  		Servers:          []xdsclient.ServerConfig{{ServerIdentifier: psi}, {ServerIdentifier: ssi}},
   256  		Node:             clients.Node{ID: nodeID},
   257  		TransportBuilder: grpctransport.NewBuilder(configs),
   258  		ResourceTypes:    resourceTypes,
   259  		// Xdstp resource names used in this test do not specify an
   260  		// authority. These will end up looking up an entry with the
   261  		// empty key in the authorities map. Having an entry with an
   262  		// empty key and empty configuration, results in these
   263  		// resources also using the top-level configuration.
   264  		Authorities: map[string]xdsclient.Authority{
   265  			"": {XDSServers: []xdsclient.ServerConfig{}},
   266  		},
   267  	}
   268  
   269  	// Create an xDS client with the above config.
   270  	client, err := xdsclient.New(xdsClientConfig)
   271  	if err != nil {
   272  		t.Fatalf("Failed to create xDS client: %v", err)
   273  	}
   274  	defer client.Close()
   275  
   276  	const listenerName = "listener"
   277  	const rdsPrimaryName = "rds-primary"
   278  	const rdsSecondaryName = "rds-secondary"
   279  
   280  	// Create a Cluster resource on the primary.
   281  	resources := e2e.UpdateOptions{
   282  		NodeID:         nodeID,
   283  		Listeners:      []*v3listenerpb.Listener{e2e.DefaultClientListener(listenerName, rdsPrimaryName)},
   284  		SkipValidation: true,
   285  	}
   286  	if err := primaryMgmtServer.Update(ctx, resources); err != nil {
   287  		t.Fatalf("Failed to update primary management server with resources: %v, err: %v", resources, err)
   288  	}
   289  
   290  	// Create a Cluster resource on the secondary .
   291  	resources = e2e.UpdateOptions{
   292  		NodeID:         nodeID,
   293  		Listeners:      []*v3listenerpb.Listener{e2e.DefaultClientListener(listenerName, rdsSecondaryName)},
   294  		SkipValidation: true,
   295  	}
   296  	if err := secondaryMgmtServer.Update(ctx, resources); err != nil {
   297  		t.Fatalf("Failed to update primary management server with resources: %v, err: %v", resources, err)
   298  	}
   299  
   300  	// Stop the primary.
   301  	primaryLis.Close()
   302  
   303  	// Register a watch.
   304  	watcher := newListenerWatcher()
   305  	ldsCancel := client.WatchResource(xdsresource.V3ListenerURL, listenerName, watcher)
   306  	defer ldsCancel()
   307  
   308  	// Ensure that the connectivity error callback is not called. Since, this
   309  	// is the first watch without cached resource, it checks for resourceErrCh
   310  	sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   311  	defer sCancel()
   312  	if v, err := watcher.resourceErrCh.Receive(sCtx); err != context.DeadlineExceeded {
   313  		t.Fatalf("Resource error callback on the watcher with error:  %v", v.(error))
   314  	}
   315  
   316  	// Ensure that the resource update callback is invoked.
   317  	wantUpdate := listenerUpdateErrTuple{
   318  		update: listenerUpdate{
   319  			RouteConfigName: rdsSecondaryName,
   320  		},
   321  	}
   322  	if err := verifyListenerUpdate(ctx, watcher.updateCh, wantUpdate); err != nil {
   323  		t.Fatal(err)
   324  	}
   325  
   326  	// Stop the secondary.
   327  	secondaryLis.Close()
   328  
   329  	// Ensure that the connectivity error callback is called as ambient error
   330  	// since cached resource exist.
   331  	if _, err := watcher.ambientErrCh.Receive(ctx); err != nil {
   332  		t.Fatal("Timeout when waiting for ambient error callback on the watcher")
   333  	}
   334  }