github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/googledirectpath/googlec2p.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 implements a resolver that configures xds to make
    20  // cloud to prod directpath connection.
    21  //
    22  // It's a combo of DNS and xDS resolvers. It delegates to DNS if
    23  // - not on GCE, or
    24  // - xDS bootstrap env var is set (so this client needs to do normal xDS, not
    25  // direct path, and clients with this scheme is not part of the xDS mesh).
    26  package googledirectpath
    27  
    28  import (
    29  	"fmt"
    30  	"time"
    31  
    32  	grpc "github.com/hxx258456/ccgo/grpc"
    33  	"github.com/hxx258456/ccgo/grpc/credentials/google"
    34  	"github.com/hxx258456/ccgo/grpc/grpclog"
    35  	"github.com/hxx258456/ccgo/grpc/internal/envconfig"
    36  	"github.com/hxx258456/ccgo/grpc/internal/googlecloud"
    37  	internalgrpclog "github.com/hxx258456/ccgo/grpc/internal/grpclog"
    38  	"github.com/hxx258456/ccgo/grpc/internal/grpcrand"
    39  	"github.com/hxx258456/ccgo/grpc/resolver"
    40  	_ "github.com/hxx258456/ccgo/grpc/xds" // To register xds resolvers and balancers.
    41  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient"
    42  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/bootstrap"
    43  	"github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource/version"
    44  	"google.golang.org/protobuf/types/known/structpb"
    45  
    46  	v3corepb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/core/v3"
    47  )
    48  
    49  const (
    50  	c2pScheme = "google-c2p-experimental"
    51  
    52  	tdURL          = "dns:///directpath-pa.googleapis.com"
    53  	httpReqTimeout = 10 * time.Second
    54  	zoneURL        = "http://metadata.google.internal/computeMetadata/v1/instance/zone"
    55  	ipv6URL        = "http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ipv6s"
    56  
    57  	gRPCUserAgentName               = "gRPC Go"
    58  	clientFeatureNoOverprovisioning = "envoy.lb.does_not_support_overprovisioning"
    59  	ipv6CapableMetadataName         = "TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE"
    60  
    61  	logPrefix = "[google-c2p-resolver]"
    62  
    63  	dnsName, xdsName = "dns", "xds"
    64  )
    65  
    66  // For overriding in unittests.
    67  var (
    68  	onGCE = googlecloud.OnGCE
    69  
    70  	newClientWithConfig = func(config *bootstrap.Config) (xdsclient.XDSClient, error) {
    71  		return xdsclient.NewWithConfig(config)
    72  	}
    73  
    74  	logger = internalgrpclog.NewPrefixLogger(grpclog.Component("directpath"), logPrefix)
    75  )
    76  
    77  func init() {
    78  	resolver.Register(c2pResolverBuilder{})
    79  }
    80  
    81  type c2pResolverBuilder struct{}
    82  
    83  func (c2pResolverBuilder) Build(t resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
    84  	if !runDirectPath() {
    85  		// If not xDS, fallback to DNS.
    86  		t.Scheme = dnsName
    87  		return resolver.Get(dnsName).Build(t, cc, opts)
    88  	}
    89  
    90  	// Note that the following calls to getZone() and getIPv6Capable() does I/O,
    91  	// and has 10 seconds timeout each.
    92  	//
    93  	// This should be fine in most of the cases. In certain error cases, this
    94  	// could block Dial() for up to 10 seconds (each blocking call has its own
    95  	// goroutine).
    96  	zoneCh, ipv6CapableCh := make(chan string), make(chan bool)
    97  	go func() { zoneCh <- getZone(httpReqTimeout) }()
    98  	go func() { ipv6CapableCh <- getIPv6Capable(httpReqTimeout) }()
    99  
   100  	balancerName := envconfig.C2PResolverTestOnlyTrafficDirectorURI
   101  	if balancerName == "" {
   102  		balancerName = tdURL
   103  	}
   104  	config := &bootstrap.Config{
   105  		XDSServer: &bootstrap.ServerConfig{
   106  			ServerURI:    balancerName,
   107  			Creds:        grpc.WithCredentialsBundle(google.NewDefaultCredentials()),
   108  			TransportAPI: version.TransportV3,
   109  			NodeProto:    newNode(<-zoneCh, <-ipv6CapableCh),
   110  		},
   111  		ClientDefaultListenerResourceNameTemplate: "%s",
   112  	}
   113  
   114  	// Create singleton xds client with this config. The xds client will be
   115  	// used by the xds resolver later.
   116  	xdsC, err := newClientWithConfig(config)
   117  	if err != nil {
   118  		return nil, fmt.Errorf("failed to start xDS client: %v", err)
   119  	}
   120  
   121  	// Create and return an xDS resolver.
   122  	t.Scheme = xdsName
   123  	xdsR, err := resolver.Get(xdsName).Build(t, cc, opts)
   124  	if err != nil {
   125  		xdsC.Close()
   126  		return nil, err
   127  	}
   128  	return &c2pResolver{
   129  		Resolver: xdsR,
   130  		client:   xdsC,
   131  	}, nil
   132  }
   133  
   134  func (c2pResolverBuilder) Scheme() string {
   135  	return c2pScheme
   136  }
   137  
   138  type c2pResolver struct {
   139  	resolver.Resolver
   140  	client xdsclient.XDSClient
   141  }
   142  
   143  func (r *c2pResolver) Close() {
   144  	r.Resolver.Close()
   145  	r.client.Close()
   146  }
   147  
   148  var ipv6EnabledMetadata = &structpb.Struct{
   149  	Fields: map[string]*structpb.Value{
   150  		ipv6CapableMetadataName: structpb.NewBoolValue(true),
   151  	},
   152  }
   153  
   154  var id = fmt.Sprintf("C2P-%d", grpcrand.Int())
   155  
   156  // newNode makes a copy of defaultNode, and populate it's Metadata and
   157  // Locality fields.
   158  func newNode(zone string, ipv6Capable bool) *v3corepb.Node {
   159  	ret := &v3corepb.Node{
   160  		// Not all required fields are set in defaultNote. Metadata will be set
   161  		// if ipv6 is enabled. Locality will be set to the value from metadata.
   162  		Id:                   id,
   163  		UserAgentName:        gRPCUserAgentName,
   164  		UserAgentVersionType: &v3corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version},
   165  		ClientFeatures:       []string{clientFeatureNoOverprovisioning},
   166  	}
   167  	ret.Locality = &v3corepb.Locality{Zone: zone}
   168  	if ipv6Capable {
   169  		ret.Metadata = ipv6EnabledMetadata
   170  	}
   171  	return ret
   172  }
   173  
   174  // runDirectPath returns whether this resolver should use direct path.
   175  //
   176  // direct path is enabled if this client is running on GCE, and the normal xDS
   177  // is not used (bootstrap env vars are not set).
   178  func runDirectPath() bool {
   179  	return envconfig.XDSBootstrapFileName == "" && envconfig.XDSBootstrapFileContent == "" && onGCE()
   180  }