...

Source file src/google.golang.org/grpc/xds/internal/xdsclient/singleton_test.go

Documentation: google.golang.org/grpc/xds/internal/xdsclient

     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
    20  
    21  import (
    22  	"context"
    23  	"testing"
    24  
    25  	"github.com/google/uuid"
    26  	"google.golang.org/grpc/internal/testutils"
    27  	"google.golang.org/grpc/internal/testutils/xds/bootstrap"
    28  )
    29  
    30  // Test that multiple New() returns the same Client. And only when the last
    31  // client is closed, the underlying client is closed.
    32  func (s) TestClientNewSingleton(t *testing.T) {
    33  	// Create a bootstrap configuration, place it in a file in the temp
    34  	// directory, and set the bootstrap env vars to point to it.
    35  	nodeID := uuid.New().String()
    36  	cleanup, err := bootstrap.CreateFile(bootstrap.Options{
    37  		NodeID:    nodeID,
    38  		ServerURI: "non-existent-server-address",
    39  	})
    40  	if err != nil {
    41  		t.Fatal(err)
    42  	}
    43  	defer cleanup()
    44  
    45  	// Override the singleton creation hook to get notified.
    46  	origSingletonClientImplCreateHook := singletonClientImplCreateHook
    47  	singletonCreationCh := testutils.NewChannel()
    48  	singletonClientImplCreateHook = func() {
    49  		singletonCreationCh.Replace(nil)
    50  	}
    51  	defer func() { singletonClientImplCreateHook = origSingletonClientImplCreateHook }()
    52  
    53  	// Override the singleton close hook to get notified.
    54  	origSingletonClientImplCloseHook := singletonClientImplCloseHook
    55  	singletonCloseCh := testutils.NewChannel()
    56  	singletonClientImplCloseHook = func() {
    57  		singletonCloseCh.Replace(nil)
    58  	}
    59  	defer func() { singletonClientImplCloseHook = origSingletonClientImplCloseHook }()
    60  
    61  	// The first call to New() should create a new singleton client.
    62  	_, closeFunc, err := New()
    63  	if err != nil {
    64  		t.Fatalf("failed to create xDS client: %v", err)
    65  	}
    66  
    67  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
    68  	defer cancel()
    69  	if _, err := singletonCreationCh.Receive(ctx); err != nil {
    70  		t.Fatalf("Timeout when waiting for singleton xDS client to be created: %v", err)
    71  	}
    72  
    73  	// Calling New() again should not create new singleton client implementations.
    74  	const count = 9
    75  	closeFuncs := make([]func(), 9)
    76  	for i := 0; i < count; i++ {
    77  		func() {
    78  			_, closeFuncs[i], err = New()
    79  			if err != nil {
    80  				t.Fatalf("%d-th call to New() failed with error: %v", i, err)
    81  			}
    82  
    83  			sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
    84  			defer sCancel()
    85  			if _, err := singletonCreationCh.Receive(sCtx); err == nil {
    86  				t.Fatalf("%d-th call to New() created a new singleton client", i)
    87  			}
    88  		}()
    89  	}
    90  
    91  	// Call Close() multiple times on each of the clients created in the above for
    92  	// loop. Close() calls are idempotent, and the underlying client
    93  	// implementation will not be closed until we release the first reference we
    94  	// acquired above, via the first call to New().
    95  	for i := 0; i < count; i++ {
    96  		func() {
    97  			closeFuncs[i]()
    98  			closeFuncs[i]()
    99  
   100  			sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   101  			defer sCancel()
   102  			if _, err := singletonCloseCh.Receive(sCtx); err == nil {
   103  				t.Fatal("singleton client implementation closed before all references are released")
   104  			}
   105  		}()
   106  	}
   107  
   108  	// Call the last Close(). The underlying implementation should be closed.
   109  	closeFunc()
   110  	if _, err := singletonCloseCh.Receive(ctx); err != nil {
   111  		t.Fatalf("Timeout waiting for singleton client implementation to be closed: %v", err)
   112  	}
   113  
   114  	// Calling New() again, after the previous Client was actually closed, should
   115  	// create a new one.
   116  	_, closeFunc, err = New()
   117  	if err != nil {
   118  		t.Fatalf("failed to create client: %v", err)
   119  	}
   120  	defer closeFunc()
   121  	if _, err := singletonCreationCh.Receive(ctx); err != nil {
   122  		t.Fatalf("Timeout when waiting for singleton xDS client to be created: %v", err)
   123  	}
   124  }
   125  

View as plain text