github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/googledirectpath/googlec2p_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  
    19  package googledirectpath
    20  
    21  import (
    22  	"strconv"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/google/go-cmp/cmp"
    27  	"github.com/google/go-cmp/cmp/cmpopts"
    28  	grpc "github.com/hxx258456/ccgo/grpc"
    29  	"github.com/hxx258456/ccgo/grpc/internal/envconfig"
    30  	"github.com/hxx258456/ccgo/grpc/resolver"
    31  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient"
    32  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/bootstrap"
    33  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource/version"
    34  	"google.golang.org/protobuf/testing/protocmp"
    35  	"google.golang.org/protobuf/types/known/structpb"
    36  
    37  	v3corepb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/core/v3"
    38  )
    39  
    40  type emptyResolver struct {
    41  	resolver.Resolver
    42  	scheme string
    43  }
    44  
    45  func (er *emptyResolver) Build(_ resolver.Target, _ resolver.ClientConn, _ resolver.BuildOptions) (resolver.Resolver, error) {
    46  	return er, nil
    47  }
    48  
    49  func (er *emptyResolver) Scheme() string {
    50  	return er.scheme
    51  }
    52  
    53  func (er *emptyResolver) Close() {}
    54  
    55  var (
    56  	testDNSResolver = &emptyResolver{scheme: "dns"}
    57  	testXDSResolver = &emptyResolver{scheme: "xds"}
    58  )
    59  
    60  func replaceResolvers() func() {
    61  	var registerForTesting bool
    62  	if resolver.Get(c2pScheme) == nil {
    63  		// If env var to enable c2p is not set, the resolver isn't registered.
    64  		// Need to register and unregister in defer.
    65  		registerForTesting = true
    66  		resolver.Register(&c2pResolverBuilder{})
    67  	}
    68  	oldDNS := resolver.Get("dns")
    69  	resolver.Register(testDNSResolver)
    70  	oldXDS := resolver.Get("xds")
    71  	resolver.Register(testXDSResolver)
    72  	return func() {
    73  		if oldDNS != nil {
    74  			resolver.Register(oldDNS)
    75  		} else {
    76  			resolver.UnregisterForTesting("dns")
    77  		}
    78  		if oldXDS != nil {
    79  			resolver.Register(oldXDS)
    80  		} else {
    81  			resolver.UnregisterForTesting("xds")
    82  		}
    83  		if registerForTesting {
    84  			resolver.UnregisterForTesting(c2pScheme)
    85  		}
    86  	}
    87  }
    88  
    89  // Test that when bootstrap env is set, fallback to DNS.
    90  func TestBuildWithBootstrapEnvSet(t *testing.T) {
    91  	defer replaceResolvers()()
    92  	builder := resolver.Get(c2pScheme)
    93  
    94  	for i, envP := range []*string{&envconfig.XDSBootstrapFileName, &envconfig.XDSBootstrapFileContent} {
    95  		t.Run(strconv.Itoa(i), func(t *testing.T) {
    96  			// Set bootstrap config env var.
    97  			oldEnv := *envP
    98  			*envP = "does not matter"
    99  			defer func() { *envP = oldEnv }()
   100  
   101  			// Build should return DNS, not xDS.
   102  			r, err := builder.Build(resolver.Target{}, nil, resolver.BuildOptions{})
   103  			if err != nil {
   104  				t.Fatalf("failed to build resolver: %v", err)
   105  			}
   106  			if r != testDNSResolver {
   107  				t.Fatalf("want dns resolver, got %#v", r)
   108  			}
   109  		})
   110  	}
   111  }
   112  
   113  // Test that when not on GCE, fallback to DNS.
   114  func TestBuildNotOnGCE(t *testing.T) {
   115  	defer replaceResolvers()()
   116  	builder := resolver.Get(c2pScheme)
   117  
   118  	oldOnGCE := onGCE
   119  	onGCE = func() bool { return false }
   120  	defer func() { onGCE = oldOnGCE }()
   121  
   122  	// Build should return DNS, not xDS.
   123  	r, err := builder.Build(resolver.Target{}, nil, resolver.BuildOptions{})
   124  	if err != nil {
   125  		t.Fatalf("failed to build resolver: %v", err)
   126  	}
   127  	if r != testDNSResolver {
   128  		t.Fatalf("want dns resolver, got %#v", r)
   129  	}
   130  }
   131  
   132  type testXDSClient struct {
   133  	xdsclient.XDSClient
   134  	closed chan struct{}
   135  }
   136  
   137  func (c *testXDSClient) Close() {
   138  	c.closed <- struct{}{}
   139  }
   140  
   141  // Test that when xDS is built, the client is built with the correct config.
   142  func TestBuildXDS(t *testing.T) {
   143  	defer replaceResolvers()()
   144  	builder := resolver.Get(c2pScheme)
   145  
   146  	oldOnGCE := onGCE
   147  	onGCE = func() bool { return true }
   148  	defer func() { onGCE = oldOnGCE }()
   149  
   150  	const testZone = "test-zone"
   151  	oldGetZone := getZone
   152  	getZone = func(time.Duration) string { return testZone }
   153  	defer func() { getZone = oldGetZone }()
   154  
   155  	for _, tt := range []struct {
   156  		name  string
   157  		ipv6  bool
   158  		tdURI string // traffic director URI will be overridden if this is set.
   159  	}{
   160  		{name: "ipv6 true", ipv6: true},
   161  		{name: "ipv6 false", ipv6: false},
   162  		{name: "override TD URI", ipv6: true, tdURI: "test-uri"},
   163  	} {
   164  		t.Run(tt.name, func(t *testing.T) {
   165  			oldGetIPv6Capability := getIPv6Capable
   166  			getIPv6Capable = func(time.Duration) bool { return tt.ipv6 }
   167  			defer func() { getIPv6Capable = oldGetIPv6Capability }()
   168  
   169  			if tt.tdURI != "" {
   170  				oldURI := envconfig.C2PResolverTestOnlyTrafficDirectorURI
   171  				envconfig.C2PResolverTestOnlyTrafficDirectorURI = tt.tdURI
   172  				defer func() {
   173  					envconfig.C2PResolverTestOnlyTrafficDirectorURI = oldURI
   174  				}()
   175  			}
   176  
   177  			tXDSClient := &testXDSClient{closed: make(chan struct{}, 1)}
   178  
   179  			configCh := make(chan *bootstrap.Config, 1)
   180  			oldNewClient := newClientWithConfig
   181  			newClientWithConfig = func(config *bootstrap.Config) (xdsclient.XDSClient, error) {
   182  				configCh <- config
   183  				return tXDSClient, nil
   184  			}
   185  			defer func() { newClientWithConfig = oldNewClient }()
   186  
   187  			// Build should return DNS, not xDS.
   188  			r, err := builder.Build(resolver.Target{}, nil, resolver.BuildOptions{})
   189  			if err != nil {
   190  				t.Fatalf("failed to build resolver: %v", err)
   191  			}
   192  			rr := r.(*c2pResolver)
   193  			if rrr := rr.Resolver; rrr != testXDSResolver {
   194  				t.Fatalf("want xds resolver, got %#v, ", rrr)
   195  			}
   196  
   197  			wantNode := &v3corepb.Node{
   198  				Id:                   id,
   199  				Metadata:             nil,
   200  				Locality:             &v3corepb.Locality{Zone: testZone},
   201  				UserAgentName:        gRPCUserAgentName,
   202  				UserAgentVersionType: &v3corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version},
   203  				ClientFeatures:       []string{clientFeatureNoOverprovisioning},
   204  			}
   205  			if tt.ipv6 {
   206  				wantNode.Metadata = &structpb.Struct{
   207  					Fields: map[string]*structpb.Value{
   208  						ipv6CapableMetadataName: {
   209  							Kind: &structpb.Value_BoolValue{BoolValue: true},
   210  						},
   211  					},
   212  				}
   213  			}
   214  			wantConfig := &bootstrap.Config{
   215  				XDSServer: &bootstrap.ServerConfig{
   216  					ServerURI:    tdURL,
   217  					TransportAPI: version.TransportV3,
   218  					NodeProto:    wantNode,
   219  				},
   220  				ClientDefaultListenerResourceNameTemplate: "%s",
   221  			}
   222  			if tt.tdURI != "" {
   223  				wantConfig.XDSServer.ServerURI = tt.tdURI
   224  			}
   225  			cmpOpts := cmp.Options{
   226  				cmpopts.IgnoreFields(bootstrap.ServerConfig{}, "Creds"),
   227  				cmp.AllowUnexported(bootstrap.ServerConfig{}),
   228  				protocmp.Transform(),
   229  			}
   230  			select {
   231  			case c := <-configCh:
   232  				if diff := cmp.Diff(c, wantConfig, cmpOpts); diff != "" {
   233  					t.Fatalf("%v", diff)
   234  				}
   235  			case <-time.After(time.Second):
   236  				t.Fatalf("timeout waiting for client config")
   237  			}
   238  
   239  			r.Close()
   240  			select {
   241  			case <-tXDSClient.closed:
   242  			case <-time.After(time.Second):
   243  				t.Fatalf("timeout waiting for client close")
   244  			}
   245  		})
   246  	}
   247  }