1
18
19 package googledirectpath
20
21 import (
22 "fmt"
23 "strconv"
24 "strings"
25 "testing"
26 "time"
27
28 "github.com/google/go-cmp/cmp"
29 "github.com/google/go-cmp/cmp/cmpopts"
30 "google.golang.org/grpc"
31 "google.golang.org/grpc/credentials/insecure"
32 "google.golang.org/grpc/internal/envconfig"
33 "google.golang.org/grpc/internal/xds/bootstrap"
34 "google.golang.org/grpc/resolver"
35 "google.golang.org/grpc/xds/internal/xdsclient"
36 "google.golang.org/protobuf/testing/protocmp"
37 "google.golang.org/protobuf/types/known/structpb"
38
39 v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
40 )
41
42 type emptyResolver struct {
43 resolver.Resolver
44 scheme string
45 }
46
47 func (er *emptyResolver) Build(_ resolver.Target, _ resolver.ClientConn, _ resolver.BuildOptions) (resolver.Resolver, error) {
48 return er, nil
49 }
50
51 func (er *emptyResolver) Scheme() string {
52 return er.scheme
53 }
54
55 func (er *emptyResolver) Close() {}
56
57 var (
58 testDNSResolver = &emptyResolver{scheme: "dns"}
59 testXDSResolver = &emptyResolver{scheme: "xds"}
60 )
61
62 func replaceResolvers() func() {
63 oldDNS := resolver.Get("dns")
64 resolver.Register(testDNSResolver)
65 oldXDS := resolver.Get("xds")
66 resolver.Register(testXDSResolver)
67 return func() {
68 resolver.Register(oldDNS)
69 resolver.Register(oldXDS)
70 }
71 }
72
73 type testXDSClient struct {
74 xdsclient.XDSClient
75 closed chan struct{}
76 }
77
78 func (c *testXDSClient) Close() {
79 c.closed <- struct{}{}
80 }
81
82
83
84 func TestBuildWithBootstrapEnvSet(t *testing.T) {
85 defer replaceResolvers()()
86 builder := resolver.Get(c2pScheme)
87
88
89 oldOnGCE := onGCE
90 onGCE = func() bool { return true }
91 defer func() { onGCE = oldOnGCE }()
92
93
94 xdsClient := &testXDSClient{closed: make(chan struct{}, 1)}
95 oldNewClient := newClientWithConfig
96 newClientWithConfig = func(config *bootstrap.Config) (xdsclient.XDSClient, func(), error) {
97 return xdsClient, func() { xdsClient.Close() }, nil
98 }
99 defer func() { newClientWithConfig = oldNewClient }()
100
101 for i, envP := range []*string{&envconfig.XDSBootstrapFileName, &envconfig.XDSBootstrapFileContent} {
102 t.Run(strconv.Itoa(i), func(t *testing.T) {
103
104 oldEnv := *envP
105 *envP = "does not matter"
106 defer func() { *envP = oldEnv }()
107
108
109 r, err := builder.Build(resolver.Target{}, nil, resolver.BuildOptions{})
110 if err != nil {
111 t.Fatalf("failed to build resolver: %v", err)
112 }
113 rr := r.(*c2pResolver)
114 if rrr := rr.Resolver; rrr != testXDSResolver {
115 t.Fatalf("want xds resolver, got %#v", rrr)
116 }
117 })
118 }
119 }
120
121
122 func TestBuildNotOnGCE(t *testing.T) {
123 defer replaceResolvers()()
124 builder := resolver.Get(c2pScheme)
125
126 oldOnGCE := onGCE
127 onGCE = func() bool { return false }
128 defer func() { onGCE = oldOnGCE }()
129
130
131 r, err := builder.Build(resolver.Target{}, nil, resolver.BuildOptions{})
132 if err != nil {
133 t.Fatalf("failed to build resolver: %v", err)
134 }
135 if r != testDNSResolver {
136 t.Fatalf("want dns resolver, got %#v", r)
137 }
138 }
139
140
141 func TestBuildXDS(t *testing.T) {
142 defer replaceResolvers()()
143 builder := resolver.Get(c2pScheme)
144
145 oldOnGCE := onGCE
146 onGCE = func() bool { return true }
147 defer func() { onGCE = oldOnGCE }()
148
149 const testZone = "test-zone"
150 oldGetZone := getZone
151 getZone = func(time.Duration) string { return testZone }
152 defer func() { getZone = oldGetZone }()
153
154 for _, tt := range []struct {
155 name string
156 ipv6 bool
157 tdURI string
158 }{
159 {name: "ipv6 true", ipv6: true},
160 {name: "ipv6 false", ipv6: false},
161 {name: "override TD URI", ipv6: true, tdURI: "test-uri"},
162 } {
163 t.Run(tt.name, func(t *testing.T) {
164 oldGetIPv6Capability := getIPv6Capable
165 getIPv6Capable = func(time.Duration) bool { return tt.ipv6 }
166 defer func() { getIPv6Capable = oldGetIPv6Capability }()
167
168 if tt.tdURI != "" {
169 oldURI := envconfig.C2PResolverTestOnlyTrafficDirectorURI
170 envconfig.C2PResolverTestOnlyTrafficDirectorURI = tt.tdURI
171 defer func() {
172 envconfig.C2PResolverTestOnlyTrafficDirectorURI = oldURI
173 }()
174 }
175
176 tXDSClient := &testXDSClient{closed: make(chan struct{}, 1)}
177
178 configCh := make(chan *bootstrap.Config, 1)
179 oldNewClient := newClientWithConfig
180 newClientWithConfig = func(config *bootstrap.Config) (xdsclient.XDSClient, func(), error) {
181 configCh <- config
182 return tXDSClient, func() { tXDSClient.Close() }, nil
183 }
184 defer func() { newClientWithConfig = oldNewClient }()
185
186
187 r, err := builder.Build(resolver.Target{}, nil, resolver.BuildOptions{})
188 if err != nil {
189 t.Fatalf("failed to build resolver: %v", err)
190 }
191 rr := r.(*c2pResolver)
192 if rrr := rr.Resolver; rrr != testXDSResolver {
193 t.Fatalf("want xds resolver, got %#v, ", rrr)
194 }
195
196 wantNode := &v3corepb.Node{
197 Id: id,
198 Metadata: nil,
199 Locality: &v3corepb.Locality{Zone: testZone},
200 UserAgentName: gRPCUserAgentName,
201 UserAgentVersionType: &v3corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version},
202 ClientFeatures: []string{clientFeatureNoOverprovisioning, clientFeatureResourceWrapper},
203 }
204 if tt.ipv6 {
205 wantNode.Metadata = &structpb.Struct{
206 Fields: map[string]*structpb.Value{
207 ipv6CapableMetadataName: {
208 Kind: &structpb.Value_BoolValue{BoolValue: true},
209 },
210 },
211 }
212 }
213 wantServerConfig, err := bootstrap.ServerConfigFromJSON([]byte(fmt.Sprintf(`{
214 "server_uri": "%s",
215 "channel_creds": [{"type": "google_default"}],
216 "server_features": ["xds_v3", "ignore_resource_deletion", "xds.config.resource-in-sotw"]
217 }`, tdURL)))
218 if err != nil {
219 t.Fatalf("Failed to build server bootstrap config: %v", err)
220 }
221 wantConfig := &bootstrap.Config{
222 XDSServer: wantServerConfig,
223 ClientDefaultListenerResourceNameTemplate: "%s",
224 Authorities: map[string]*bootstrap.Authority{
225 "traffic-director-c2p.xds.googleapis.com": {
226 XDSServer: wantServerConfig,
227 ClientListenerResourceNameTemplate: "xdstp://traffic-director-c2p.xds.googleapis.com/envoy.config.listener.v3.Listener/%s",
228 },
229 },
230 NodeProto: wantNode,
231 }
232 if tt.tdURI != "" {
233 wantConfig.XDSServer.ServerURI = tt.tdURI
234 }
235 cmpOpts := cmp.Options{
236 cmpopts.IgnoreFields(bootstrap.ServerConfig{}, "Creds"),
237 cmp.AllowUnexported(bootstrap.ServerConfig{}),
238 protocmp.Transform(),
239 }
240 select {
241 case gotConfig := <-configCh:
242 if diff := cmp.Diff(wantConfig, gotConfig, cmpOpts); diff != "" {
243 t.Fatalf("Unexpected diff in bootstrap config (-want +got):\n%s", diff)
244 }
245 case <-time.After(time.Second):
246 t.Fatalf("timeout waiting for client config")
247 }
248
249 r.Close()
250 select {
251 case <-tXDSClient.closed:
252 case <-time.After(time.Second):
253 t.Fatalf("timeout waiting for client close")
254 }
255 })
256 }
257 }
258
259
260
261
262 func TestBuildFailsWhenCalledWithAuthority(t *testing.T) {
263 uri := "google-c2p://an-authority/resource"
264 cc, err := grpc.Dial(uri, grpc.WithTransportCredentials(insecure.NewCredentials()))
265 defer func() {
266 if cc != nil {
267 cc.Close()
268 }
269 }()
270 wantErr := "google-c2p URI scheme does not support authorities"
271 if err == nil || !strings.Contains(err.Error(), wantErr) {
272 t.Fatalf("grpc.Dial(%s) returned error: %v, want: %v", uri, err, wantErr)
273 }
274 }
275
View as plain text