1
// Copyright Istio Authors
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
7
// http://www.apache.org/licenses/LICENSE-2.0
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.
15
// An example implementation of a client.
27
"github.com/spf13/cobra"
28
// To install the xds resolvers and balancers.
29
_ "google.golang.org/grpc/xds"
30
wrappers "google.golang.org/protobuf/types/known/wrapperspb"
32
"istio.io/istio/pkg/cmd"
33
"istio.io/istio/pkg/log"
34
"istio.io/istio/pkg/test/echo/common"
35
"istio.io/istio/pkg/test/echo/proto"
36
"istio.io/istio/pkg/test/echo/server/forwarder"
51
insecureSkipVerify bool
56
newConnectionPerRequest bool
66
hboneClientCert string
69
hboneInsecureSkipVerify bool
71
loggingOptions = log.DefaultOptions()
73
rootCmd = &cobra.Command{
75
Short: "Istio test Echo client.",
77
Long: `Istio test Echo client used for generating traffic between instances of the Echo service.
78
For Kubernetes, this can be run from a source pod via "kubectl exec" with the desired flags.
79
In general, Echo's gRPC interface (ForwardEcho) should be preferred. This client is only needed in cases
80
where the network configuration doesn't support gRPC to the source pod.'
82
Args: cobra.ExactArgs(1),
83
PersistentPreRunE: configureLogging,
84
Run: func(cmd *cobra.Command, args []string) {
85
expectSet = cmd.Flags().Changed("expect")
86
// Create a request from the flags.
87
request, err := getRequest(args[0])
92
// Create a forwarder.
98
// Forward the requests.
99
response, err := f.ForwardEcho(context.Background(), &forwarder.Config{
104
log.Fatalf("Error %s\n", err) // nolint: revive
108
// Log the output to stdout.
109
for _, line := range response.Output {
113
log.Infof("All requests succeeded")
118
func configureLogging(_ *cobra.Command, _ []string) error {
119
if err := log.Configure(loggingOptions); err != nil {
126
rootCmd.PersistentFlags().IntVar(&count, "count", common.DefaultCount, "Number of times to make the request")
127
rootCmd.PersistentFlags().IntVar(&qps, "qps", 0, "Queries per second")
128
rootCmd.PersistentFlags().DurationVar(&timeout, "timeout", common.DefaultRequestTimeout, "Request timeout")
129
rootCmd.PersistentFlags().StringVar(&uds, "uds", "",
130
"Specify the Unix Domain Socket to connect to")
131
rootCmd.PersistentFlags().StringSliceVarP(&headers, "header", "H", headers,
132
"A list of http headers (use Host for authority) - 'name: value', following curl syntax")
133
rootCmd.PersistentFlags().StringVar(&caFile, "ca", "", "CA root cert file")
134
rootCmd.PersistentFlags().StringVar(&msg, "msg", "HelloWorld",
135
"message to send (for websockets)")
136
rootCmd.PersistentFlags().StringVar(&expect, "expect", "",
137
"message to expect (for tcp)")
138
rootCmd.PersistentFlags().StringVar(&method, "method", "", "method to use (for HTTP)")
139
rootCmd.PersistentFlags().BoolVar(&http2, "http2", false,
140
"send http requests as HTTP2 with prior knowledge")
141
rootCmd.PersistentFlags().BoolVar(&http3, "http3", false,
142
"send http requests as HTTP 3")
143
rootCmd.PersistentFlags().BoolVarP(&insecureSkipVerify, "insecure-skip-verify", "k", insecureSkipVerify,
145
rootCmd.PersistentFlags().BoolVar(&serverFirst, "server-first", false,
146
"Treat as a server first protocol; do not send request until magic string is received")
147
rootCmd.PersistentFlags().BoolVarP(&followRedirects, "follow-redirects", "L", false,
148
"If enabled, will follow 3xx redirects with the Location header")
149
rootCmd.PersistentFlags().BoolVar(&newConnectionPerRequest, "new-connection-per-request", false,
150
"If enabled, a new connection will be made to the server for each individual request. "+
151
"If false, an attempt will be made to re-use the connection for the life of the forward request. "+
152
"This is automatically set for DNS, TCP, TLS, and WebSocket protocols.")
153
rootCmd.PersistentFlags().BoolVar(&forceDNSLookup, "force-dns-lookup", false,
154
"If enabled, each request will force a DNS lookup. Only applies if new-connection-per-request is also enabled.")
155
rootCmd.PersistentFlags().StringVar(&clientCert, "client-cert", "", "client certificate file to use for request")
156
rootCmd.PersistentFlags().StringVar(&clientKey, "client-key", "", "client certificate key file to use for request")
157
rootCmd.PersistentFlags().StringSliceVarP(&alpn, "alpn", "", nil, "alpn to set")
158
rootCmd.PersistentFlags().StringVarP(&serverName, "server-name", "", serverName, "server name to set")
160
rootCmd.PersistentFlags().StringVar(&hboneAddress, "hbone", "", "address to send HBONE request to")
161
rootCmd.PersistentFlags().StringSliceVarP(&hboneHeaders, "hbone-header", "M", hboneHeaders,
162
"A list of http headers for HBONE connection (use Host for authority) - 'name: value', following curl syntax")
163
rootCmd.PersistentFlags().StringVar(&hboneCaFile, "hbone-ca", "", "CA root cert file used for the HBONE request")
164
rootCmd.PersistentFlags().StringVar(&hboneClientCert, "hbone-client-cert", "", "client certificate file used for the HBONE request")
165
rootCmd.PersistentFlags().StringVar(&hboneClientKey, "hbone-client-key", "", "client certificate key file used for the HBONE request")
166
rootCmd.PersistentFlags().BoolVar(&hboneInsecureSkipVerify, "hbone-insecure-skip-verify", hboneInsecureSkipVerify, "skip TLS verification of HBONE request")
168
loggingOptions.AttachCobraFlags(rootCmd)
170
cmd.AddFlags(rootCmd)
173
// Adds http scheme to and url if not set. This matches curl logic
174
func defaultScheme(u string) string {
175
p, err := url.Parse(u)
185
func getRequest(url string) (*proto.ForwardEchoRequest, error) {
186
request := &proto.ForwardEchoRequest{
187
Url: defaultScheme(url),
188
TimeoutMicros: common.DurationToMicros(timeout),
194
ServerFirst: serverFirst,
195
FollowRedirects: followRedirects,
197
ServerName: serverName,
198
InsecureSkipVerify: insecureSkipVerify,
199
NewConnectionPerRequest: newConnectionPerRequest,
200
ForceDNSLookup: forceDNSLookup,
202
if len(hboneAddress) > 0 {
203
request.Hbone = &proto.HBONE{
204
Address: hboneAddress,
205
CertFile: hboneClientCert,
206
KeyFile: hboneClientKey,
207
CaCertFile: hboneCaFile,
208
InsecureSkipVerify: hboneInsecureSkipVerify,
210
for _, header := range hboneHeaders {
211
parts := strings.SplitN(header, ":", 2)
212
// require name:value format
214
return nil, fmt.Errorf("invalid header format: %q (want name:value)", header)
217
request.Hbone.Headers = append(request.Hbone.Headers, &proto.Header{
219
Value: strings.Trim(parts[1], " "),
225
request.ExpectedResponse = &wrappers.StringValue{Value: expect}
229
request.Alpn = &proto.Alpn{Value: alpn}
232
for _, header := range headers {
233
parts := strings.Split(header, ":")
235
// require name:value format
237
return nil, fmt.Errorf("invalid header format: %q (want name:value)", header)
240
request.Headers = append(request.Headers, &proto.Header{
242
Value: strings.Trim(parts[1], " "),
246
if clientCert != "" && clientKey != "" {
247
request.CertFile = clientCert
248
request.KeyFile = clientKey
251
request.CaCertFile = caFile
257
if err := rootCmd.Execute(); err != nil {