google.golang.org/grpc@v1.72.2/xds/internal/xdsclient/tests/client_custom_dialopts_test.go (about) 1 /* 2 * 3 * Copyright 2024 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 xdsclient_test 20 21 import ( 22 "context" 23 "encoding/json" 24 "fmt" 25 "testing" 26 27 "github.com/google/go-cmp/cmp" 28 "github.com/google/uuid" 29 "google.golang.org/grpc" 30 "google.golang.org/grpc/credentials" 31 "google.golang.org/grpc/credentials/insecure" 32 "google.golang.org/grpc/internal" 33 "google.golang.org/grpc/internal/stubserver" 34 "google.golang.org/grpc/internal/testutils" 35 "google.golang.org/grpc/internal/testutils/xds/e2e" 36 internalbootstrap "google.golang.org/grpc/internal/xds/bootstrap" 37 testgrpc "google.golang.org/grpc/interop/grpc_testing" 38 testpb "google.golang.org/grpc/interop/grpc_testing" 39 "google.golang.org/grpc/resolver" 40 "google.golang.org/grpc/xds/bootstrap" 41 xci "google.golang.org/grpc/xds/internal/xdsclient/internal" 42 ) 43 44 // nopDialOption is a no-op grpc.DialOption with a name. 45 type nopDialOption struct { 46 grpc.EmptyDialOption 47 name string 48 } 49 50 // testCredsBundle implements `credentials.Bundle` and `extraDialOptions`. 51 type testCredsBundle struct { 52 credentials.Bundle 53 testDialOptNames []string 54 } 55 56 func (t *testCredsBundle) DialOptions() []grpc.DialOption { 57 var opts []grpc.DialOption 58 for _, name := range t.testDialOptNames { 59 opts = append(opts, &nopDialOption{name: name}) 60 } 61 return opts 62 } 63 64 type testCredsBuilder struct { 65 testDialOptNames []string 66 } 67 68 func (t *testCredsBuilder) Build(config json.RawMessage) (credentials.Bundle, func(), error) { 69 return &testCredsBundle{ 70 Bundle: insecure.NewBundle(), 71 testDialOptNames: t.testDialOptNames, 72 }, func() {}, nil 73 } 74 75 func (t *testCredsBuilder) Name() string { 76 return "test_dialer_creds" 77 } 78 79 func (s) TestClientCustomDialOptsFromCredentialsBundle(t *testing.T) { 80 // Create and register the credentials bundle builder. 81 credsBuilder := &testCredsBuilder{ 82 testDialOptNames: []string{"opt1", "opt2", "opt3"}, 83 } 84 bootstrap.RegisterCredentials(credsBuilder) 85 86 // Start an xDS management server. 87 mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{}) 88 89 // Create bootstrap configuration pointing to the above management server. 90 nodeID := uuid.New().String() 91 bc, err := internalbootstrap.NewContentsForTesting(internalbootstrap.ConfigOptionsForTesting{ 92 Servers: []byte(fmt.Sprintf(`[{ 93 "server_uri": %q, 94 "channel_creds": [{ 95 "type": %q, 96 "config": {"mgmt_server_address": %q} 97 }] 98 }]`, mgmtServer.Address, credsBuilder.Name(), mgmtServer.Address)), 99 Node: []byte(fmt.Sprintf(`{"id": "%s"}`, nodeID)), 100 }) 101 if err != nil { 102 t.Fatalf("Failed to create bootstrap configuration: %v", err) 103 } 104 105 // Create an xDS resolver with the above bootstrap configuration. 106 var resolverBuilder resolver.Builder 107 if newResolver := internal.NewXDSResolverWithConfigForTesting; newResolver != nil { 108 resolverBuilder, err = newResolver.(func([]byte) (resolver.Builder, error))(bc) 109 if err != nil { 110 t.Fatalf("Failed to create xDS resolver for testing: %v", err) 111 } 112 } 113 114 // Spin up a test backend. 115 server := stubserver.StartTestService(t, nil) 116 defer server.Stop() 117 118 // Configure client side xDS resources on the management server. 119 const serviceName = "my-service-client-side-xds" 120 resources := e2e.DefaultClientResources(e2e.ResourceParams{ 121 DialTarget: serviceName, 122 NodeID: nodeID, 123 Host: "localhost", 124 Port: testutils.ParsePort(t, server.Address), 125 SecLevel: e2e.SecurityLevelNone, 126 }) 127 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 128 defer cancel() 129 if err := mgmtServer.Update(ctx, resources); err != nil { 130 t.Fatal(err) 131 } 132 133 // Intercept a grpc.NewClient call from the xds client to validate DialOptions. 134 xci.GRPCNewClient = func(target string, opts ...grpc.DialOption) (conn *grpc.ClientConn, err error) { 135 got := map[string]int{} 136 for _, opt := range opts { 137 if mo, ok := opt.(*nopDialOption); ok { 138 got[mo.name]++ 139 } 140 } 141 want := map[string]int{} 142 for _, name := range credsBuilder.testDialOptNames { 143 want[name]++ 144 } 145 if !cmp.Equal(got, want) { 146 t.Errorf("grpc.NewClient() was called with unexpected DialOptions: got %v, want %v", got, want) 147 } 148 return grpc.NewClient(target, opts...) 149 } 150 defer func() { xci.GRPCNewClient = grpc.NewClient }() 151 152 // Create a ClientConn and make a successful RPC. The insecure transport 153 // credentials passed into the gRPC.NewClient is the credentials for the 154 // data plane communication with the test backend. 155 cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(resolverBuilder)) 156 if err != nil { 157 t.Fatalf("Failed to dial local test server: %v", err) 158 } 159 160 client := testgrpc.NewTestServiceClient(cc) 161 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil { 162 t.Fatalf("EmptyCall() failed: %v", err) 163 } 164 cc.Close() 165 }