mirror of
https://github.com/hanzoai/traces.git
synced 2026-11-03 04:05:45 +00:00
| datasource | package | from | to | | ---------- | ---------------------- | ------- | ------- | | go | google.golang.org/grpc | v1.76.0 | v1.77.0 | Signed-off-by: renovate-sh-app[bot] <219655108+renovate-sh-app[bot]@users.noreply.github.com> Co-authored-by: renovate-sh-app[bot] <219655108+renovate-sh-app[bot]@users.noreply.github.com>
238 lines
7.4 KiB
Go
238 lines
7.4 KiB
Go
/*
|
||
*
|
||
* Copyright 2021 gRPC authors.
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*
|
||
*/
|
||
|
||
// Package googledirectpath implements a resolver that configures xds to make
|
||
// cloud to prod directpath connection.
|
||
//
|
||
// It's a combo of DNS and xDS resolvers. It delegates to DNS if
|
||
// - not on GCE, or
|
||
// - xDS bootstrap env var is set (so this client needs to do normal xDS, not
|
||
// direct path, and clients with this scheme is not part of the xDS mesh).
|
||
package googledirectpath
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
rand "math/rand/v2"
|
||
"net/url"
|
||
"sync"
|
||
"time"
|
||
|
||
"google.golang.org/grpc/grpclog"
|
||
"google.golang.org/grpc/internal/envconfig"
|
||
"google.golang.org/grpc/internal/googlecloud"
|
||
internalgrpclog "google.golang.org/grpc/internal/grpclog"
|
||
"google.golang.org/grpc/internal/xds/bootstrap"
|
||
"google.golang.org/grpc/internal/xds/xdsclient"
|
||
"google.golang.org/grpc/resolver"
|
||
|
||
_ "google.golang.org/grpc/xds" // To register xds resolvers and balancers.
|
||
)
|
||
|
||
const (
|
||
c2pScheme = "google-c2p"
|
||
c2pAuthority = "traffic-director-c2p.xds.googleapis.com"
|
||
|
||
defaultUniverseDomain = "googleapis.com"
|
||
zoneURL = "http://metadata.google.internal/computeMetadata/v1/instance/zone"
|
||
ipv6URL = "http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ipv6s"
|
||
ipv6CapableMetadataName = "TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE"
|
||
httpReqTimeout = 10 * time.Second
|
||
|
||
logPrefix = "[google-c2p-resolver]"
|
||
dnsName, xdsName = "dns", "xds"
|
||
)
|
||
|
||
var (
|
||
logger = internalgrpclog.NewPrefixLogger(grpclog.Component("directpath"), logPrefix)
|
||
universeDomainMu sync.Mutex
|
||
universeDomain = ""
|
||
// For overriding in unittests.
|
||
onGCE = googlecloud.OnGCE
|
||
randInt = rand.Int
|
||
xdsClientPool = xdsclient.DefaultPool
|
||
)
|
||
|
||
func init() {
|
||
resolver.Register(c2pResolverBuilder{})
|
||
}
|
||
|
||
// SetUniverseDomain informs the gRPC library of the universe domain
|
||
// in which the process is running (for example, "googleapis.com").
|
||
// It is the caller's responsibility to ensure that the domain is correct.
|
||
//
|
||
// This setting is used by the "google-c2p" resolver (the resolver used
|
||
// for URIs with the "google-c2p" scheme) to configure its dependencies.
|
||
//
|
||
// If a gRPC channel is created with the "google-c2p" URI scheme and this
|
||
// function has NOT been called, then gRPC configures the universe domain as
|
||
// "googleapis.com".
|
||
//
|
||
// Returns nil if either:
|
||
//
|
||
// a) The universe domain has not yet been configured.
|
||
// b) The universe domain has been configured and matches the provided value.
|
||
//
|
||
// Otherwise, returns an error.
|
||
func SetUniverseDomain(domain string) error {
|
||
universeDomainMu.Lock()
|
||
defer universeDomainMu.Unlock()
|
||
if domain == "" {
|
||
return fmt.Errorf("universe domain cannot be empty")
|
||
}
|
||
if universeDomain == "" {
|
||
universeDomain = domain
|
||
return nil
|
||
}
|
||
if universeDomain != domain {
|
||
return fmt.Errorf("universe domain cannot be set to %s, already set to different value: %s", domain, universeDomain)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func getXdsServerURI() string {
|
||
universeDomainMu.Lock()
|
||
defer universeDomainMu.Unlock()
|
||
if universeDomain == "" {
|
||
universeDomain = defaultUniverseDomain
|
||
}
|
||
// Put env var override logic after default value logic so
|
||
// that tests still run the default value logic.
|
||
if envconfig.C2PResolverTestOnlyTrafficDirectorURI != "" {
|
||
return envconfig.C2PResolverTestOnlyTrafficDirectorURI
|
||
}
|
||
return fmt.Sprintf("dns:///directpath-pa.%s", universeDomain)
|
||
}
|
||
|
||
type c2pResolverWrapper struct {
|
||
resolver.Resolver
|
||
cancel func() // Release the reference to the xDS client that was created in Build().
|
||
}
|
||
|
||
func (r *c2pResolverWrapper) Close() {
|
||
r.Resolver.Close()
|
||
r.cancel()
|
||
}
|
||
|
||
type c2pResolverBuilder struct{}
|
||
|
||
func (c2pResolverBuilder) Build(t resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
|
||
if t.URL.Host != "" {
|
||
return nil, fmt.Errorf("google-c2p URI scheme does not support authorities")
|
||
}
|
||
|
||
if !runDirectPath() {
|
||
// If not xDS, fallback to DNS.
|
||
t.URL.Scheme = dnsName
|
||
return resolver.Get(dnsName).Build(t, cc, opts)
|
||
}
|
||
|
||
// Note that the following calls to getZone() and getIPv6Capable() does I/O,
|
||
// and has 10 seconds timeout each.
|
||
//
|
||
// This should be fine in most of the cases. In certain error cases, this
|
||
// could block Dial() for up to 10 seconds (each blocking call has its own
|
||
// goroutine).
|
||
zoneCh, ipv6CapableCh := make(chan string), make(chan bool)
|
||
go func() { zoneCh <- getZone(httpReqTimeout) }()
|
||
go func() { ipv6CapableCh <- getIPv6Capable(httpReqTimeout) }()
|
||
|
||
xdsServerURI := getXdsServerURI()
|
||
nodeCfg := newNodeConfig(<-zoneCh, <-ipv6CapableCh)
|
||
xdsServerCfg := newXdsServerConfig(xdsServerURI)
|
||
authoritiesCfg := newAuthoritiesConfig(xdsServerCfg)
|
||
|
||
cfg := map[string]any{
|
||
"xds_servers": []any{xdsServerCfg},
|
||
"client_default_listener_resource_name_template": "%s",
|
||
"authorities": authoritiesCfg,
|
||
"node": nodeCfg,
|
||
}
|
||
cfgJSON, err := json.Marshal(cfg)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to marshal bootstrap configuration: %v", err)
|
||
}
|
||
config, err := bootstrap.NewConfigFromContents(cfgJSON)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to parse bootstrap contents: %s, %v", string(cfgJSON), err)
|
||
}
|
||
|
||
t = resolver.Target{
|
||
URL: url.URL{
|
||
Scheme: xdsName,
|
||
Host: c2pAuthority,
|
||
Path: t.URL.Path,
|
||
},
|
||
}
|
||
|
||
// Create a new xDS client for this target using the provided bootstrap
|
||
// configuration. This client is stored in the xdsclient pool’s internal
|
||
// cache, keeping it alive and associated with this resolver until Closed().
|
||
// While the c2p resolver itself does not directly use the client, creating
|
||
// it ensures that when the xDS resolver later requests a client for the
|
||
// same target, the existing instance will be reused.
|
||
_, cancel, err := xdsClientPool.NewClientWithConfig(t.String(), opts.MetricsRecorder, config)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to create xds client: %v", err)
|
||
}
|
||
|
||
r, err := resolver.Get(xdsName).Build(t, cc, opts)
|
||
if err != nil {
|
||
cancel()
|
||
return nil, err
|
||
}
|
||
return &c2pResolverWrapper{Resolver: r, cancel: cancel}, nil
|
||
}
|
||
|
||
func (b c2pResolverBuilder) Scheme() string {
|
||
return c2pScheme
|
||
}
|
||
|
||
func newNodeConfig(zone string, ipv6Capable bool) map[string]any {
|
||
node := map[string]any{
|
||
"id": fmt.Sprintf("C2P-%d", randInt()),
|
||
"locality": map[string]any{"zone": zone},
|
||
}
|
||
// Enable dualstack endpoints in TD.
|
||
if ipv6Capable {
|
||
node["metadata"] = map[string]any{ipv6CapableMetadataName: true}
|
||
}
|
||
return node
|
||
}
|
||
|
||
func newAuthoritiesConfig(serverCfg map[string]any) map[string]any {
|
||
return map[string]any{
|
||
c2pAuthority: map[string]any{"xds_servers": []any{serverCfg}},
|
||
}
|
||
}
|
||
|
||
func newXdsServerConfig(uri string) map[string]any {
|
||
return map[string]any{
|
||
"server_uri": uri,
|
||
"channel_creds": []map[string]any{{"type": "google_default"}},
|
||
"server_features": []any{"ignore_resource_deletion"},
|
||
}
|
||
}
|
||
|
||
// runDirectPath returns whether this resolver should use direct path.
|
||
//
|
||
// direct path is enabled if this client is running on GCE.
|
||
func runDirectPath() bool {
|
||
return onGCE()
|
||
}
|