istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/echo/client.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package echo 16 17 import ( 18 "context" 19 "crypto/tls" 20 "crypto/x509" 21 "fmt" 22 "io" 23 "strings" 24 "time" 25 26 "go.uber.org/atomic" 27 "google.golang.org/grpc" 28 "google.golang.org/grpc/credentials" 29 "google.golang.org/grpc/credentials/insecure" 30 xdscreds "google.golang.org/grpc/credentials/xds" 31 32 "istio.io/istio/pkg/test/echo/common" 33 "istio.io/istio/pkg/test/echo/proto" 34 ) 35 36 var _ io.Closer = &Client{} 37 38 // Client of an Echo server that simplifies request/response processing for Forward commands. 39 type Client struct { 40 conn *grpc.ClientConn 41 client proto.EchoTestServiceClient 42 } 43 44 // New creates a new echo client.Instance that is connected to the given server address. 45 func New(address string, tlsSettings *common.TLSSettings, extraDialOpts ...grpc.DialOption) (*Client, error) { 46 // Connect to the GRPC (command) endpoint of 'this' app. 47 // TODO: make use of common.ConnectionTimeout once it increases 48 ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) 49 defer cancel() 50 dialOptions := []grpc.DialOption{grpc.WithBlock()} 51 if tlsSettings != nil { 52 cert, err := tls.X509KeyPair([]byte(tlsSettings.ClientCert), []byte(tlsSettings.Key)) 53 if err != nil { 54 return nil, err 55 } 56 57 var certPool *x509.CertPool 58 certPool, err = x509.SystemCertPool() 59 if err != nil { 60 return nil, fmt.Errorf("failed to fetch Cert from SystemCertPool: %v", err) 61 } 62 63 if tlsSettings.RootCert != "" && !certPool.AppendCertsFromPEM([]byte(tlsSettings.RootCert)) { 64 return nil, fmt.Errorf("failed to create cert pool") 65 } 66 cfg := credentials.NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}, RootCAs: certPool, MinVersion: tls.VersionTLS12}) 67 // If provided, override the hostname 68 if tlsSettings.Hostname != "" { 69 dialOptions = append(dialOptions, grpc.WithAuthority(tlsSettings.Hostname)) 70 } 71 dialOptions = append(dialOptions, grpc.WithTransportCredentials(cfg)) 72 } else if strings.HasPrefix(address, "xds:///") { 73 creds, err := xdscreds.NewClientCredentials(xdscreds.ClientOptions{FallbackCreds: insecure.NewCredentials()}) 74 if err != nil { 75 return nil, err 76 } 77 dialOptions = append(dialOptions, grpc.WithTransportCredentials(creds)) 78 } else { 79 dialOptions = append(dialOptions, grpc.WithTransportCredentials(insecure.NewCredentials())) 80 } 81 dialOptions = append(dialOptions, extraDialOpts...) 82 conn, err := grpc.DialContext(ctx, address, dialOptions...) 83 if err != nil { 84 return nil, err 85 } 86 client := proto.NewEchoTestServiceClient(conn) 87 88 return &Client{ 89 conn: conn, 90 client: client, 91 }, nil 92 } 93 94 // Close the EchoClient and free any resources. 95 func (c *Client) Close() error { 96 if c.conn != nil { 97 return c.conn.Close() 98 } 99 return nil 100 } 101 102 func (c *Client) Echo(ctx context.Context, request *proto.EchoRequest) (Response, error) { 103 resp, err := c.client.Echo(ctx, request) 104 if err != nil { 105 return Response{}, err 106 } 107 return parseResponse(resp.Message), nil 108 } 109 110 // ForwardEcho sends the given forward request and parses the response for easier processing. Only fails if the request fails. 111 func (c *Client) ForwardEcho(ctx context.Context, request *proto.ForwardEchoRequest) (Responses, error) { 112 // Forward a request from 'this' service to the destination service. 113 GlobalEchoRequests.Add(uint64(request.Count)) 114 resp, err := c.client.ForwardEcho(ctx, request) 115 if err != nil { 116 return nil, err 117 } 118 119 return ParseResponses(request, resp), nil 120 } 121 122 // GlobalEchoRequests records how many echo calls we have made total, from all sources. 123 // Note: go tests are distinct binaries per test suite, so this is the suite level number of calls 124 var GlobalEchoRequests = atomic.NewUint64(0)