Skip to content

Commit 2963f7e

Browse files
apolcynmisvivek
authored andcommitted
resolver/google-c2p: introduce SetUniverseDomain API (grpc#7719)
1 parent df19238 commit 2963f7e

File tree

2 files changed

+258
-8
lines changed

2 files changed

+258
-8
lines changed

xds/googledirectpath/googlec2p.go

+54-8
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"fmt"
3131
"math/rand"
3232
"net/url"
33+
"sync"
3334
"time"
3435

3536
"google.golang.org/grpc/grpclog"
@@ -46,7 +47,7 @@ const (
4647
c2pScheme = "google-c2p"
4748
c2pAuthority = "traffic-director-c2p.xds.googleapis.com"
4849

49-
tdURL = "dns:///directpath-pa.googleapis.com"
50+
defaultUniverseDomain = "googleapis.com"
5051
zoneURL = "http://metadata.google.internal/computeMetadata/v1/instance/zone"
5152
ipv6URL = "http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ipv6s"
5253
ipv6CapableMetadataName = "TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE"
@@ -56,17 +57,66 @@ const (
5657
dnsName, xdsName = "dns", "xds"
5758
)
5859

59-
// For overriding in unittests.
6060
var (
61+
logger = internalgrpclog.NewPrefixLogger(grpclog.Component("directpath"), logPrefix)
62+
universeDomainMu sync.Mutex
63+
universeDomain = ""
64+
// For overriding in unittests.
6165
onGCE = googlecloud.OnGCE
6266
randInt = rand.Int
63-
logger = internalgrpclog.NewPrefixLogger(grpclog.Component("directpath"), logPrefix)
6467
)
6568

6669
func init() {
6770
resolver.Register(c2pResolverBuilder{})
6871
}
6972

73+
// SetUniverseDomain informs the gRPC library of the universe domain
74+
// in which the process is running (for example, "googleapis.com").
75+
// It is the caller's responsibility to ensure that the domain is correct.
76+
//
77+
// This setting is used by the "google-c2p" resolver (the resolver used
78+
// for URIs with the "google-c2p" scheme) to configure its dependencies.
79+
//
80+
// If a gRPC channel is created with the "google-c2p" URI scheme and this
81+
// function has NOT been called, then gRPC configures the universe domain as
82+
// "googleapis.com".
83+
//
84+
// Returns nil if either:
85+
//
86+
// a) The universe domain has not yet been configured.
87+
// b) The universe domain has been configured and matches the provided value.
88+
//
89+
// Otherwise, returns an error.
90+
func SetUniverseDomain(domain string) error {
91+
universeDomainMu.Lock()
92+
defer universeDomainMu.Unlock()
93+
if domain == "" {
94+
return fmt.Errorf("universe domain cannot be empty")
95+
}
96+
if universeDomain == "" {
97+
universeDomain = domain
98+
return nil
99+
}
100+
if universeDomain != domain {
101+
return fmt.Errorf("universe domain cannot be set to %s, already set to different value: %s", domain, universeDomain)
102+
}
103+
return nil
104+
}
105+
106+
func getXdsServerURI() string {
107+
universeDomainMu.Lock()
108+
defer universeDomainMu.Unlock()
109+
if universeDomain == "" {
110+
universeDomain = defaultUniverseDomain
111+
}
112+
// Put env var override logic after default value logic so
113+
// that tests still run the default value logic.
114+
if envconfig.C2PResolverTestOnlyTrafficDirectorURI != "" {
115+
return envconfig.C2PResolverTestOnlyTrafficDirectorURI
116+
}
117+
return fmt.Sprintf("dns:///directpath-pa.%s", universeDomain)
118+
}
119+
70120
type c2pResolverBuilder struct{}
71121

72122
func (c2pResolverBuilder) Build(t resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
@@ -90,11 +140,7 @@ func (c2pResolverBuilder) Build(t resolver.Target, cc resolver.ClientConn, opts
90140
go func() { zoneCh <- getZone(httpReqTimeout) }()
91141
go func() { ipv6CapableCh <- getIPv6Capable(httpReqTimeout) }()
92142

93-
xdsServerURI := envconfig.C2PResolverTestOnlyTrafficDirectorURI
94-
if xdsServerURI == "" {
95-
xdsServerURI = tdURL
96-
}
97-
143+
xdsServerURI := getXdsServerURI()
98144
nodeCfg := newNodeConfig(<-zoneCh, <-ipv6CapableCh)
99145
xdsServerCfg := newXdsServerConfig(xdsServerURI)
100146
authoritiesCfg := newAuthoritiesConfig(xdsServerCfg)

xds/googledirectpath/googlec2p_test.go

+204
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,29 @@ func simulateRunningOnGCE(t *testing.T, gce bool) {
8282
t.Cleanup(func() { onGCE = oldOnGCE })
8383
}
8484

85+
// ensure universeDomain is set to the expected default,
86+
// and clean it up again after the test.
87+
func useCleanUniverseDomain(t *testing.T) {
88+
universeDomainMu.Lock()
89+
defer universeDomainMu.Unlock()
90+
if universeDomain != "" {
91+
t.Fatalf("universe domain unexpectedly initialized: %v", universeDomain)
92+
}
93+
t.Cleanup(func() {
94+
universeDomainMu.Lock()
95+
universeDomain = ""
96+
universeDomainMu.Unlock()
97+
})
98+
}
99+
85100
// Tests the scenario where the bootstrap env vars are set and we're running on
86101
// GCE. The test builds a google-c2p resolver and verifies that an xDS resolver
87102
// is built and that we don't fallback to DNS (because federation is enabled by
88103
// default).
89104
func (s) TestBuildWithBootstrapEnvSet(t *testing.T) {
90105
replaceResolvers(t)
91106
simulateRunningOnGCE(t, true)
107+
useCleanUniverseDomain(t)
92108

93109
builder := resolver.Get(c2pScheme)
94110
for i, envP := range []*string{&envconfig.XDSBootstrapFileName, &envconfig.XDSBootstrapFileContent} {
@@ -118,6 +134,7 @@ func (s) TestBuildWithBootstrapEnvSet(t *testing.T) {
118134
func (s) TestBuildNotOnGCE(t *testing.T) {
119135
replaceResolvers(t)
120136
simulateRunningOnGCE(t, false)
137+
useCleanUniverseDomain(t)
121138
builder := resolver.Get(c2pScheme)
122139

123140
// Build the google-c2p resolver.
@@ -152,6 +169,7 @@ func bootstrapConfig(t *testing.T, opts bootstrap.ConfigOptionsForTesting) *boot
152169
func (s) TestBuildXDS(t *testing.T) {
153170
replaceResolvers(t)
154171
simulateRunningOnGCE(t, true)
172+
useCleanUniverseDomain(t)
155173
builder := resolver.Get(c2pScheme)
156174

157175
// Override the zone returned by the metadata server.
@@ -295,6 +313,7 @@ func (s) TestBuildXDS(t *testing.T) {
295313
// google-c2p scheme with a non-empty authority and verifies that it fails with
296314
// an expected error.
297315
func (s) TestBuildFailsWhenCalledWithAuthority(t *testing.T) {
316+
useCleanUniverseDomain(t)
298317
uri := "google-c2p://an-authority/resource"
299318
cc, err := grpc.Dial(uri, grpc.WithTransportCredentials(insecure.NewCredentials()))
300319
defer func() {
@@ -307,3 +326,188 @@ func (s) TestBuildFailsWhenCalledWithAuthority(t *testing.T) {
307326
t.Fatalf("grpc.Dial(%s) returned error: %v, want: %v", uri, err, wantErr)
308327
}
309328
}
329+
330+
func (s) TestSetUniverseDomainNonDefault(t *testing.T) {
331+
replaceResolvers(t)
332+
simulateRunningOnGCE(t, true)
333+
useCleanUniverseDomain(t)
334+
builder := resolver.Get(c2pScheme)
335+
336+
// Override the zone returned by the metadata server.
337+
oldGetZone := getZone
338+
getZone = func(time.Duration) string { return "test-zone" }
339+
defer func() { getZone = oldGetZone }()
340+
341+
// Override IPv6 capability returned by the metadata server.
342+
oldGetIPv6Capability := getIPv6Capable
343+
getIPv6Capable = func(time.Duration) bool { return false }
344+
defer func() { getIPv6Capable = oldGetIPv6Capability }()
345+
346+
// Override the random func used in the node ID.
347+
origRandInd := randInt
348+
randInt = func() int { return 666 }
349+
defer func() { randInt = origRandInd }()
350+
351+
// Set the universe domain
352+
testUniverseDomain := "test-universe-domain.test"
353+
if err := SetUniverseDomain(testUniverseDomain); err != nil {
354+
t.Fatalf("SetUniverseDomain(%s) failed: %v", testUniverseDomain, err)
355+
}
356+
357+
// Now set universe domain to something different, it should fail
358+
domain := "test-universe-domain-2.test"
359+
err := SetUniverseDomain(domain)
360+
wantErr := "already set"
361+
if err == nil || !strings.Contains(err.Error(), wantErr) {
362+
t.Fatalf("googlec2p.SetUniverseDomain(%s) returned error: %v, want: %v", domain, err, wantErr)
363+
}
364+
365+
// Now explicitly set universe domain to the default, it should also fail
366+
domain = "googleapis.com"
367+
err = SetUniverseDomain(domain)
368+
wantErr = "already set"
369+
if err == nil || !strings.Contains(err.Error(), wantErr) {
370+
t.Fatalf("googlec2p.SetUniverseDomain(%s) returned error: %v, want: %v", domain, err, wantErr)
371+
}
372+
373+
// Now set universe domain to the original value, it should work
374+
if err := SetUniverseDomain(testUniverseDomain); err != nil {
375+
t.Fatalf("googlec2p.SetUniverseDomain(%s) failed: %v", testUniverseDomain, err)
376+
}
377+
378+
// Build the google-c2p resolver.
379+
r, err := builder.Build(resolver.Target{}, nil, resolver.BuildOptions{})
380+
if err != nil {
381+
t.Fatalf("failed to build resolver: %v", err)
382+
}
383+
defer r.Close()
384+
385+
// Build should return xDS, not DNS.
386+
if r != testXDSResolver {
387+
t.Fatalf("Build() returned %#v, want xds resolver", r)
388+
}
389+
390+
gotConfig, err := bootstrap.GetConfiguration()
391+
if err != nil {
392+
t.Fatalf("Failed to get bootstrap config: %v", err)
393+
}
394+
395+
// Check that we use directpath-pa.test-universe-domain.test in the
396+
// bootstrap config.
397+
wantBootstrapConfig := bootstrapConfig(t, bootstrap.ConfigOptionsForTesting{
398+
Servers: []byte(`[{
399+
"server_uri": "dns:///directpath-pa.test-universe-domain.test",
400+
"channel_creds": [{"type": "google_default"}],
401+
"server_features": ["ignore_resource_deletion"]
402+
}]`),
403+
Authorities: map[string]json.RawMessage{
404+
"traffic-director-c2p.xds.googleapis.com": []byte(`{
405+
"xds_servers": [
406+
{
407+
"server_uri": "dns:///directpath-pa.test-universe-domain.test",
408+
"channel_creds": [{"type": "google_default"}],
409+
"server_features": ["ignore_resource_deletion"]
410+
}
411+
]
412+
}`),
413+
},
414+
Node: []byte(`{
415+
"id": "C2P-666",
416+
"locality": {"zone": "test-zone"}
417+
}`),
418+
})
419+
if diff := cmp.Diff(wantBootstrapConfig, gotConfig); diff != "" {
420+
t.Fatalf("Unexpected diff in bootstrap config (-want +got):\n%s", diff)
421+
}
422+
}
423+
424+
func (s) TestDefaultUniverseDomain(t *testing.T) {
425+
replaceResolvers(t)
426+
simulateRunningOnGCE(t, true)
427+
useCleanUniverseDomain(t)
428+
builder := resolver.Get(c2pScheme)
429+
430+
// Override the zone returned by the metadata server.
431+
oldGetZone := getZone
432+
getZone = func(time.Duration) string { return "test-zone" }
433+
defer func() { getZone = oldGetZone }()
434+
435+
// Override IPv6 capability returned by the metadata server.
436+
oldGetIPv6Capability := getIPv6Capable
437+
getIPv6Capable = func(time.Duration) bool { return false }
438+
defer func() { getIPv6Capable = oldGetIPv6Capability }()
439+
440+
// Override the random func used in the node ID.
441+
origRandInd := randInt
442+
randInt = func() int { return 666 }
443+
defer func() { randInt = origRandInd }()
444+
445+
// Build the google-c2p resolver.
446+
r, err := builder.Build(resolver.Target{}, nil, resolver.BuildOptions{})
447+
if err != nil {
448+
t.Fatalf("failed to build resolver: %v", err)
449+
}
450+
defer r.Close()
451+
452+
// Build should return xDS, not DNS.
453+
if r != testXDSResolver {
454+
t.Fatalf("Build() returned %#v, want xds resolver", r)
455+
}
456+
457+
gotConfig, err := bootstrap.GetConfiguration()
458+
if err != nil {
459+
t.Fatalf("Failed to get bootstrap config: %v", err)
460+
}
461+
462+
// Check that we use directpath-pa.googleapis.com in the bootstrap config
463+
wantBootstrapConfig := bootstrapConfig(t, bootstrap.ConfigOptionsForTesting{
464+
Servers: []byte(`[{
465+
"server_uri": "dns:///directpath-pa.googleapis.com",
466+
"channel_creds": [{"type": "google_default"}],
467+
"server_features": ["ignore_resource_deletion"]
468+
}]`),
469+
Authorities: map[string]json.RawMessage{
470+
"traffic-director-c2p.xds.googleapis.com": []byte(`{
471+
"xds_servers": [
472+
{
473+
"server_uri": "dns:///directpath-pa.googleapis.com",
474+
"channel_creds": [{"type": "google_default"}],
475+
"server_features": ["ignore_resource_deletion"]
476+
}
477+
]
478+
}`),
479+
},
480+
Node: []byte(`{
481+
"id": "C2P-666",
482+
"locality": {"zone": "test-zone"}
483+
}`),
484+
})
485+
if diff := cmp.Diff(wantBootstrapConfig, gotConfig); diff != "" {
486+
t.Fatalf("Unexpected diff in bootstrap config (-want +got):\n%s", diff)
487+
}
488+
489+
// Now set universe domain to something different than the default, it should fail
490+
domain := "test-universe-domain.test"
491+
err = SetUniverseDomain(domain)
492+
wantErr := "already set"
493+
if err == nil || !strings.Contains(err.Error(), wantErr) {
494+
t.Fatalf("googlec2p.SetUniverseDomain(%s) returned error: %v, want: %v", domain, err, wantErr)
495+
}
496+
497+
// Now explicitly set universe domain to the default, it should work
498+
domain = "googleapis.com"
499+
if err := SetUniverseDomain(domain); err != nil {
500+
t.Fatalf("googlec2p.SetUniverseDomain(%s) failed: %v", domain, err)
501+
}
502+
}
503+
504+
func (s) TestSetUniverseDomainEmptyString(t *testing.T) {
505+
replaceResolvers(t)
506+
simulateRunningOnGCE(t, true)
507+
useCleanUniverseDomain(t)
508+
wantErr := "cannot be empty"
509+
err := SetUniverseDomain("")
510+
if err == nil || !strings.Contains(err.Error(), wantErr) {
511+
t.Fatalf("googlec2p.SetUniverseDomain(\"\") returned error: %v, want: %v", err, wantErr)
512+
}
513+
}

0 commit comments

Comments
 (0)