snapshot of https://github.com/grpc/grpc-go.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
This commit is contained in:
69
interop/alts/client/client.go
Normal file
69
interop/alts/client/client.go
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2018 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// This binary can only run on Google Cloud Platform (GCP).
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/alts"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
testpb "google.golang.org/grpc/interop/grpc_testing"
|
||||
)
|
||||
|
||||
var (
|
||||
hsAddr = flag.String("alts_handshaker_service_address", "", "ALTS handshaker gRPC service address")
|
||||
serverAddr = flag.String("server_address", ":8080", "The port on which the server is listening")
|
||||
|
||||
logger = grpclog.Component("interop")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
opts := alts.DefaultClientOptions()
|
||||
if *hsAddr != "" {
|
||||
opts.HandshakerServiceAddress = *hsAddr
|
||||
}
|
||||
altsTC := alts.NewClientCreds(opts)
|
||||
conn, err := grpc.NewClient(*serverAddr, grpc.WithTransportCredentials(altsTC))
|
||||
if err != nil {
|
||||
logger.Fatalf("grpc.NewClient(%q) = %v", *serverAddr, err)
|
||||
}
|
||||
defer conn.Close()
|
||||
grpcClient := testgrpc.NewTestServiceClient(conn)
|
||||
|
||||
// Call the EmptyCall API.
|
||||
ctx := context.Background()
|
||||
request := &testpb.Empty{}
|
||||
// Block until the server is ready.
|
||||
if _, err := grpcClient.EmptyCall(ctx, request, grpc.WaitForReady(true)); err != nil {
|
||||
logger.Fatalf("grpc Client: EmptyCall(_, %v) failed: %v", request, err)
|
||||
}
|
||||
logger.Info("grpc Client: empty call succeeded")
|
||||
|
||||
// This sleep prevents the connection from being abruptly disconnected
|
||||
// when running this binary (along with grpc_server) on GCP dev cluster.
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
88
interop/alts/server/server.go
Normal file
88
interop/alts/server/server.go
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2018 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// This binary can only run on Google Cloud Platform (GCP).
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/alts"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/interop"
|
||||
"google.golang.org/grpc/tap"
|
||||
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
)
|
||||
|
||||
const (
|
||||
udsAddrPrefix = "unix:"
|
||||
)
|
||||
|
||||
var (
|
||||
hsAddr = flag.String("alts_handshaker_service_address", "", "ALTS handshaker gRPC service address")
|
||||
serverAddr = flag.String("server_address", ":8080", "The address on which the server is listening. Only two types of addresses are supported, 'host:port' and 'unix:/path'.")
|
||||
|
||||
logger = grpclog.Component("interop")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
// If the server address starts with `unix:`, then we have a UDS address.
|
||||
network := "tcp"
|
||||
address := *serverAddr
|
||||
if strings.HasPrefix(address, udsAddrPrefix) {
|
||||
network = "unix"
|
||||
address = strings.TrimPrefix(address, udsAddrPrefix)
|
||||
}
|
||||
lis, err := net.Listen(network, address)
|
||||
if err != nil {
|
||||
logger.Fatalf("gRPC Server: failed to start the server at %v: %v", address, err)
|
||||
}
|
||||
opts := alts.DefaultServerOptions()
|
||||
if *hsAddr != "" {
|
||||
opts.HandshakerServiceAddress = *hsAddr
|
||||
}
|
||||
altsTC := alts.NewServerCreds(opts)
|
||||
grpcServer := grpc.NewServer(grpc.Creds(altsTC), grpc.InTapHandle(authz))
|
||||
testgrpc.RegisterTestServiceServer(grpcServer, interop.NewTestServer())
|
||||
grpcServer.Serve(lis)
|
||||
}
|
||||
|
||||
// authz shows how to access client information at the server side to perform
|
||||
// application-layer authorization checks.
|
||||
func authz(ctx context.Context, info *tap.Info) (context.Context, error) {
|
||||
authInfo, err := alts.AuthInfoFromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Access all alts.AuthInfo data:
|
||||
logger.Infof("authInfo.ApplicationProtocol() = %v", authInfo.ApplicationProtocol())
|
||||
logger.Infof("authInfo.RecordProtocol() = %v", authInfo.RecordProtocol())
|
||||
logger.Infof("authInfo.SecurityLevel() = %v", authInfo.SecurityLevel())
|
||||
logger.Infof("authInfo.PeerServiceAccount() = %v", authInfo.PeerServiceAccount())
|
||||
logger.Infof("authInfo.LocalServiceAccount() = %v", authInfo.LocalServiceAccount())
|
||||
logger.Infof("authInfo.PeerRPCVersions() = %v", authInfo.PeerRPCVersions())
|
||||
logger.Infof("info.FullMethodName = %v", info.FullMethodName)
|
||||
return ctx, nil
|
||||
}
|
||||
408
interop/client/client.go
Normal file
408
interop/client/client.go
Normal file
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2014 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Binary client is an interop client.
|
||||
//
|
||||
// See interop test case descriptions [here].
|
||||
//
|
||||
// [here]: https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/alts"
|
||||
"google.golang.org/grpc/credentials/google"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/credentials/oauth"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/interop"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/resolver"
|
||||
"google.golang.org/grpc/testdata"
|
||||
|
||||
_ "google.golang.org/grpc/balancer/grpclb" // Register the grpclb load balancing policy.
|
||||
_ "google.golang.org/grpc/balancer/rls" // Register the RLS load balancing policy.
|
||||
"google.golang.org/grpc/xds/googledirectpath" // Register xDS resolver required for c2p directpath.
|
||||
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
)
|
||||
|
||||
const (
|
||||
googleDefaultCredsName = "google_default_credentials"
|
||||
computeEngineCredsName = "compute_engine_channel_creds"
|
||||
)
|
||||
|
||||
var (
|
||||
caFile = flag.String("ca_file", "", "The file containing the CA root cert file")
|
||||
useTLS = flag.Bool("use_tls", false, "Connection uses TLS if true")
|
||||
useALTS = flag.Bool("use_alts", false, "Connection uses ALTS if true (this option can only be used on GCP)")
|
||||
customCredentialsType = flag.String("custom_credentials_type", "", "Custom creds to use, excluding TLS or ALTS")
|
||||
altsHSAddr = flag.String("alts_handshaker_service_address", "", "ALTS handshaker gRPC service address")
|
||||
testCA = flag.Bool("use_test_ca", false, "Whether to replace platform root CAs with test CA as the CA root")
|
||||
serviceAccountKeyFile = flag.String("service_account_key_file", "", "Path to service account json key file")
|
||||
oauthScope = flag.String("oauth_scope", "", "The scope for OAuth2 tokens")
|
||||
defaultServiceAccount = flag.String("default_service_account", "", "Email of GCE default service account")
|
||||
googleC2PUniverseDomain = flag.String("google_c2p_universe_domain", "", "Universe domain for google-c2p resolve")
|
||||
serverHost = flag.String("server_host", "localhost", "The server host name")
|
||||
serverPort = flag.Int("server_port", 10000, "The server port number")
|
||||
serviceConfigJSON = flag.String("service_config_json", "", "Disables service config lookups and sets the provided string as the default service config.")
|
||||
soakIterations = flag.Int("soak_iterations", 10, "The number of iterations to use for the two soak tests: rpc_soak and channel_soak")
|
||||
soakMaxFailures = flag.Int("soak_max_failures", 0, "The number of iterations in soak tests that are allowed to fail (either due to non-OK status code or exceeding the per-iteration max acceptable latency).")
|
||||
soakPerIterationMaxAcceptableLatencyMs = flag.Int("soak_per_iteration_max_acceptable_latency_ms", 1000, "The number of milliseconds a single iteration in the two soak tests (rpc_soak and channel_soak) should take.")
|
||||
soakOverallTimeoutSeconds = flag.Int("soak_overall_timeout_seconds", 10, "The overall number of seconds after which a soak test should stop and fail, if the desired number of iterations have not yet completed.")
|
||||
soakMinTimeMsBetweenRPCs = flag.Int("soak_min_time_ms_between_rpcs", 0, "The minimum time in milliseconds between consecutive RPCs in a soak test (rpc_soak or channel_soak), useful for limiting QPS")
|
||||
soakRequestSize = flag.Int("soak_request_size", 271828, "The request size in a soak RPC. The default value is set based on the interop large unary test case.")
|
||||
soakResponseSize = flag.Int("soak_response_size", 314159, "The response size in a soak RPC. The default value is set based on the interop large unary test case.")
|
||||
soakNumThreads = flag.Int("soak_num_threads", 1, "The number of threads for concurrent execution of the soak tests (rpc_soak or channel_soak). The default value is set based on the interop large unary test case.")
|
||||
tlsServerName = flag.String("server_host_override", "", "The server name used to verify the hostname returned by TLS handshake if it is not empty. Otherwise, --server_host is used.")
|
||||
additionalMetadata = flag.String("additional_metadata", "", "Additional metadata to send in each request, as a semicolon-separated list of key:value pairs.")
|
||||
testCase = flag.String("test_case", "large_unary",
|
||||
`Configure different test cases. Valid options are:
|
||||
empty_unary : empty (zero bytes) request and response;
|
||||
large_unary : single request and (large) response;
|
||||
client_streaming : request streaming with single response;
|
||||
server_streaming : single request with response streaming;
|
||||
ping_pong : full-duplex streaming;
|
||||
empty_stream : full-duplex streaming with zero message;
|
||||
timeout_on_sleeping_server: fullduplex streaming on a sleeping server;
|
||||
compute_engine_creds: large_unary with compute engine auth;
|
||||
service_account_creds: large_unary with service account auth;
|
||||
jwt_token_creds: large_unary with jwt token auth;
|
||||
per_rpc_creds: large_unary with per rpc token;
|
||||
oauth2_auth_token: large_unary with oauth2 token auth;
|
||||
google_default_credentials: large_unary with google default credentials
|
||||
compute_engine_channel_credentials: large_unary with compute engine creds
|
||||
cancel_after_begin: cancellation after metadata has been sent but before payloads are sent;
|
||||
cancel_after_first_response: cancellation after receiving 1st message from the server;
|
||||
status_code_and_message: status code propagated back to client;
|
||||
special_status_message: Unicode and whitespace is correctly processed in status message;
|
||||
custom_metadata: server will echo custom metadata;
|
||||
unimplemented_method: client attempts to call unimplemented method;
|
||||
unimplemented_service: client attempts to call unimplemented service;
|
||||
pick_first_unary: all requests are sent to one server despite multiple servers are resolved;
|
||||
orca_per_rpc: the client verifies ORCA per-RPC metrics are provided;
|
||||
orca_oob: the client verifies ORCA out-of-band metrics are provided.`)
|
||||
|
||||
logger = grpclog.Component("interop")
|
||||
)
|
||||
|
||||
type credsMode uint8
|
||||
|
||||
const (
|
||||
credsNone credsMode = iota
|
||||
credsTLS
|
||||
credsALTS
|
||||
credsGoogleDefaultCreds
|
||||
credsComputeEngineCreds
|
||||
)
|
||||
|
||||
// Parses the --additional_metadata flag and returns metadata to send on each RPC,
|
||||
// formatted as per https://pkg.go.dev/google.golang.org/grpc/metadata#Pairs.
|
||||
// Allow any character but semicolons in values. If the flag is empty, return a nil map.
|
||||
func parseAdditionalMetadataFlag() []string {
|
||||
if len(*additionalMetadata) == 0 {
|
||||
return nil
|
||||
}
|
||||
r := *additionalMetadata
|
||||
addMd := make([]string, 0)
|
||||
for len(r) > 0 {
|
||||
i := strings.Index(r, ":")
|
||||
if i < 0 {
|
||||
logger.Fatalf("Error parsing --additional_metadata flag: missing colon separator")
|
||||
}
|
||||
addMd = append(addMd, r[:i]) // append key
|
||||
r = r[i+1:]
|
||||
i = strings.Index(r, ";")
|
||||
// append value
|
||||
if i < 0 {
|
||||
addMd = append(addMd, r)
|
||||
break
|
||||
}
|
||||
addMd = append(addMd, r[:i])
|
||||
r = r[i+1:]
|
||||
}
|
||||
return addMd
|
||||
}
|
||||
|
||||
// createSoakTestConfig creates a shared configuration structure for soak tests.
|
||||
func createBaseSoakConfig(serverAddr string) interop.SoakTestConfig {
|
||||
return interop.SoakTestConfig{
|
||||
RequestSize: *soakRequestSize,
|
||||
ResponseSize: *soakResponseSize,
|
||||
PerIterationMaxAcceptableLatency: time.Duration(*soakPerIterationMaxAcceptableLatencyMs) * time.Millisecond,
|
||||
MinTimeBetweenRPCs: time.Duration(*soakMinTimeMsBetweenRPCs) * time.Millisecond,
|
||||
OverallTimeout: time.Duration(*soakOverallTimeoutSeconds) * time.Second,
|
||||
ServerAddr: serverAddr,
|
||||
NumWorkers: *soakNumThreads,
|
||||
Iterations: *soakIterations,
|
||||
MaxFailures: *soakMaxFailures,
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
logger.Infof("Client running with test case %q", *testCase)
|
||||
var useGDC bool // use google default creds
|
||||
var useCEC bool // use compute engine creds
|
||||
if *customCredentialsType != "" {
|
||||
switch *customCredentialsType {
|
||||
case googleDefaultCredsName:
|
||||
useGDC = true
|
||||
case computeEngineCredsName:
|
||||
useCEC = true
|
||||
default:
|
||||
logger.Fatalf("If set, custom_credentials_type can only be set to one of %v or %v",
|
||||
googleDefaultCredsName, computeEngineCredsName)
|
||||
}
|
||||
}
|
||||
if (*useTLS && *useALTS) || (*useTLS && useGDC) || (*useALTS && useGDC) || (*useTLS && useCEC) || (*useALTS && useCEC) {
|
||||
logger.Fatalf("only one of TLS, ALTS, google default creds, or compute engine creds can be used")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
var credsChosen credsMode
|
||||
switch {
|
||||
case *useTLS:
|
||||
credsChosen = credsTLS
|
||||
case *useALTS:
|
||||
credsChosen = credsALTS
|
||||
case useGDC:
|
||||
credsChosen = credsGoogleDefaultCreds
|
||||
case useCEC:
|
||||
credsChosen = credsComputeEngineCreds
|
||||
}
|
||||
|
||||
resolver.SetDefaultScheme("dns")
|
||||
if len(*googleC2PUniverseDomain) > 0 {
|
||||
if err := googledirectpath.SetUniverseDomain(*googleC2PUniverseDomain); err != nil {
|
||||
log.Fatalf("googlec2p.SetUniverseDomain(%s) failed: %v", *googleC2PUniverseDomain, err)
|
||||
}
|
||||
}
|
||||
serverAddr := *serverHost
|
||||
if *serverPort != 0 {
|
||||
serverAddr = net.JoinHostPort(*serverHost, strconv.Itoa(*serverPort))
|
||||
}
|
||||
var opts []grpc.DialOption
|
||||
switch credsChosen {
|
||||
case credsTLS:
|
||||
var roots *x509.CertPool
|
||||
if *testCA {
|
||||
if *caFile == "" {
|
||||
*caFile = testdata.Path("ca.pem")
|
||||
}
|
||||
b, err := os.ReadFile(*caFile)
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to read root certificate file %q: %v", *caFile, err)
|
||||
}
|
||||
roots = x509.NewCertPool()
|
||||
if !roots.AppendCertsFromPEM(b) {
|
||||
logger.Fatalf("Failed to append certificates: %s", string(b))
|
||||
}
|
||||
}
|
||||
var creds credentials.TransportCredentials
|
||||
if *tlsServerName != "" {
|
||||
creds = credentials.NewClientTLSFromCert(roots, *tlsServerName)
|
||||
} else {
|
||||
creds = credentials.NewTLS(&tls.Config{RootCAs: roots})
|
||||
}
|
||||
opts = append(opts, grpc.WithTransportCredentials(creds))
|
||||
case credsALTS:
|
||||
altsOpts := alts.DefaultClientOptions()
|
||||
if *altsHSAddr != "" {
|
||||
altsOpts.HandshakerServiceAddress = *altsHSAddr
|
||||
}
|
||||
altsTC := alts.NewClientCreds(altsOpts)
|
||||
opts = append(opts, grpc.WithTransportCredentials(altsTC))
|
||||
case credsGoogleDefaultCreds:
|
||||
opts = append(opts, grpc.WithCredentialsBundle(google.NewDefaultCredentials()))
|
||||
case credsComputeEngineCreds:
|
||||
opts = append(opts, grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()))
|
||||
case credsNone:
|
||||
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
default:
|
||||
logger.Fatal("Invalid creds")
|
||||
}
|
||||
if credsChosen == credsTLS {
|
||||
if *testCase == "compute_engine_creds" {
|
||||
opts = append(opts, grpc.WithPerRPCCredentials(oauth.NewComputeEngine()))
|
||||
} else if *testCase == "service_account_creds" {
|
||||
jwtCreds, err := oauth.NewServiceAccountFromFile(*serviceAccountKeyFile, *oauthScope)
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to create JWT credentials: %v", err)
|
||||
}
|
||||
opts = append(opts, grpc.WithPerRPCCredentials(jwtCreds))
|
||||
} else if *testCase == "jwt_token_creds" {
|
||||
jwtCreds, err := oauth.NewJWTAccessFromFile(*serviceAccountKeyFile)
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to create JWT credentials: %v", err)
|
||||
}
|
||||
opts = append(opts, grpc.WithPerRPCCredentials(jwtCreds))
|
||||
} else if *testCase == "oauth2_auth_token" {
|
||||
opts = append(opts, grpc.WithPerRPCCredentials(oauth.TokenSource{TokenSource: oauth2.StaticTokenSource(interop.GetToken(ctx, *serviceAccountKeyFile, *oauthScope))}))
|
||||
}
|
||||
}
|
||||
if len(*serviceConfigJSON) > 0 {
|
||||
opts = append(opts, grpc.WithDisableServiceConfig(), grpc.WithDefaultServiceConfig(*serviceConfigJSON))
|
||||
}
|
||||
if addMd := parseAdditionalMetadataFlag(); addMd != nil {
|
||||
unaryAddMd := func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, addMd...)
|
||||
return invoker(ctx, method, req, reply, cc, opts...)
|
||||
}
|
||||
streamingAddMd := func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, addMd...)
|
||||
return streamer(ctx, desc, cc, method, opts...)
|
||||
}
|
||||
opts = append(opts, grpc.WithUnaryInterceptor(unaryAddMd), grpc.WithStreamInterceptor(streamingAddMd))
|
||||
}
|
||||
conn, err := grpc.NewClient(serverAddr, opts...)
|
||||
if err != nil {
|
||||
logger.Fatalf("grpc.NewClient(%q) = %v", serverAddr, err)
|
||||
}
|
||||
defer conn.Close()
|
||||
tc := testgrpc.NewTestServiceClient(conn)
|
||||
ctxWithDeadline, cancel := context.WithTimeout(ctx, time.Duration(*soakOverallTimeoutSeconds)*time.Second)
|
||||
defer cancel()
|
||||
switch *testCase {
|
||||
case "empty_unary":
|
||||
interop.DoEmptyUnaryCall(ctx, tc)
|
||||
logger.Infoln("EmptyUnaryCall done")
|
||||
case "large_unary":
|
||||
interop.DoLargeUnaryCall(ctx, tc)
|
||||
logger.Infoln("LargeUnaryCall done")
|
||||
case "client_streaming":
|
||||
interop.DoClientStreaming(ctx, tc)
|
||||
logger.Infoln("ClientStreaming done")
|
||||
case "server_streaming":
|
||||
interop.DoServerStreaming(ctx, tc)
|
||||
logger.Infoln("ServerStreaming done")
|
||||
case "ping_pong":
|
||||
interop.DoPingPong(ctx, tc)
|
||||
logger.Infoln("Pingpong done")
|
||||
case "empty_stream":
|
||||
interop.DoEmptyStream(ctx, tc)
|
||||
logger.Infoln("Emptystream done")
|
||||
case "timeout_on_sleeping_server":
|
||||
interop.DoTimeoutOnSleepingServer(ctx, tc)
|
||||
logger.Infoln("TimeoutOnSleepingServer done")
|
||||
case "compute_engine_creds":
|
||||
if credsChosen != credsTLS {
|
||||
logger.Fatalf("TLS credentials need to be set for compute_engine_creds test case.")
|
||||
}
|
||||
interop.DoComputeEngineCreds(ctx, tc, *defaultServiceAccount, *oauthScope)
|
||||
logger.Infoln("ComputeEngineCreds done")
|
||||
case "service_account_creds":
|
||||
if credsChosen != credsTLS {
|
||||
logger.Fatalf("TLS credentials need to be set for service_account_creds test case.")
|
||||
}
|
||||
interop.DoServiceAccountCreds(ctx, tc, *serviceAccountKeyFile, *oauthScope)
|
||||
logger.Infoln("ServiceAccountCreds done")
|
||||
case "jwt_token_creds":
|
||||
if credsChosen != credsTLS {
|
||||
logger.Fatalf("TLS credentials need to be set for jwt_token_creds test case.")
|
||||
}
|
||||
interop.DoJWTTokenCreds(ctx, tc, *serviceAccountKeyFile)
|
||||
logger.Infoln("JWTtokenCreds done")
|
||||
case "per_rpc_creds":
|
||||
if credsChosen != credsTLS {
|
||||
logger.Fatalf("TLS credentials need to be set for per_rpc_creds test case.")
|
||||
}
|
||||
interop.DoPerRPCCreds(ctx, tc, *serviceAccountKeyFile, *oauthScope)
|
||||
logger.Infoln("PerRPCCreds done")
|
||||
case "oauth2_auth_token":
|
||||
if credsChosen != credsTLS {
|
||||
logger.Fatalf("TLS credentials need to be set for oauth2_auth_token test case.")
|
||||
}
|
||||
interop.DoOauth2TokenCreds(ctx, tc, *serviceAccountKeyFile, *oauthScope)
|
||||
logger.Infoln("Oauth2TokenCreds done")
|
||||
case "google_default_credentials":
|
||||
if credsChosen != credsGoogleDefaultCreds {
|
||||
logger.Fatalf("GoogleDefaultCredentials need to be set for google_default_credentials test case.")
|
||||
}
|
||||
interop.DoGoogleDefaultCredentials(ctx, tc, *defaultServiceAccount)
|
||||
logger.Infoln("GoogleDefaultCredentials done")
|
||||
case "compute_engine_channel_credentials":
|
||||
if credsChosen != credsComputeEngineCreds {
|
||||
logger.Fatalf("ComputeEngineCreds need to be set for compute_engine_channel_credentials test case.")
|
||||
}
|
||||
interop.DoComputeEngineChannelCredentials(ctx, tc, *defaultServiceAccount)
|
||||
logger.Infoln("ComputeEngineChannelCredentials done")
|
||||
case "cancel_after_begin":
|
||||
interop.DoCancelAfterBegin(ctx, tc)
|
||||
logger.Infoln("CancelAfterBegin done")
|
||||
case "cancel_after_first_response":
|
||||
interop.DoCancelAfterFirstResponse(ctx, tc)
|
||||
logger.Infoln("CancelAfterFirstResponse done")
|
||||
case "status_code_and_message":
|
||||
interop.DoStatusCodeAndMessage(ctx, tc)
|
||||
logger.Infoln("StatusCodeAndMessage done")
|
||||
case "special_status_message":
|
||||
interop.DoSpecialStatusMessage(ctx, tc)
|
||||
logger.Infoln("SpecialStatusMessage done")
|
||||
case "custom_metadata":
|
||||
interop.DoCustomMetadata(ctx, tc)
|
||||
logger.Infoln("CustomMetadata done")
|
||||
case "unimplemented_method":
|
||||
interop.DoUnimplementedMethod(ctx, conn)
|
||||
logger.Infoln("UnimplementedMethod done")
|
||||
case "unimplemented_service":
|
||||
interop.DoUnimplementedService(ctx, testgrpc.NewUnimplementedServiceClient(conn))
|
||||
logger.Infoln("UnimplementedService done")
|
||||
case "pick_first_unary":
|
||||
interop.DoPickFirstUnary(ctx, tc)
|
||||
logger.Infoln("PickFirstUnary done")
|
||||
case "rpc_soak":
|
||||
rpcSoakConfig := createBaseSoakConfig(serverAddr)
|
||||
rpcSoakConfig.ChannelForTest = func() (*grpc.ClientConn, func()) { return conn, func() {} }
|
||||
interop.DoSoakTest(ctxWithDeadline, rpcSoakConfig)
|
||||
logger.Infoln("RpcSoak done")
|
||||
case "channel_soak":
|
||||
channelSoakConfig := createBaseSoakConfig(serverAddr)
|
||||
channelSoakConfig.ChannelForTest = func() (*grpc.ClientConn, func()) {
|
||||
cc, err := grpc.NewClient(serverAddr, opts...)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create shared channel: %v", err)
|
||||
}
|
||||
return cc, func() { cc.Close() }
|
||||
}
|
||||
interop.DoSoakTest(ctxWithDeadline, channelSoakConfig)
|
||||
logger.Infoln("ChannelSoak done")
|
||||
case "orca_per_rpc":
|
||||
interop.DoORCAPerRPCTest(ctx, tc)
|
||||
logger.Infoln("ORCAPerRPC done")
|
||||
case "orca_oob":
|
||||
interop.DoORCAOOBTest(ctx, tc)
|
||||
logger.Infoln("ORCAOOB done")
|
||||
default:
|
||||
logger.Fatal("Unsupported test case: ", *testCase)
|
||||
}
|
||||
}
|
||||
83
interop/fake_grpclb/fake_grpclb.go
Normal file
83
interop/fake_grpclb/fake_grpclb.go
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2018 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// This file is for testing only. Runs a fake grpclb balancer server.
|
||||
// The name of the service to load balance for and the addresses
|
||||
// of that service are provided by command line flags.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/alts"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/internal/testutils/fakegrpclb"
|
||||
"google.golang.org/grpc/testdata"
|
||||
)
|
||||
|
||||
var (
|
||||
port = flag.Int("port", 10000, "Port to listen on.")
|
||||
backendAddrs = flag.String("backend_addrs", "", "Comma separated list of backend IP/port addresses.")
|
||||
useALTS = flag.Bool("use_alts", false, "Listen on ALTS credentials.")
|
||||
useTLS = flag.Bool("use_tls", false, "Listen on TLS credentials, using a test certificate.")
|
||||
shortStream = flag.Bool("short_stream", false, "End the balancer stream immediately after sending the first server list.")
|
||||
serviceName = flag.String("service_name", "UNSET", "Name of the service being load balanced for.")
|
||||
|
||||
logger = grpclog.Component("interop")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
var opts []grpc.ServerOption
|
||||
if *useTLS {
|
||||
certFile := testdata.Path("server1.pem")
|
||||
keyFile := testdata.Path("server1.key")
|
||||
creds, err := credentials.NewServerTLSFromFile(certFile, keyFile)
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to generate credentials: %v", err)
|
||||
}
|
||||
opts = append(opts, grpc.Creds(creds))
|
||||
} else if *useALTS {
|
||||
altsOpts := alts.DefaultServerOptions()
|
||||
altsTC := alts.NewServerCreds(altsOpts)
|
||||
opts = append(opts, grpc.Creds(altsTC))
|
||||
}
|
||||
|
||||
rawBackendAddrs := strings.Split(*backendAddrs, ",")
|
||||
server, err := fakegrpclb.NewServer(fakegrpclb.ServerParams{
|
||||
ListenPort: *port,
|
||||
ServerOptions: opts,
|
||||
LoadBalancedServiceName: *serviceName,
|
||||
LoadBalancedServicePort: 443, // TODO: make this configurable?
|
||||
BackendAddresses: rawBackendAddrs,
|
||||
ShortStream: *shortStream,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to create balancer server: %v", err)
|
||||
}
|
||||
|
||||
// Serve() starts serving and blocks until Stop() is called. We don't need to
|
||||
// call Stop() here since we want the server to run until we are killed.
|
||||
if err := server.Serve(); err != nil {
|
||||
logger.Fatalf("Failed to start balancer server: %v", err)
|
||||
}
|
||||
}
|
||||
97
interop/grpc_testing/benchmark_service.pb.go
Normal file
97
interop/grpc_testing/benchmark_service.pb.go
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// An integration test service that covers all the method signature permutations
|
||||
// of unary/streaming requests/responses.
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.12
|
||||
// protoc v5.27.1
|
||||
// source: grpc/testing/benchmark_service.proto
|
||||
|
||||
package grpc_testing
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
var File_grpc_testing_benchmark_service_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_grpc_testing_benchmark_service_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"$grpc/testing/benchmark_service.proto\x12\fgrpc.testing\x1a\x1bgrpc/testing/messages.proto2\xa6\x03\n" +
|
||||
"\x10BenchmarkService\x12F\n" +
|
||||
"\tUnaryCall\x12\x1b.grpc.testing.SimpleRequest\x1a\x1c.grpc.testing.SimpleResponse\x12N\n" +
|
||||
"\rStreamingCall\x12\x1b.grpc.testing.SimpleRequest\x1a\x1c.grpc.testing.SimpleResponse(\x010\x01\x12R\n" +
|
||||
"\x13StreamingFromClient\x12\x1b.grpc.testing.SimpleRequest\x1a\x1c.grpc.testing.SimpleResponse(\x01\x12R\n" +
|
||||
"\x13StreamingFromServer\x12\x1b.grpc.testing.SimpleRequest\x1a\x1c.grpc.testing.SimpleResponse0\x01\x12R\n" +
|
||||
"\x11StreamingBothWays\x12\x1b.grpc.testing.SimpleRequest\x1a\x1c.grpc.testing.SimpleResponse(\x010\x01B*\n" +
|
||||
"\x0fio.grpc.testingB\x15BenchmarkServiceProtoP\x01b\x06proto3"
|
||||
|
||||
var file_grpc_testing_benchmark_service_proto_goTypes = []any{
|
||||
(*SimpleRequest)(nil), // 0: grpc.testing.SimpleRequest
|
||||
(*SimpleResponse)(nil), // 1: grpc.testing.SimpleResponse
|
||||
}
|
||||
var file_grpc_testing_benchmark_service_proto_depIdxs = []int32{
|
||||
0, // 0: grpc.testing.BenchmarkService.UnaryCall:input_type -> grpc.testing.SimpleRequest
|
||||
0, // 1: grpc.testing.BenchmarkService.StreamingCall:input_type -> grpc.testing.SimpleRequest
|
||||
0, // 2: grpc.testing.BenchmarkService.StreamingFromClient:input_type -> grpc.testing.SimpleRequest
|
||||
0, // 3: grpc.testing.BenchmarkService.StreamingFromServer:input_type -> grpc.testing.SimpleRequest
|
||||
0, // 4: grpc.testing.BenchmarkService.StreamingBothWays:input_type -> grpc.testing.SimpleRequest
|
||||
1, // 5: grpc.testing.BenchmarkService.UnaryCall:output_type -> grpc.testing.SimpleResponse
|
||||
1, // 6: grpc.testing.BenchmarkService.StreamingCall:output_type -> grpc.testing.SimpleResponse
|
||||
1, // 7: grpc.testing.BenchmarkService.StreamingFromClient:output_type -> grpc.testing.SimpleResponse
|
||||
1, // 8: grpc.testing.BenchmarkService.StreamingFromServer:output_type -> grpc.testing.SimpleResponse
|
||||
1, // 9: grpc.testing.BenchmarkService.StreamingBothWays:output_type -> grpc.testing.SimpleResponse
|
||||
5, // [5:10] is the sub-list for method output_type
|
||||
0, // [0:5] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_grpc_testing_benchmark_service_proto_init() }
|
||||
func file_grpc_testing_benchmark_service_proto_init() {
|
||||
if File_grpc_testing_benchmark_service_proto != nil {
|
||||
return
|
||||
}
|
||||
file_grpc_testing_messages_proto_init()
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_grpc_testing_benchmark_service_proto_rawDesc), len(file_grpc_testing_benchmark_service_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 0,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_grpc_testing_benchmark_service_proto_goTypes,
|
||||
DependencyIndexes: file_grpc_testing_benchmark_service_proto_depIdxs,
|
||||
}.Build()
|
||||
File_grpc_testing_benchmark_service_proto = out.File
|
||||
file_grpc_testing_benchmark_service_proto_goTypes = nil
|
||||
file_grpc_testing_benchmark_service_proto_depIdxs = nil
|
||||
}
|
||||
297
interop/grpc_testing/benchmark_service_grpc.pb.go
Normal file
297
interop/grpc_testing/benchmark_service_grpc.pb.go
Normal file
@@ -0,0 +1,297 @@
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// An integration test service that covers all the method signature permutations
|
||||
// of unary/streaming requests/responses.
|
||||
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc v5.27.1
|
||||
// source: grpc/testing/benchmark_service.proto
|
||||
|
||||
package grpc_testing
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
BenchmarkService_UnaryCall_FullMethodName = "/grpc.testing.BenchmarkService/UnaryCall"
|
||||
BenchmarkService_StreamingCall_FullMethodName = "/grpc.testing.BenchmarkService/StreamingCall"
|
||||
BenchmarkService_StreamingFromClient_FullMethodName = "/grpc.testing.BenchmarkService/StreamingFromClient"
|
||||
BenchmarkService_StreamingFromServer_FullMethodName = "/grpc.testing.BenchmarkService/StreamingFromServer"
|
||||
BenchmarkService_StreamingBothWays_FullMethodName = "/grpc.testing.BenchmarkService/StreamingBothWays"
|
||||
)
|
||||
|
||||
// BenchmarkServiceClient is the client API for BenchmarkService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type BenchmarkServiceClient interface {
|
||||
// One request followed by one response.
|
||||
// The server returns the client payload as-is.
|
||||
UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error)
|
||||
// Repeated sequence of one request followed by one response.
|
||||
// Should be called streaming ping-pong
|
||||
// The server returns the client payload as-is on each response
|
||||
StreamingCall(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SimpleRequest, SimpleResponse], error)
|
||||
// Single-sided unbounded streaming from client to server
|
||||
// The server returns the client payload as-is once the client does WritesDone
|
||||
StreamingFromClient(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[SimpleRequest, SimpleResponse], error)
|
||||
// Single-sided unbounded streaming from server to client
|
||||
// The server repeatedly returns the client payload as-is
|
||||
StreamingFromServer(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SimpleResponse], error)
|
||||
// Two-sided unbounded streaming between server to client
|
||||
// Both sides send the content of their own choice to the other
|
||||
StreamingBothWays(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SimpleRequest, SimpleResponse], error)
|
||||
}
|
||||
|
||||
type benchmarkServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewBenchmarkServiceClient(cc grpc.ClientConnInterface) BenchmarkServiceClient {
|
||||
return &benchmarkServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *benchmarkServiceClient) UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SimpleResponse)
|
||||
err := c.cc.Invoke(ctx, BenchmarkService_UnaryCall_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *benchmarkServiceClient) StreamingCall(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SimpleRequest, SimpleResponse], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &BenchmarkService_ServiceDesc.Streams[0], BenchmarkService_StreamingCall_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[SimpleRequest, SimpleResponse]{ClientStream: stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type BenchmarkService_StreamingCallClient = grpc.BidiStreamingClient[SimpleRequest, SimpleResponse]
|
||||
|
||||
func (c *benchmarkServiceClient) StreamingFromClient(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[SimpleRequest, SimpleResponse], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &BenchmarkService_ServiceDesc.Streams[1], BenchmarkService_StreamingFromClient_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[SimpleRequest, SimpleResponse]{ClientStream: stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type BenchmarkService_StreamingFromClientClient = grpc.ClientStreamingClient[SimpleRequest, SimpleResponse]
|
||||
|
||||
func (c *benchmarkServiceClient) StreamingFromServer(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SimpleResponse], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &BenchmarkService_ServiceDesc.Streams[2], BenchmarkService_StreamingFromServer_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[SimpleRequest, SimpleResponse]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type BenchmarkService_StreamingFromServerClient = grpc.ServerStreamingClient[SimpleResponse]
|
||||
|
||||
func (c *benchmarkServiceClient) StreamingBothWays(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SimpleRequest, SimpleResponse], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &BenchmarkService_ServiceDesc.Streams[3], BenchmarkService_StreamingBothWays_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[SimpleRequest, SimpleResponse]{ClientStream: stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type BenchmarkService_StreamingBothWaysClient = grpc.BidiStreamingClient[SimpleRequest, SimpleResponse]
|
||||
|
||||
// BenchmarkServiceServer is the server API for BenchmarkService service.
|
||||
// All implementations must embed UnimplementedBenchmarkServiceServer
|
||||
// for forward compatibility.
|
||||
type BenchmarkServiceServer interface {
|
||||
// One request followed by one response.
|
||||
// The server returns the client payload as-is.
|
||||
UnaryCall(context.Context, *SimpleRequest) (*SimpleResponse, error)
|
||||
// Repeated sequence of one request followed by one response.
|
||||
// Should be called streaming ping-pong
|
||||
// The server returns the client payload as-is on each response
|
||||
StreamingCall(grpc.BidiStreamingServer[SimpleRequest, SimpleResponse]) error
|
||||
// Single-sided unbounded streaming from client to server
|
||||
// The server returns the client payload as-is once the client does WritesDone
|
||||
StreamingFromClient(grpc.ClientStreamingServer[SimpleRequest, SimpleResponse]) error
|
||||
// Single-sided unbounded streaming from server to client
|
||||
// The server repeatedly returns the client payload as-is
|
||||
StreamingFromServer(*SimpleRequest, grpc.ServerStreamingServer[SimpleResponse]) error
|
||||
// Two-sided unbounded streaming between server to client
|
||||
// Both sides send the content of their own choice to the other
|
||||
StreamingBothWays(grpc.BidiStreamingServer[SimpleRequest, SimpleResponse]) error
|
||||
mustEmbedUnimplementedBenchmarkServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedBenchmarkServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedBenchmarkServiceServer struct{}
|
||||
|
||||
func (UnimplementedBenchmarkServiceServer) UnaryCall(context.Context, *SimpleRequest) (*SimpleResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method UnaryCall not implemented")
|
||||
}
|
||||
func (UnimplementedBenchmarkServiceServer) StreamingCall(grpc.BidiStreamingServer[SimpleRequest, SimpleResponse]) error {
|
||||
return status.Error(codes.Unimplemented, "method StreamingCall not implemented")
|
||||
}
|
||||
func (UnimplementedBenchmarkServiceServer) StreamingFromClient(grpc.ClientStreamingServer[SimpleRequest, SimpleResponse]) error {
|
||||
return status.Error(codes.Unimplemented, "method StreamingFromClient not implemented")
|
||||
}
|
||||
func (UnimplementedBenchmarkServiceServer) StreamingFromServer(*SimpleRequest, grpc.ServerStreamingServer[SimpleResponse]) error {
|
||||
return status.Error(codes.Unimplemented, "method StreamingFromServer not implemented")
|
||||
}
|
||||
func (UnimplementedBenchmarkServiceServer) StreamingBothWays(grpc.BidiStreamingServer[SimpleRequest, SimpleResponse]) error {
|
||||
return status.Error(codes.Unimplemented, "method StreamingBothWays not implemented")
|
||||
}
|
||||
func (UnimplementedBenchmarkServiceServer) mustEmbedUnimplementedBenchmarkServiceServer() {}
|
||||
func (UnimplementedBenchmarkServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeBenchmarkServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to BenchmarkServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeBenchmarkServiceServer interface {
|
||||
mustEmbedUnimplementedBenchmarkServiceServer()
|
||||
}
|
||||
|
||||
func RegisterBenchmarkServiceServer(s grpc.ServiceRegistrar, srv BenchmarkServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedBenchmarkServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&BenchmarkService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _BenchmarkService_UnaryCall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SimpleRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BenchmarkServiceServer).UnaryCall(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: BenchmarkService_UnaryCall_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BenchmarkServiceServer).UnaryCall(ctx, req.(*SimpleRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _BenchmarkService_StreamingCall_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(BenchmarkServiceServer).StreamingCall(&grpc.GenericServerStream[SimpleRequest, SimpleResponse]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type BenchmarkService_StreamingCallServer = grpc.BidiStreamingServer[SimpleRequest, SimpleResponse]
|
||||
|
||||
func _BenchmarkService_StreamingFromClient_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(BenchmarkServiceServer).StreamingFromClient(&grpc.GenericServerStream[SimpleRequest, SimpleResponse]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type BenchmarkService_StreamingFromClientServer = grpc.ClientStreamingServer[SimpleRequest, SimpleResponse]
|
||||
|
||||
func _BenchmarkService_StreamingFromServer_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(SimpleRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(BenchmarkServiceServer).StreamingFromServer(m, &grpc.GenericServerStream[SimpleRequest, SimpleResponse]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type BenchmarkService_StreamingFromServerServer = grpc.ServerStreamingServer[SimpleResponse]
|
||||
|
||||
func _BenchmarkService_StreamingBothWays_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(BenchmarkServiceServer).StreamingBothWays(&grpc.GenericServerStream[SimpleRequest, SimpleResponse]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type BenchmarkService_StreamingBothWaysServer = grpc.BidiStreamingServer[SimpleRequest, SimpleResponse]
|
||||
|
||||
// BenchmarkService_ServiceDesc is the grpc.ServiceDesc for BenchmarkService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var BenchmarkService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "grpc.testing.BenchmarkService",
|
||||
HandlerType: (*BenchmarkServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "UnaryCall",
|
||||
Handler: _BenchmarkService_UnaryCall_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "StreamingCall",
|
||||
Handler: _BenchmarkService_StreamingCall_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "StreamingFromClient",
|
||||
Handler: _BenchmarkService_StreamingFromClient_Handler,
|
||||
ClientStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "StreamingFromServer",
|
||||
Handler: _BenchmarkService_StreamingFromServer_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "StreamingBothWays",
|
||||
Handler: _BenchmarkService_StreamingBothWays_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "grpc/testing/benchmark_service.proto",
|
||||
}
|
||||
2069
interop/grpc_testing/control.pb.go
Normal file
2069
interop/grpc_testing/control.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
343
interop/grpc_testing/core/stats.pb.go
Normal file
343
interop/grpc_testing/core/stats.pb.go
Normal file
@@ -0,0 +1,343 @@
|
||||
// Copyright 2017 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.12
|
||||
// protoc v5.27.1
|
||||
// source: grpc/core/stats.proto
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Bucket struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Start float64 `protobuf:"fixed64,1,opt,name=start,proto3" json:"start,omitempty"`
|
||||
Count uint64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Bucket) Reset() {
|
||||
*x = Bucket{}
|
||||
mi := &file_grpc_core_stats_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Bucket) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Bucket) ProtoMessage() {}
|
||||
|
||||
func (x *Bucket) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_grpc_core_stats_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Bucket.ProtoReflect.Descriptor instead.
|
||||
func (*Bucket) Descriptor() ([]byte, []int) {
|
||||
return file_grpc_core_stats_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Bucket) GetStart() float64 {
|
||||
if x != nil {
|
||||
return x.Start
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Bucket) GetCount() uint64 {
|
||||
if x != nil {
|
||||
return x.Count
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type Histogram struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Buckets []*Bucket `protobuf:"bytes,1,rep,name=buckets,proto3" json:"buckets,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Histogram) Reset() {
|
||||
*x = Histogram{}
|
||||
mi := &file_grpc_core_stats_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Histogram) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Histogram) ProtoMessage() {}
|
||||
|
||||
func (x *Histogram) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_grpc_core_stats_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Histogram.ProtoReflect.Descriptor instead.
|
||||
func (*Histogram) Descriptor() ([]byte, []int) {
|
||||
return file_grpc_core_stats_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Histogram) GetBuckets() []*Bucket {
|
||||
if x != nil {
|
||||
return x.Buckets
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Metric struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
// Types that are valid to be assigned to Value:
|
||||
//
|
||||
// *Metric_Count
|
||||
// *Metric_Histogram
|
||||
Value isMetric_Value `protobuf_oneof:"value"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Metric) Reset() {
|
||||
*x = Metric{}
|
||||
mi := &file_grpc_core_stats_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Metric) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Metric) ProtoMessage() {}
|
||||
|
||||
func (x *Metric) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_grpc_core_stats_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Metric.ProtoReflect.Descriptor instead.
|
||||
func (*Metric) Descriptor() ([]byte, []int) {
|
||||
return file_grpc_core_stats_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *Metric) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Metric) GetValue() isMetric_Value {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Metric) GetCount() uint64 {
|
||||
if x != nil {
|
||||
if x, ok := x.Value.(*Metric_Count); ok {
|
||||
return x.Count
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Metric) GetHistogram() *Histogram {
|
||||
if x != nil {
|
||||
if x, ok := x.Value.(*Metric_Histogram); ok {
|
||||
return x.Histogram
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isMetric_Value interface {
|
||||
isMetric_Value()
|
||||
}
|
||||
|
||||
type Metric_Count struct {
|
||||
Count uint64 `protobuf:"varint,10,opt,name=count,proto3,oneof"`
|
||||
}
|
||||
|
||||
type Metric_Histogram struct {
|
||||
Histogram *Histogram `protobuf:"bytes,11,opt,name=histogram,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*Metric_Count) isMetric_Value() {}
|
||||
|
||||
func (*Metric_Histogram) isMetric_Value() {}
|
||||
|
||||
type Stats struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Metrics []*Metric `protobuf:"bytes,1,rep,name=metrics,proto3" json:"metrics,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Stats) Reset() {
|
||||
*x = Stats{}
|
||||
mi := &file_grpc_core_stats_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Stats) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Stats) ProtoMessage() {}
|
||||
|
||||
func (x *Stats) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_grpc_core_stats_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Stats.ProtoReflect.Descriptor instead.
|
||||
func (*Stats) Descriptor() ([]byte, []int) {
|
||||
return file_grpc_core_stats_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *Stats) GetMetrics() []*Metric {
|
||||
if x != nil {
|
||||
return x.Metrics
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_grpc_core_stats_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_grpc_core_stats_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x15grpc/core/stats.proto\x12\tgrpc.core\"4\n" +
|
||||
"\x06Bucket\x12\x14\n" +
|
||||
"\x05start\x18\x01 \x01(\x01R\x05start\x12\x14\n" +
|
||||
"\x05count\x18\x02 \x01(\x04R\x05count\"8\n" +
|
||||
"\tHistogram\x12+\n" +
|
||||
"\abuckets\x18\x01 \x03(\v2\x11.grpc.core.BucketR\abuckets\"s\n" +
|
||||
"\x06Metric\x12\x12\n" +
|
||||
"\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n" +
|
||||
"\x05count\x18\n" +
|
||||
" \x01(\x04H\x00R\x05count\x124\n" +
|
||||
"\thistogram\x18\v \x01(\v2\x14.grpc.core.HistogramH\x00R\thistogramB\a\n" +
|
||||
"\x05value\"4\n" +
|
||||
"\x05Stats\x12+\n" +
|
||||
"\ametrics\x18\x01 \x03(\v2\x11.grpc.core.MetricR\ametricsb\x06proto3"
|
||||
|
||||
var (
|
||||
file_grpc_core_stats_proto_rawDescOnce sync.Once
|
||||
file_grpc_core_stats_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_grpc_core_stats_proto_rawDescGZIP() []byte {
|
||||
file_grpc_core_stats_proto_rawDescOnce.Do(func() {
|
||||
file_grpc_core_stats_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_grpc_core_stats_proto_rawDesc), len(file_grpc_core_stats_proto_rawDesc)))
|
||||
})
|
||||
return file_grpc_core_stats_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_grpc_core_stats_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_grpc_core_stats_proto_goTypes = []any{
|
||||
(*Bucket)(nil), // 0: grpc.core.Bucket
|
||||
(*Histogram)(nil), // 1: grpc.core.Histogram
|
||||
(*Metric)(nil), // 2: grpc.core.Metric
|
||||
(*Stats)(nil), // 3: grpc.core.Stats
|
||||
}
|
||||
var file_grpc_core_stats_proto_depIdxs = []int32{
|
||||
0, // 0: grpc.core.Histogram.buckets:type_name -> grpc.core.Bucket
|
||||
1, // 1: grpc.core.Metric.histogram:type_name -> grpc.core.Histogram
|
||||
2, // 2: grpc.core.Stats.metrics:type_name -> grpc.core.Metric
|
||||
3, // [3:3] is the sub-list for method output_type
|
||||
3, // [3:3] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
3, // [3:3] is the sub-list for extension extendee
|
||||
0, // [0:3] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_grpc_core_stats_proto_init() }
|
||||
func file_grpc_core_stats_proto_init() {
|
||||
if File_grpc_core_stats_proto != nil {
|
||||
return
|
||||
}
|
||||
file_grpc_core_stats_proto_msgTypes[2].OneofWrappers = []any{
|
||||
(*Metric_Count)(nil),
|
||||
(*Metric_Histogram)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_grpc_core_stats_proto_rawDesc), len(file_grpc_core_stats_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_grpc_core_stats_proto_goTypes,
|
||||
DependencyIndexes: file_grpc_core_stats_proto_depIdxs,
|
||||
MessageInfos: file_grpc_core_stats_proto_msgTypes,
|
||||
}.Build()
|
||||
File_grpc_core_stats_proto = out.File
|
||||
file_grpc_core_stats_proto_goTypes = nil
|
||||
file_grpc_core_stats_proto_depIdxs = nil
|
||||
}
|
||||
135
interop/grpc_testing/empty.pb.go
Normal file
135
interop/grpc_testing/empty.pb.go
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.12
|
||||
// protoc v5.27.1
|
||||
// source: grpc/testing/empty.proto
|
||||
|
||||
package grpc_testing
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// An empty message that you can re-use to avoid defining duplicated empty
|
||||
// messages in your project. A typical example is to use it as argument or the
|
||||
// return value of a service API. For instance:
|
||||
//
|
||||
// service Foo {
|
||||
// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { };
|
||||
// };
|
||||
type Empty struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Empty) Reset() {
|
||||
*x = Empty{}
|
||||
mi := &file_grpc_testing_empty_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Empty) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Empty) ProtoMessage() {}
|
||||
|
||||
func (x *Empty) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_grpc_testing_empty_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
|
||||
func (*Empty) Descriptor() ([]byte, []int) {
|
||||
return file_grpc_testing_empty_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
var File_grpc_testing_empty_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_grpc_testing_empty_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x18grpc/testing/empty.proto\x12\fgrpc.testing\"\a\n" +
|
||||
"\x05EmptyB*\n" +
|
||||
"\x1bio.grpc.testing.integrationB\vEmptyProtosb\x06proto3"
|
||||
|
||||
var (
|
||||
file_grpc_testing_empty_proto_rawDescOnce sync.Once
|
||||
file_grpc_testing_empty_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_grpc_testing_empty_proto_rawDescGZIP() []byte {
|
||||
file_grpc_testing_empty_proto_rawDescOnce.Do(func() {
|
||||
file_grpc_testing_empty_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_grpc_testing_empty_proto_rawDesc), len(file_grpc_testing_empty_proto_rawDesc)))
|
||||
})
|
||||
return file_grpc_testing_empty_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_grpc_testing_empty_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_grpc_testing_empty_proto_goTypes = []any{
|
||||
(*Empty)(nil), // 0: grpc.testing.Empty
|
||||
}
|
||||
var file_grpc_testing_empty_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_grpc_testing_empty_proto_init() }
|
||||
func file_grpc_testing_empty_proto_init() {
|
||||
if File_grpc_testing_empty_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_grpc_testing_empty_proto_rawDesc), len(file_grpc_testing_empty_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_grpc_testing_empty_proto_goTypes,
|
||||
DependencyIndexes: file_grpc_testing_empty_proto_depIdxs,
|
||||
MessageInfos: file_grpc_testing_empty_proto_msgTypes,
|
||||
}.Build()
|
||||
File_grpc_testing_empty_proto = out.File
|
||||
file_grpc_testing_empty_proto_goTypes = nil
|
||||
file_grpc_testing_empty_proto_depIdxs = nil
|
||||
}
|
||||
2369
interop/grpc_testing/messages.pb.go
Normal file
2369
interop/grpc_testing/messages.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
352
interop/grpc_testing/payloads.pb.go
Normal file
352
interop/grpc_testing/payloads.pb.go
Normal file
@@ -0,0 +1,352 @@
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.12
|
||||
// protoc v5.27.1
|
||||
// source: grpc/testing/payloads.proto
|
||||
|
||||
package grpc_testing
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type ByteBufferParams struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
ReqSize int32 `protobuf:"varint,1,opt,name=req_size,json=reqSize,proto3" json:"req_size,omitempty"`
|
||||
RespSize int32 `protobuf:"varint,2,opt,name=resp_size,json=respSize,proto3" json:"resp_size,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ByteBufferParams) Reset() {
|
||||
*x = ByteBufferParams{}
|
||||
mi := &file_grpc_testing_payloads_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ByteBufferParams) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ByteBufferParams) ProtoMessage() {}
|
||||
|
||||
func (x *ByteBufferParams) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_grpc_testing_payloads_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ByteBufferParams.ProtoReflect.Descriptor instead.
|
||||
func (*ByteBufferParams) Descriptor() ([]byte, []int) {
|
||||
return file_grpc_testing_payloads_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ByteBufferParams) GetReqSize() int32 {
|
||||
if x != nil {
|
||||
return x.ReqSize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ByteBufferParams) GetRespSize() int32 {
|
||||
if x != nil {
|
||||
return x.RespSize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type SimpleProtoParams struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
ReqSize int32 `protobuf:"varint,1,opt,name=req_size,json=reqSize,proto3" json:"req_size,omitempty"`
|
||||
RespSize int32 `protobuf:"varint,2,opt,name=resp_size,json=respSize,proto3" json:"resp_size,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SimpleProtoParams) Reset() {
|
||||
*x = SimpleProtoParams{}
|
||||
mi := &file_grpc_testing_payloads_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *SimpleProtoParams) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SimpleProtoParams) ProtoMessage() {}
|
||||
|
||||
func (x *SimpleProtoParams) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_grpc_testing_payloads_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SimpleProtoParams.ProtoReflect.Descriptor instead.
|
||||
func (*SimpleProtoParams) Descriptor() ([]byte, []int) {
|
||||
return file_grpc_testing_payloads_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *SimpleProtoParams) GetReqSize() int32 {
|
||||
if x != nil {
|
||||
return x.ReqSize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SimpleProtoParams) GetRespSize() int32 {
|
||||
if x != nil {
|
||||
return x.RespSize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ComplexProtoParams struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ComplexProtoParams) Reset() {
|
||||
*x = ComplexProtoParams{}
|
||||
mi := &file_grpc_testing_payloads_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ComplexProtoParams) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ComplexProtoParams) ProtoMessage() {}
|
||||
|
||||
func (x *ComplexProtoParams) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_grpc_testing_payloads_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ComplexProtoParams.ProtoReflect.Descriptor instead.
|
||||
func (*ComplexProtoParams) Descriptor() ([]byte, []int) {
|
||||
return file_grpc_testing_payloads_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
type PayloadConfig struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Types that are valid to be assigned to Payload:
|
||||
//
|
||||
// *PayloadConfig_BytebufParams
|
||||
// *PayloadConfig_SimpleParams
|
||||
// *PayloadConfig_ComplexParams
|
||||
Payload isPayloadConfig_Payload `protobuf_oneof:"payload"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *PayloadConfig) Reset() {
|
||||
*x = PayloadConfig{}
|
||||
mi := &file_grpc_testing_payloads_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *PayloadConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PayloadConfig) ProtoMessage() {}
|
||||
|
||||
func (x *PayloadConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_grpc_testing_payloads_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PayloadConfig.ProtoReflect.Descriptor instead.
|
||||
func (*PayloadConfig) Descriptor() ([]byte, []int) {
|
||||
return file_grpc_testing_payloads_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *PayloadConfig) GetPayload() isPayloadConfig_Payload {
|
||||
if x != nil {
|
||||
return x.Payload
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PayloadConfig) GetBytebufParams() *ByteBufferParams {
|
||||
if x != nil {
|
||||
if x, ok := x.Payload.(*PayloadConfig_BytebufParams); ok {
|
||||
return x.BytebufParams
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PayloadConfig) GetSimpleParams() *SimpleProtoParams {
|
||||
if x != nil {
|
||||
if x, ok := x.Payload.(*PayloadConfig_SimpleParams); ok {
|
||||
return x.SimpleParams
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PayloadConfig) GetComplexParams() *ComplexProtoParams {
|
||||
if x != nil {
|
||||
if x, ok := x.Payload.(*PayloadConfig_ComplexParams); ok {
|
||||
return x.ComplexParams
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isPayloadConfig_Payload interface {
|
||||
isPayloadConfig_Payload()
|
||||
}
|
||||
|
||||
type PayloadConfig_BytebufParams struct {
|
||||
BytebufParams *ByteBufferParams `protobuf:"bytes,1,opt,name=bytebuf_params,json=bytebufParams,proto3,oneof"`
|
||||
}
|
||||
|
||||
type PayloadConfig_SimpleParams struct {
|
||||
SimpleParams *SimpleProtoParams `protobuf:"bytes,2,opt,name=simple_params,json=simpleParams,proto3,oneof"`
|
||||
}
|
||||
|
||||
type PayloadConfig_ComplexParams struct {
|
||||
ComplexParams *ComplexProtoParams `protobuf:"bytes,3,opt,name=complex_params,json=complexParams,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*PayloadConfig_BytebufParams) isPayloadConfig_Payload() {}
|
||||
|
||||
func (*PayloadConfig_SimpleParams) isPayloadConfig_Payload() {}
|
||||
|
||||
func (*PayloadConfig_ComplexParams) isPayloadConfig_Payload() {}
|
||||
|
||||
var File_grpc_testing_payloads_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_grpc_testing_payloads_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x1bgrpc/testing/payloads.proto\x12\fgrpc.testing\"J\n" +
|
||||
"\x10ByteBufferParams\x12\x19\n" +
|
||||
"\breq_size\x18\x01 \x01(\x05R\areqSize\x12\x1b\n" +
|
||||
"\tresp_size\x18\x02 \x01(\x05R\brespSize\"K\n" +
|
||||
"\x11SimpleProtoParams\x12\x19\n" +
|
||||
"\breq_size\x18\x01 \x01(\x05R\areqSize\x12\x1b\n" +
|
||||
"\tresp_size\x18\x02 \x01(\x05R\brespSize\"\x14\n" +
|
||||
"\x12ComplexProtoParams\"\xf6\x01\n" +
|
||||
"\rPayloadConfig\x12G\n" +
|
||||
"\x0ebytebuf_params\x18\x01 \x01(\v2\x1e.grpc.testing.ByteBufferParamsH\x00R\rbytebufParams\x12F\n" +
|
||||
"\rsimple_params\x18\x02 \x01(\v2\x1f.grpc.testing.SimpleProtoParamsH\x00R\fsimpleParams\x12I\n" +
|
||||
"\x0ecomplex_params\x18\x03 \x01(\v2 .grpc.testing.ComplexProtoParamsH\x00R\rcomplexParamsB\t\n" +
|
||||
"\apayloadB\"\n" +
|
||||
"\x0fio.grpc.testingB\rPayloadsProtoP\x01b\x06proto3"
|
||||
|
||||
var (
|
||||
file_grpc_testing_payloads_proto_rawDescOnce sync.Once
|
||||
file_grpc_testing_payloads_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_grpc_testing_payloads_proto_rawDescGZIP() []byte {
|
||||
file_grpc_testing_payloads_proto_rawDescOnce.Do(func() {
|
||||
file_grpc_testing_payloads_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_grpc_testing_payloads_proto_rawDesc), len(file_grpc_testing_payloads_proto_rawDesc)))
|
||||
})
|
||||
return file_grpc_testing_payloads_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_grpc_testing_payloads_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_grpc_testing_payloads_proto_goTypes = []any{
|
||||
(*ByteBufferParams)(nil), // 0: grpc.testing.ByteBufferParams
|
||||
(*SimpleProtoParams)(nil), // 1: grpc.testing.SimpleProtoParams
|
||||
(*ComplexProtoParams)(nil), // 2: grpc.testing.ComplexProtoParams
|
||||
(*PayloadConfig)(nil), // 3: grpc.testing.PayloadConfig
|
||||
}
|
||||
var file_grpc_testing_payloads_proto_depIdxs = []int32{
|
||||
0, // 0: grpc.testing.PayloadConfig.bytebuf_params:type_name -> grpc.testing.ByteBufferParams
|
||||
1, // 1: grpc.testing.PayloadConfig.simple_params:type_name -> grpc.testing.SimpleProtoParams
|
||||
2, // 2: grpc.testing.PayloadConfig.complex_params:type_name -> grpc.testing.ComplexProtoParams
|
||||
3, // [3:3] is the sub-list for method output_type
|
||||
3, // [3:3] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
3, // [3:3] is the sub-list for extension extendee
|
||||
0, // [0:3] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_grpc_testing_payloads_proto_init() }
|
||||
func file_grpc_testing_payloads_proto_init() {
|
||||
if File_grpc_testing_payloads_proto != nil {
|
||||
return
|
||||
}
|
||||
file_grpc_testing_payloads_proto_msgTypes[3].OneofWrappers = []any{
|
||||
(*PayloadConfig_BytebufParams)(nil),
|
||||
(*PayloadConfig_SimpleParams)(nil),
|
||||
(*PayloadConfig_ComplexParams)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_grpc_testing_payloads_proto_rawDesc), len(file_grpc_testing_payloads_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_grpc_testing_payloads_proto_goTypes,
|
||||
DependencyIndexes: file_grpc_testing_payloads_proto_depIdxs,
|
||||
MessageInfos: file_grpc_testing_payloads_proto_msgTypes,
|
||||
}.Build()
|
||||
File_grpc_testing_payloads_proto = out.File
|
||||
file_grpc_testing_payloads_proto_goTypes = nil
|
||||
file_grpc_testing_payloads_proto_depIdxs = nil
|
||||
}
|
||||
85
interop/grpc_testing/report_qps_scenario_service.pb.go
Normal file
85
interop/grpc_testing/report_qps_scenario_service.pb.go
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// An integration test service that covers all the method signature permutations
|
||||
// of unary/streaming requests/responses.
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.12
|
||||
// protoc v5.27.1
|
||||
// source: grpc/testing/report_qps_scenario_service.proto
|
||||
|
||||
package grpc_testing
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
var File_grpc_testing_report_qps_scenario_service_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_grpc_testing_report_qps_scenario_service_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
".grpc/testing/report_qps_scenario_service.proto\x12\fgrpc.testing\x1a\x1agrpc/testing/control.proto2^\n" +
|
||||
"\x18ReportQpsScenarioService\x12B\n" +
|
||||
"\x0eReportScenario\x12\x1c.grpc.testing.ScenarioResult\x1a\x12.grpc.testing.VoidB2\n" +
|
||||
"\x0fio.grpc.testingB\x1dReportQpsScenarioServiceProtoP\x01b\x06proto3"
|
||||
|
||||
var file_grpc_testing_report_qps_scenario_service_proto_goTypes = []any{
|
||||
(*ScenarioResult)(nil), // 0: grpc.testing.ScenarioResult
|
||||
(*Void)(nil), // 1: grpc.testing.Void
|
||||
}
|
||||
var file_grpc_testing_report_qps_scenario_service_proto_depIdxs = []int32{
|
||||
0, // 0: grpc.testing.ReportQpsScenarioService.ReportScenario:input_type -> grpc.testing.ScenarioResult
|
||||
1, // 1: grpc.testing.ReportQpsScenarioService.ReportScenario:output_type -> grpc.testing.Void
|
||||
1, // [1:2] is the sub-list for method output_type
|
||||
0, // [0:1] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_grpc_testing_report_qps_scenario_service_proto_init() }
|
||||
func file_grpc_testing_report_qps_scenario_service_proto_init() {
|
||||
if File_grpc_testing_report_qps_scenario_service_proto != nil {
|
||||
return
|
||||
}
|
||||
file_grpc_testing_control_proto_init()
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_grpc_testing_report_qps_scenario_service_proto_rawDesc), len(file_grpc_testing_report_qps_scenario_service_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 0,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_grpc_testing_report_qps_scenario_service_proto_goTypes,
|
||||
DependencyIndexes: file_grpc_testing_report_qps_scenario_service_proto_depIdxs,
|
||||
}.Build()
|
||||
File_grpc_testing_report_qps_scenario_service_proto = out.File
|
||||
file_grpc_testing_report_qps_scenario_service_proto_goTypes = nil
|
||||
file_grpc_testing_report_qps_scenario_service_proto_depIdxs = nil
|
||||
}
|
||||
141
interop/grpc_testing/report_qps_scenario_service_grpc.pb.go
Normal file
141
interop/grpc_testing/report_qps_scenario_service_grpc.pb.go
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// An integration test service that covers all the method signature permutations
|
||||
// of unary/streaming requests/responses.
|
||||
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc v5.27.1
|
||||
// source: grpc/testing/report_qps_scenario_service.proto
|
||||
|
||||
package grpc_testing
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
ReportQpsScenarioService_ReportScenario_FullMethodName = "/grpc.testing.ReportQpsScenarioService/ReportScenario"
|
||||
)
|
||||
|
||||
// ReportQpsScenarioServiceClient is the client API for ReportQpsScenarioService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type ReportQpsScenarioServiceClient interface {
|
||||
// Report results of a QPS test benchmark scenario.
|
||||
ReportScenario(ctx context.Context, in *ScenarioResult, opts ...grpc.CallOption) (*Void, error)
|
||||
}
|
||||
|
||||
type reportQpsScenarioServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewReportQpsScenarioServiceClient(cc grpc.ClientConnInterface) ReportQpsScenarioServiceClient {
|
||||
return &reportQpsScenarioServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *reportQpsScenarioServiceClient) ReportScenario(ctx context.Context, in *ScenarioResult, opts ...grpc.CallOption) (*Void, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(Void)
|
||||
err := c.cc.Invoke(ctx, ReportQpsScenarioService_ReportScenario_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ReportQpsScenarioServiceServer is the server API for ReportQpsScenarioService service.
|
||||
// All implementations must embed UnimplementedReportQpsScenarioServiceServer
|
||||
// for forward compatibility.
|
||||
type ReportQpsScenarioServiceServer interface {
|
||||
// Report results of a QPS test benchmark scenario.
|
||||
ReportScenario(context.Context, *ScenarioResult) (*Void, error)
|
||||
mustEmbedUnimplementedReportQpsScenarioServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedReportQpsScenarioServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedReportQpsScenarioServiceServer struct{}
|
||||
|
||||
func (UnimplementedReportQpsScenarioServiceServer) ReportScenario(context.Context, *ScenarioResult) (*Void, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ReportScenario not implemented")
|
||||
}
|
||||
func (UnimplementedReportQpsScenarioServiceServer) mustEmbedUnimplementedReportQpsScenarioServiceServer() {
|
||||
}
|
||||
func (UnimplementedReportQpsScenarioServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeReportQpsScenarioServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to ReportQpsScenarioServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeReportQpsScenarioServiceServer interface {
|
||||
mustEmbedUnimplementedReportQpsScenarioServiceServer()
|
||||
}
|
||||
|
||||
func RegisterReportQpsScenarioServiceServer(s grpc.ServiceRegistrar, srv ReportQpsScenarioServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedReportQpsScenarioServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&ReportQpsScenarioService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _ReportQpsScenarioService_ReportScenario_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ScenarioResult)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ReportQpsScenarioServiceServer).ReportScenario(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ReportQpsScenarioService_ReportScenario_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ReportQpsScenarioServiceServer).ReportScenario(ctx, req.(*ScenarioResult))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// ReportQpsScenarioService_ServiceDesc is the grpc.ServiceDesc for ReportQpsScenarioService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var ReportQpsScenarioService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "grpc.testing.ReportQpsScenarioService",
|
||||
HandlerType: (*ReportQpsScenarioServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "ReportScenario",
|
||||
Handler: _ReportQpsScenarioService_ReportScenario_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "grpc/testing/report_qps_scenario_service.proto",
|
||||
}
|
||||
525
interop/grpc_testing/stats.pb.go
Normal file
525
interop/grpc_testing/stats.pb.go
Normal file
@@ -0,0 +1,525 @@
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.12
|
||||
// protoc v5.27.1
|
||||
// source: grpc/testing/stats.proto
|
||||
|
||||
package grpc_testing
|
||||
|
||||
import (
|
||||
core "google.golang.org/grpc/interop/grpc_testing/core"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type ServerStats struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// wall clock time change in seconds since last reset
|
||||
TimeElapsed float64 `protobuf:"fixed64,1,opt,name=time_elapsed,json=timeElapsed,proto3" json:"time_elapsed,omitempty"`
|
||||
// change in user time (in seconds) used by the server since last reset
|
||||
TimeUser float64 `protobuf:"fixed64,2,opt,name=time_user,json=timeUser,proto3" json:"time_user,omitempty"`
|
||||
// change in server time (in seconds) used by the server process and all
|
||||
// threads since last reset
|
||||
TimeSystem float64 `protobuf:"fixed64,3,opt,name=time_system,json=timeSystem,proto3" json:"time_system,omitempty"`
|
||||
// change in total cpu time of the server (data from proc/stat)
|
||||
TotalCpuTime uint64 `protobuf:"varint,4,opt,name=total_cpu_time,json=totalCpuTime,proto3" json:"total_cpu_time,omitempty"`
|
||||
// change in idle time of the server (data from proc/stat)
|
||||
IdleCpuTime uint64 `protobuf:"varint,5,opt,name=idle_cpu_time,json=idleCpuTime,proto3" json:"idle_cpu_time,omitempty"`
|
||||
// Number of polls called inside completion queue
|
||||
CqPollCount uint64 `protobuf:"varint,6,opt,name=cq_poll_count,json=cqPollCount,proto3" json:"cq_poll_count,omitempty"`
|
||||
// Core library stats
|
||||
CoreStats *core.Stats `protobuf:"bytes,7,opt,name=core_stats,json=coreStats,proto3" json:"core_stats,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ServerStats) Reset() {
|
||||
*x = ServerStats{}
|
||||
mi := &file_grpc_testing_stats_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ServerStats) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ServerStats) ProtoMessage() {}
|
||||
|
||||
func (x *ServerStats) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_grpc_testing_stats_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ServerStats.ProtoReflect.Descriptor instead.
|
||||
func (*ServerStats) Descriptor() ([]byte, []int) {
|
||||
return file_grpc_testing_stats_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ServerStats) GetTimeElapsed() float64 {
|
||||
if x != nil {
|
||||
return x.TimeElapsed
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ServerStats) GetTimeUser() float64 {
|
||||
if x != nil {
|
||||
return x.TimeUser
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ServerStats) GetTimeSystem() float64 {
|
||||
if x != nil {
|
||||
return x.TimeSystem
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ServerStats) GetTotalCpuTime() uint64 {
|
||||
if x != nil {
|
||||
return x.TotalCpuTime
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ServerStats) GetIdleCpuTime() uint64 {
|
||||
if x != nil {
|
||||
return x.IdleCpuTime
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ServerStats) GetCqPollCount() uint64 {
|
||||
if x != nil {
|
||||
return x.CqPollCount
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ServerStats) GetCoreStats() *core.Stats {
|
||||
if x != nil {
|
||||
return x.CoreStats
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Histogram params based on grpc/support/histogram.c
|
||||
type HistogramParams struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Resolution float64 `protobuf:"fixed64,1,opt,name=resolution,proto3" json:"resolution,omitempty"` // first bucket is [0, 1 + resolution)
|
||||
MaxPossible float64 `protobuf:"fixed64,2,opt,name=max_possible,json=maxPossible,proto3" json:"max_possible,omitempty"` // use enough buckets to allow this value
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *HistogramParams) Reset() {
|
||||
*x = HistogramParams{}
|
||||
mi := &file_grpc_testing_stats_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *HistogramParams) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HistogramParams) ProtoMessage() {}
|
||||
|
||||
func (x *HistogramParams) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_grpc_testing_stats_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HistogramParams.ProtoReflect.Descriptor instead.
|
||||
func (*HistogramParams) Descriptor() ([]byte, []int) {
|
||||
return file_grpc_testing_stats_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *HistogramParams) GetResolution() float64 {
|
||||
if x != nil {
|
||||
return x.Resolution
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HistogramParams) GetMaxPossible() float64 {
|
||||
if x != nil {
|
||||
return x.MaxPossible
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Histogram data based on grpc/support/histogram.c
|
||||
type HistogramData struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Bucket []uint32 `protobuf:"varint,1,rep,packed,name=bucket,proto3" json:"bucket,omitempty"`
|
||||
MinSeen float64 `protobuf:"fixed64,2,opt,name=min_seen,json=minSeen,proto3" json:"min_seen,omitempty"`
|
||||
MaxSeen float64 `protobuf:"fixed64,3,opt,name=max_seen,json=maxSeen,proto3" json:"max_seen,omitempty"`
|
||||
Sum float64 `protobuf:"fixed64,4,opt,name=sum,proto3" json:"sum,omitempty"`
|
||||
SumOfSquares float64 `protobuf:"fixed64,5,opt,name=sum_of_squares,json=sumOfSquares,proto3" json:"sum_of_squares,omitempty"`
|
||||
Count float64 `protobuf:"fixed64,6,opt,name=count,proto3" json:"count,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *HistogramData) Reset() {
|
||||
*x = HistogramData{}
|
||||
mi := &file_grpc_testing_stats_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *HistogramData) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HistogramData) ProtoMessage() {}
|
||||
|
||||
func (x *HistogramData) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_grpc_testing_stats_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HistogramData.ProtoReflect.Descriptor instead.
|
||||
func (*HistogramData) Descriptor() ([]byte, []int) {
|
||||
return file_grpc_testing_stats_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *HistogramData) GetBucket() []uint32 {
|
||||
if x != nil {
|
||||
return x.Bucket
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HistogramData) GetMinSeen() float64 {
|
||||
if x != nil {
|
||||
return x.MinSeen
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HistogramData) GetMaxSeen() float64 {
|
||||
if x != nil {
|
||||
return x.MaxSeen
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HistogramData) GetSum() float64 {
|
||||
if x != nil {
|
||||
return x.Sum
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HistogramData) GetSumOfSquares() float64 {
|
||||
if x != nil {
|
||||
return x.SumOfSquares
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HistogramData) GetCount() float64 {
|
||||
if x != nil {
|
||||
return x.Count
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type RequestResultCount struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
StatusCode int32 `protobuf:"varint,1,opt,name=status_code,json=statusCode,proto3" json:"status_code,omitempty"`
|
||||
Count int64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RequestResultCount) Reset() {
|
||||
*x = RequestResultCount{}
|
||||
mi := &file_grpc_testing_stats_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *RequestResultCount) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RequestResultCount) ProtoMessage() {}
|
||||
|
||||
func (x *RequestResultCount) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_grpc_testing_stats_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RequestResultCount.ProtoReflect.Descriptor instead.
|
||||
func (*RequestResultCount) Descriptor() ([]byte, []int) {
|
||||
return file_grpc_testing_stats_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *RequestResultCount) GetStatusCode() int32 {
|
||||
if x != nil {
|
||||
return x.StatusCode
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *RequestResultCount) GetCount() int64 {
|
||||
if x != nil {
|
||||
return x.Count
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ClientStats struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Latency histogram. Data points are in nanoseconds.
|
||||
Latencies *HistogramData `protobuf:"bytes,1,opt,name=latencies,proto3" json:"latencies,omitempty"`
|
||||
// See ServerStats for details.
|
||||
TimeElapsed float64 `protobuf:"fixed64,2,opt,name=time_elapsed,json=timeElapsed,proto3" json:"time_elapsed,omitempty"`
|
||||
TimeUser float64 `protobuf:"fixed64,3,opt,name=time_user,json=timeUser,proto3" json:"time_user,omitempty"`
|
||||
TimeSystem float64 `protobuf:"fixed64,4,opt,name=time_system,json=timeSystem,proto3" json:"time_system,omitempty"`
|
||||
// Number of failed requests (one row per status code seen)
|
||||
RequestResults []*RequestResultCount `protobuf:"bytes,5,rep,name=request_results,json=requestResults,proto3" json:"request_results,omitempty"`
|
||||
// Number of polls called inside completion queue
|
||||
CqPollCount uint64 `protobuf:"varint,6,opt,name=cq_poll_count,json=cqPollCount,proto3" json:"cq_poll_count,omitempty"`
|
||||
// Core library stats
|
||||
CoreStats *core.Stats `protobuf:"bytes,7,opt,name=core_stats,json=coreStats,proto3" json:"core_stats,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ClientStats) Reset() {
|
||||
*x = ClientStats{}
|
||||
mi := &file_grpc_testing_stats_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ClientStats) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ClientStats) ProtoMessage() {}
|
||||
|
||||
func (x *ClientStats) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_grpc_testing_stats_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientStats.ProtoReflect.Descriptor instead.
|
||||
func (*ClientStats) Descriptor() ([]byte, []int) {
|
||||
return file_grpc_testing_stats_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *ClientStats) GetLatencies() *HistogramData {
|
||||
if x != nil {
|
||||
return x.Latencies
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientStats) GetTimeElapsed() float64 {
|
||||
if x != nil {
|
||||
return x.TimeElapsed
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ClientStats) GetTimeUser() float64 {
|
||||
if x != nil {
|
||||
return x.TimeUser
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ClientStats) GetTimeSystem() float64 {
|
||||
if x != nil {
|
||||
return x.TimeSystem
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ClientStats) GetRequestResults() []*RequestResultCount {
|
||||
if x != nil {
|
||||
return x.RequestResults
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientStats) GetCqPollCount() uint64 {
|
||||
if x != nil {
|
||||
return x.CqPollCount
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ClientStats) GetCoreStats() *core.Stats {
|
||||
if x != nil {
|
||||
return x.CoreStats
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_grpc_testing_stats_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_grpc_testing_stats_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x18grpc/testing/stats.proto\x12\fgrpc.testing\x1a\x15grpc/core/stats.proto\"\x8d\x02\n" +
|
||||
"\vServerStats\x12!\n" +
|
||||
"\ftime_elapsed\x18\x01 \x01(\x01R\vtimeElapsed\x12\x1b\n" +
|
||||
"\ttime_user\x18\x02 \x01(\x01R\btimeUser\x12\x1f\n" +
|
||||
"\vtime_system\x18\x03 \x01(\x01R\n" +
|
||||
"timeSystem\x12$\n" +
|
||||
"\x0etotal_cpu_time\x18\x04 \x01(\x04R\ftotalCpuTime\x12\"\n" +
|
||||
"\ridle_cpu_time\x18\x05 \x01(\x04R\vidleCpuTime\x12\"\n" +
|
||||
"\rcq_poll_count\x18\x06 \x01(\x04R\vcqPollCount\x12/\n" +
|
||||
"\n" +
|
||||
"core_stats\x18\a \x01(\v2\x10.grpc.core.StatsR\tcoreStats\"T\n" +
|
||||
"\x0fHistogramParams\x12\x1e\n" +
|
||||
"\n" +
|
||||
"resolution\x18\x01 \x01(\x01R\n" +
|
||||
"resolution\x12!\n" +
|
||||
"\fmax_possible\x18\x02 \x01(\x01R\vmaxPossible\"\xab\x01\n" +
|
||||
"\rHistogramData\x12\x16\n" +
|
||||
"\x06bucket\x18\x01 \x03(\rR\x06bucket\x12\x19\n" +
|
||||
"\bmin_seen\x18\x02 \x01(\x01R\aminSeen\x12\x19\n" +
|
||||
"\bmax_seen\x18\x03 \x01(\x01R\amaxSeen\x12\x10\n" +
|
||||
"\x03sum\x18\x04 \x01(\x01R\x03sum\x12$\n" +
|
||||
"\x0esum_of_squares\x18\x05 \x01(\x01R\fsumOfSquares\x12\x14\n" +
|
||||
"\x05count\x18\x06 \x01(\x01R\x05count\"K\n" +
|
||||
"\x12RequestResultCount\x12\x1f\n" +
|
||||
"\vstatus_code\x18\x01 \x01(\x05R\n" +
|
||||
"statusCode\x12\x14\n" +
|
||||
"\x05count\x18\x02 \x01(\x03R\x05count\"\xc9\x02\n" +
|
||||
"\vClientStats\x129\n" +
|
||||
"\tlatencies\x18\x01 \x01(\v2\x1b.grpc.testing.HistogramDataR\tlatencies\x12!\n" +
|
||||
"\ftime_elapsed\x18\x02 \x01(\x01R\vtimeElapsed\x12\x1b\n" +
|
||||
"\ttime_user\x18\x03 \x01(\x01R\btimeUser\x12\x1f\n" +
|
||||
"\vtime_system\x18\x04 \x01(\x01R\n" +
|
||||
"timeSystem\x12I\n" +
|
||||
"\x0frequest_results\x18\x05 \x03(\v2 .grpc.testing.RequestResultCountR\x0erequestResults\x12\"\n" +
|
||||
"\rcq_poll_count\x18\x06 \x01(\x04R\vcqPollCount\x12/\n" +
|
||||
"\n" +
|
||||
"core_stats\x18\a \x01(\v2\x10.grpc.core.StatsR\tcoreStatsB\x1f\n" +
|
||||
"\x0fio.grpc.testingB\n" +
|
||||
"StatsProtoP\x01b\x06proto3"
|
||||
|
||||
var (
|
||||
file_grpc_testing_stats_proto_rawDescOnce sync.Once
|
||||
file_grpc_testing_stats_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_grpc_testing_stats_proto_rawDescGZIP() []byte {
|
||||
file_grpc_testing_stats_proto_rawDescOnce.Do(func() {
|
||||
file_grpc_testing_stats_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_grpc_testing_stats_proto_rawDesc), len(file_grpc_testing_stats_proto_rawDesc)))
|
||||
})
|
||||
return file_grpc_testing_stats_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_grpc_testing_stats_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||
var file_grpc_testing_stats_proto_goTypes = []any{
|
||||
(*ServerStats)(nil), // 0: grpc.testing.ServerStats
|
||||
(*HistogramParams)(nil), // 1: grpc.testing.HistogramParams
|
||||
(*HistogramData)(nil), // 2: grpc.testing.HistogramData
|
||||
(*RequestResultCount)(nil), // 3: grpc.testing.RequestResultCount
|
||||
(*ClientStats)(nil), // 4: grpc.testing.ClientStats
|
||||
(*core.Stats)(nil), // 5: grpc.core.Stats
|
||||
}
|
||||
var file_grpc_testing_stats_proto_depIdxs = []int32{
|
||||
5, // 0: grpc.testing.ServerStats.core_stats:type_name -> grpc.core.Stats
|
||||
2, // 1: grpc.testing.ClientStats.latencies:type_name -> grpc.testing.HistogramData
|
||||
3, // 2: grpc.testing.ClientStats.request_results:type_name -> grpc.testing.RequestResultCount
|
||||
5, // 3: grpc.testing.ClientStats.core_stats:type_name -> grpc.core.Stats
|
||||
4, // [4:4] is the sub-list for method output_type
|
||||
4, // [4:4] is the sub-list for method input_type
|
||||
4, // [4:4] is the sub-list for extension type_name
|
||||
4, // [4:4] is the sub-list for extension extendee
|
||||
0, // [0:4] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_grpc_testing_stats_proto_init() }
|
||||
func file_grpc_testing_stats_proto_init() {
|
||||
if File_grpc_testing_stats_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_grpc_testing_stats_proto_rawDesc), len(file_grpc_testing_stats_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 5,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_grpc_testing_stats_proto_goTypes,
|
||||
DependencyIndexes: file_grpc_testing_stats_proto_depIdxs,
|
||||
MessageInfos: file_grpc_testing_stats_proto_msgTypes,
|
||||
}.Build()
|
||||
File_grpc_testing_stats_proto = out.File
|
||||
file_grpc_testing_stats_proto_goTypes = nil
|
||||
file_grpc_testing_stats_proto_depIdxs = nil
|
||||
}
|
||||
166
interop/grpc_testing/test.pb.go
Normal file
166
interop/grpc_testing/test.pb.go
Normal file
@@ -0,0 +1,166 @@
|
||||
// Copyright 2015-2016 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// An integration test service that covers all the method signature permutations
|
||||
// of unary/streaming requests/responses.
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.12
|
||||
// protoc v5.27.1
|
||||
// source: grpc/testing/test.proto
|
||||
|
||||
package grpc_testing
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
var File_grpc_testing_test_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_grpc_testing_test_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x17grpc/testing/test.proto\x12\fgrpc.testing\x1a\x18grpc/testing/empty.proto\x1a\x1bgrpc/testing/messages.proto2\xcb\x05\n" +
|
||||
"\vTestService\x125\n" +
|
||||
"\tEmptyCall\x12\x13.grpc.testing.Empty\x1a\x13.grpc.testing.Empty\x12F\n" +
|
||||
"\tUnaryCall\x12\x1b.grpc.testing.SimpleRequest\x1a\x1c.grpc.testing.SimpleResponse\x12O\n" +
|
||||
"\x12CacheableUnaryCall\x12\x1b.grpc.testing.SimpleRequest\x1a\x1c.grpc.testing.SimpleResponse\x12l\n" +
|
||||
"\x13StreamingOutputCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse0\x01\x12i\n" +
|
||||
"\x12StreamingInputCall\x12'.grpc.testing.StreamingInputCallRequest\x1a(.grpc.testing.StreamingInputCallResponse(\x01\x12i\n" +
|
||||
"\x0eFullDuplexCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse(\x010\x01\x12i\n" +
|
||||
"\x0eHalfDuplexCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse(\x010\x01\x12=\n" +
|
||||
"\x11UnimplementedCall\x12\x13.grpc.testing.Empty\x1a\x13.grpc.testing.Empty2U\n" +
|
||||
"\x14UnimplementedService\x12=\n" +
|
||||
"\x11UnimplementedCall\x12\x13.grpc.testing.Empty\x1a\x13.grpc.testing.Empty2\x89\x01\n" +
|
||||
"\x10ReconnectService\x12;\n" +
|
||||
"\x05Start\x12\x1d.grpc.testing.ReconnectParams\x1a\x13.grpc.testing.Empty\x128\n" +
|
||||
"\x04Stop\x12\x13.grpc.testing.Empty\x1a\x1b.grpc.testing.ReconnectInfo2\x86\x02\n" +
|
||||
"\x18LoadBalancerStatsService\x12c\n" +
|
||||
"\x0eGetClientStats\x12&.grpc.testing.LoadBalancerStatsRequest\x1a'.grpc.testing.LoadBalancerStatsResponse\"\x00\x12\x84\x01\n" +
|
||||
"\x19GetClientAccumulatedStats\x121.grpc.testing.LoadBalancerAccumulatedStatsRequest\x1a2.grpc.testing.LoadBalancerAccumulatedStatsResponse\"\x002\xcc\x01\n" +
|
||||
"\vHookService\x120\n" +
|
||||
"\x04Hook\x12\x13.grpc.testing.Empty\x1a\x13.grpc.testing.Empty\x12L\n" +
|
||||
"\x0fSetReturnStatus\x12$.grpc.testing.SetReturnStatusRequest\x1a\x13.grpc.testing.Empty\x12=\n" +
|
||||
"\x11ClearReturnStatus\x12\x13.grpc.testing.Empty\x1a\x13.grpc.testing.Empty2\xd5\x01\n" +
|
||||
"\x16XdsUpdateHealthService\x126\n" +
|
||||
"\n" +
|
||||
"SetServing\x12\x13.grpc.testing.Empty\x1a\x13.grpc.testing.Empty\x129\n" +
|
||||
"\rSetNotServing\x12\x13.grpc.testing.Empty\x1a\x13.grpc.testing.Empty\x12H\n" +
|
||||
"\x0fSendHookRequest\x12\x19.grpc.testing.HookRequest\x1a\x1a.grpc.testing.HookResponse2{\n" +
|
||||
"\x1fXdsUpdateClientConfigureService\x12X\n" +
|
||||
"\tConfigure\x12$.grpc.testing.ClientConfigureRequest\x1a%.grpc.testing.ClientConfigureResponseB\x1d\n" +
|
||||
"\x1bio.grpc.testing.integrationb\x06proto3"
|
||||
|
||||
var file_grpc_testing_test_proto_goTypes = []any{
|
||||
(*Empty)(nil), // 0: grpc.testing.Empty
|
||||
(*SimpleRequest)(nil), // 1: grpc.testing.SimpleRequest
|
||||
(*StreamingOutputCallRequest)(nil), // 2: grpc.testing.StreamingOutputCallRequest
|
||||
(*StreamingInputCallRequest)(nil), // 3: grpc.testing.StreamingInputCallRequest
|
||||
(*ReconnectParams)(nil), // 4: grpc.testing.ReconnectParams
|
||||
(*LoadBalancerStatsRequest)(nil), // 5: grpc.testing.LoadBalancerStatsRequest
|
||||
(*LoadBalancerAccumulatedStatsRequest)(nil), // 6: grpc.testing.LoadBalancerAccumulatedStatsRequest
|
||||
(*SetReturnStatusRequest)(nil), // 7: grpc.testing.SetReturnStatusRequest
|
||||
(*HookRequest)(nil), // 8: grpc.testing.HookRequest
|
||||
(*ClientConfigureRequest)(nil), // 9: grpc.testing.ClientConfigureRequest
|
||||
(*SimpleResponse)(nil), // 10: grpc.testing.SimpleResponse
|
||||
(*StreamingOutputCallResponse)(nil), // 11: grpc.testing.StreamingOutputCallResponse
|
||||
(*StreamingInputCallResponse)(nil), // 12: grpc.testing.StreamingInputCallResponse
|
||||
(*ReconnectInfo)(nil), // 13: grpc.testing.ReconnectInfo
|
||||
(*LoadBalancerStatsResponse)(nil), // 14: grpc.testing.LoadBalancerStatsResponse
|
||||
(*LoadBalancerAccumulatedStatsResponse)(nil), // 15: grpc.testing.LoadBalancerAccumulatedStatsResponse
|
||||
(*HookResponse)(nil), // 16: grpc.testing.HookResponse
|
||||
(*ClientConfigureResponse)(nil), // 17: grpc.testing.ClientConfigureResponse
|
||||
}
|
||||
var file_grpc_testing_test_proto_depIdxs = []int32{
|
||||
0, // 0: grpc.testing.TestService.EmptyCall:input_type -> grpc.testing.Empty
|
||||
1, // 1: grpc.testing.TestService.UnaryCall:input_type -> grpc.testing.SimpleRequest
|
||||
1, // 2: grpc.testing.TestService.CacheableUnaryCall:input_type -> grpc.testing.SimpleRequest
|
||||
2, // 3: grpc.testing.TestService.StreamingOutputCall:input_type -> grpc.testing.StreamingOutputCallRequest
|
||||
3, // 4: grpc.testing.TestService.StreamingInputCall:input_type -> grpc.testing.StreamingInputCallRequest
|
||||
2, // 5: grpc.testing.TestService.FullDuplexCall:input_type -> grpc.testing.StreamingOutputCallRequest
|
||||
2, // 6: grpc.testing.TestService.HalfDuplexCall:input_type -> grpc.testing.StreamingOutputCallRequest
|
||||
0, // 7: grpc.testing.TestService.UnimplementedCall:input_type -> grpc.testing.Empty
|
||||
0, // 8: grpc.testing.UnimplementedService.UnimplementedCall:input_type -> grpc.testing.Empty
|
||||
4, // 9: grpc.testing.ReconnectService.Start:input_type -> grpc.testing.ReconnectParams
|
||||
0, // 10: grpc.testing.ReconnectService.Stop:input_type -> grpc.testing.Empty
|
||||
5, // 11: grpc.testing.LoadBalancerStatsService.GetClientStats:input_type -> grpc.testing.LoadBalancerStatsRequest
|
||||
6, // 12: grpc.testing.LoadBalancerStatsService.GetClientAccumulatedStats:input_type -> grpc.testing.LoadBalancerAccumulatedStatsRequest
|
||||
0, // 13: grpc.testing.HookService.Hook:input_type -> grpc.testing.Empty
|
||||
7, // 14: grpc.testing.HookService.SetReturnStatus:input_type -> grpc.testing.SetReturnStatusRequest
|
||||
0, // 15: grpc.testing.HookService.ClearReturnStatus:input_type -> grpc.testing.Empty
|
||||
0, // 16: grpc.testing.XdsUpdateHealthService.SetServing:input_type -> grpc.testing.Empty
|
||||
0, // 17: grpc.testing.XdsUpdateHealthService.SetNotServing:input_type -> grpc.testing.Empty
|
||||
8, // 18: grpc.testing.XdsUpdateHealthService.SendHookRequest:input_type -> grpc.testing.HookRequest
|
||||
9, // 19: grpc.testing.XdsUpdateClientConfigureService.Configure:input_type -> grpc.testing.ClientConfigureRequest
|
||||
0, // 20: grpc.testing.TestService.EmptyCall:output_type -> grpc.testing.Empty
|
||||
10, // 21: grpc.testing.TestService.UnaryCall:output_type -> grpc.testing.SimpleResponse
|
||||
10, // 22: grpc.testing.TestService.CacheableUnaryCall:output_type -> grpc.testing.SimpleResponse
|
||||
11, // 23: grpc.testing.TestService.StreamingOutputCall:output_type -> grpc.testing.StreamingOutputCallResponse
|
||||
12, // 24: grpc.testing.TestService.StreamingInputCall:output_type -> grpc.testing.StreamingInputCallResponse
|
||||
11, // 25: grpc.testing.TestService.FullDuplexCall:output_type -> grpc.testing.StreamingOutputCallResponse
|
||||
11, // 26: grpc.testing.TestService.HalfDuplexCall:output_type -> grpc.testing.StreamingOutputCallResponse
|
||||
0, // 27: grpc.testing.TestService.UnimplementedCall:output_type -> grpc.testing.Empty
|
||||
0, // 28: grpc.testing.UnimplementedService.UnimplementedCall:output_type -> grpc.testing.Empty
|
||||
0, // 29: grpc.testing.ReconnectService.Start:output_type -> grpc.testing.Empty
|
||||
13, // 30: grpc.testing.ReconnectService.Stop:output_type -> grpc.testing.ReconnectInfo
|
||||
14, // 31: grpc.testing.LoadBalancerStatsService.GetClientStats:output_type -> grpc.testing.LoadBalancerStatsResponse
|
||||
15, // 32: grpc.testing.LoadBalancerStatsService.GetClientAccumulatedStats:output_type -> grpc.testing.LoadBalancerAccumulatedStatsResponse
|
||||
0, // 33: grpc.testing.HookService.Hook:output_type -> grpc.testing.Empty
|
||||
0, // 34: grpc.testing.HookService.SetReturnStatus:output_type -> grpc.testing.Empty
|
||||
0, // 35: grpc.testing.HookService.ClearReturnStatus:output_type -> grpc.testing.Empty
|
||||
0, // 36: grpc.testing.XdsUpdateHealthService.SetServing:output_type -> grpc.testing.Empty
|
||||
0, // 37: grpc.testing.XdsUpdateHealthService.SetNotServing:output_type -> grpc.testing.Empty
|
||||
16, // 38: grpc.testing.XdsUpdateHealthService.SendHookRequest:output_type -> grpc.testing.HookResponse
|
||||
17, // 39: grpc.testing.XdsUpdateClientConfigureService.Configure:output_type -> grpc.testing.ClientConfigureResponse
|
||||
20, // [20:40] is the sub-list for method output_type
|
||||
0, // [0:20] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_grpc_testing_test_proto_init() }
|
||||
func file_grpc_testing_test_proto_init() {
|
||||
if File_grpc_testing_test_proto != nil {
|
||||
return
|
||||
}
|
||||
file_grpc_testing_empty_proto_init()
|
||||
file_grpc_testing_messages_proto_init()
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_grpc_testing_test_proto_rawDesc), len(file_grpc_testing_test_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 0,
|
||||
NumExtensions: 0,
|
||||
NumServices: 7,
|
||||
},
|
||||
GoTypes: file_grpc_testing_test_proto_goTypes,
|
||||
DependencyIndexes: file_grpc_testing_test_proto_depIdxs,
|
||||
}.Build()
|
||||
File_grpc_testing_test_proto = out.File
|
||||
file_grpc_testing_test_proto_goTypes = nil
|
||||
file_grpc_testing_test_proto_depIdxs = nil
|
||||
}
|
||||
1316
interop/grpc_testing/test_grpc.pb.go
Normal file
1316
interop/grpc_testing/test_grpc.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
100
interop/grpc_testing/worker_service.pb.go
Normal file
100
interop/grpc_testing/worker_service.pb.go
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// An integration test service that covers all the method signature permutations
|
||||
// of unary/streaming requests/responses.
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.12
|
||||
// protoc v5.27.1
|
||||
// source: grpc/testing/worker_service.proto
|
||||
|
||||
package grpc_testing
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
var File_grpc_testing_worker_service_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_grpc_testing_worker_service_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"!grpc/testing/worker_service.proto\x12\fgrpc.testing\x1a\x1agrpc/testing/control.proto2\x97\x02\n" +
|
||||
"\rWorkerService\x12E\n" +
|
||||
"\tRunServer\x12\x18.grpc.testing.ServerArgs\x1a\x1a.grpc.testing.ServerStatus(\x010\x01\x12E\n" +
|
||||
"\tRunClient\x12\x18.grpc.testing.ClientArgs\x1a\x1a.grpc.testing.ClientStatus(\x010\x01\x12B\n" +
|
||||
"\tCoreCount\x12\x19.grpc.testing.CoreRequest\x1a\x1a.grpc.testing.CoreResponse\x124\n" +
|
||||
"\n" +
|
||||
"QuitWorker\x12\x12.grpc.testing.Void\x1a\x12.grpc.testing.VoidB'\n" +
|
||||
"\x0fio.grpc.testingB\x12WorkerServiceProtoP\x01b\x06proto3"
|
||||
|
||||
var file_grpc_testing_worker_service_proto_goTypes = []any{
|
||||
(*ServerArgs)(nil), // 0: grpc.testing.ServerArgs
|
||||
(*ClientArgs)(nil), // 1: grpc.testing.ClientArgs
|
||||
(*CoreRequest)(nil), // 2: grpc.testing.CoreRequest
|
||||
(*Void)(nil), // 3: grpc.testing.Void
|
||||
(*ServerStatus)(nil), // 4: grpc.testing.ServerStatus
|
||||
(*ClientStatus)(nil), // 5: grpc.testing.ClientStatus
|
||||
(*CoreResponse)(nil), // 6: grpc.testing.CoreResponse
|
||||
}
|
||||
var file_grpc_testing_worker_service_proto_depIdxs = []int32{
|
||||
0, // 0: grpc.testing.WorkerService.RunServer:input_type -> grpc.testing.ServerArgs
|
||||
1, // 1: grpc.testing.WorkerService.RunClient:input_type -> grpc.testing.ClientArgs
|
||||
2, // 2: grpc.testing.WorkerService.CoreCount:input_type -> grpc.testing.CoreRequest
|
||||
3, // 3: grpc.testing.WorkerService.QuitWorker:input_type -> grpc.testing.Void
|
||||
4, // 4: grpc.testing.WorkerService.RunServer:output_type -> grpc.testing.ServerStatus
|
||||
5, // 5: grpc.testing.WorkerService.RunClient:output_type -> grpc.testing.ClientStatus
|
||||
6, // 6: grpc.testing.WorkerService.CoreCount:output_type -> grpc.testing.CoreResponse
|
||||
3, // 7: grpc.testing.WorkerService.QuitWorker:output_type -> grpc.testing.Void
|
||||
4, // [4:8] is the sub-list for method output_type
|
||||
0, // [0:4] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_grpc_testing_worker_service_proto_init() }
|
||||
func file_grpc_testing_worker_service_proto_init() {
|
||||
if File_grpc_testing_worker_service_proto != nil {
|
||||
return
|
||||
}
|
||||
file_grpc_testing_control_proto_init()
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_grpc_testing_worker_service_proto_rawDesc), len(file_grpc_testing_worker_service_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 0,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_grpc_testing_worker_service_proto_goTypes,
|
||||
DependencyIndexes: file_grpc_testing_worker_service_proto_depIdxs,
|
||||
}.Build()
|
||||
File_grpc_testing_worker_service_proto = out.File
|
||||
file_grpc_testing_worker_service_proto_goTypes = nil
|
||||
file_grpc_testing_worker_service_proto_depIdxs = nil
|
||||
}
|
||||
269
interop/grpc_testing/worker_service_grpc.pb.go
Normal file
269
interop/grpc_testing/worker_service_grpc.pb.go
Normal file
@@ -0,0 +1,269 @@
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// An integration test service that covers all the method signature permutations
|
||||
// of unary/streaming requests/responses.
|
||||
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc v5.27.1
|
||||
// source: grpc/testing/worker_service.proto
|
||||
|
||||
package grpc_testing
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
WorkerService_RunServer_FullMethodName = "/grpc.testing.WorkerService/RunServer"
|
||||
WorkerService_RunClient_FullMethodName = "/grpc.testing.WorkerService/RunClient"
|
||||
WorkerService_CoreCount_FullMethodName = "/grpc.testing.WorkerService/CoreCount"
|
||||
WorkerService_QuitWorker_FullMethodName = "/grpc.testing.WorkerService/QuitWorker"
|
||||
)
|
||||
|
||||
// WorkerServiceClient is the client API for WorkerService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type WorkerServiceClient interface {
|
||||
// Start server with specified workload.
|
||||
// First request sent specifies the ServerConfig followed by ServerStatus
|
||||
// response. After that, a "Mark" can be sent anytime to request the latest
|
||||
// stats. Closing the stream will initiate shutdown of the test server
|
||||
// and once the shutdown has finished, the OK status is sent to terminate
|
||||
// this RPC.
|
||||
RunServer(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ServerArgs, ServerStatus], error)
|
||||
// Start client with specified workload.
|
||||
// First request sent specifies the ClientConfig followed by ClientStatus
|
||||
// response. After that, a "Mark" can be sent anytime to request the latest
|
||||
// stats. Closing the stream will initiate shutdown of the test client
|
||||
// and once the shutdown has finished, the OK status is sent to terminate
|
||||
// this RPC.
|
||||
RunClient(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ClientArgs, ClientStatus], error)
|
||||
// Just return the core count - unary call
|
||||
CoreCount(ctx context.Context, in *CoreRequest, opts ...grpc.CallOption) (*CoreResponse, error)
|
||||
// Quit this worker
|
||||
QuitWorker(ctx context.Context, in *Void, opts ...grpc.CallOption) (*Void, error)
|
||||
}
|
||||
|
||||
type workerServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewWorkerServiceClient(cc grpc.ClientConnInterface) WorkerServiceClient {
|
||||
return &workerServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *workerServiceClient) RunServer(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ServerArgs, ServerStatus], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &WorkerService_ServiceDesc.Streams[0], WorkerService_RunServer_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[ServerArgs, ServerStatus]{ClientStream: stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type WorkerService_RunServerClient = grpc.BidiStreamingClient[ServerArgs, ServerStatus]
|
||||
|
||||
func (c *workerServiceClient) RunClient(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ClientArgs, ClientStatus], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &WorkerService_ServiceDesc.Streams[1], WorkerService_RunClient_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[ClientArgs, ClientStatus]{ClientStream: stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type WorkerService_RunClientClient = grpc.BidiStreamingClient[ClientArgs, ClientStatus]
|
||||
|
||||
func (c *workerServiceClient) CoreCount(ctx context.Context, in *CoreRequest, opts ...grpc.CallOption) (*CoreResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CoreResponse)
|
||||
err := c.cc.Invoke(ctx, WorkerService_CoreCount_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *workerServiceClient) QuitWorker(ctx context.Context, in *Void, opts ...grpc.CallOption) (*Void, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(Void)
|
||||
err := c.cc.Invoke(ctx, WorkerService_QuitWorker_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// WorkerServiceServer is the server API for WorkerService service.
|
||||
// All implementations must embed UnimplementedWorkerServiceServer
|
||||
// for forward compatibility.
|
||||
type WorkerServiceServer interface {
|
||||
// Start server with specified workload.
|
||||
// First request sent specifies the ServerConfig followed by ServerStatus
|
||||
// response. After that, a "Mark" can be sent anytime to request the latest
|
||||
// stats. Closing the stream will initiate shutdown of the test server
|
||||
// and once the shutdown has finished, the OK status is sent to terminate
|
||||
// this RPC.
|
||||
RunServer(grpc.BidiStreamingServer[ServerArgs, ServerStatus]) error
|
||||
// Start client with specified workload.
|
||||
// First request sent specifies the ClientConfig followed by ClientStatus
|
||||
// response. After that, a "Mark" can be sent anytime to request the latest
|
||||
// stats. Closing the stream will initiate shutdown of the test client
|
||||
// and once the shutdown has finished, the OK status is sent to terminate
|
||||
// this RPC.
|
||||
RunClient(grpc.BidiStreamingServer[ClientArgs, ClientStatus]) error
|
||||
// Just return the core count - unary call
|
||||
CoreCount(context.Context, *CoreRequest) (*CoreResponse, error)
|
||||
// Quit this worker
|
||||
QuitWorker(context.Context, *Void) (*Void, error)
|
||||
mustEmbedUnimplementedWorkerServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedWorkerServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedWorkerServiceServer struct{}
|
||||
|
||||
func (UnimplementedWorkerServiceServer) RunServer(grpc.BidiStreamingServer[ServerArgs, ServerStatus]) error {
|
||||
return status.Error(codes.Unimplemented, "method RunServer not implemented")
|
||||
}
|
||||
func (UnimplementedWorkerServiceServer) RunClient(grpc.BidiStreamingServer[ClientArgs, ClientStatus]) error {
|
||||
return status.Error(codes.Unimplemented, "method RunClient not implemented")
|
||||
}
|
||||
func (UnimplementedWorkerServiceServer) CoreCount(context.Context, *CoreRequest) (*CoreResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method CoreCount not implemented")
|
||||
}
|
||||
func (UnimplementedWorkerServiceServer) QuitWorker(context.Context, *Void) (*Void, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method QuitWorker not implemented")
|
||||
}
|
||||
func (UnimplementedWorkerServiceServer) mustEmbedUnimplementedWorkerServiceServer() {}
|
||||
func (UnimplementedWorkerServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeWorkerServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to WorkerServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeWorkerServiceServer interface {
|
||||
mustEmbedUnimplementedWorkerServiceServer()
|
||||
}
|
||||
|
||||
func RegisterWorkerServiceServer(s grpc.ServiceRegistrar, srv WorkerServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedWorkerServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&WorkerService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _WorkerService_RunServer_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(WorkerServiceServer).RunServer(&grpc.GenericServerStream[ServerArgs, ServerStatus]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type WorkerService_RunServerServer = grpc.BidiStreamingServer[ServerArgs, ServerStatus]
|
||||
|
||||
func _WorkerService_RunClient_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(WorkerServiceServer).RunClient(&grpc.GenericServerStream[ClientArgs, ClientStatus]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type WorkerService_RunClientServer = grpc.BidiStreamingServer[ClientArgs, ClientStatus]
|
||||
|
||||
func _WorkerService_CoreCount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CoreRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(WorkerServiceServer).CoreCount(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: WorkerService_CoreCount_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(WorkerServiceServer).CoreCount(ctx, req.(*CoreRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _WorkerService_QuitWorker_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Void)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(WorkerServiceServer).QuitWorker(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: WorkerService_QuitWorker_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(WorkerServiceServer).QuitWorker(ctx, req.(*Void))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// WorkerService_ServiceDesc is the grpc.ServiceDesc for WorkerService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var WorkerService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "grpc.testing.WorkerService",
|
||||
HandlerType: (*WorkerServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "CoreCount",
|
||||
Handler: _WorkerService_CoreCount_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "QuitWorker",
|
||||
Handler: _WorkerService_QuitWorker_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "RunServer",
|
||||
Handler: _WorkerService_RunServer_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "RunClient",
|
||||
Handler: _WorkerService_RunClient_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "grpc/testing/worker_service.proto",
|
||||
}
|
||||
192
interop/grpclb_fallback/client_linux.go
Normal file
192
interop/grpclb_fallback/client_linux.go
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2019 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Binary grpclb_fallback is an interop test client for grpclb fallback.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"google.golang.org/grpc"
|
||||
_ "google.golang.org/grpc/balancer/grpclb"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/alts"
|
||||
"google.golang.org/grpc/credentials/google"
|
||||
_ "google.golang.org/grpc/xds/googledirectpath"
|
||||
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
testpb "google.golang.org/grpc/interop/grpc_testing"
|
||||
)
|
||||
|
||||
var (
|
||||
customCredentialsType = flag.String("custom_credentials_type", "", "Client creds to use")
|
||||
serverURI = flag.String("server_uri", "dns:///staging-grpc-directpath-fallback-test.googleapis.com:443", "The server host name")
|
||||
induceFallbackCmd = flag.String("induce_fallback_cmd", "", "Command to induce fallback e.g. by making certain addresses unroutable")
|
||||
fallbackDeadlineSeconds = flag.Int("fallback_deadline_seconds", 1, "How long to wait for fallback to happen after induce_fallback_cmd")
|
||||
testCase = flag.String("test_case", "",
|
||||
`Configure different test cases. Valid options are:
|
||||
fallback_before_startup : LB/backend connections fail before RPC's have been made;
|
||||
fallback_after_startup : LB/backend connections fail after RPC's have been made;`)
|
||||
infoLog = log.New(os.Stderr, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||
errorLog = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||
)
|
||||
|
||||
func doRPCAndGetPath(client testgrpc.TestServiceClient, timeout time.Duration) testpb.GrpclbRouteType {
|
||||
infoLog.Printf("doRPCAndGetPath timeout:%v\n", timeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
req := &testpb.SimpleRequest{
|
||||
FillGrpclbRouteType: true,
|
||||
}
|
||||
reply, err := client.UnaryCall(ctx, req)
|
||||
if err != nil {
|
||||
infoLog.Printf("doRPCAndGetPath error:%v\n", err)
|
||||
return testpb.GrpclbRouteType_GRPCLB_ROUTE_TYPE_UNKNOWN
|
||||
}
|
||||
g := reply.GetGrpclbRouteType()
|
||||
infoLog.Printf("doRPCAndGetPath got grpclb route type: %v\n", g)
|
||||
if g != testpb.GrpclbRouteType_GRPCLB_ROUTE_TYPE_FALLBACK && g != testpb.GrpclbRouteType_GRPCLB_ROUTE_TYPE_BACKEND {
|
||||
errorLog.Fatalf("Expected grpclb route type to be either backend or fallback; got: %d", g)
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
func dialTCPUserTimeout(ctx context.Context, addr string) (net.Conn, error) {
|
||||
control := func(_, _ string, c syscall.RawConn) error {
|
||||
var syscallErr error
|
||||
controlErr := c.Control(func(fd uintptr) {
|
||||
syscallErr = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_USER_TIMEOUT, 20000)
|
||||
})
|
||||
if syscallErr != nil {
|
||||
errorLog.Fatalf("syscall error setting sockopt TCP_USER_TIMEOUT: %v", syscallErr)
|
||||
}
|
||||
if controlErr != nil {
|
||||
errorLog.Fatalf("control error setting sockopt TCP_USER_TIMEOUT: %v", syscallErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
d := &net.Dialer{
|
||||
Control: control,
|
||||
}
|
||||
return d.DialContext(ctx, "tcp", addr)
|
||||
}
|
||||
|
||||
func createTestConn() *grpc.ClientConn {
|
||||
opts := []grpc.DialOption{
|
||||
grpc.WithContextDialer(dialTCPUserTimeout),
|
||||
}
|
||||
switch *customCredentialsType {
|
||||
case "tls":
|
||||
creds := credentials.NewClientTLSFromCert(nil, "")
|
||||
opts = append(opts, grpc.WithTransportCredentials(creds))
|
||||
case "alts":
|
||||
creds := alts.NewClientCreds(alts.DefaultClientOptions())
|
||||
opts = append(opts, grpc.WithTransportCredentials(creds))
|
||||
case "google_default_credentials":
|
||||
opts = append(opts, grpc.WithCredentialsBundle(google.NewDefaultCredentials()))
|
||||
case "compute_engine_channel_creds":
|
||||
opts = append(opts, grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()))
|
||||
default:
|
||||
errorLog.Fatalf("Invalid --custom_credentials_type:%v", *customCredentialsType)
|
||||
}
|
||||
conn, err := grpc.NewClient(*serverURI, opts...)
|
||||
if err != nil {
|
||||
errorLog.Fatalf("grpc.NewClient(%q) = %v", *serverURI, err)
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
||||
func runCmd(command string) {
|
||||
infoLog.Printf("Running cmd:|%v|\n", command)
|
||||
if err := exec.Command("bash", "-c", command).Run(); err != nil {
|
||||
errorLog.Fatalf("error running cmd:|%v| : %v", command, err)
|
||||
}
|
||||
}
|
||||
|
||||
func waitForFallbackAndDoRPCs(client testgrpc.TestServiceClient, fallbackDeadline time.Time) {
|
||||
fallbackRetryCount := 0
|
||||
fellBack := false
|
||||
for time.Now().Before(fallbackDeadline) {
|
||||
g := doRPCAndGetPath(client, 20*time.Second)
|
||||
if g == testpb.GrpclbRouteType_GRPCLB_ROUTE_TYPE_FALLBACK {
|
||||
infoLog.Println("Made one successful RPC to a fallback. Now expect the same for the rest.")
|
||||
fellBack = true
|
||||
break
|
||||
} else if g == testpb.GrpclbRouteType_GRPCLB_ROUTE_TYPE_BACKEND {
|
||||
errorLog.Fatalf("Got RPC type backend. This suggests an error in test implementation")
|
||||
} else {
|
||||
infoLog.Println("Retryable RPC failure on iteration:", fallbackRetryCount)
|
||||
}
|
||||
fallbackRetryCount++
|
||||
}
|
||||
if !fellBack {
|
||||
infoLog.Fatalf("Didn't fall back before deadline: %v\n", fallbackDeadline)
|
||||
}
|
||||
for i := 0; i < 30; i++ {
|
||||
if g := doRPCAndGetPath(client, 20*time.Second); g != testpb.GrpclbRouteType_GRPCLB_ROUTE_TYPE_FALLBACK {
|
||||
errorLog.Fatalf("Expected RPC to take grpclb route type FALLBACK. Got: %v", g)
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func doFallbackBeforeStartup() {
|
||||
runCmd(*induceFallbackCmd)
|
||||
fallbackDeadline := time.Now().Add(time.Duration(*fallbackDeadlineSeconds) * time.Second)
|
||||
conn := createTestConn()
|
||||
defer conn.Close()
|
||||
client := testgrpc.NewTestServiceClient(conn)
|
||||
waitForFallbackAndDoRPCs(client, fallbackDeadline)
|
||||
}
|
||||
|
||||
func doFallbackAfterStartup() {
|
||||
conn := createTestConn()
|
||||
defer conn.Close()
|
||||
client := testgrpc.NewTestServiceClient(conn)
|
||||
if g := doRPCAndGetPath(client, 20*time.Second); g != testpb.GrpclbRouteType_GRPCLB_ROUTE_TYPE_BACKEND {
|
||||
errorLog.Fatalf("Expected RPC to take grpclb route type BACKEND. Got: %v", g)
|
||||
}
|
||||
runCmd(*induceFallbackCmd)
|
||||
fallbackDeadline := time.Now().Add(time.Duration(*fallbackDeadlineSeconds) * time.Second)
|
||||
waitForFallbackAndDoRPCs(client, fallbackDeadline)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if len(*induceFallbackCmd) == 0 {
|
||||
errorLog.Fatalf("--induce_fallback_cmd unset")
|
||||
}
|
||||
switch *testCase {
|
||||
case "fallback_before_startup":
|
||||
doFallbackBeforeStartup()
|
||||
log.Printf("FallbackBeforeStartup done!\n")
|
||||
case "fallback_after_startup":
|
||||
doFallbackAfterStartup()
|
||||
log.Printf("FallbackAfterStartup done!\n")
|
||||
default:
|
||||
errorLog.Fatalf("Unsupported test case: %v", *testCase)
|
||||
}
|
||||
}
|
||||
165
interop/http2/negative_http2_client.go
Normal file
165
interop/http2/negative_http2_client.go
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2016 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Binary http2 is used to test http2 error edge cases like GOAWAYs and
|
||||
// RST_STREAMs
|
||||
//
|
||||
// Documentation:
|
||||
// https://github.com/grpc/grpc/blob/master/doc/negative-http2-interop-test-descriptions.md
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/interop"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
testpb "google.golang.org/grpc/interop/grpc_testing"
|
||||
)
|
||||
|
||||
var (
|
||||
serverHost = flag.String("server_host", "localhost", "The server host name")
|
||||
serverPort = flag.Int("server_port", 8080, "The server port number")
|
||||
testCase = flag.String("test_case", "goaway",
|
||||
`Configure different test cases. Valid options are:
|
||||
goaway : client sends two requests, the server will send a goaway in between;
|
||||
rst_after_header : server will send rst_stream after it sends headers;
|
||||
rst_during_data : server will send rst_stream while sending data;
|
||||
rst_after_data : server will send rst_stream after sending data;
|
||||
ping : server will send pings between each http2 frame;
|
||||
max_streams : server will ensure that the max_concurrent_streams limit is upheld;`)
|
||||
largeReqSize = 271828
|
||||
largeRespSize = 314159
|
||||
|
||||
logger = grpclog.Component("interop")
|
||||
)
|
||||
|
||||
func largeSimpleRequest() *testpb.SimpleRequest {
|
||||
pl := interop.ClientNewPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
|
||||
return &testpb.SimpleRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
ResponseSize: int32(largeRespSize),
|
||||
Payload: pl,
|
||||
}
|
||||
}
|
||||
|
||||
// sends two unary calls. The server asserts that the calls use different connections.
|
||||
func goaway(ctx context.Context, tc testgrpc.TestServiceClient) {
|
||||
interop.DoLargeUnaryCall(ctx, tc)
|
||||
// sleep to ensure that the client has time to recv the GOAWAY.
|
||||
// TODO(ncteisen): make this less hacky.
|
||||
time.Sleep(1 * time.Second)
|
||||
interop.DoLargeUnaryCall(ctx, tc)
|
||||
}
|
||||
|
||||
func rstAfterHeader(tc testgrpc.TestServiceClient) {
|
||||
req := largeSimpleRequest()
|
||||
reply, err := tc.UnaryCall(context.Background(), req)
|
||||
if reply != nil {
|
||||
logger.Fatal("Client received reply despite server sending rst stream after header")
|
||||
}
|
||||
if status.Code(err) != codes.Internal {
|
||||
logger.Fatalf("%v.UnaryCall() = _, %v, want _, %v", tc, status.Code(err), codes.Internal)
|
||||
}
|
||||
}
|
||||
|
||||
func rstDuringData(tc testgrpc.TestServiceClient) {
|
||||
req := largeSimpleRequest()
|
||||
reply, err := tc.UnaryCall(context.Background(), req)
|
||||
if reply != nil {
|
||||
logger.Fatal("Client received reply despite server sending rst stream during data")
|
||||
}
|
||||
if status.Code(err) != codes.Unknown {
|
||||
logger.Fatalf("%v.UnaryCall() = _, %v, want _, %v", tc, status.Code(err), codes.Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func rstAfterData(tc testgrpc.TestServiceClient) {
|
||||
req := largeSimpleRequest()
|
||||
reply, err := tc.UnaryCall(context.Background(), req)
|
||||
if reply != nil {
|
||||
logger.Fatal("Client received reply despite server sending rst stream after data")
|
||||
}
|
||||
if status.Code(err) != codes.Internal {
|
||||
logger.Fatalf("%v.UnaryCall() = _, %v, want _, %v", tc, status.Code(err), codes.Internal)
|
||||
}
|
||||
}
|
||||
|
||||
func ping(ctx context.Context, tc testgrpc.TestServiceClient) {
|
||||
// The server will assert that every ping it sends was ACK-ed by the client.
|
||||
interop.DoLargeUnaryCall(ctx, tc)
|
||||
}
|
||||
|
||||
func maxStreams(ctx context.Context, tc testgrpc.TestServiceClient) {
|
||||
interop.DoLargeUnaryCall(ctx, tc)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 15; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
interop.DoLargeUnaryCall(ctx, tc)
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
serverAddr := net.JoinHostPort(*serverHost, strconv.Itoa(*serverPort))
|
||||
var opts []grpc.DialOption
|
||||
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
conn, err := grpc.NewClient(serverAddr, opts...)
|
||||
if err != nil {
|
||||
logger.Fatalf("grpc.NewClient(%q) = %v", serverAddr, err)
|
||||
}
|
||||
defer conn.Close()
|
||||
tc := testgrpc.NewTestServiceClient(conn)
|
||||
ctx := context.Background()
|
||||
switch *testCase {
|
||||
case "goaway":
|
||||
goaway(ctx, tc)
|
||||
logger.Infoln("goaway done")
|
||||
case "rst_after_header":
|
||||
rstAfterHeader(tc)
|
||||
logger.Infoln("rst_after_header done")
|
||||
case "rst_during_data":
|
||||
rstDuringData(tc)
|
||||
logger.Infoln("rst_during_data done")
|
||||
case "rst_after_data":
|
||||
rstAfterData(tc)
|
||||
logger.Infoln("rst_after_data done")
|
||||
case "ping":
|
||||
ping(ctx, tc)
|
||||
logger.Infoln("ping done")
|
||||
case "max_streams":
|
||||
maxStreams(ctx, tc)
|
||||
logger.Infoln("max_streams done")
|
||||
default:
|
||||
logger.Fatal("Unsupported test case: ", *testCase)
|
||||
}
|
||||
}
|
||||
140
interop/interop_test.sh
Executable file
140
interop/interop_test.sh
Executable file
@@ -0,0 +1,140 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2019 gRPC authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
set -e +x
|
||||
|
||||
export TMPDIR=$(mktemp -d)
|
||||
trap "rm -rf ${TMPDIR}" EXIT
|
||||
|
||||
clean () {
|
||||
for i in {1..10}; do
|
||||
jobs -p | xargs -n1 pkill -P
|
||||
# A simple "wait" just hangs sometimes. Running `jobs` seems to help.
|
||||
sleep 1
|
||||
if jobs | read; then
|
||||
return
|
||||
fi
|
||||
done
|
||||
echo "$(tput setaf 1) clean failed to kill tests $(tput sgr 0)"
|
||||
jobs
|
||||
pstree
|
||||
exit 1
|
||||
}
|
||||
|
||||
fail () {
|
||||
echo "$(tput setaf 1) $(date): $1 $(tput sgr 0)"
|
||||
clean
|
||||
exit 1
|
||||
}
|
||||
|
||||
pass () {
|
||||
echo "$(tput setaf 2) $(date): $1 $(tput sgr 0)"
|
||||
}
|
||||
|
||||
withTimeout () {
|
||||
timer=$1
|
||||
shift
|
||||
|
||||
# Run command in the background.
|
||||
cmd=$(printf '%q ' "$@")
|
||||
eval "$cmd" &
|
||||
wpid=$!
|
||||
# Kill after $timer seconds.
|
||||
sleep $timer && kill $wpid &
|
||||
kpid=$!
|
||||
# Wait for the background thread.
|
||||
wait $wpid
|
||||
res=$?
|
||||
# Kill the killer pid in case it's still running.
|
||||
kill $kpid || true
|
||||
wait $kpid || true
|
||||
return $res
|
||||
}
|
||||
|
||||
# Don't run some tests that need a special environment:
|
||||
# "google_default_credentials"
|
||||
# "compute_engine_channel_credentials"
|
||||
# "compute_engine_creds"
|
||||
# "service_account_creds"
|
||||
# "jwt_token_creds"
|
||||
# "oauth2_auth_token"
|
||||
# "per_rpc_creds"
|
||||
# "pick_first_unary"
|
||||
|
||||
CASES=(
|
||||
"empty_unary"
|
||||
"large_unary"
|
||||
"client_streaming"
|
||||
"server_streaming"
|
||||
"ping_pong"
|
||||
"empty_stream"
|
||||
"timeout_on_sleeping_server"
|
||||
"cancel_after_begin"
|
||||
"cancel_after_first_response"
|
||||
"status_code_and_message"
|
||||
"special_status_message"
|
||||
"custom_metadata"
|
||||
"unimplemented_method"
|
||||
"unimplemented_service"
|
||||
"orca_per_rpc"
|
||||
"orca_oob"
|
||||
"rpc_soak"
|
||||
"channel_soak"
|
||||
)
|
||||
|
||||
# Build server
|
||||
echo "$(tput setaf 4) $(date): building server $(tput sgr 0)"
|
||||
if ! go build -o /dev/null ./interop/server; then
|
||||
fail "failed to build server"
|
||||
else
|
||||
pass "successfully built server"
|
||||
fi
|
||||
|
||||
# Build client
|
||||
echo "$(tput setaf 4) $(date): building client $(tput sgr 0)"
|
||||
if ! go build -o /dev/null ./interop/client; then
|
||||
fail "failed to build client"
|
||||
else
|
||||
pass "successfully built client"
|
||||
fi
|
||||
|
||||
# Start server
|
||||
SERVER_LOG="$(mktemp)"
|
||||
GRPC_GO_LOG_SEVERITY_LEVEL=info go run ./interop/server --use_tls &> $SERVER_LOG &
|
||||
|
||||
for case in ${CASES[@]}; do
|
||||
echo "$(tput setaf 4) $(date): testing: ${case} $(tput sgr 0)"
|
||||
|
||||
CLIENT_LOG="$(mktemp)"
|
||||
if ! GRPC_GO_LOG_SEVERITY_LEVEL=info withTimeout 20 go run ./interop/client \
|
||||
--use_tls \
|
||||
--server_host_override=foo.test.google.fr \
|
||||
--use_test_ca --test_case="${case}" \
|
||||
--service_config_json='{ "loadBalancingConfig": [{ "test_backend_metrics_load_balancer": {} }]}' \
|
||||
&> $CLIENT_LOG; then
|
||||
fail "FAIL: test case ${case}
|
||||
got server log:
|
||||
$(cat $SERVER_LOG)
|
||||
got client log:
|
||||
$(cat $CLIENT_LOG)
|
||||
"
|
||||
else
|
||||
pass "PASS: test case ${case}"
|
||||
fi
|
||||
done
|
||||
|
||||
clean
|
||||
53
interop/observability/Dockerfile
Normal file
53
interop/observability/Dockerfile
Normal file
@@ -0,0 +1,53 @@
|
||||
# Copyright 2022 gRPC authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
#
|
||||
# Stage 1: Build the interop test client and server
|
||||
#
|
||||
|
||||
FROM golang:1.25-bullseye as build
|
||||
|
||||
WORKDIR /grpc-go
|
||||
COPY . .
|
||||
|
||||
WORKDIR /grpc-go/interop/observability
|
||||
RUN go build -o server/ server/server.go && \
|
||||
go build -o client/ client/client.go
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Stage 2:
|
||||
#
|
||||
# - Copy only the necessary files to reduce Docker image size.
|
||||
# - Have an ENTRYPOINT script which will launch the interop test client or server
|
||||
# with the given parameters.
|
||||
#
|
||||
|
||||
FROM golang:1.24-bullseye
|
||||
|
||||
ENV GRPC_GO_LOG_SEVERITY_LEVEL info
|
||||
ENV GRPC_GO_LOG_VERBOSITY_LEVEL 2
|
||||
|
||||
WORKDIR /grpc-go/interop/observability/server
|
||||
COPY --from=build /grpc-go/interop/observability/server/server .
|
||||
|
||||
WORKDIR /grpc-go/interop/observability/client
|
||||
COPY --from=build /grpc-go/interop/observability/client/client .
|
||||
|
||||
WORKDIR /grpc-go/interop/observability
|
||||
COPY --from=build /grpc-go/interop/observability/run.sh .
|
||||
|
||||
ENTRYPOINT ["/grpc-go/interop/observability/run.sh"]
|
||||
26
interop/observability/build_docker.sh
Executable file
26
interop/observability/build_docker.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2022 gRPC authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -ex
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
# Environment Variables:
|
||||
#
|
||||
# TAG_NAME: the docker image tag name
|
||||
#
|
||||
|
||||
echo Building ${TAG_NAME}
|
||||
|
||||
docker build --no-cache -t ${TAG_NAME} -f ./interop/observability/Dockerfile .
|
||||
84
interop/observability/client/client.go
Normal file
84
interop/observability/client/client.go
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2022 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Binary client is an interop client for Observability.
|
||||
//
|
||||
// See interop test case descriptions [here].
|
||||
//
|
||||
// [here]: https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/gcp/observability"
|
||||
"google.golang.org/grpc/interop"
|
||||
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
)
|
||||
|
||||
var (
|
||||
serverHost = flag.String("server_host", "localhost", "The server host name")
|
||||
serverPort = flag.Int("server_port", 10000, "The server port number")
|
||||
testCase = flag.String("test_case", "large_unary", "The action to perform")
|
||||
numTimes = flag.Int("num_times", 1, "Number of times to run the test case")
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := observability.Start(context.Background())
|
||||
if err != nil {
|
||||
log.Fatalf("observability start failed: %v", err)
|
||||
}
|
||||
defer observability.End()
|
||||
flag.Parse()
|
||||
serverAddr := *serverHost
|
||||
if *serverPort != 0 {
|
||||
serverAddr = net.JoinHostPort(*serverHost, strconv.Itoa(*serverPort))
|
||||
}
|
||||
conn, err := grpc.NewClient(serverAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
log.Fatalf("grpc.NewClient(%q) = %v", serverAddr, err)
|
||||
}
|
||||
defer conn.Close()
|
||||
tc := testgrpc.NewTestServiceClient(conn)
|
||||
ctx := context.Background()
|
||||
for i := 0; i < *numTimes; i++ {
|
||||
if *testCase == "ping_pong" {
|
||||
interop.DoPingPong(ctx, tc)
|
||||
} else if *testCase == "large_unary" {
|
||||
interop.DoLargeUnaryCall(ctx, tc)
|
||||
} else if *testCase == "custom_metadata" {
|
||||
interop.DoCustomMetadata(ctx, tc)
|
||||
} else {
|
||||
log.Fatalf("Invalid test case: %s", *testCase)
|
||||
}
|
||||
}
|
||||
// TODO(stanleycheung): remove this once the observability exporter plugin is able to
|
||||
// gracefully flush observability data to cloud at shutdown
|
||||
// TODO(stanleycheung): see if we can reduce the number 65
|
||||
const exporterSleepDuration = 65 * time.Second
|
||||
log.Printf("Sleeping %v before closing...", exporterSleepDuration)
|
||||
time.Sleep(exporterSleepDuration)
|
||||
}
|
||||
73
interop/observability/go.mod
Normal file
73
interop/observability/go.mod
Normal file
@@ -0,0 +1,73 @@
|
||||
module google.golang.org/grpc/interop/observability
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
google.golang.org/grpc v1.83.0
|
||||
google.golang.org/grpc/gcp/observability v1.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.123.0 // indirect
|
||||
cloud.google.com/go/auth v0.23.1 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
cloud.google.com/go/logging v1.19.1 // indirect
|
||||
cloud.google.com/go/longrunning v1.2.0 // indirect
|
||||
cloud.google.com/go/monitoring v1.30.0 // indirect
|
||||
cloud.google.com/go/trace v1.16.0 // indirect
|
||||
contrib.go.opencensus.io/exporter/stackdriver v0.13.15-0.20230702191903-2de6d2748484 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.43.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.37 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.36 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.37 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.37 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.37 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.38 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.37 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.5.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.33.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.38.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.45.6 // indirect
|
||||
github.com/aws/smithy-go v1.27.8 // indirect
|
||||
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect
|
||||
github.com/felixge/httpsnoop v1.1.0 // indirect
|
||||
github.com/go-logr/logr v1.4.4 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.21 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.23.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.70.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0 // indirect
|
||||
go.opentelemetry.io/otel v1.45.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.45.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.45.0 // indirect
|
||||
golang.org/x/crypto v0.55.0 // indirect
|
||||
golang.org/x/net v0.58.0 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
golang.org/x/sync v0.22.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
golang.org/x/text v0.41.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
google.golang.org/api v0.293.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20260817212433-ac3dfec99bb1 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260817212433-ac3dfec99bb1 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 // indirect
|
||||
google.golang.org/grpc/stats/opencensus v1.0.0 // indirect
|
||||
google.golang.org/protobuf v1.36.12 // indirect
|
||||
)
|
||||
|
||||
replace google.golang.org/grpc => ../..
|
||||
|
||||
replace google.golang.org/grpc/gcp/observability => ../../gcp/observability
|
||||
|
||||
replace google.golang.org/grpc/stats/opencensus => ../../stats/opencensus
|
||||
29
interop/observability/run.sh
Executable file
29
interop/observability/run.sh
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2022 gRPC authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -ex
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
if [ "$1" = "server" ] ; then
|
||||
/grpc-go/interop/observability/server/server "${@:2}"
|
||||
|
||||
elif [ "$1" = "client" ] ; then
|
||||
/grpc-go/interop/observability/client/client "${@:2}"
|
||||
|
||||
else
|
||||
echo "Invalid action: $1. Usage:"
|
||||
echo " $ .../run.sh [server|client] --server_host=<hostname> --server_port=<port> ..."
|
||||
exit 1
|
||||
fi
|
||||
60
interop/observability/server/server.go
Normal file
60
interop/observability/server/server.go
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2022 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Binary server is an interop server for Observability.
|
||||
//
|
||||
// See interop test case descriptions [here].
|
||||
//
|
||||
// [here]: https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/gcp/observability"
|
||||
"google.golang.org/grpc/interop"
|
||||
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
)
|
||||
|
||||
var (
|
||||
port = flag.Int("port", 10000, "The server port")
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := observability.Start(context.Background())
|
||||
if err != nil {
|
||||
log.Fatalf("observability start failed: %v", err)
|
||||
}
|
||||
defer observability.End()
|
||||
flag.Parse()
|
||||
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
|
||||
if err != nil {
|
||||
log.Fatalf("failed to listen: %v", err)
|
||||
}
|
||||
server := grpc.NewServer()
|
||||
defer server.Stop()
|
||||
testgrpc.RegisterTestServiceServer(server, interop.NewTestServer())
|
||||
log.Printf("Observability interop server listening on %v", lis.Addr())
|
||||
server.Serve(lis)
|
||||
}
|
||||
256
interop/orcalb.go
Normal file
256
interop/orcalb.go
Normal file
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2023 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package interop
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/balancer/endpointsharding"
|
||||
"google.golang.org/grpc/balancer/pickfirst"
|
||||
"google.golang.org/grpc/connectivity"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
internalgrpclog "google.golang.org/grpc/internal/grpclog"
|
||||
"google.golang.org/grpc/orca"
|
||||
"google.golang.org/grpc/resolver"
|
||||
|
||||
v3orcapb "github.com/cncf/xds/go/xds/data/orca/v3"
|
||||
)
|
||||
|
||||
var orcaLogger = grpclog.Component("orca")
|
||||
|
||||
func init() {
|
||||
balancer.Register(orcabb{})
|
||||
}
|
||||
|
||||
type orcabb struct{}
|
||||
|
||||
// Build creates a new orcab balancer.
|
||||
func (orcabb) Build(cc balancer.ClientConn, bOpts balancer.BuildOptions) balancer.Balancer {
|
||||
b := &orcab{
|
||||
ClientConn: cc,
|
||||
stopOOBListeners: make(map[balancer.SubConn]func()),
|
||||
oobState: &oobState{
|
||||
reports: make(map[balancer.SubConn]*v3orcapb.OrcaLoadReport),
|
||||
},
|
||||
}
|
||||
b.logger = internalgrpclog.NewPrefixLogger(orcaLogger, fmt.Sprintf("[%p] ", b))
|
||||
b.child = endpointsharding.NewBalancer(b, bOpts, balancer.Get(pickfirst.Name).Build, endpointsharding.Options{})
|
||||
b.logger.Infof("Created")
|
||||
return b
|
||||
}
|
||||
|
||||
func (orcabb) Name() string {
|
||||
return "test_backend_metrics_load_balancer"
|
||||
}
|
||||
|
||||
// orcab is the balancer for the test_backend_metrics_load_balancer policy.
|
||||
// It delegates SubConn management to endpointsharding + pick_first and
|
||||
// intercepts NewSubConn calls to register OOB listeners on READY SubConns.
|
||||
type orcab struct {
|
||||
// The following fields are initialized at build time and read-only after
|
||||
// that and therefore do not need to be guarded by a mutex.
|
||||
balancer.ClientConn // Embeds to intercept NewSubConn and UpdateState calls
|
||||
child balancer.Balancer
|
||||
oobState *oobState
|
||||
logger *internalgrpclog.PrefixLogger
|
||||
|
||||
// mu guards the fields below.
|
||||
mu sync.Mutex
|
||||
stopOOBListeners map[balancer.SubConn]func()
|
||||
}
|
||||
|
||||
func (b *orcab) UpdateClientConnState(s balancer.ClientConnState) error {
|
||||
// Delegate to the child endpoint sharding balancer, which distributes
|
||||
// state updates to its pick_first children.
|
||||
return b.child.UpdateClientConnState(s)
|
||||
}
|
||||
|
||||
func (b *orcab) ResolverError(err error) {
|
||||
// Will cause an inline picker update from endpoint sharding.
|
||||
b.child.ResolverError(err)
|
||||
}
|
||||
|
||||
func (b *orcab) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
|
||||
orcaLogger.Errorf("UpdateSubConnState(%v, %+v) called unexpectedly", sc, state)
|
||||
}
|
||||
|
||||
func (b *orcab) ExitIdle() {
|
||||
// Propagate to the child endpoint sharding balancer.
|
||||
b.child.ExitIdle()
|
||||
}
|
||||
|
||||
func (b *orcab) Close() {
|
||||
b.mu.Lock()
|
||||
listeners := b.stopOOBListeners
|
||||
b.stopOOBListeners = nil
|
||||
b.mu.Unlock()
|
||||
for _, stop := range listeners {
|
||||
stop()
|
||||
}
|
||||
b.child.Close()
|
||||
}
|
||||
|
||||
// NewSubConn intercepts SubConn creation from pick_first children to wrap the
|
||||
// StateListener for OOB listener management on READY transitions.
|
||||
func (b *orcab) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
|
||||
// The variable sc is declared before the closure so the closure captures
|
||||
// the variable, not its (zero) value. After ClientConn.NewSubConn assigns
|
||||
// to sc, the closure sees the real SubConn when invoked.
|
||||
var sc balancer.SubConn
|
||||
oldListener := opts.StateListener
|
||||
opts.StateListener = func(state balancer.SubConnState) {
|
||||
b.updateSubConnState(sc, state)
|
||||
if oldListener != nil {
|
||||
oldListener(state)
|
||||
}
|
||||
}
|
||||
sc, err := b.ClientConn.NewSubConn(addrs, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sc, nil
|
||||
}
|
||||
|
||||
func (b *orcab) updateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
|
||||
b.mu.Lock()
|
||||
if b.stopOOBListeners == nil {
|
||||
b.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
if state.ConnectivityState == connectivity.Ready {
|
||||
oldStop := b.stopOOBListeners[sc]
|
||||
if oldStop != nil {
|
||||
oldStop()
|
||||
}
|
||||
stop := orca.RegisterOOBListener(sc, &orcaOOBListener{subConn: sc, balancer: b}, orca.OOBListenerOptions{ReportInterval: time.Second})
|
||||
b.stopOOBListeners[sc] = stop
|
||||
b.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
stop := b.stopOOBListeners[sc]
|
||||
if stop != nil {
|
||||
delete(b.stopOOBListeners, sc)
|
||||
defer func() { stop() }()
|
||||
}
|
||||
b.mu.Unlock()
|
||||
|
||||
if state.ConnectivityState == connectivity.Shutdown {
|
||||
b.oobState.mu.Lock()
|
||||
delete(b.oobState.reports, sc)
|
||||
b.oobState.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateState intercepts state updates from endpointsharding to wrap the
|
||||
// picker with ORCA load report handling.
|
||||
func (b *orcab) UpdateState(state balancer.State) {
|
||||
// If READY, wrap the picker to inject the ORCA Done callback; otherwise,
|
||||
// pass through the child picker as-is.
|
||||
if state.ConnectivityState == connectivity.Ready {
|
||||
state = balancer.State{
|
||||
ConnectivityState: state.ConnectivityState,
|
||||
Picker: &orcaPicker{childPicker: state.Picker, oobState: b.oobState},
|
||||
}
|
||||
}
|
||||
b.ClientConn.UpdateState(state)
|
||||
}
|
||||
|
||||
type orcaOOBListener struct {
|
||||
subConn balancer.SubConn
|
||||
balancer *orcab
|
||||
}
|
||||
|
||||
// OnLoadReport implements orca.OOBListener.
|
||||
func (l *orcaOOBListener) OnLoadReport(r *v3orcapb.OrcaLoadReport) {
|
||||
l.balancer.oobState.mu.Lock()
|
||||
defer l.balancer.oobState.mu.Unlock()
|
||||
l.balancer.oobState.reports[l.subConn] = r
|
||||
}
|
||||
|
||||
type oobState struct {
|
||||
mu sync.Mutex
|
||||
reports map[balancer.SubConn]*v3orcapb.OrcaLoadReport
|
||||
}
|
||||
type orcaPicker struct {
|
||||
childPicker balancer.Picker
|
||||
oobState *oobState
|
||||
}
|
||||
|
||||
func (p *orcaPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
|
||||
res, err := p.childPicker.Pick(info)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
p.oobState.mu.Lock()
|
||||
lr := p.oobState.reports[res.SubConn]
|
||||
p.oobState.mu.Unlock()
|
||||
|
||||
origDone := res.Done
|
||||
res.Done = func(di balancer.DoneInfo) {
|
||||
perRPCLR, _ := di.ServerLoad.(*v3orcapb.OrcaLoadReport)
|
||||
if perRPCLR != nil && (perRPCLR.CpuUtilization != 0 || perRPCLR.MemUtilization != 0 || len(perRPCLR.Utilization) > 0 || len(perRPCLR.RequestCost) > 0) {
|
||||
setContextCMR(info.Ctx, perRPCLR)
|
||||
} else if lr != nil {
|
||||
setContextCMR(info.Ctx, lr)
|
||||
}
|
||||
if origDone != nil {
|
||||
origDone(di)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func setContextCMR(ctx context.Context, lr *v3orcapb.OrcaLoadReport) {
|
||||
if r := orcaResultFromContext(ctx); r != nil {
|
||||
*r = lr
|
||||
}
|
||||
}
|
||||
|
||||
type orcaKey string
|
||||
|
||||
var orcaCtxKey = orcaKey("orcaResult")
|
||||
|
||||
// contextWithORCAResult sets a key in ctx with a pointer to an ORCA load
|
||||
// report that is to be filled in by the "test_backend_metrics_load_balancer"
|
||||
// LB policy's Picker's Done callback.
|
||||
//
|
||||
// If a per-call load report is provided from the server for the call, result
|
||||
// will be filled with that, otherwise the most recent OOB load report is used.
|
||||
// If no OOB report has been received, result is not modified.
|
||||
func contextWithORCAResult(ctx context.Context, result **v3orcapb.OrcaLoadReport) context.Context {
|
||||
return context.WithValue(ctx, orcaCtxKey, result)
|
||||
}
|
||||
|
||||
// orcaResultFromContext returns the ORCA load report stored in the context.
|
||||
// The LB policy uses this to communicate the load report back to the interop
|
||||
// client application.
|
||||
func orcaResultFromContext(ctx context.Context) **v3orcapb.OrcaLoadReport {
|
||||
v := ctx.Value(orcaCtxKey)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
return v.(**v3orcapb.OrcaLoadReport)
|
||||
}
|
||||
363
interop/orcalb_test.go
Normal file
363
interop/orcalb_test.go
Normal file
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2026 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package interop
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/internal"
|
||||
"google.golang.org/grpc/internal/grpctest"
|
||||
"google.golang.org/grpc/internal/stubserver"
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
testpb "google.golang.org/grpc/interop/grpc_testing"
|
||||
"google.golang.org/grpc/orca"
|
||||
"google.golang.org/grpc/resolver"
|
||||
"google.golang.org/grpc/resolver/manual"
|
||||
|
||||
v3orcapb "github.com/cncf/xds/go/xds/data/orca/v3"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
)
|
||||
|
||||
type s struct {
|
||||
grpctest.Tester
|
||||
}
|
||||
|
||||
func Test(t *testing.T) {
|
||||
grpctest.RunSubTests(t, s{})
|
||||
}
|
||||
|
||||
const defaultTestTimeout = 30 * time.Second
|
||||
|
||||
type orcaServer struct {
|
||||
*stubserver.StubServer
|
||||
metricsRecorder orca.ServerMetricsRecorder
|
||||
}
|
||||
|
||||
func startORCAServer(t *testing.T) *orcaServer {
|
||||
t.Helper()
|
||||
|
||||
smr := orca.NewServerMetricsRecorder()
|
||||
ts := NewTestServer(NewTestServerOptions{MetricsRecorder: smr})
|
||||
|
||||
stub := &stubserver.StubServer{
|
||||
UnaryCallF: func(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
|
||||
return ts.UnaryCall(ctx, in)
|
||||
},
|
||||
FullDuplexCallF: func(stream testgrpc.TestService_FullDuplexCallServer) error {
|
||||
return ts.FullDuplexCall(stream)
|
||||
},
|
||||
}
|
||||
|
||||
sopts := []grpc.ServerOption{orca.CallMetricsServerOption(nil)}
|
||||
|
||||
oso := orca.ServiceOptions{
|
||||
ServerMetricsProvider: smr,
|
||||
MinReportingInterval: time.Second,
|
||||
}
|
||||
internal.ORCAAllowAnyMinReportingInterval.(func(so *orca.ServiceOptions))(&oso)
|
||||
sopts = append(sopts, stubserver.RegisterServiceServerOption(func(s grpc.ServiceRegistrar) {
|
||||
if err := orca.Register(s, oso); err != nil {
|
||||
t.Fatalf("Failed to register ORCA service: %v", err)
|
||||
}
|
||||
}))
|
||||
|
||||
if err := stub.StartServer(sopts...); err != nil {
|
||||
t.Fatalf("Error starting server: %v", err)
|
||||
}
|
||||
t.Cleanup(stub.Stop)
|
||||
|
||||
return &orcaServer{
|
||||
StubServer: stub,
|
||||
metricsRecorder: smr,
|
||||
}
|
||||
}
|
||||
|
||||
func orcaSvcConfig() string {
|
||||
return `{"loadBalancingConfig": [{"test_backend_metrics_load_balancer": {}}]}`
|
||||
}
|
||||
|
||||
// TestORCAPerRPCReport verifies that per-call ORCA load reports flow from the
|
||||
// server through the orcaPicker's Done callback into the context.
|
||||
func (s) TestORCAPerRPCReport(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
|
||||
defer cancel()
|
||||
|
||||
srv := startORCAServer(t)
|
||||
if err := srv.StartClient(grpc.WithDefaultServiceConfig(orcaSvcConfig())); err != nil {
|
||||
t.Fatalf("Error starting client: %v", err)
|
||||
}
|
||||
|
||||
DoORCAPerRPCTest(ctx, srv.Client)
|
||||
}
|
||||
|
||||
// TestORCAOOBReport verifies that OOB ORCA load reports flow through
|
||||
// OnLoadReport and are returned by the picker when no per-call report is
|
||||
// present.
|
||||
func (s) TestORCAOOBReport(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
|
||||
defer cancel()
|
||||
|
||||
srv := startORCAServer(t)
|
||||
if err := srv.StartClient(grpc.WithDefaultServiceConfig(orcaSvcConfig())); err != nil {
|
||||
t.Fatalf("Error starting client: %v", err)
|
||||
}
|
||||
|
||||
DoORCAOOBTest(ctx, srv.Client)
|
||||
}
|
||||
|
||||
// TestORCAOOBFallback verifies the fallback behavior: when a per-call report
|
||||
// is present but has all zero fields, the picker uses the most recent OOB
|
||||
// report instead.
|
||||
func (s) TestORCAOOBFallback(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
|
||||
defer cancel()
|
||||
|
||||
srv := startORCAServer(t)
|
||||
if err := srv.StartClient(grpc.WithDefaultServiceConfig(orcaSvcConfig())); err != nil {
|
||||
t.Fatalf("Error starting client: %v", err)
|
||||
}
|
||||
|
||||
stream, err := srv.Client.FullDuplexCall(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("FullDuplexCall failed: %v", err)
|
||||
}
|
||||
req := &testpb.StreamingOutputCallRequest{
|
||||
OrcaOobReport: &testpb.TestOrcaReport{
|
||||
CpuUtilization: 0.73,
|
||||
MemoryUtilization: 0.55,
|
||||
Utilization: map[string]float64{"util": 0.42},
|
||||
},
|
||||
ResponseParameters: []*testpb.ResponseParameters{{Size: 1}},
|
||||
}
|
||||
if err := stream.Send(req); err != nil {
|
||||
t.Fatalf("stream.Send failed: %v", err)
|
||||
}
|
||||
if _, err := stream.Recv(); err != nil {
|
||||
t.Fatalf("stream.Recv failed: %v", err)
|
||||
}
|
||||
|
||||
oobWant := &v3orcapb.OrcaLoadReport{
|
||||
CpuUtilization: 0.73,
|
||||
MemUtilization: 0.55,
|
||||
Utilization: map[string]float64{"util": 0.42},
|
||||
}
|
||||
pollORCAResult(ctx, t, srv.Client, oobWant)
|
||||
|
||||
// OrcaPerQueryReport is nil: per-call report has all-zero fields,
|
||||
// triggering the OOB fallback path in orcaPicker.
|
||||
orcaRes := &v3orcapb.OrcaLoadReport{}
|
||||
if _, err := srv.Client.UnaryCall(contextWithORCAResult(ctx, &orcaRes), &testpb.SimpleRequest{}); err != nil {
|
||||
t.Fatalf("UnaryCall failed: %v", err)
|
||||
}
|
||||
if diff := cmp.Diff(orcaRes, oobWant, protocmp.Transform()); diff != "" {
|
||||
t.Fatalf("ORCA load report with zero per-call mismatch (-got +want):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEndpoints_MultipleAddresses verifies client behavior when an endpoint has
|
||||
// multiple addresses and the first is unreachable. It validates that the
|
||||
// pick_first falls through the address list and connects via the first
|
||||
// reachable address, so per-call ORCA reports flow correctly.
|
||||
func (s) TestEndpoints_MultipleAddresses(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
|
||||
defer cancel()
|
||||
|
||||
srv := startORCAServer(t)
|
||||
if err := srv.StartClient(grpc.WithDefaultServiceConfig(orcaSvcConfig())); err != nil {
|
||||
t.Fatalf("Error starting client: %v", err)
|
||||
}
|
||||
|
||||
// First address is unreachable; pick_first falls through to the good one.
|
||||
state := resolver.State{
|
||||
Endpoints: []resolver.Endpoint{
|
||||
{Addresses: []resolver.Address{
|
||||
{Addr: "bad-address"},
|
||||
{Addr: srv.Address},
|
||||
}},
|
||||
},
|
||||
}
|
||||
srv.R.UpdateState(state)
|
||||
// Transient errors while pick_first probes the bad address are expected.
|
||||
want := &v3orcapb.OrcaLoadReport{CpuUtilization: 0.42, MemUtilization: 0.21}
|
||||
for ; ctx.Err() == nil; <-time.After(100 * time.Millisecond) {
|
||||
orcaRes := &v3orcapb.OrcaLoadReport{}
|
||||
req := &testpb.SimpleRequest{
|
||||
OrcaPerQueryReport: &testpb.TestOrcaReport{
|
||||
CpuUtilization: 0.42,
|
||||
MemoryUtilization: 0.21,
|
||||
},
|
||||
}
|
||||
if _, err := srv.Client.UnaryCall(contextWithORCAResult(ctx, &orcaRes), req); err != nil {
|
||||
continue
|
||||
}
|
||||
if diff := cmp.Diff(orcaRes, want, protocmp.Transform()); diff != "" {
|
||||
t.Fatalf("Per-RPC ORCA mismatch (-got +want):\n%s", diff)
|
||||
}
|
||||
return
|
||||
}
|
||||
t.Fatalf("timed out waiting for connection through good address")
|
||||
}
|
||||
|
||||
// TestMultipleEndpoints_OOBListeners verifies that N endpoints produce N
|
||||
// independent pick_first instances and N independent OOB listeners, each
|
||||
// receiving reports from its respective server.
|
||||
func (s) TestMultipleEndpoints_OOBListeners(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
|
||||
defer cancel()
|
||||
|
||||
srvA := startORCAServer(t)
|
||||
srvB := startORCAServer(t)
|
||||
|
||||
srvA.metricsRecorder.SetCPUUtilization(0.1)
|
||||
srvB.metricsRecorder.SetCPUUtilization(0.9)
|
||||
|
||||
r := manual.NewBuilderWithScheme("test")
|
||||
cc, err := grpc.Dial(r.Scheme()+":///test",
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithResolvers(r),
|
||||
grpc.WithDefaultServiceConfig(orcaSvcConfig()),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
tc := testgrpc.NewTestServiceClient(cc)
|
||||
|
||||
// One endpoint per server: endpointsharding creates two independent
|
||||
// pick_first children, each with its own OOB listener.
|
||||
state := resolver.State{
|
||||
Endpoints: []resolver.Endpoint{
|
||||
{Addresses: []resolver.Address{{Addr: srvA.Address}}},
|
||||
{Addresses: []resolver.Address{{Addr: srvB.Address}}},
|
||||
},
|
||||
}
|
||||
r.UpdateState(state)
|
||||
// No per-call report triggers OOB fallback. With two active listeners,
|
||||
// b.report alternates between servers; both values must eventually appear.
|
||||
wantA := &v3orcapb.OrcaLoadReport{CpuUtilization: 0.1}
|
||||
wantB := &v3orcapb.OrcaLoadReport{CpuUtilization: 0.9}
|
||||
seenA, seenB := false, false
|
||||
for ; ctx.Err() == nil && (!seenA || !seenB); <-time.After(100 * time.Millisecond) {
|
||||
orcaRes := &v3orcapb.OrcaLoadReport{}
|
||||
if _, err := tc.UnaryCall(contextWithORCAResult(ctx, &orcaRes), &testpb.SimpleRequest{}); err != nil {
|
||||
continue
|
||||
}
|
||||
if diff := cmp.Diff(orcaRes, wantA, protocmp.Transform()); diff == "" {
|
||||
seenA = true
|
||||
} else if diff := cmp.Diff(orcaRes, wantB, protocmp.Transform()); diff == "" {
|
||||
seenB = true
|
||||
}
|
||||
}
|
||||
if ctx.Err() != nil {
|
||||
t.Fatalf("timed out waiting for OOB reports from both endpoints; seenA=%v seenB=%v", seenA, seenB)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEndpointUpdate verifies client behavior in response to a resolver update
|
||||
// that changes an endpoint's address. It ensures that the client disconnects
|
||||
// from the old address and connects to the new address via pick_first. It
|
||||
// also validates that the OOB listener for the old connection is stopped,
|
||||
// and a new OOB listener is registered for the new connection once it becomes
|
||||
// READY.
|
||||
func (s) TestEndpointUpdate(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
|
||||
defer cancel()
|
||||
|
||||
srvA := startORCAServer(t)
|
||||
srvB := startORCAServer(t)
|
||||
|
||||
r := manual.NewBuilderWithScheme("test")
|
||||
cc, err := grpc.Dial(r.Scheme()+":///test",
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithResolvers(r),
|
||||
grpc.WithDefaultServiceConfig(orcaSvcConfig()),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
tc := testgrpc.NewTestServiceClient(cc)
|
||||
|
||||
state := resolver.State{
|
||||
Endpoints: []resolver.Endpoint{
|
||||
{Addresses: []resolver.Address{{Addr: srvA.Address}}},
|
||||
},
|
||||
}
|
||||
r.UpdateState(state)
|
||||
|
||||
orcaRes := &v3orcapb.OrcaLoadReport{}
|
||||
req := &testpb.SimpleRequest{
|
||||
OrcaPerQueryReport: &testpb.TestOrcaReport{
|
||||
CpuUtilization: 0.11,
|
||||
MemoryUtilization: 0.22,
|
||||
},
|
||||
}
|
||||
if _, err := tc.UnaryCall(contextWithORCAResult(ctx, &orcaRes), req); err != nil {
|
||||
t.Fatalf("UnaryCall to srvA failed: %v", err)
|
||||
}
|
||||
wantA := &v3orcapb.OrcaLoadReport{CpuUtilization: 0.11, MemUtilization: 0.22}
|
||||
if diff := cmp.Diff(orcaRes, wantA, protocmp.Transform()); diff != "" {
|
||||
t.Fatalf("ORCA from srvA mismatch (-got +want):\n%s", diff)
|
||||
}
|
||||
|
||||
// Distinctive OOB metric on srvB to confirm the switch via OOB fallback.
|
||||
srvB.metricsRecorder.SetCPUUtilization(0.77)
|
||||
|
||||
state = resolver.State{
|
||||
Endpoints: []resolver.Endpoint{
|
||||
{Addresses: []resolver.Address{{Addr: srvB.Address}}},
|
||||
},
|
||||
}
|
||||
r.UpdateState(state)
|
||||
// No per-call report forces OOB fallback. Once srvB's metric appears,
|
||||
// all three are confirmed: pick_first reconnected, new OOB listener
|
||||
// registered for srvB, old one for srvA stopped.
|
||||
wantB := &v3orcapb.OrcaLoadReport{CpuUtilization: 0.77}
|
||||
for ; ctx.Err() == nil; <-time.After(100 * time.Millisecond) {
|
||||
orcaRes := &v3orcapb.OrcaLoadReport{}
|
||||
if _, err := tc.UnaryCall(contextWithORCAResult(ctx, &orcaRes), &testpb.SimpleRequest{}); err != nil {
|
||||
continue
|
||||
}
|
||||
if diff := cmp.Diff(orcaRes, wantB, protocmp.Transform()); diff == "" {
|
||||
return
|
||||
}
|
||||
t.Logf("ORCA after endpoint update = %v; want %v; retrying...", orcaRes, wantB)
|
||||
}
|
||||
t.Fatalf("timed out waiting for srvB OOB report after endpoint update")
|
||||
}
|
||||
|
||||
func pollORCAResult(ctx context.Context, t *testing.T, tc testgrpc.TestServiceClient, want *v3orcapb.OrcaLoadReport) {
|
||||
t.Helper()
|
||||
for ; ctx.Err() == nil; <-time.After(time.Second) {
|
||||
orcaRes := &v3orcapb.OrcaLoadReport{}
|
||||
if _, err := tc.UnaryCall(contextWithORCAResult(ctx, &orcaRes), &testpb.SimpleRequest{}); err != nil {
|
||||
t.Fatalf("UnaryCall failed: %v", err)
|
||||
}
|
||||
if diff := cmp.Diff(orcaRes, want, protocmp.Transform()); diff == "" {
|
||||
return
|
||||
}
|
||||
t.Logf("ORCA load report = %v; want %v; retrying...", orcaRes, want)
|
||||
}
|
||||
t.Fatalf("Timed out waiting for expected ORCA load report %v", want)
|
||||
}
|
||||
97
interop/server/server.go
Normal file
97
interop/server/server.go
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2014 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Binary server is an interop server.
|
||||
//
|
||||
// See interop test case descriptions [here].
|
||||
//
|
||||
// [here]: https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/alts"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/internal"
|
||||
"google.golang.org/grpc/interop"
|
||||
"google.golang.org/grpc/orca"
|
||||
"google.golang.org/grpc/testdata"
|
||||
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
)
|
||||
|
||||
var (
|
||||
useTLS = flag.Bool("use_tls", false, "Connection uses TLS if true, else plain TCP")
|
||||
useALTS = flag.Bool("use_alts", false, "Connection uses ALTS if true (this option can only be used on GCP)")
|
||||
altsHSAddr = flag.String("alts_handshaker_service_address", "", "ALTS handshaker gRPC service address")
|
||||
certFile = flag.String("tls_cert_file", "", "The TLS cert file")
|
||||
keyFile = flag.String("tls_key_file", "", "The TLS key file")
|
||||
port = flag.Int("port", 10000, "The server port")
|
||||
|
||||
logger = grpclog.Component("interop")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *useTLS && *useALTS {
|
||||
logger.Fatal("-use_tls and -use_alts cannot be both set to true")
|
||||
}
|
||||
p := strconv.Itoa(*port)
|
||||
lis, err := net.Listen("tcp", ":"+p)
|
||||
if err != nil {
|
||||
logger.Fatalf("failed to listen: %v", err)
|
||||
}
|
||||
logger.Infof("interop server listening on %v", lis.Addr())
|
||||
opts := []grpc.ServerOption{orca.CallMetricsServerOption(nil)}
|
||||
if *useTLS {
|
||||
if *certFile == "" {
|
||||
*certFile = testdata.Path("server1.pem")
|
||||
}
|
||||
if *keyFile == "" {
|
||||
*keyFile = testdata.Path("server1.key")
|
||||
}
|
||||
creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to generate credentials: %v", err)
|
||||
}
|
||||
opts = append(opts, grpc.Creds(creds))
|
||||
} else if *useALTS {
|
||||
altsOpts := alts.DefaultServerOptions()
|
||||
if *altsHSAddr != "" {
|
||||
altsOpts.HandshakerServiceAddress = *altsHSAddr
|
||||
}
|
||||
altsTC := alts.NewServerCreds(altsOpts)
|
||||
opts = append(opts, grpc.Creds(altsTC))
|
||||
}
|
||||
server := grpc.NewServer(opts...)
|
||||
metricsRecorder := orca.NewServerMetricsRecorder()
|
||||
sopts := orca.ServiceOptions{
|
||||
MinReportingInterval: time.Second,
|
||||
ServerMetricsProvider: metricsRecorder,
|
||||
}
|
||||
internal.ORCAAllowAnyMinReportingInterval.(func(*orca.ServiceOptions))(&sopts)
|
||||
orca.Register(server, sopts)
|
||||
testgrpc.RegisterTestServiceServer(server, interop.NewTestServer(interop.NewTestServerOptions{MetricsRecorder: metricsRecorder}))
|
||||
server.Serve(lis)
|
||||
}
|
||||
203
interop/soak_tests.go
Normal file
203
interop/soak_tests.go
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2014 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package interop
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/benchmark/stats"
|
||||
"google.golang.org/grpc/peer"
|
||||
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
testpb "google.golang.org/grpc/interop/grpc_testing"
|
||||
)
|
||||
|
||||
// SoakWorkerResults stores the aggregated results for a specific worker during the soak test.
|
||||
type SoakWorkerResults struct {
|
||||
iterationsSucceeded int
|
||||
Failures int
|
||||
Latencies *stats.Histogram
|
||||
}
|
||||
|
||||
// SoakIterationConfig holds the parameters required for a single soak iteration.
|
||||
type SoakIterationConfig struct {
|
||||
RequestSize int // The size of the request payload in bytes.
|
||||
ResponseSize int // The expected size of the response payload in bytes.
|
||||
Client testgrpc.TestServiceClient // The gRPC client to make the call.
|
||||
CallOptions []grpc.CallOption // Call options for the RPC.
|
||||
}
|
||||
|
||||
// SoakTestConfig holds the configuration for the entire soak test.
|
||||
type SoakTestConfig struct {
|
||||
RequestSize int
|
||||
ResponseSize int
|
||||
PerIterationMaxAcceptableLatency time.Duration
|
||||
MinTimeBetweenRPCs time.Duration
|
||||
OverallTimeout time.Duration
|
||||
ServerAddr string
|
||||
NumWorkers int
|
||||
Iterations int
|
||||
MaxFailures int
|
||||
ChannelForTest func() (*grpc.ClientConn, func())
|
||||
}
|
||||
|
||||
func doOneSoakIteration(ctx context.Context, config SoakIterationConfig) (latency time.Duration, err error) {
|
||||
start := time.Now()
|
||||
// Do a large-unary RPC.
|
||||
// Create the request payload.
|
||||
pl := ClientNewPayload(testpb.PayloadType_COMPRESSABLE, config.RequestSize)
|
||||
req := &testpb.SimpleRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
ResponseSize: int32(config.ResponseSize),
|
||||
Payload: pl,
|
||||
}
|
||||
// Perform the GRPC call.
|
||||
var reply *testpb.SimpleResponse
|
||||
reply, err = config.Client.UnaryCall(ctx, req, config.CallOptions...)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("/TestService/UnaryCall RPC failed: %s", err)
|
||||
return 0, err
|
||||
}
|
||||
// Validate response.
|
||||
t := reply.GetPayload().GetType()
|
||||
s := len(reply.GetPayload().GetBody())
|
||||
if t != testpb.PayloadType_COMPRESSABLE || s != config.ResponseSize {
|
||||
err = fmt.Errorf("got the reply with type %d len %d; want %d, %d", t, s, testpb.PayloadType_COMPRESSABLE, config.ResponseSize)
|
||||
return 0, err
|
||||
}
|
||||
// Calculate latency and return result.
|
||||
latency = time.Since(start)
|
||||
return latency, nil
|
||||
}
|
||||
|
||||
func executeSoakTestInWorker(ctx context.Context, config SoakTestConfig, startTime time.Time, workerID int, soakWorkerResults *SoakWorkerResults) {
|
||||
timeoutDuration := config.OverallTimeout
|
||||
soakIterationsPerWorker := config.Iterations / config.NumWorkers
|
||||
if soakWorkerResults.Latencies == nil {
|
||||
soakWorkerResults.Latencies = stats.NewHistogram(stats.HistogramOptions{
|
||||
NumBuckets: 20,
|
||||
GrowthFactor: 1,
|
||||
BaseBucketSize: 1,
|
||||
MinValue: 0,
|
||||
})
|
||||
}
|
||||
|
||||
for i := 0; i < soakIterationsPerWorker; i++ {
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
if time.Since(startTime) >= timeoutDuration {
|
||||
fmt.Printf("Test exceeded overall timeout of %v, stopping...\n", config.OverallTimeout)
|
||||
return
|
||||
}
|
||||
earliestNextStart := time.After(config.MinTimeBetweenRPCs)
|
||||
currentChannel, cleanup := config.ChannelForTest()
|
||||
defer cleanup()
|
||||
client := testgrpc.NewTestServiceClient(currentChannel)
|
||||
var p peer.Peer
|
||||
iterationConfig := SoakIterationConfig{
|
||||
RequestSize: config.RequestSize,
|
||||
ResponseSize: config.ResponseSize,
|
||||
Client: client,
|
||||
CallOptions: []grpc.CallOption{grpc.Peer(&p)},
|
||||
}
|
||||
latency, err := doOneSoakIteration(ctx, iterationConfig)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Worker %d: soak iteration: %d elapsed_ms: %d peer: %v server_uri: %s failed: %s\n", workerID, i, 0, p.Addr, config.ServerAddr, err)
|
||||
soakWorkerResults.Failures++
|
||||
<-earliestNextStart
|
||||
continue
|
||||
}
|
||||
if latency > config.PerIterationMaxAcceptableLatency {
|
||||
fmt.Fprintf(os.Stderr, "Worker %d: soak iteration: %d elapsed_ms: %d peer: %v server_uri: %s exceeds max acceptable latency: %d\n", workerID, i, latency.Milliseconds(), p.Addr, config.ServerAddr, config.PerIterationMaxAcceptableLatency.Milliseconds())
|
||||
soakWorkerResults.Failures++
|
||||
<-earliestNextStart
|
||||
continue
|
||||
}
|
||||
// Success: log the details of the iteration.
|
||||
soakWorkerResults.Latencies.Add(latency.Milliseconds())
|
||||
soakWorkerResults.iterationsSucceeded++
|
||||
fmt.Fprintf(os.Stderr, "Worker %d: soak iteration: %d elapsed_ms: %d peer: %v server_uri: %s succeeded\n", workerID, i, latency.Milliseconds(), p.Addr, config.ServerAddr)
|
||||
<-earliestNextStart
|
||||
}
|
||||
}
|
||||
|
||||
// DoSoakTest runs large unary RPCs in a loop for a configurable number of times, with configurable failure thresholds.
|
||||
// If resetChannel is false, then each RPC will be performed on tc. Otherwise, each RPC will be performed on a new
|
||||
// stub that is created with the provided server address and dial options.
|
||||
// TODO(mohanli-ml): Create SoakTestOptions as a parameter for this method.
|
||||
func DoSoakTest(ctx context.Context, soakConfig SoakTestConfig) {
|
||||
if soakConfig.Iterations%soakConfig.NumWorkers != 0 {
|
||||
fmt.Fprintf(os.Stderr, "soakIterations must be evenly divisible by soakNumWThreads\n")
|
||||
}
|
||||
startTime := time.Now()
|
||||
var wg sync.WaitGroup
|
||||
soakWorkerResults := make([]SoakWorkerResults, soakConfig.NumWorkers)
|
||||
for i := 0; i < soakConfig.NumWorkers; i++ {
|
||||
wg.Add(1)
|
||||
go func(workerID int) {
|
||||
defer wg.Done()
|
||||
executeSoakTestInWorker(ctx, soakConfig, startTime, workerID, &soakWorkerResults[workerID])
|
||||
}(i)
|
||||
}
|
||||
// Wait for all goroutines to complete.
|
||||
wg.Wait()
|
||||
|
||||
// Handle results.
|
||||
totalSuccesses := 0
|
||||
totalFailures := 0
|
||||
latencies := stats.NewHistogram(stats.HistogramOptions{
|
||||
NumBuckets: 20,
|
||||
GrowthFactor: 1,
|
||||
BaseBucketSize: 1,
|
||||
MinValue: 0,
|
||||
})
|
||||
for _, worker := range soakWorkerResults {
|
||||
totalSuccesses += worker.iterationsSucceeded
|
||||
totalFailures += worker.Failures
|
||||
if worker.Latencies != nil {
|
||||
// Add latencies from the worker's Histogram to the main latencies.
|
||||
latencies.Merge(worker.Latencies)
|
||||
}
|
||||
}
|
||||
var b bytes.Buffer
|
||||
totalIterations := totalSuccesses + totalFailures
|
||||
latencies.Print(&b)
|
||||
fmt.Fprintf(os.Stderr,
|
||||
"(server_uri: %s) soak test successes: %d / %d iterations. Total failures: %d. Latencies in milliseconds: %s\n",
|
||||
soakConfig.ServerAddr, totalSuccesses, soakConfig.Iterations, totalFailures, b.String())
|
||||
|
||||
if totalIterations != soakConfig.Iterations {
|
||||
logger.Fatalf("Soak test consumed all %v of time and quit early, ran %d out of %d iterations.\n", soakConfig.OverallTimeout, totalIterations, soakConfig.Iterations)
|
||||
}
|
||||
|
||||
if totalFailures > soakConfig.MaxFailures {
|
||||
logger.Fatalf("Soak test total failures: %d exceeded max failures threshold: %d\n", totalFailures, soakConfig.MaxFailures)
|
||||
}
|
||||
if soakConfig.ChannelForTest != nil {
|
||||
_, cleanup := soakConfig.ChannelForTest()
|
||||
defer cleanup()
|
||||
}
|
||||
}
|
||||
364
interop/stress/client/main.go
Normal file
364
interop/stress/client/main.go
Normal file
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2016 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// client starts an interop client to do stress test and a metrics server to report qps.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
rand "math/rand/v2"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/google"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/interop"
|
||||
"google.golang.org/grpc/resolver"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/grpc/testdata"
|
||||
|
||||
_ "google.golang.org/grpc/xds/googledirectpath" // Register xDS resolver required for c2p directpath.
|
||||
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
metricspb "google.golang.org/grpc/interop/stress/grpc_testing"
|
||||
)
|
||||
|
||||
const (
|
||||
googleDefaultCredsName = "google_default_credentials"
|
||||
computeEngineCredsName = "compute_engine_channel_creds"
|
||||
)
|
||||
|
||||
var (
|
||||
serverAddresses = flag.String("server_addresses", "localhost:8080", "a list of server addresses")
|
||||
testCases = flag.String("test_cases", "", "a list of test cases along with the relative weights")
|
||||
testDurationSecs = flag.Int("test_duration_secs", -1, "test duration in seconds")
|
||||
numChannelsPerServer = flag.Int("num_channels_per_server", 1, "Number of channels (i.e connections) to each server")
|
||||
numStubsPerChannel = flag.Int("num_stubs_per_channel", 1, "Number of client stubs per each connection to server")
|
||||
metricsPort = flag.Int("metrics_port", 8081, "The port at which the stress client exposes QPS metrics")
|
||||
useTLS = flag.Bool("use_tls", false, "Connection uses TLS if true, else plain TCP")
|
||||
testCA = flag.Bool("use_test_ca", false, "Whether to replace platform root CAs with test CA as the CA root")
|
||||
tlsServerName = flag.String("server_host_override", "foo.test.google.fr", "The server name use to verify the hostname returned by TLS handshake if it is not empty. Otherwise, --server_host is used.")
|
||||
caFile = flag.String("ca_file", "", "The file containing the CA root cert file")
|
||||
customCredentialsType = flag.String("custom_credentials_type", "", "Custom credentials type to use")
|
||||
|
||||
totalNumCalls int64
|
||||
logger = grpclog.Component("stress")
|
||||
)
|
||||
|
||||
// testCaseWithWeight contains the test case type and its weight.
|
||||
type testCaseWithWeight struct {
|
||||
name string
|
||||
weight int
|
||||
}
|
||||
|
||||
// parseTestCases converts test case string to a list of struct testCaseWithWeight.
|
||||
func parseTestCases(testCaseString string) []testCaseWithWeight {
|
||||
testCaseStrings := strings.Split(testCaseString, ",")
|
||||
testCases := make([]testCaseWithWeight, len(testCaseStrings))
|
||||
for i, str := range testCaseStrings {
|
||||
testCaseNameAndWeight := strings.Split(str, ":")
|
||||
if len(testCaseNameAndWeight) != 2 {
|
||||
panic(fmt.Sprintf("invalid test case with weight: %s", str))
|
||||
}
|
||||
// Check if test case is supported.
|
||||
testCaseName := strings.ToLower(testCaseNameAndWeight[0])
|
||||
switch testCaseName {
|
||||
case
|
||||
"empty_unary",
|
||||
"large_unary",
|
||||
"client_streaming",
|
||||
"server_streaming",
|
||||
"ping_pong",
|
||||
"empty_stream",
|
||||
"timeout_on_sleeping_server",
|
||||
"cancel_after_begin",
|
||||
"cancel_after_first_response",
|
||||
"status_code_and_message",
|
||||
"custom_metadata":
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown test type: %s", testCaseNameAndWeight[0]))
|
||||
}
|
||||
testCases[i].name = testCaseName
|
||||
w, err := strconv.Atoi(testCaseNameAndWeight[1])
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("%v", err))
|
||||
}
|
||||
testCases[i].weight = w
|
||||
}
|
||||
return testCases
|
||||
}
|
||||
|
||||
// weightedRandomTestSelector defines a weighted random selector for test case types.
|
||||
type weightedRandomTestSelector struct {
|
||||
tests []testCaseWithWeight
|
||||
totalWeight int
|
||||
}
|
||||
|
||||
// newWeightedRandomTestSelector constructs a weightedRandomTestSelector with the given list of testCaseWithWeight.
|
||||
func newWeightedRandomTestSelector(tests []testCaseWithWeight) *weightedRandomTestSelector {
|
||||
var totalWeight int
|
||||
for _, t := range tests {
|
||||
totalWeight += t.weight
|
||||
}
|
||||
return &weightedRandomTestSelector{tests, totalWeight}
|
||||
}
|
||||
|
||||
func (selector weightedRandomTestSelector) getNextTest() string {
|
||||
random := rand.IntN(selector.totalWeight)
|
||||
var weightSofar int
|
||||
for _, test := range selector.tests {
|
||||
weightSofar += test.weight
|
||||
if random < weightSofar {
|
||||
return test.name
|
||||
}
|
||||
}
|
||||
panic("no test case selected by weightedRandomTestSelector")
|
||||
}
|
||||
|
||||
// gauge stores the qps of one interop client (one stub).
|
||||
type gauge struct {
|
||||
mutex sync.RWMutex
|
||||
val int64
|
||||
}
|
||||
|
||||
func (g *gauge) set(v int64) {
|
||||
g.mutex.Lock()
|
||||
defer g.mutex.Unlock()
|
||||
g.val = v
|
||||
}
|
||||
|
||||
func (g *gauge) get() int64 {
|
||||
g.mutex.RLock()
|
||||
defer g.mutex.RUnlock()
|
||||
return g.val
|
||||
}
|
||||
|
||||
// server implements metrics server functions.
|
||||
type server struct {
|
||||
metricspb.UnimplementedMetricsServiceServer
|
||||
mutex sync.RWMutex
|
||||
// gauges is a map from /stress_test/server_<n>/channel_<n>/stub_<n>/qps to its qps gauge.
|
||||
gauges map[string]*gauge
|
||||
}
|
||||
|
||||
// newMetricsServer returns a new metrics server.
|
||||
func newMetricsServer() *server {
|
||||
return &server{gauges: make(map[string]*gauge)}
|
||||
}
|
||||
|
||||
// GetAllGauges returns all gauges.
|
||||
func (s *server) GetAllGauges(_ *metricspb.EmptyMessage, stream metricspb.MetricsService_GetAllGaugesServer) error {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
|
||||
for name, gauge := range s.gauges {
|
||||
if err := stream.Send(&metricspb.GaugeResponse{Name: name, Value: &metricspb.GaugeResponse_LongValue{LongValue: gauge.get()}}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetGauge returns the gauge for the given name.
|
||||
func (s *server) GetGauge(_ context.Context, in *metricspb.GaugeRequest) (*metricspb.GaugeResponse, error) {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
|
||||
if g, ok := s.gauges[in.Name]; ok {
|
||||
return &metricspb.GaugeResponse{Name: in.Name, Value: &metricspb.GaugeResponse_LongValue{LongValue: g.get()}}, nil
|
||||
}
|
||||
return nil, status.Errorf(codes.InvalidArgument, "gauge with name %s not found", in.Name)
|
||||
}
|
||||
|
||||
// createGauge creates a gauge using the given name in metrics server.
|
||||
func (s *server) createGauge(name string) *gauge {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
if _, ok := s.gauges[name]; ok {
|
||||
// gauge already exists.
|
||||
panic(fmt.Sprintf("gauge %s already exists", name))
|
||||
}
|
||||
var g gauge
|
||||
s.gauges[name] = &g
|
||||
return &g
|
||||
}
|
||||
|
||||
func startServer(server *server, port int) {
|
||||
lis, err := net.Listen("tcp", ":"+strconv.Itoa(port))
|
||||
if err != nil {
|
||||
logger.Fatalf("failed to listen: %v", err)
|
||||
}
|
||||
|
||||
s := grpc.NewServer()
|
||||
metricspb.RegisterMetricsServiceServer(s, server)
|
||||
s.Serve(lis)
|
||||
}
|
||||
|
||||
// performRPCs uses weightedRandomTestSelector to select test case and runs the tests.
|
||||
func performRPCs(gauge *gauge, conn *grpc.ClientConn, selector *weightedRandomTestSelector, stop <-chan bool) {
|
||||
client := testgrpc.NewTestServiceClient(conn)
|
||||
var numCalls int64
|
||||
ctx := context.Background()
|
||||
startTime := time.Now()
|
||||
for {
|
||||
test := selector.getNextTest()
|
||||
switch test {
|
||||
case "empty_unary":
|
||||
interop.DoEmptyUnaryCall(ctx, client)
|
||||
case "large_unary":
|
||||
interop.DoLargeUnaryCall(ctx, client)
|
||||
case "client_streaming":
|
||||
interop.DoClientStreaming(ctx, client)
|
||||
case "server_streaming":
|
||||
interop.DoServerStreaming(ctx, client)
|
||||
case "ping_pong":
|
||||
interop.DoPingPong(ctx, client)
|
||||
case "empty_stream":
|
||||
interop.DoEmptyStream(ctx, client)
|
||||
case "timeout_on_sleeping_server":
|
||||
interop.DoTimeoutOnSleepingServer(ctx, client)
|
||||
case "cancel_after_begin":
|
||||
interop.DoCancelAfterBegin(ctx, client)
|
||||
case "cancel_after_first_response":
|
||||
interop.DoCancelAfterFirstResponse(ctx, client)
|
||||
case "status_code_and_message":
|
||||
interop.DoStatusCodeAndMessage(ctx, client)
|
||||
case "custom_metadata":
|
||||
interop.DoCustomMetadata(ctx, client)
|
||||
}
|
||||
numCalls++
|
||||
defer func() { atomic.AddInt64(&totalNumCalls, numCalls) }()
|
||||
gauge.set(int64(float64(numCalls) / time.Since(startTime).Seconds()))
|
||||
|
||||
select {
|
||||
case <-stop:
|
||||
return
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func logParameterInfo(addresses []string, tests []testCaseWithWeight) {
|
||||
logger.Infof("server_addresses: %s", *serverAddresses)
|
||||
logger.Infof("test_cases: %s", *testCases)
|
||||
logger.Infof("test_duration_secs: %d", *testDurationSecs)
|
||||
logger.Infof("num_channels_per_server: %d", *numChannelsPerServer)
|
||||
logger.Infof("num_stubs_per_channel: %d", *numStubsPerChannel)
|
||||
logger.Infof("metrics_port: %d", *metricsPort)
|
||||
logger.Infof("use_tls: %t", *useTLS)
|
||||
logger.Infof("use_test_ca: %t", *testCA)
|
||||
logger.Infof("server_host_override: %s", *tlsServerName)
|
||||
logger.Infof("custom_credentials_type: %s", *customCredentialsType)
|
||||
|
||||
logger.Infoln("addresses:")
|
||||
for i, addr := range addresses {
|
||||
logger.Infof("%d. %s\n", i+1, addr)
|
||||
}
|
||||
logger.Infoln("tests:")
|
||||
for i, test := range tests {
|
||||
logger.Infof("%d. %v\n", i+1, test)
|
||||
}
|
||||
}
|
||||
|
||||
func newConn(address string, useTLS, testCA bool, tlsServerName string) (*grpc.ClientConn, error) {
|
||||
var opts []grpc.DialOption
|
||||
if *customCredentialsType != "" {
|
||||
if *customCredentialsType == googleDefaultCredsName {
|
||||
opts = append(opts, grpc.WithCredentialsBundle(google.NewDefaultCredentials()))
|
||||
} else if *customCredentialsType == computeEngineCredsName {
|
||||
opts = append(opts, grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()))
|
||||
} else {
|
||||
logger.Fatalf("Unknown custom credentials: %v", *customCredentialsType)
|
||||
}
|
||||
} else if useTLS {
|
||||
var sn string
|
||||
if tlsServerName != "" {
|
||||
sn = tlsServerName
|
||||
}
|
||||
var creds credentials.TransportCredentials
|
||||
if testCA {
|
||||
var err error
|
||||
if *caFile == "" {
|
||||
*caFile = testdata.Path("x509/server_ca_cert.pem")
|
||||
}
|
||||
creds, err = credentials.NewClientTLSFromFile(*caFile, sn)
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to create TLS credentials: %v", err)
|
||||
}
|
||||
} else {
|
||||
creds = credentials.NewClientTLSFromCert(nil, sn)
|
||||
}
|
||||
opts = append(opts, grpc.WithTransportCredentials(creds))
|
||||
} else {
|
||||
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
}
|
||||
return grpc.NewClient(address, opts...)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
resolver.SetDefaultScheme("dns")
|
||||
addresses := strings.Split(*serverAddresses, ",")
|
||||
tests := parseTestCases(*testCases)
|
||||
logParameterInfo(addresses, tests)
|
||||
testSelector := newWeightedRandomTestSelector(tests)
|
||||
metricsServer := newMetricsServer()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(addresses) * *numChannelsPerServer * *numStubsPerChannel)
|
||||
stop := make(chan bool)
|
||||
|
||||
for serverIndex, address := range addresses {
|
||||
for connIndex := 0; connIndex < *numChannelsPerServer; connIndex++ {
|
||||
conn, err := newConn(address, *useTLS, *testCA, *tlsServerName)
|
||||
if err != nil {
|
||||
logger.Fatalf("Fail to dial: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
for clientIndex := 0; clientIndex < *numStubsPerChannel; clientIndex++ {
|
||||
name := fmt.Sprintf("/stress_test/server_%d/channel_%d/stub_%d/qps", serverIndex+1, connIndex+1, clientIndex+1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
g := metricsServer.createGauge(name)
|
||||
performRPCs(g, conn, testSelector, stop)
|
||||
}()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
go startServer(metricsServer, *metricsPort)
|
||||
if *testDurationSecs > 0 {
|
||||
time.Sleep(time.Duration(*testDurationSecs) * time.Second)
|
||||
close(stop)
|
||||
}
|
||||
wg.Wait()
|
||||
fmt.Fprintf(os.Stdout, "Total calls made: %v\n", totalNumCalls)
|
||||
logger.Infof(" ===== ALL DONE ===== ")
|
||||
}
|
||||
309
interop/stress/grpc_testing/metrics.pb.go
Normal file
309
interop/stress/grpc_testing/metrics.pb.go
Normal file
@@ -0,0 +1,309 @@
|
||||
// Copyright 2015-2016 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Contains the definitions for a metrics service and the type of metrics
|
||||
// exposed by the service.
|
||||
//
|
||||
// Currently, 'Gauge' (i.e a metric that represents the measured value of
|
||||
// something at an instant of time) is the only metric type supported by the
|
||||
// service.
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.12
|
||||
// protoc v5.27.1
|
||||
// source: interop/stress/grpc_testing/metrics.proto
|
||||
|
||||
package grpc_testing
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// Response message containing the gauge name and value
|
||||
type GaugeResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
// Types that are valid to be assigned to Value:
|
||||
//
|
||||
// *GaugeResponse_LongValue
|
||||
// *GaugeResponse_DoubleValue
|
||||
// *GaugeResponse_StringValue
|
||||
Value isGaugeResponse_Value `protobuf_oneof:"value"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GaugeResponse) Reset() {
|
||||
*x = GaugeResponse{}
|
||||
mi := &file_interop_stress_grpc_testing_metrics_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GaugeResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GaugeResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GaugeResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_interop_stress_grpc_testing_metrics_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GaugeResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GaugeResponse) Descriptor() ([]byte, []int) {
|
||||
return file_interop_stress_grpc_testing_metrics_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *GaugeResponse) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GaugeResponse) GetValue() isGaugeResponse_Value {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *GaugeResponse) GetLongValue() int64 {
|
||||
if x != nil {
|
||||
if x, ok := x.Value.(*GaugeResponse_LongValue); ok {
|
||||
return x.LongValue
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GaugeResponse) GetDoubleValue() float64 {
|
||||
if x != nil {
|
||||
if x, ok := x.Value.(*GaugeResponse_DoubleValue); ok {
|
||||
return x.DoubleValue
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GaugeResponse) GetStringValue() string {
|
||||
if x != nil {
|
||||
if x, ok := x.Value.(*GaugeResponse_StringValue); ok {
|
||||
return x.StringValue
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type isGaugeResponse_Value interface {
|
||||
isGaugeResponse_Value()
|
||||
}
|
||||
|
||||
type GaugeResponse_LongValue struct {
|
||||
LongValue int64 `protobuf:"varint,2,opt,name=long_value,json=longValue,proto3,oneof"`
|
||||
}
|
||||
|
||||
type GaugeResponse_DoubleValue struct {
|
||||
DoubleValue float64 `protobuf:"fixed64,3,opt,name=double_value,json=doubleValue,proto3,oneof"`
|
||||
}
|
||||
|
||||
type GaugeResponse_StringValue struct {
|
||||
StringValue string `protobuf:"bytes,4,opt,name=string_value,json=stringValue,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*GaugeResponse_LongValue) isGaugeResponse_Value() {}
|
||||
|
||||
func (*GaugeResponse_DoubleValue) isGaugeResponse_Value() {}
|
||||
|
||||
func (*GaugeResponse_StringValue) isGaugeResponse_Value() {}
|
||||
|
||||
// Request message containing the gauge name
|
||||
type GaugeRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GaugeRequest) Reset() {
|
||||
*x = GaugeRequest{}
|
||||
mi := &file_interop_stress_grpc_testing_metrics_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GaugeRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GaugeRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GaugeRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_interop_stress_grpc_testing_metrics_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GaugeRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GaugeRequest) Descriptor() ([]byte, []int) {
|
||||
return file_interop_stress_grpc_testing_metrics_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *GaugeRequest) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type EmptyMessage struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *EmptyMessage) Reset() {
|
||||
*x = EmptyMessage{}
|
||||
mi := &file_interop_stress_grpc_testing_metrics_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *EmptyMessage) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*EmptyMessage) ProtoMessage() {}
|
||||
|
||||
func (x *EmptyMessage) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_interop_stress_grpc_testing_metrics_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use EmptyMessage.ProtoReflect.Descriptor instead.
|
||||
func (*EmptyMessage) Descriptor() ([]byte, []int) {
|
||||
return file_interop_stress_grpc_testing_metrics_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
var File_interop_stress_grpc_testing_metrics_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_interop_stress_grpc_testing_metrics_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
")interop/stress/grpc_testing/metrics.proto\x12\fgrpc.testing\"\x97\x01\n" +
|
||||
"\rGaugeResponse\x12\x12\n" +
|
||||
"\x04name\x18\x01 \x01(\tR\x04name\x12\x1f\n" +
|
||||
"\n" +
|
||||
"long_value\x18\x02 \x01(\x03H\x00R\tlongValue\x12#\n" +
|
||||
"\fdouble_value\x18\x03 \x01(\x01H\x00R\vdoubleValue\x12#\n" +
|
||||
"\fstring_value\x18\x04 \x01(\tH\x00R\vstringValueB\a\n" +
|
||||
"\x05value\"\"\n" +
|
||||
"\fGaugeRequest\x12\x12\n" +
|
||||
"\x04name\x18\x01 \x01(\tR\x04name\"\x0e\n" +
|
||||
"\fEmptyMessage2\xa0\x01\n" +
|
||||
"\x0eMetricsService\x12I\n" +
|
||||
"\fGetAllGauges\x12\x1a.grpc.testing.EmptyMessage\x1a\x1b.grpc.testing.GaugeResponse0\x01\x12C\n" +
|
||||
"\bGetGauge\x12\x1a.grpc.testing.GaugeRequest\x1a\x1b.grpc.testing.GaugeResponseB4Z2google.golang.org/grpc/interop/stress/grpc_testingb\x06proto3"
|
||||
|
||||
var (
|
||||
file_interop_stress_grpc_testing_metrics_proto_rawDescOnce sync.Once
|
||||
file_interop_stress_grpc_testing_metrics_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_interop_stress_grpc_testing_metrics_proto_rawDescGZIP() []byte {
|
||||
file_interop_stress_grpc_testing_metrics_proto_rawDescOnce.Do(func() {
|
||||
file_interop_stress_grpc_testing_metrics_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_interop_stress_grpc_testing_metrics_proto_rawDesc), len(file_interop_stress_grpc_testing_metrics_proto_rawDesc)))
|
||||
})
|
||||
return file_interop_stress_grpc_testing_metrics_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_interop_stress_grpc_testing_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_interop_stress_grpc_testing_metrics_proto_goTypes = []any{
|
||||
(*GaugeResponse)(nil), // 0: grpc.testing.GaugeResponse
|
||||
(*GaugeRequest)(nil), // 1: grpc.testing.GaugeRequest
|
||||
(*EmptyMessage)(nil), // 2: grpc.testing.EmptyMessage
|
||||
}
|
||||
var file_interop_stress_grpc_testing_metrics_proto_depIdxs = []int32{
|
||||
2, // 0: grpc.testing.MetricsService.GetAllGauges:input_type -> grpc.testing.EmptyMessage
|
||||
1, // 1: grpc.testing.MetricsService.GetGauge:input_type -> grpc.testing.GaugeRequest
|
||||
0, // 2: grpc.testing.MetricsService.GetAllGauges:output_type -> grpc.testing.GaugeResponse
|
||||
0, // 3: grpc.testing.MetricsService.GetGauge:output_type -> grpc.testing.GaugeResponse
|
||||
2, // [2:4] is the sub-list for method output_type
|
||||
0, // [0:2] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_interop_stress_grpc_testing_metrics_proto_init() }
|
||||
func file_interop_stress_grpc_testing_metrics_proto_init() {
|
||||
if File_interop_stress_grpc_testing_metrics_proto != nil {
|
||||
return
|
||||
}
|
||||
file_interop_stress_grpc_testing_metrics_proto_msgTypes[0].OneofWrappers = []any{
|
||||
(*GaugeResponse_LongValue)(nil),
|
||||
(*GaugeResponse_DoubleValue)(nil),
|
||||
(*GaugeResponse_StringValue)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_interop_stress_grpc_testing_metrics_proto_rawDesc), len(file_interop_stress_grpc_testing_metrics_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_interop_stress_grpc_testing_metrics_proto_goTypes,
|
||||
DependencyIndexes: file_interop_stress_grpc_testing_metrics_proto_depIdxs,
|
||||
MessageInfos: file_interop_stress_grpc_testing_metrics_proto_msgTypes,
|
||||
}.Build()
|
||||
File_interop_stress_grpc_testing_metrics_proto = out.File
|
||||
file_interop_stress_grpc_testing_metrics_proto_goTypes = nil
|
||||
file_interop_stress_grpc_testing_metrics_proto_depIdxs = nil
|
||||
}
|
||||
51
interop/stress/grpc_testing/metrics.proto
Normal file
51
interop/stress/grpc_testing/metrics.proto
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright 2015-2016 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Contains the definitions for a metrics service and the type of metrics
|
||||
// exposed by the service.
|
||||
//
|
||||
// Currently, 'Gauge' (i.e a metric that represents the measured value of
|
||||
// something at an instant of time) is the only metric type supported by the
|
||||
// service.
|
||||
syntax = "proto3";
|
||||
|
||||
option go_package = "google.golang.org/grpc/interop/stress/grpc_testing";
|
||||
|
||||
package grpc.testing;
|
||||
|
||||
// Response message containing the gauge name and value
|
||||
message GaugeResponse {
|
||||
string name = 1;
|
||||
oneof value {
|
||||
int64 long_value = 2;
|
||||
double double_value = 3;
|
||||
string string_value = 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Request message containing the gauge name
|
||||
message GaugeRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message EmptyMessage {}
|
||||
|
||||
service MetricsService {
|
||||
// Returns the values of all the gauges that are currently being maintained by
|
||||
// the service
|
||||
rpc GetAllGauges(EmptyMessage) returns (stream GaugeResponse);
|
||||
|
||||
// Returns the value of one gauge
|
||||
rpc GetGauge(GaugeRequest) returns (GaugeResponse);
|
||||
}
|
||||
190
interop/stress/grpc_testing/metrics_grpc.pb.go
Normal file
190
interop/stress/grpc_testing/metrics_grpc.pb.go
Normal file
@@ -0,0 +1,190 @@
|
||||
// Copyright 2015-2016 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Contains the definitions for a metrics service and the type of metrics
|
||||
// exposed by the service.
|
||||
//
|
||||
// Currently, 'Gauge' (i.e a metric that represents the measured value of
|
||||
// something at an instant of time) is the only metric type supported by the
|
||||
// service.
|
||||
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc v5.27.1
|
||||
// source: interop/stress/grpc_testing/metrics.proto
|
||||
|
||||
package grpc_testing
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
MetricsService_GetAllGauges_FullMethodName = "/grpc.testing.MetricsService/GetAllGauges"
|
||||
MetricsService_GetGauge_FullMethodName = "/grpc.testing.MetricsService/GetGauge"
|
||||
)
|
||||
|
||||
// MetricsServiceClient is the client API for MetricsService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type MetricsServiceClient interface {
|
||||
// Returns the values of all the gauges that are currently being maintained by
|
||||
// the service
|
||||
GetAllGauges(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (grpc.ServerStreamingClient[GaugeResponse], error)
|
||||
// Returns the value of one gauge
|
||||
GetGauge(ctx context.Context, in *GaugeRequest, opts ...grpc.CallOption) (*GaugeResponse, error)
|
||||
}
|
||||
|
||||
type metricsServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewMetricsServiceClient(cc grpc.ClientConnInterface) MetricsServiceClient {
|
||||
return &metricsServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *metricsServiceClient) GetAllGauges(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (grpc.ServerStreamingClient[GaugeResponse], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &MetricsService_ServiceDesc.Streams[0], MetricsService_GetAllGauges_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[EmptyMessage, GaugeResponse]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MetricsService_GetAllGaugesClient = grpc.ServerStreamingClient[GaugeResponse]
|
||||
|
||||
func (c *metricsServiceClient) GetGauge(ctx context.Context, in *GaugeRequest, opts ...grpc.CallOption) (*GaugeResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GaugeResponse)
|
||||
err := c.cc.Invoke(ctx, MetricsService_GetGauge_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// MetricsServiceServer is the server API for MetricsService service.
|
||||
// All implementations must embed UnimplementedMetricsServiceServer
|
||||
// for forward compatibility.
|
||||
type MetricsServiceServer interface {
|
||||
// Returns the values of all the gauges that are currently being maintained by
|
||||
// the service
|
||||
GetAllGauges(*EmptyMessage, grpc.ServerStreamingServer[GaugeResponse]) error
|
||||
// Returns the value of one gauge
|
||||
GetGauge(context.Context, *GaugeRequest) (*GaugeResponse, error)
|
||||
mustEmbedUnimplementedMetricsServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedMetricsServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedMetricsServiceServer struct{}
|
||||
|
||||
func (UnimplementedMetricsServiceServer) GetAllGauges(*EmptyMessage, grpc.ServerStreamingServer[GaugeResponse]) error {
|
||||
return status.Error(codes.Unimplemented, "method GetAllGauges not implemented")
|
||||
}
|
||||
func (UnimplementedMetricsServiceServer) GetGauge(context.Context, *GaugeRequest) (*GaugeResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetGauge not implemented")
|
||||
}
|
||||
func (UnimplementedMetricsServiceServer) mustEmbedUnimplementedMetricsServiceServer() {}
|
||||
func (UnimplementedMetricsServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeMetricsServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to MetricsServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeMetricsServiceServer interface {
|
||||
mustEmbedUnimplementedMetricsServiceServer()
|
||||
}
|
||||
|
||||
func RegisterMetricsServiceServer(s grpc.ServiceRegistrar, srv MetricsServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedMetricsServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&MetricsService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _MetricsService_GetAllGauges_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(EmptyMessage)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(MetricsServiceServer).GetAllGauges(m, &grpc.GenericServerStream[EmptyMessage, GaugeResponse]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type MetricsService_GetAllGaugesServer = grpc.ServerStreamingServer[GaugeResponse]
|
||||
|
||||
func _MetricsService_GetGauge_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GaugeRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MetricsServiceServer).GetGauge(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: MetricsService_GetGauge_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MetricsServiceServer).GetGauge(ctx, req.(*GaugeRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// MetricsService_ServiceDesc is the grpc.ServiceDesc for MetricsService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var MetricsService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "grpc.testing.MetricsService",
|
||||
HandlerType: (*MetricsServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetGauge",
|
||||
Handler: _MetricsService_GetGauge_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "GetAllGauges",
|
||||
Handler: _MetricsService_GetAllGauges_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "interop/stress/grpc_testing/metrics.proto",
|
||||
}
|
||||
86
interop/stress/metrics_client/main.go
Normal file
86
interop/stress/metrics_client/main.go
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2016 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Binary metrics_client is a client to retrieve metrics from the server.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
metricspb "google.golang.org/grpc/interop/stress/grpc_testing"
|
||||
)
|
||||
|
||||
var (
|
||||
metricsServerAddress = flag.String("metrics_server_address", "", "The metrics server addresses in the format <hostname>:<port>")
|
||||
totalOnly = flag.Bool("total_only", false, "If true, this prints only the total value of all gauges")
|
||||
|
||||
logger = grpclog.Component("stress")
|
||||
)
|
||||
|
||||
func printMetrics(client metricspb.MetricsServiceClient, totalOnly bool) {
|
||||
stream, err := client.GetAllGauges(context.Background(), &metricspb.EmptyMessage{})
|
||||
if err != nil {
|
||||
logger.Fatalf("failed to call GetAllGauges: %v", err)
|
||||
}
|
||||
|
||||
var (
|
||||
overallQPS int64
|
||||
rpcStatus error
|
||||
)
|
||||
for {
|
||||
gaugeResponse, err := stream.Recv()
|
||||
if err != nil {
|
||||
rpcStatus = err
|
||||
break
|
||||
}
|
||||
if _, ok := gaugeResponse.GetValue().(*metricspb.GaugeResponse_LongValue); !ok {
|
||||
panic(fmt.Sprintf("gauge %s is not a long value", gaugeResponse.Name))
|
||||
}
|
||||
v := gaugeResponse.GetLongValue()
|
||||
if !totalOnly {
|
||||
logger.Infof("%s: %d", gaugeResponse.Name, v)
|
||||
}
|
||||
overallQPS += v
|
||||
}
|
||||
if rpcStatus != io.EOF {
|
||||
logger.Fatalf("failed to finish server streaming: %v", rpcStatus)
|
||||
}
|
||||
logger.Infof("overall qps: %d", overallQPS)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *metricsServerAddress == "" {
|
||||
logger.Fatal("-metrics_server_address is unset")
|
||||
}
|
||||
|
||||
conn, err := grpc.NewClient(*metricsServerAddress, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
logger.Fatalf("grpc.NewClient(%q) = %v", metricsServerAddress, err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
c := metricspb.NewMetricsServiceClient(conn)
|
||||
printMetrics(c, *totalOnly)
|
||||
}
|
||||
990
interop/test_utils.go
Normal file
990
interop/test_utils.go
Normal file
@@ -0,0 +1,990 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2014 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Package interop contains functions used by interop client/server.
|
||||
//
|
||||
// See interop test case descriptions [here].
|
||||
//
|
||||
// [here]: https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
|
||||
package interop
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/orca"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
v3orcapb "github.com/cncf/xds/go/xds/data/orca/v3"
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
testpb "google.golang.org/grpc/interop/grpc_testing"
|
||||
)
|
||||
|
||||
var (
|
||||
reqSizes = []int{27182, 8, 1828, 45904}
|
||||
respSizes = []int{31415, 9, 2653, 58979}
|
||||
largeReqSize = 271828
|
||||
largeRespSize = 314159
|
||||
initialMetadataKey = "x-grpc-test-echo-initial"
|
||||
trailingMetadataKey = "x-grpc-test-echo-trailing-bin"
|
||||
|
||||
logger = grpclog.Component("interop")
|
||||
)
|
||||
|
||||
// ClientNewPayload returns a payload of the given type and size.
|
||||
func ClientNewPayload(t testpb.PayloadType, size int) *testpb.Payload {
|
||||
if size < 0 {
|
||||
logger.Fatalf("Requested a response with invalid length %d", size)
|
||||
}
|
||||
body := make([]byte, size)
|
||||
switch t {
|
||||
case testpb.PayloadType_COMPRESSABLE:
|
||||
default:
|
||||
logger.Fatalf("Unsupported payload type: %d", t)
|
||||
}
|
||||
return &testpb.Payload{
|
||||
Type: t,
|
||||
Body: body,
|
||||
}
|
||||
}
|
||||
|
||||
// DoEmptyUnaryCall performs a unary RPC with empty request and response messages.
|
||||
func DoEmptyUnaryCall(ctx context.Context, tc testgrpc.TestServiceClient, args ...grpc.CallOption) {
|
||||
reply, err := tc.EmptyCall(ctx, &testpb.Empty{}, args...)
|
||||
if err != nil {
|
||||
logger.Fatal("/TestService/EmptyCall RPC failed: ", err)
|
||||
}
|
||||
if !proto.Equal(&testpb.Empty{}, reply) {
|
||||
logger.Fatalf("/TestService/EmptyCall receives %v, want %v", reply, testpb.Empty{})
|
||||
}
|
||||
}
|
||||
|
||||
// DoLargeUnaryCall performs a unary RPC with large payload in the request and response.
|
||||
func DoLargeUnaryCall(ctx context.Context, tc testgrpc.TestServiceClient, args ...grpc.CallOption) {
|
||||
pl := ClientNewPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
|
||||
req := &testpb.SimpleRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
ResponseSize: int32(largeRespSize),
|
||||
Payload: pl,
|
||||
}
|
||||
reply, err := tc.UnaryCall(ctx, req, args...)
|
||||
if err != nil {
|
||||
logger.Fatal("/TestService/UnaryCall RPC failed: ", err)
|
||||
}
|
||||
t := reply.GetPayload().GetType()
|
||||
s := len(reply.GetPayload().GetBody())
|
||||
if t != testpb.PayloadType_COMPRESSABLE || s != largeRespSize {
|
||||
logger.Fatalf("Got the reply with type %d len %d; want %d, %d", t, s, testpb.PayloadType_COMPRESSABLE, largeRespSize)
|
||||
}
|
||||
}
|
||||
|
||||
// DoClientStreaming performs a client streaming RPC.
|
||||
func DoClientStreaming(ctx context.Context, tc testgrpc.TestServiceClient, args ...grpc.CallOption) {
|
||||
stream, err := tc.StreamingInputCall(ctx, args...)
|
||||
if err != nil {
|
||||
logger.Fatalf("%v.StreamingInputCall(_) = _, %v", tc, err)
|
||||
}
|
||||
var sum int
|
||||
for _, s := range reqSizes {
|
||||
pl := ClientNewPayload(testpb.PayloadType_COMPRESSABLE, s)
|
||||
req := &testpb.StreamingInputCallRequest{
|
||||
Payload: pl,
|
||||
}
|
||||
if err := stream.Send(req); err != nil {
|
||||
logger.Fatalf("%v has error %v while sending %v", stream, err, req)
|
||||
}
|
||||
sum += s
|
||||
}
|
||||
reply, err := stream.CloseAndRecv()
|
||||
if err != nil {
|
||||
logger.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil)
|
||||
}
|
||||
if reply.GetAggregatedPayloadSize() != int32(sum) {
|
||||
logger.Fatalf("%v.CloseAndRecv().GetAggregatePayloadSize() = %v; want %v", stream, reply.GetAggregatedPayloadSize(), sum)
|
||||
}
|
||||
}
|
||||
|
||||
// DoServerStreaming performs a server streaming RPC.
|
||||
func DoServerStreaming(ctx context.Context, tc testgrpc.TestServiceClient, args ...grpc.CallOption) {
|
||||
respParam := make([]*testpb.ResponseParameters, len(respSizes))
|
||||
for i, s := range respSizes {
|
||||
respParam[i] = &testpb.ResponseParameters{
|
||||
Size: int32(s),
|
||||
}
|
||||
}
|
||||
req := &testpb.StreamingOutputCallRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
ResponseParameters: respParam,
|
||||
}
|
||||
stream, err := tc.StreamingOutputCall(ctx, req, args...)
|
||||
if err != nil {
|
||||
logger.Fatalf("%v.StreamingOutputCall(_) = _, %v", tc, err)
|
||||
}
|
||||
var rpcStatus error
|
||||
var respCnt int
|
||||
var index int
|
||||
for {
|
||||
reply, err := stream.Recv()
|
||||
if err != nil {
|
||||
rpcStatus = err
|
||||
break
|
||||
}
|
||||
t := reply.GetPayload().GetType()
|
||||
if t != testpb.PayloadType_COMPRESSABLE {
|
||||
logger.Fatalf("Got the reply of type %d, want %d", t, testpb.PayloadType_COMPRESSABLE)
|
||||
}
|
||||
size := len(reply.GetPayload().GetBody())
|
||||
if size != respSizes[index] {
|
||||
logger.Fatalf("Got reply body of length %d, want %d", size, respSizes[index])
|
||||
}
|
||||
index++
|
||||
respCnt++
|
||||
}
|
||||
if rpcStatus != io.EOF {
|
||||
logger.Fatalf("Failed to finish the server streaming rpc: %v", rpcStatus)
|
||||
}
|
||||
if respCnt != len(respSizes) {
|
||||
logger.Fatalf("Got %d reply, want %d", len(respSizes), respCnt)
|
||||
}
|
||||
}
|
||||
|
||||
// DoPingPong performs ping-pong style bi-directional streaming RPC.
|
||||
func DoPingPong(ctx context.Context, tc testgrpc.TestServiceClient, args ...grpc.CallOption) {
|
||||
stream, err := tc.FullDuplexCall(ctx, args...)
|
||||
if err != nil {
|
||||
logger.Fatalf("%v.FullDuplexCall(_) = _, %v", tc, err)
|
||||
}
|
||||
var index int
|
||||
for index < len(reqSizes) {
|
||||
respParam := []*testpb.ResponseParameters{
|
||||
{
|
||||
Size: int32(respSizes[index]),
|
||||
},
|
||||
}
|
||||
pl := ClientNewPayload(testpb.PayloadType_COMPRESSABLE, reqSizes[index])
|
||||
req := &testpb.StreamingOutputCallRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
ResponseParameters: respParam,
|
||||
Payload: pl,
|
||||
}
|
||||
if err := stream.Send(req); err != nil {
|
||||
logger.Fatalf("%v has error %v while sending %v", stream, err, req)
|
||||
}
|
||||
reply, err := stream.Recv()
|
||||
if err != nil {
|
||||
logger.Fatalf("%v.Recv() = %v", stream, err)
|
||||
}
|
||||
t := reply.GetPayload().GetType()
|
||||
if t != testpb.PayloadType_COMPRESSABLE {
|
||||
logger.Fatalf("Got the reply of type %d, want %d", t, testpb.PayloadType_COMPRESSABLE)
|
||||
}
|
||||
size := len(reply.GetPayload().GetBody())
|
||||
if size != respSizes[index] {
|
||||
logger.Fatalf("Got reply body of length %d, want %d", size, respSizes[index])
|
||||
}
|
||||
index++
|
||||
}
|
||||
if err := stream.CloseSend(); err != nil {
|
||||
logger.Fatalf("%v.CloseSend() got %v, want %v", stream, err, nil)
|
||||
}
|
||||
if _, err := stream.Recv(); err != io.EOF {
|
||||
logger.Fatalf("%v failed to complele the ping pong test: %v", stream, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DoEmptyStream sets up a bi-directional streaming with zero message.
|
||||
func DoEmptyStream(ctx context.Context, tc testgrpc.TestServiceClient, args ...grpc.CallOption) {
|
||||
stream, err := tc.FullDuplexCall(ctx, args...)
|
||||
if err != nil {
|
||||
logger.Fatalf("%v.FullDuplexCall(_) = _, %v", tc, err)
|
||||
}
|
||||
if err := stream.CloseSend(); err != nil {
|
||||
logger.Fatalf("%v.CloseSend() got %v, want %v", stream, err, nil)
|
||||
}
|
||||
if _, err := stream.Recv(); err != io.EOF {
|
||||
logger.Fatalf("%v failed to complete the empty stream test: %v", stream, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DoTimeoutOnSleepingServer performs an RPC on a sleep server which causes RPC timeout.
|
||||
func DoTimeoutOnSleepingServer(ctx context.Context, tc testgrpc.TestServiceClient, args ...grpc.CallOption) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 1*time.Millisecond)
|
||||
defer cancel()
|
||||
stream, err := tc.FullDuplexCall(ctx, args...)
|
||||
if err != nil {
|
||||
if status.Code(err) == codes.DeadlineExceeded {
|
||||
return
|
||||
}
|
||||
logger.Fatalf("%v.FullDuplexCall(_) = _, %v", tc, err)
|
||||
}
|
||||
pl := ClientNewPayload(testpb.PayloadType_COMPRESSABLE, 27182)
|
||||
req := &testpb.StreamingOutputCallRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
Payload: pl,
|
||||
}
|
||||
if err := stream.Send(req); err != nil && err != io.EOF {
|
||||
logger.Fatalf("%v.Send(_) = %v", stream, err)
|
||||
}
|
||||
if _, err := stream.Recv(); status.Code(err) != codes.DeadlineExceeded {
|
||||
logger.Fatalf("%v.Recv() = _, %v, want error code %d", stream, err, codes.DeadlineExceeded)
|
||||
}
|
||||
}
|
||||
|
||||
// DoComputeEngineCreds performs a unary RPC with compute engine auth.
|
||||
func DoComputeEngineCreds(ctx context.Context, tc testgrpc.TestServiceClient, serviceAccount, oauthScope string) {
|
||||
pl := ClientNewPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
|
||||
req := &testpb.SimpleRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
ResponseSize: int32(largeRespSize),
|
||||
Payload: pl,
|
||||
FillUsername: true,
|
||||
FillOauthScope: true,
|
||||
}
|
||||
reply, err := tc.UnaryCall(ctx, req)
|
||||
if err != nil {
|
||||
logger.Fatal("/TestService/UnaryCall RPC failed: ", err)
|
||||
}
|
||||
user := reply.GetUsername()
|
||||
scope := reply.GetOauthScope()
|
||||
if user != serviceAccount {
|
||||
logger.Fatalf("Got user name %q, want %q.", user, serviceAccount)
|
||||
}
|
||||
if !strings.Contains(oauthScope, scope) {
|
||||
logger.Fatalf("Got OAuth scope %q which is NOT a substring of %q.", scope, oauthScope)
|
||||
}
|
||||
}
|
||||
|
||||
func getServiceAccountJSONKey(keyFile string) []byte {
|
||||
jsonKey, err := os.ReadFile(keyFile)
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to read the service account key file: %v", err)
|
||||
}
|
||||
return jsonKey
|
||||
}
|
||||
|
||||
// DoServiceAccountCreds performs a unary RPC with service account auth.
|
||||
func DoServiceAccountCreds(ctx context.Context, tc testgrpc.TestServiceClient, serviceAccountKeyFile, oauthScope string) {
|
||||
pl := ClientNewPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
|
||||
req := &testpb.SimpleRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
ResponseSize: int32(largeRespSize),
|
||||
Payload: pl,
|
||||
FillUsername: true,
|
||||
FillOauthScope: true,
|
||||
}
|
||||
reply, err := tc.UnaryCall(ctx, req)
|
||||
if err != nil {
|
||||
logger.Fatal("/TestService/UnaryCall RPC failed: ", err)
|
||||
}
|
||||
jsonKey := getServiceAccountJSONKey(serviceAccountKeyFile)
|
||||
user := reply.GetUsername()
|
||||
scope := reply.GetOauthScope()
|
||||
if !strings.Contains(string(jsonKey), user) {
|
||||
logger.Fatalf("Got user name %q which is NOT a substring of %q.", user, jsonKey)
|
||||
}
|
||||
if !strings.Contains(oauthScope, scope) {
|
||||
logger.Fatalf("Got OAuth scope %q which is NOT a substring of %q.", scope, oauthScope)
|
||||
}
|
||||
}
|
||||
|
||||
// DoJWTTokenCreds performs a unary RPC with JWT token auth.
|
||||
func DoJWTTokenCreds(ctx context.Context, tc testgrpc.TestServiceClient, serviceAccountKeyFile string) {
|
||||
pl := ClientNewPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
|
||||
req := &testpb.SimpleRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
ResponseSize: int32(largeRespSize),
|
||||
Payload: pl,
|
||||
FillUsername: true,
|
||||
}
|
||||
reply, err := tc.UnaryCall(ctx, req)
|
||||
if err != nil {
|
||||
logger.Fatal("/TestService/UnaryCall RPC failed: ", err)
|
||||
}
|
||||
jsonKey := getServiceAccountJSONKey(serviceAccountKeyFile)
|
||||
user := reply.GetUsername()
|
||||
if !strings.Contains(string(jsonKey), user) {
|
||||
logger.Fatalf("Got user name %q which is NOT a substring of %q.", user, jsonKey)
|
||||
}
|
||||
}
|
||||
|
||||
// GetToken obtains an OAUTH token from the input.
|
||||
func GetToken(ctx context.Context, serviceAccountKeyFile string, oauthScope string) *oauth2.Token {
|
||||
jsonKey := getServiceAccountJSONKey(serviceAccountKeyFile)
|
||||
config, err := google.JWTConfigFromJSON(jsonKey, oauthScope)
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to get the config: %v", err)
|
||||
}
|
||||
token, err := config.TokenSource(ctx).Token()
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to get the token: %v", err)
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
// DoOauth2TokenCreds performs a unary RPC with OAUTH2 token auth.
|
||||
func DoOauth2TokenCreds(ctx context.Context, tc testgrpc.TestServiceClient, serviceAccountKeyFile, oauthScope string) {
|
||||
pl := ClientNewPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
|
||||
req := &testpb.SimpleRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
ResponseSize: int32(largeRespSize),
|
||||
Payload: pl,
|
||||
FillUsername: true,
|
||||
FillOauthScope: true,
|
||||
}
|
||||
reply, err := tc.UnaryCall(ctx, req)
|
||||
if err != nil {
|
||||
logger.Fatal("/TestService/UnaryCall RPC failed: ", err)
|
||||
}
|
||||
jsonKey := getServiceAccountJSONKey(serviceAccountKeyFile)
|
||||
user := reply.GetUsername()
|
||||
scope := reply.GetOauthScope()
|
||||
if !strings.Contains(string(jsonKey), user) {
|
||||
logger.Fatalf("Got user name %q which is NOT a substring of %q.", user, jsonKey)
|
||||
}
|
||||
if !strings.Contains(oauthScope, scope) {
|
||||
logger.Fatalf("Got OAuth scope %q which is NOT a substring of %q.", scope, oauthScope)
|
||||
}
|
||||
}
|
||||
|
||||
// DoPerRPCCreds performs a unary RPC with per RPC OAUTH2 token.
|
||||
func DoPerRPCCreds(ctx context.Context, tc testgrpc.TestServiceClient, serviceAccountKeyFile, oauthScope string) {
|
||||
jsonKey := getServiceAccountJSONKey(serviceAccountKeyFile)
|
||||
pl := ClientNewPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
|
||||
req := &testpb.SimpleRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
ResponseSize: int32(largeRespSize),
|
||||
Payload: pl,
|
||||
FillUsername: true,
|
||||
FillOauthScope: true,
|
||||
}
|
||||
token := GetToken(ctx, serviceAccountKeyFile, oauthScope)
|
||||
kv := map[string]string{"authorization": token.Type() + " " + token.AccessToken}
|
||||
ctx = metadata.NewOutgoingContext(ctx, metadata.MD{"authorization": []string{kv["authorization"]}})
|
||||
reply, err := tc.UnaryCall(ctx, req)
|
||||
if err != nil {
|
||||
logger.Fatal("/TestService/UnaryCall RPC failed: ", err)
|
||||
}
|
||||
user := reply.GetUsername()
|
||||
scope := reply.GetOauthScope()
|
||||
if !strings.Contains(string(jsonKey), user) {
|
||||
logger.Fatalf("Got user name %q which is NOT a substring of %q.", user, jsonKey)
|
||||
}
|
||||
if !strings.Contains(oauthScope, scope) {
|
||||
logger.Fatalf("Got OAuth scope %q which is NOT a substring of %q.", scope, oauthScope)
|
||||
}
|
||||
}
|
||||
|
||||
// DoGoogleDefaultCredentials performs a unary RPC with google default credentials
|
||||
func DoGoogleDefaultCredentials(ctx context.Context, tc testgrpc.TestServiceClient, defaultServiceAccount string) {
|
||||
pl := ClientNewPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
|
||||
req := &testpb.SimpleRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
ResponseSize: int32(largeRespSize),
|
||||
Payload: pl,
|
||||
FillUsername: true,
|
||||
FillOauthScope: true,
|
||||
}
|
||||
reply, err := tc.UnaryCall(ctx, req)
|
||||
if err != nil {
|
||||
logger.Fatal("/TestService/UnaryCall RPC failed: ", err)
|
||||
}
|
||||
if reply.GetUsername() != defaultServiceAccount {
|
||||
logger.Fatalf("Got user name %q; wanted %q. ", reply.GetUsername(), defaultServiceAccount)
|
||||
}
|
||||
}
|
||||
|
||||
// DoComputeEngineChannelCredentials performs a unary RPC with compute engine channel credentials
|
||||
func DoComputeEngineChannelCredentials(ctx context.Context, tc testgrpc.TestServiceClient, defaultServiceAccount string) {
|
||||
pl := ClientNewPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
|
||||
req := &testpb.SimpleRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
ResponseSize: int32(largeRespSize),
|
||||
Payload: pl,
|
||||
FillUsername: true,
|
||||
FillOauthScope: true,
|
||||
}
|
||||
reply, err := tc.UnaryCall(ctx, req)
|
||||
if err != nil {
|
||||
logger.Fatal("/TestService/UnaryCall RPC failed: ", err)
|
||||
}
|
||||
if reply.GetUsername() != defaultServiceAccount {
|
||||
logger.Fatalf("Got user name %q; wanted %q. ", reply.GetUsername(), defaultServiceAccount)
|
||||
}
|
||||
}
|
||||
|
||||
var testMetadata = metadata.MD{
|
||||
"key1": []string{"value1"},
|
||||
"key2": []string{"value2"},
|
||||
}
|
||||
|
||||
// DoCancelAfterBegin cancels the RPC after metadata has been sent but before payloads are sent.
|
||||
func DoCancelAfterBegin(ctx context.Context, tc testgrpc.TestServiceClient, args ...grpc.CallOption) {
|
||||
ctx, cancel := context.WithCancel(metadata.NewOutgoingContext(ctx, testMetadata))
|
||||
stream, err := tc.StreamingInputCall(ctx, args...)
|
||||
if err != nil {
|
||||
logger.Fatalf("%v.StreamingInputCall(_) = _, %v", tc, err)
|
||||
}
|
||||
cancel()
|
||||
_, err = stream.CloseAndRecv()
|
||||
if status.Code(err) != codes.Canceled {
|
||||
logger.Fatalf("%v.CloseAndRecv() got error code %d, want %d", stream, status.Code(err), codes.Canceled)
|
||||
}
|
||||
}
|
||||
|
||||
// DoCancelAfterFirstResponse cancels the RPC after receiving the first message from the server.
|
||||
func DoCancelAfterFirstResponse(ctx context.Context, tc testgrpc.TestServiceClient, args ...grpc.CallOption) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
stream, err := tc.FullDuplexCall(ctx, args...)
|
||||
if err != nil {
|
||||
logger.Fatalf("%v.FullDuplexCall(_) = _, %v", tc, err)
|
||||
}
|
||||
respParam := []*testpb.ResponseParameters{
|
||||
{
|
||||
Size: 31415,
|
||||
},
|
||||
}
|
||||
pl := ClientNewPayload(testpb.PayloadType_COMPRESSABLE, 27182)
|
||||
req := &testpb.StreamingOutputCallRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
ResponseParameters: respParam,
|
||||
Payload: pl,
|
||||
}
|
||||
if err := stream.Send(req); err != nil {
|
||||
logger.Fatalf("%v has error %v while sending %v", stream, err, req)
|
||||
}
|
||||
if _, err := stream.Recv(); err != nil {
|
||||
logger.Fatalf("%v.Recv() = %v", stream, err)
|
||||
}
|
||||
cancel()
|
||||
if _, err := stream.Recv(); status.Code(err) != codes.Canceled {
|
||||
logger.Fatalf("%v compleled with error code %d, want %d", stream, status.Code(err), codes.Canceled)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
initialMetadataValue = "test_initial_metadata_value"
|
||||
trailingMetadataValue = "\x0a\x0b\x0a\x0b\x0a\x0b"
|
||||
customMetadata = metadata.Pairs(
|
||||
initialMetadataKey, initialMetadataValue,
|
||||
trailingMetadataKey, trailingMetadataValue,
|
||||
)
|
||||
)
|
||||
|
||||
func validateMetadata(header, trailer metadata.MD) {
|
||||
if len(header[initialMetadataKey]) != 1 {
|
||||
logger.Fatalf("Expected exactly one header from server. Received %d", len(header[initialMetadataKey]))
|
||||
}
|
||||
if header[initialMetadataKey][0] != initialMetadataValue {
|
||||
logger.Fatalf("Got header %s; want %s", header[initialMetadataKey][0], initialMetadataValue)
|
||||
}
|
||||
if len(trailer[trailingMetadataKey]) != 1 {
|
||||
logger.Fatalf("Expected exactly one trailer from server. Received %d", len(trailer[trailingMetadataKey]))
|
||||
}
|
||||
if trailer[trailingMetadataKey][0] != trailingMetadataValue {
|
||||
logger.Fatalf("Got trailer %s; want %s", trailer[trailingMetadataKey][0], trailingMetadataValue)
|
||||
}
|
||||
}
|
||||
|
||||
// DoCustomMetadata checks that metadata is echoed back to the client.
|
||||
func DoCustomMetadata(ctx context.Context, tc testgrpc.TestServiceClient, args ...grpc.CallOption) {
|
||||
// Testing with UnaryCall.
|
||||
pl := ClientNewPayload(testpb.PayloadType_COMPRESSABLE, 1)
|
||||
req := &testpb.SimpleRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
ResponseSize: int32(1),
|
||||
Payload: pl,
|
||||
}
|
||||
ctx = metadata.NewOutgoingContext(ctx, customMetadata)
|
||||
var header, trailer metadata.MD
|
||||
args = append(args, grpc.Header(&header), grpc.Trailer(&trailer))
|
||||
reply, err := tc.UnaryCall(
|
||||
ctx,
|
||||
req,
|
||||
args...,
|
||||
)
|
||||
if err != nil {
|
||||
logger.Fatal("/TestService/UnaryCall RPC failed: ", err)
|
||||
}
|
||||
t := reply.GetPayload().GetType()
|
||||
s := len(reply.GetPayload().GetBody())
|
||||
if t != testpb.PayloadType_COMPRESSABLE || s != 1 {
|
||||
logger.Fatalf("Got the reply with type %d len %d; want %d, %d", t, s, testpb.PayloadType_COMPRESSABLE, 1)
|
||||
}
|
||||
validateMetadata(header, trailer)
|
||||
|
||||
// Testing with FullDuplex.
|
||||
stream, err := tc.FullDuplexCall(ctx, args...)
|
||||
if err != nil {
|
||||
logger.Fatalf("%v.FullDuplexCall(_) = _, %v, want <nil>", tc, err)
|
||||
}
|
||||
respParam := []*testpb.ResponseParameters{
|
||||
{
|
||||
Size: 1,
|
||||
},
|
||||
}
|
||||
streamReq := &testpb.StreamingOutputCallRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
ResponseParameters: respParam,
|
||||
Payload: pl,
|
||||
}
|
||||
if err := stream.Send(streamReq); err != nil {
|
||||
logger.Fatalf("%v has error %v while sending %v", stream, err, streamReq)
|
||||
}
|
||||
streamHeader, err := stream.Header()
|
||||
if err != nil {
|
||||
logger.Fatalf("%v.Header() = %v", stream, err)
|
||||
}
|
||||
if _, err := stream.Recv(); err != nil {
|
||||
logger.Fatalf("%v.Recv() = %v", stream, err)
|
||||
}
|
||||
if err := stream.CloseSend(); err != nil {
|
||||
logger.Fatalf("%v.CloseSend() = %v, want <nil>", stream, err)
|
||||
}
|
||||
if _, err := stream.Recv(); err != io.EOF {
|
||||
logger.Fatalf("%v failed to complete the custom metadata test: %v", stream, err)
|
||||
}
|
||||
streamTrailer := stream.Trailer()
|
||||
validateMetadata(streamHeader, streamTrailer)
|
||||
}
|
||||
|
||||
// DoStatusCodeAndMessage checks that the status code is propagated back to the client.
|
||||
func DoStatusCodeAndMessage(ctx context.Context, tc testgrpc.TestServiceClient, args ...grpc.CallOption) {
|
||||
var code int32 = 2
|
||||
msg := "test status message"
|
||||
expectedErr := status.Error(codes.Code(code), msg)
|
||||
respStatus := &testpb.EchoStatus{
|
||||
Code: code,
|
||||
Message: msg,
|
||||
}
|
||||
// Test UnaryCall.
|
||||
req := &testpb.SimpleRequest{
|
||||
ResponseStatus: respStatus,
|
||||
}
|
||||
if _, err := tc.UnaryCall(ctx, req, args...); err == nil || err.Error() != expectedErr.Error() {
|
||||
logger.Fatalf("%v.UnaryCall(_, %v) = _, %v, want _, %v", tc, req, err, expectedErr)
|
||||
}
|
||||
// Test FullDuplexCall.
|
||||
stream, err := tc.FullDuplexCall(ctx, args...)
|
||||
if err != nil {
|
||||
logger.Fatalf("%v.FullDuplexCall(_) = _, %v, want <nil>", tc, err)
|
||||
}
|
||||
streamReq := &testpb.StreamingOutputCallRequest{
|
||||
ResponseStatus: respStatus,
|
||||
}
|
||||
if err := stream.Send(streamReq); err != nil {
|
||||
logger.Fatalf("%v has error %v while sending %v, want <nil>", stream, err, streamReq)
|
||||
}
|
||||
if err := stream.CloseSend(); err != nil {
|
||||
logger.Fatalf("%v.CloseSend() = %v, want <nil>", stream, err)
|
||||
}
|
||||
if _, err = stream.Recv(); err.Error() != expectedErr.Error() {
|
||||
logger.Fatalf("%v.Recv() returned error %v, want %v", stream, err, expectedErr)
|
||||
}
|
||||
}
|
||||
|
||||
// DoSpecialStatusMessage verifies Unicode and whitespace is correctly processed
|
||||
// in status message.
|
||||
func DoSpecialStatusMessage(ctx context.Context, tc testgrpc.TestServiceClient, args ...grpc.CallOption) {
|
||||
const (
|
||||
code int32 = 2
|
||||
msg string = "\t\ntest with whitespace\r\nand Unicode BMP ☺ and non-BMP 😈\t\n"
|
||||
)
|
||||
expectedErr := status.Error(codes.Code(code), msg)
|
||||
req := &testpb.SimpleRequest{
|
||||
ResponseStatus: &testpb.EchoStatus{
|
||||
Code: code,
|
||||
Message: msg,
|
||||
},
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer cancel()
|
||||
if _, err := tc.UnaryCall(ctx, req, args...); err == nil || err.Error() != expectedErr.Error() {
|
||||
logger.Fatalf("%v.UnaryCall(_, %v) = _, %v, want _, %v", tc, req, err, expectedErr)
|
||||
}
|
||||
}
|
||||
|
||||
// DoUnimplementedService attempts to call a method from an unimplemented service.
|
||||
func DoUnimplementedService(ctx context.Context, tc testgrpc.UnimplementedServiceClient) {
|
||||
_, err := tc.UnimplementedCall(ctx, &testpb.Empty{})
|
||||
if status.Code(err) != codes.Unimplemented {
|
||||
logger.Fatalf("%v.UnimplementedCall() = _, %v, want _, %v", tc, status.Code(err), codes.Unimplemented)
|
||||
}
|
||||
}
|
||||
|
||||
// DoUnimplementedMethod attempts to call an unimplemented method.
|
||||
func DoUnimplementedMethod(ctx context.Context, cc *grpc.ClientConn) {
|
||||
var req, reply proto.Message
|
||||
if err := cc.Invoke(ctx, "/grpc.testing.TestService/UnimplementedCall", req, reply); err == nil || status.Code(err) != codes.Unimplemented {
|
||||
logger.Fatalf("ClientConn.Invoke(_, _, _, _, _) = %v, want error code %s", err, codes.Unimplemented)
|
||||
}
|
||||
}
|
||||
|
||||
// DoPickFirstUnary runs multiple RPCs (rpcCount) and checks that all requests
|
||||
// are sent to the same backend.
|
||||
func DoPickFirstUnary(ctx context.Context, tc testgrpc.TestServiceClient) {
|
||||
const rpcCount = 100
|
||||
|
||||
pl := ClientNewPayload(testpb.PayloadType_COMPRESSABLE, 1)
|
||||
req := &testpb.SimpleRequest{
|
||||
ResponseType: testpb.PayloadType_COMPRESSABLE,
|
||||
ResponseSize: int32(1),
|
||||
Payload: pl,
|
||||
FillServerId: true,
|
||||
}
|
||||
// TODO(mohanli): Revert the timeout back to 10s once TD migrates to xdstp.
|
||||
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||
defer cancel()
|
||||
var serverID string
|
||||
for i := 0; i < rpcCount; i++ {
|
||||
resp, err := tc.UnaryCall(ctx, req)
|
||||
if err != nil {
|
||||
logger.Fatalf("iteration %d, failed to do UnaryCall: %v", i, err)
|
||||
}
|
||||
id := resp.ServerId
|
||||
if id == "" {
|
||||
logger.Fatalf("iteration %d, got empty server ID", i)
|
||||
}
|
||||
if i == 0 {
|
||||
serverID = id
|
||||
continue
|
||||
}
|
||||
if serverID != id {
|
||||
logger.Fatalf("iteration %d, got different server ids: %q vs %q", i, serverID, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type testServer struct {
|
||||
testgrpc.UnimplementedTestServiceServer
|
||||
|
||||
orcaMu sync.Mutex
|
||||
metricsRecorder orca.ServerMetricsRecorder
|
||||
}
|
||||
|
||||
// NewTestServerOptions contains options that control the behavior of the test
|
||||
// server returned by NewTestServer.
|
||||
type NewTestServerOptions struct {
|
||||
MetricsRecorder orca.ServerMetricsRecorder
|
||||
}
|
||||
|
||||
// NewTestServer creates a test server for test service. opts carries optional
|
||||
// settings and does not need to be provided. If multiple opts are provided,
|
||||
// only the first one is used.
|
||||
func NewTestServer(opts ...NewTestServerOptions) testgrpc.TestServiceServer {
|
||||
if len(opts) > 0 {
|
||||
return &testServer{metricsRecorder: opts[0].MetricsRecorder}
|
||||
}
|
||||
return &testServer{}
|
||||
}
|
||||
|
||||
func (s *testServer) EmptyCall(context.Context, *testpb.Empty) (*testpb.Empty, error) {
|
||||
return new(testpb.Empty), nil
|
||||
}
|
||||
|
||||
func serverNewPayload(t testpb.PayloadType, size int32) (*testpb.Payload, error) {
|
||||
if size < 0 {
|
||||
return nil, fmt.Errorf("requested a response with invalid length %d", size)
|
||||
}
|
||||
body := make([]byte, size)
|
||||
switch t {
|
||||
case testpb.PayloadType_COMPRESSABLE:
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported payload type: %d", t)
|
||||
}
|
||||
return &testpb.Payload{
|
||||
Type: t,
|
||||
Body: body,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *testServer) UnaryCall(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
|
||||
st := in.GetResponseStatus()
|
||||
if md, ok := metadata.FromIncomingContext(ctx); ok {
|
||||
if initialMetadata, ok := md[initialMetadataKey]; ok {
|
||||
header := metadata.Pairs(initialMetadataKey, initialMetadata[0])
|
||||
grpc.SendHeader(ctx, header)
|
||||
}
|
||||
if trailingMetadata, ok := md[trailingMetadataKey]; ok {
|
||||
trailer := metadata.Pairs(trailingMetadataKey, trailingMetadata[0])
|
||||
grpc.SetTrailer(ctx, trailer)
|
||||
}
|
||||
}
|
||||
if st != nil && st.Code != 0 {
|
||||
return nil, status.Error(codes.Code(st.Code), st.Message)
|
||||
}
|
||||
pl, err := serverNewPayload(in.GetResponseType(), in.GetResponseSize())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r, orcaData := orca.CallMetricsRecorderFromContext(ctx), in.GetOrcaPerQueryReport(); r != nil && orcaData != nil {
|
||||
// Transfer the request's per-Call ORCA data to the call metrics
|
||||
// recorder in the context, if present.
|
||||
setORCAMetrics(r, orcaData)
|
||||
}
|
||||
return &testpb.SimpleResponse{
|
||||
Payload: pl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func setORCAMetrics(r orca.ServerMetricsRecorder, orcaData *testpb.TestOrcaReport) {
|
||||
r.SetCPUUtilization(orcaData.CpuUtilization)
|
||||
r.SetMemoryUtilization(orcaData.MemoryUtilization)
|
||||
if rq, ok := r.(orca.CallMetricsRecorder); ok {
|
||||
for k, v := range orcaData.RequestCost {
|
||||
rq.SetRequestCost(k, v)
|
||||
}
|
||||
}
|
||||
for k, v := range orcaData.Utilization {
|
||||
r.SetNamedUtilization(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testServer) StreamingOutputCall(args *testpb.StreamingOutputCallRequest, stream testgrpc.TestService_StreamingOutputCallServer) error {
|
||||
cs := args.GetResponseParameters()
|
||||
for _, c := range cs {
|
||||
if us := c.GetIntervalUs(); us > 0 {
|
||||
time.Sleep(time.Duration(us) * time.Microsecond)
|
||||
}
|
||||
pl, err := serverNewPayload(args.GetResponseType(), c.GetSize())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := stream.Send(&testpb.StreamingOutputCallResponse{
|
||||
Payload: pl,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *testServer) StreamingInputCall(stream testgrpc.TestService_StreamingInputCallServer) error {
|
||||
var sum int
|
||||
for {
|
||||
in, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
return stream.SendAndClose(&testpb.StreamingInputCallResponse{
|
||||
AggregatedPayloadSize: int32(sum),
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p := in.GetPayload().GetBody()
|
||||
sum += len(p)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testServer) FullDuplexCall(stream testgrpc.TestService_FullDuplexCallServer) error {
|
||||
if md, ok := metadata.FromIncomingContext(stream.Context()); ok {
|
||||
if initialMetadata, ok := md[initialMetadataKey]; ok {
|
||||
header := metadata.Pairs(initialMetadataKey, initialMetadata[0])
|
||||
stream.SendHeader(header)
|
||||
}
|
||||
if trailingMetadata, ok := md[trailingMetadataKey]; ok {
|
||||
trailer := metadata.Pairs(trailingMetadataKey, trailingMetadata[0])
|
||||
stream.SetTrailer(trailer)
|
||||
}
|
||||
}
|
||||
hasORCALock := false
|
||||
for {
|
||||
in, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
// read done.
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
st := in.GetResponseStatus()
|
||||
if st != nil && st.Code != 0 {
|
||||
return status.Error(codes.Code(st.Code), st.Message)
|
||||
}
|
||||
|
||||
if r, orcaData := s.metricsRecorder, in.GetOrcaOobReport(); r != nil && orcaData != nil {
|
||||
// Transfer the request's OOB ORCA data to the server metrics recorder
|
||||
// in the server, if present.
|
||||
if !hasORCALock {
|
||||
s.orcaMu.Lock()
|
||||
defer s.orcaMu.Unlock()
|
||||
hasORCALock = true
|
||||
}
|
||||
setORCAMetrics(r, orcaData)
|
||||
}
|
||||
|
||||
cs := in.GetResponseParameters()
|
||||
for _, c := range cs {
|
||||
if us := c.GetIntervalUs(); us > 0 {
|
||||
time.Sleep(time.Duration(us) * time.Microsecond)
|
||||
}
|
||||
pl, err := serverNewPayload(in.GetResponseType(), c.GetSize())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := stream.Send(&testpb.StreamingOutputCallResponse{
|
||||
Payload: pl,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testServer) HalfDuplexCall(stream testgrpc.TestService_HalfDuplexCallServer) error {
|
||||
var msgBuf []*testpb.StreamingOutputCallRequest
|
||||
for {
|
||||
in, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
// read done.
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msgBuf = append(msgBuf, in)
|
||||
}
|
||||
for _, m := range msgBuf {
|
||||
cs := m.GetResponseParameters()
|
||||
for _, c := range cs {
|
||||
if us := c.GetIntervalUs(); us > 0 {
|
||||
time.Sleep(time.Duration(us) * time.Microsecond)
|
||||
}
|
||||
pl, err := serverNewPayload(m.GetResponseType(), c.GetSize())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := stream.Send(&testpb.StreamingOutputCallResponse{
|
||||
Payload: pl,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DoORCAPerRPCTest performs a unary RPC that enables ORCA per-call reporting
|
||||
// and verifies the load report sent back to the LB policy's Done callback.
|
||||
func DoORCAPerRPCTest(ctx context.Context, tc testgrpc.TestServiceClient) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||
defer cancel()
|
||||
orcaRes := &v3orcapb.OrcaLoadReport{}
|
||||
_, err := tc.UnaryCall(contextWithORCAResult(ctx, &orcaRes), &testpb.SimpleRequest{
|
||||
OrcaPerQueryReport: &testpb.TestOrcaReport{
|
||||
CpuUtilization: 0.8210,
|
||||
MemoryUtilization: 0.5847,
|
||||
RequestCost: map[string]float64{"cost": 3456.32},
|
||||
Utilization: map[string]float64{"util": 0.30499},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
logger.Fatalf("/TestService/UnaryCall RPC failed: ", err)
|
||||
}
|
||||
want := &v3orcapb.OrcaLoadReport{
|
||||
CpuUtilization: 0.8210,
|
||||
MemUtilization: 0.5847,
|
||||
RequestCost: map[string]float64{"cost": 3456.32},
|
||||
Utilization: map[string]float64{"util": 0.30499},
|
||||
}
|
||||
if !proto.Equal(orcaRes, want) {
|
||||
logger.Fatalf("/TestService/UnaryCall RPC received ORCA load report %+v; want %+v", orcaRes, want)
|
||||
}
|
||||
}
|
||||
|
||||
// DoORCAOOBTest performs a streaming RPC that enables ORCA OOB reporting and
|
||||
// verifies the load report sent to the LB policy's OOB listener.
|
||||
func DoORCAOOBTest(ctx context.Context, tc testgrpc.TestServiceClient) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||
defer cancel()
|
||||
stream, err := tc.FullDuplexCall(ctx)
|
||||
if err != nil {
|
||||
logger.Fatalf("/TestService/FullDuplexCall received error starting stream: %v", err)
|
||||
}
|
||||
err = stream.Send(&testpb.StreamingOutputCallRequest{
|
||||
OrcaOobReport: &testpb.TestOrcaReport{
|
||||
CpuUtilization: 0.8210,
|
||||
MemoryUtilization: 0.5847,
|
||||
Utilization: map[string]float64{"util": 0.30499},
|
||||
},
|
||||
ResponseParameters: []*testpb.ResponseParameters{{Size: 1}},
|
||||
})
|
||||
if err != nil {
|
||||
logger.Fatalf("/TestService/FullDuplexCall received error sending: %v", err)
|
||||
}
|
||||
_, err = stream.Recv()
|
||||
if err != nil {
|
||||
logger.Fatalf("/TestService/FullDuplexCall received error receiving: %v", err)
|
||||
}
|
||||
|
||||
want := &v3orcapb.OrcaLoadReport{
|
||||
CpuUtilization: 0.8210,
|
||||
MemUtilization: 0.5847,
|
||||
Utilization: map[string]float64{"util": 0.30499},
|
||||
}
|
||||
checkORCAMetrics(ctx, tc, want)
|
||||
|
||||
err = stream.Send(&testpb.StreamingOutputCallRequest{
|
||||
OrcaOobReport: &testpb.TestOrcaReport{
|
||||
CpuUtilization: 0.29309,
|
||||
MemoryUtilization: 0.2,
|
||||
Utilization: map[string]float64{"util": 0.2039},
|
||||
},
|
||||
ResponseParameters: []*testpb.ResponseParameters{{Size: 1}},
|
||||
})
|
||||
if err != nil {
|
||||
logger.Fatalf("/TestService/FullDuplexCall received error sending: %v", err)
|
||||
}
|
||||
_, err = stream.Recv()
|
||||
if err != nil {
|
||||
logger.Fatalf("/TestService/FullDuplexCall received error receiving: %v", err)
|
||||
}
|
||||
|
||||
want = &v3orcapb.OrcaLoadReport{
|
||||
CpuUtilization: 0.29309,
|
||||
MemUtilization: 0.2,
|
||||
Utilization: map[string]float64{"util": 0.2039},
|
||||
}
|
||||
checkORCAMetrics(ctx, tc, want)
|
||||
}
|
||||
|
||||
func checkORCAMetrics(ctx context.Context, tc testgrpc.TestServiceClient, want *v3orcapb.OrcaLoadReport) {
|
||||
for ctx.Err() == nil {
|
||||
orcaRes := &v3orcapb.OrcaLoadReport{}
|
||||
if _, err := tc.UnaryCall(contextWithORCAResult(ctx, &orcaRes), &testpb.SimpleRequest{}); err != nil {
|
||||
logger.Fatalf("/TestService/UnaryCall RPC failed: ", err)
|
||||
}
|
||||
if proto.Equal(orcaRes, want) {
|
||||
return
|
||||
}
|
||||
logger.Infof("/TestService/UnaryCall RPC received ORCA load report %+v; want %+v", orcaRes, want)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
logger.Fatalf("timed out waiting for expected ORCA load report")
|
||||
}
|
||||
37
interop/xds/client/Dockerfile
Normal file
37
interop/xds/client/Dockerfile
Normal file
@@ -0,0 +1,37 @@
|
||||
# Copyright 2021 gRPC authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Dockerfile for building the xDS interop client. To build the image, run the
|
||||
# following command from grpc-go directory:
|
||||
# docker build -t <TAG> -f interop/xds/client/Dockerfile .
|
||||
|
||||
FROM golang:1.25-alpine as build
|
||||
|
||||
# Make a grpc-go directory and copy the repo into it.
|
||||
WORKDIR /go/src/grpc-go
|
||||
COPY . .
|
||||
|
||||
# Build a static binary without cgo so that we can copy just the binary in the
|
||||
# final image, and can get rid of Go compiler and gRPC-Go dependencies.
|
||||
RUN cd interop/xds/client && go build -tags osusergo,netgo .
|
||||
|
||||
# Second stage of the build which copies over only the client binary and skips
|
||||
# the Go compiler and gRPC repo from the earlier stage. This significantly
|
||||
# reduces the docker image size.
|
||||
FROM alpine
|
||||
COPY --from=build /go/src/grpc-go/interop/xds/client/client .
|
||||
ENV GRPC_GO_LOG_VERBOSITY_LEVEL=99
|
||||
ENV GRPC_GO_LOG_SEVERITY_LEVEL="info"
|
||||
ENV GRPC_GO_LOG_FORMATTER="json"
|
||||
ENTRYPOINT ["./client"]
|
||||
554
interop/xds/client/client.go
Normal file
554
interop/xds/client/client.go
Normal file
@@ -0,0 +1,554 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2020 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Binary client for xDS interop tests.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/admin"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/credentials/xds"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
_ "google.golang.org/grpc/interop/xds" // to register Custom LB.
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/peer"
|
||||
"google.golang.org/grpc/reflection"
|
||||
"google.golang.org/grpc/stats/opentelemetry"
|
||||
"google.golang.org/grpc/stats/opentelemetry/csm"
|
||||
"google.golang.org/grpc/status"
|
||||
_ "google.golang.org/grpc/xds"
|
||||
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
testpb "google.golang.org/grpc/interop/grpc_testing"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"go.opentelemetry.io/otel/exporters/prometheus"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rpcCfgs.Store([]*rpcConfig{{typ: unaryCall}})
|
||||
}
|
||||
|
||||
type statsWatcherKey struct {
|
||||
startID int32
|
||||
endID int32
|
||||
}
|
||||
|
||||
// rpcInfo contains the rpc type and the hostname where the response is received
|
||||
// from.
|
||||
type rpcInfo struct {
|
||||
typ string
|
||||
hostname string
|
||||
}
|
||||
|
||||
type statsWatcher struct {
|
||||
rpcsByPeer map[string]int32
|
||||
rpcsByType map[string]map[string]int32
|
||||
numFailures int32
|
||||
remainingRPCs int32
|
||||
chanHosts chan *rpcInfo
|
||||
}
|
||||
|
||||
func (watcher *statsWatcher) buildResp() *testpb.LoadBalancerStatsResponse {
|
||||
rpcsByType := make(map[string]*testpb.LoadBalancerStatsResponse_RpcsByPeer, len(watcher.rpcsByType))
|
||||
for t, rpcsByPeer := range watcher.rpcsByType {
|
||||
rpcsByType[t] = &testpb.LoadBalancerStatsResponse_RpcsByPeer{
|
||||
RpcsByPeer: rpcsByPeer,
|
||||
}
|
||||
}
|
||||
|
||||
return &testpb.LoadBalancerStatsResponse{
|
||||
NumFailures: watcher.numFailures + watcher.remainingRPCs,
|
||||
RpcsByPeer: watcher.rpcsByPeer,
|
||||
RpcsByMethod: rpcsByType,
|
||||
}
|
||||
}
|
||||
|
||||
type accumulatedStats struct {
|
||||
mu sync.Mutex
|
||||
numRPCsStartedByMethod map[string]int32
|
||||
numRPCsSucceededByMethod map[string]int32
|
||||
numRPCsFailedByMethod map[string]int32
|
||||
rpcStatusByMethod map[string]map[int32]int32
|
||||
}
|
||||
|
||||
func convertRPCName(in string) string {
|
||||
switch in {
|
||||
case unaryCall:
|
||||
return testpb.ClientConfigureRequest_UNARY_CALL.String()
|
||||
case emptyCall:
|
||||
return testpb.ClientConfigureRequest_EMPTY_CALL.String()
|
||||
}
|
||||
logger.Warningf("unrecognized rpc type: %s", in)
|
||||
return in
|
||||
}
|
||||
|
||||
// copyStatsMap makes a copy of the map.
|
||||
func copyStatsMap(originalMap map[string]int32) map[string]int32 {
|
||||
newMap := make(map[string]int32, len(originalMap))
|
||||
for k, v := range originalMap {
|
||||
newMap[k] = v
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// copyStatsIntMap makes a copy of the map.
|
||||
func copyStatsIntMap(originalMap map[int32]int32) map[int32]int32 {
|
||||
newMap := make(map[int32]int32, len(originalMap))
|
||||
for k, v := range originalMap {
|
||||
newMap[k] = v
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
func (as *accumulatedStats) makeStatsMap() map[string]*testpb.LoadBalancerAccumulatedStatsResponse_MethodStats {
|
||||
m := make(map[string]*testpb.LoadBalancerAccumulatedStatsResponse_MethodStats)
|
||||
for k, v := range as.numRPCsStartedByMethod {
|
||||
m[k] = &testpb.LoadBalancerAccumulatedStatsResponse_MethodStats{RpcsStarted: v}
|
||||
}
|
||||
for k, v := range as.rpcStatusByMethod {
|
||||
if m[k] == nil {
|
||||
m[k] = &testpb.LoadBalancerAccumulatedStatsResponse_MethodStats{}
|
||||
}
|
||||
m[k].Result = copyStatsIntMap(v)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (as *accumulatedStats) buildResp() *testpb.LoadBalancerAccumulatedStatsResponse {
|
||||
as.mu.Lock()
|
||||
defer as.mu.Unlock()
|
||||
return &testpb.LoadBalancerAccumulatedStatsResponse{
|
||||
NumRpcsStartedByMethod: copyStatsMap(as.numRPCsStartedByMethod),
|
||||
NumRpcsSucceededByMethod: copyStatsMap(as.numRPCsSucceededByMethod),
|
||||
NumRpcsFailedByMethod: copyStatsMap(as.numRPCsFailedByMethod),
|
||||
StatsPerMethod: as.makeStatsMap(),
|
||||
}
|
||||
}
|
||||
|
||||
func (as *accumulatedStats) startRPC(rpcType string) {
|
||||
as.mu.Lock()
|
||||
defer as.mu.Unlock()
|
||||
as.numRPCsStartedByMethod[convertRPCName(rpcType)]++
|
||||
}
|
||||
|
||||
func (as *accumulatedStats) finishRPC(rpcType string, err error) {
|
||||
as.mu.Lock()
|
||||
defer as.mu.Unlock()
|
||||
name := convertRPCName(rpcType)
|
||||
if as.rpcStatusByMethod[name] == nil {
|
||||
as.rpcStatusByMethod[name] = make(map[int32]int32)
|
||||
}
|
||||
as.rpcStatusByMethod[name][int32(status.Convert(err).Code())]++
|
||||
if err != nil {
|
||||
as.numRPCsFailedByMethod[name]++
|
||||
return
|
||||
}
|
||||
as.numRPCsSucceededByMethod[name]++
|
||||
}
|
||||
|
||||
var (
|
||||
failOnFailedRPC = flag.Bool("fail_on_failed_rpc", false, "Fail client if any RPCs fail after first success")
|
||||
numChannels = flag.Int("num_channels", 1, "Num of channels")
|
||||
printResponse = flag.Bool("print_response", false, "Write RPC response to stdout")
|
||||
qps = flag.Int("qps", 1, "QPS per channel, for each type of RPC")
|
||||
rpc = flag.String("rpc", "UnaryCall", "Types of RPCs to make, ',' separated string. RPCs can be EmptyCall or UnaryCall. Deprecated: Use Configure RPC to XdsUpdateClientConfigureServiceServer instead.")
|
||||
rpcMetadata = flag.String("metadata", "", "The metadata to send with RPC, in format EmptyCall:key1:value1,UnaryCall:key2:value2. Deprecated: Use Configure RPC to XdsUpdateClientConfigureServiceServer instead.")
|
||||
rpcTimeout = flag.Duration("rpc_timeout", 20*time.Second, "Per RPC timeout")
|
||||
server = flag.String("server", "localhost:8080", "Address of server to connect to")
|
||||
statsPort = flag.Int("stats_port", 8081, "Port to expose peer distribution stats service")
|
||||
secureMode = flag.Bool("secure_mode", false, "If true, retrieve security configuration from the management server. Else, use insecure credentials.")
|
||||
enableCSMObservability = flag.Bool("enable_csm_observability", false, "Whether to enable CSM Observability")
|
||||
requestPayloadSize = flag.Int("request_payload_size", 0, "Ask the server to respond with SimpleResponse.payload.body of the given length (may not be implemented on the server).")
|
||||
responsePayloadSize = flag.Int("response_payload_size", 0, "Ask the server to respond with SimpleResponse.payload.body of the given length (may not be implemented on the server).")
|
||||
|
||||
rpcCfgs atomic.Value
|
||||
|
||||
mu sync.Mutex
|
||||
currentRequestID int32
|
||||
watchers = make(map[statsWatcherKey]*statsWatcher)
|
||||
|
||||
accStats = accumulatedStats{
|
||||
numRPCsStartedByMethod: make(map[string]int32),
|
||||
numRPCsSucceededByMethod: make(map[string]int32),
|
||||
numRPCsFailedByMethod: make(map[string]int32),
|
||||
rpcStatusByMethod: make(map[string]map[int32]int32),
|
||||
}
|
||||
|
||||
// 0 or 1 representing an RPC has succeeded. Use hasRPCSucceeded and
|
||||
// setRPCSucceeded to access in a safe manner.
|
||||
rpcSucceeded uint32
|
||||
|
||||
logger = grpclog.Component("interop")
|
||||
)
|
||||
|
||||
type statsService struct {
|
||||
testgrpc.UnimplementedLoadBalancerStatsServiceServer
|
||||
}
|
||||
|
||||
func hasRPCSucceeded() bool {
|
||||
return atomic.LoadUint32(&rpcSucceeded) > 0
|
||||
}
|
||||
|
||||
func setRPCSucceeded() {
|
||||
atomic.StoreUint32(&rpcSucceeded, 1)
|
||||
}
|
||||
|
||||
// Wait for the next LoadBalancerStatsRequest.GetNumRpcs to start and complete,
|
||||
// and return the distribution of remote peers. This is essentially a clientside
|
||||
// LB reporting mechanism that is designed to be queried by an external test
|
||||
// driver when verifying that the client is distributing RPCs as expected.
|
||||
func (s *statsService) GetClientStats(ctx context.Context, in *testpb.LoadBalancerStatsRequest) (*testpb.LoadBalancerStatsResponse, error) {
|
||||
mu.Lock()
|
||||
watcherKey := statsWatcherKey{currentRequestID, currentRequestID + in.GetNumRpcs()}
|
||||
watcher, ok := watchers[watcherKey]
|
||||
if !ok {
|
||||
watcher = &statsWatcher{
|
||||
rpcsByPeer: make(map[string]int32),
|
||||
rpcsByType: make(map[string]map[string]int32),
|
||||
numFailures: 0,
|
||||
remainingRPCs: in.GetNumRpcs(),
|
||||
chanHosts: make(chan *rpcInfo),
|
||||
}
|
||||
watchers[watcherKey] = watcher
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Duration(in.GetTimeoutSec())*time.Second)
|
||||
defer cancel()
|
||||
|
||||
defer func() {
|
||||
mu.Lock()
|
||||
delete(watchers, watcherKey)
|
||||
mu.Unlock()
|
||||
}()
|
||||
|
||||
// Wait until the requested RPCs have all been recorded or timeout occurs.
|
||||
for {
|
||||
select {
|
||||
case info := <-watcher.chanHosts:
|
||||
if info != nil {
|
||||
watcher.rpcsByPeer[info.hostname]++
|
||||
|
||||
rpcsByPeerForType := watcher.rpcsByType[info.typ]
|
||||
if rpcsByPeerForType == nil {
|
||||
rpcsByPeerForType = make(map[string]int32)
|
||||
watcher.rpcsByType[info.typ] = rpcsByPeerForType
|
||||
}
|
||||
rpcsByPeerForType[info.hostname]++
|
||||
} else {
|
||||
watcher.numFailures++
|
||||
}
|
||||
watcher.remainingRPCs--
|
||||
if watcher.remainingRPCs == 0 {
|
||||
return watcher.buildResp(), nil
|
||||
}
|
||||
case <-ctx.Done():
|
||||
logger.Info("Timed out, returning partial stats")
|
||||
return watcher.buildResp(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *statsService) GetClientAccumulatedStats(context.Context, *testpb.LoadBalancerAccumulatedStatsRequest) (*testpb.LoadBalancerAccumulatedStatsResponse, error) {
|
||||
return accStats.buildResp(), nil
|
||||
}
|
||||
|
||||
type configureService struct {
|
||||
testgrpc.UnimplementedXdsUpdateClientConfigureServiceServer
|
||||
}
|
||||
|
||||
func (s *configureService) Configure(_ context.Context, in *testpb.ClientConfigureRequest) (*testpb.ClientConfigureResponse, error) {
|
||||
rpcsToMD := make(map[testpb.ClientConfigureRequest_RpcType][]string)
|
||||
for _, typ := range in.GetTypes() {
|
||||
rpcsToMD[typ] = nil
|
||||
}
|
||||
for _, md := range in.GetMetadata() {
|
||||
typ := md.GetType()
|
||||
strs, ok := rpcsToMD[typ]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
rpcsToMD[typ] = append(strs, md.GetKey(), md.GetValue())
|
||||
}
|
||||
cfgs := make([]*rpcConfig, 0, len(rpcsToMD))
|
||||
for typ, md := range rpcsToMD {
|
||||
var rpcType string
|
||||
switch typ {
|
||||
case testpb.ClientConfigureRequest_UNARY_CALL:
|
||||
rpcType = unaryCall
|
||||
case testpb.ClientConfigureRequest_EMPTY_CALL:
|
||||
rpcType = emptyCall
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported RPC type: %v", typ)
|
||||
}
|
||||
cfgs = append(cfgs, &rpcConfig{
|
||||
typ: rpcType,
|
||||
md: metadata.Pairs(md...),
|
||||
timeout: in.GetTimeoutSec(),
|
||||
})
|
||||
}
|
||||
rpcCfgs.Store(cfgs)
|
||||
return &testpb.ClientConfigureResponse{}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
unaryCall string = "UnaryCall"
|
||||
emptyCall string = "EmptyCall"
|
||||
)
|
||||
|
||||
func parseRPCTypes(rpcStr string) []string {
|
||||
if len(rpcStr) == 0 {
|
||||
return []string{unaryCall}
|
||||
}
|
||||
|
||||
rpcs := strings.Split(rpcStr, ",")
|
||||
ret := make([]string, 0, len(rpcStr))
|
||||
for _, r := range rpcs {
|
||||
switch r {
|
||||
case unaryCall, emptyCall:
|
||||
ret = append(ret, r)
|
||||
default:
|
||||
flag.PrintDefaults()
|
||||
log.Fatalf("unsupported RPC type: %v", r)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
type rpcConfig struct {
|
||||
typ string
|
||||
md metadata.MD
|
||||
timeout int32
|
||||
}
|
||||
|
||||
// parseRPCMetadata turns EmptyCall:key1:value1 into
|
||||
//
|
||||
// {typ: emptyCall, md: {key1:value1}}.
|
||||
func parseRPCMetadata(rpcMetadataStr string, rpcs []string) []*rpcConfig {
|
||||
rpcMetadataSplit := strings.Split(rpcMetadataStr, ",")
|
||||
rpcsToMD := make(map[string][]string)
|
||||
for _, rm := range rpcMetadataSplit {
|
||||
rmSplit := strings.Split(rm, ":")
|
||||
if len(rmSplit)%2 != 1 {
|
||||
log.Fatalf("invalid metadata config %v, want EmptyCall:key1:value1", rm)
|
||||
}
|
||||
rpcsToMD[rmSplit[0]] = append(rpcsToMD[rmSplit[0]], rmSplit[1:]...)
|
||||
}
|
||||
ret := make([]*rpcConfig, 0, len(rpcs))
|
||||
for _, rpcT := range rpcs {
|
||||
rpcC := &rpcConfig{
|
||||
typ: rpcT,
|
||||
}
|
||||
if md := rpcsToMD[string(rpcT)]; len(md) > 0 {
|
||||
rpcC.md = metadata.Pairs(md...)
|
||||
}
|
||||
ret = append(ret, rpcC)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *enableCSMObservability {
|
||||
exporter, err := prometheus.New()
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to start prometheus exporter: %v", err)
|
||||
}
|
||||
provider := metric.NewMeterProvider(
|
||||
metric.WithReader(exporter),
|
||||
)
|
||||
var addr string
|
||||
var ok bool
|
||||
if addr, ok = os.LookupEnv("OTEL_EXPORTER_PROMETHEUS_HOST"); !ok {
|
||||
addr = ""
|
||||
}
|
||||
var port string
|
||||
if port, ok = os.LookupEnv("OTEL_EXPORTER_PROMETHEUS_PORT"); !ok {
|
||||
port = "9464"
|
||||
}
|
||||
go func() {
|
||||
if err := http.ListenAndServe(addr+":"+port, promhttp.Handler()); err != nil {
|
||||
logger.Fatalf("error listening: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
cleanup := csm.EnableObservability(ctx, opentelemetry.Options{MetricsOptions: opentelemetry.MetricsOptions{MeterProvider: provider}})
|
||||
defer cleanup()
|
||||
}
|
||||
|
||||
rpcCfgs.Store(parseRPCMetadata(*rpcMetadata, parseRPCTypes(*rpc)))
|
||||
|
||||
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *statsPort))
|
||||
if err != nil {
|
||||
logger.Fatalf("failed to listen: %v", err)
|
||||
}
|
||||
s := grpc.NewServer()
|
||||
defer s.Stop()
|
||||
testgrpc.RegisterLoadBalancerStatsServiceServer(s, &statsService{})
|
||||
testgrpc.RegisterXdsUpdateClientConfigureServiceServer(s, &configureService{})
|
||||
reflection.Register(s)
|
||||
cleanup, err := admin.Register(s)
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to register admin: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
go s.Serve(lis)
|
||||
|
||||
creds := insecure.NewCredentials()
|
||||
if *secureMode {
|
||||
var err error
|
||||
creds, err = xds.NewClientCredentials(xds.ClientOptions{FallbackCreds: insecure.NewCredentials()})
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to create xDS credentials: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
clients := make([]testgrpc.TestServiceClient, *numChannels)
|
||||
for i := 0; i < *numChannels; i++ {
|
||||
conn, err := grpc.NewClient(*server, grpc.WithTransportCredentials(creds))
|
||||
if err != nil {
|
||||
logger.Fatalf("grpc.NewClient(%q) = %v", *server, err)
|
||||
}
|
||||
defer conn.Close()
|
||||
clients[i] = testgrpc.NewTestServiceClient(conn)
|
||||
}
|
||||
ticker := time.NewTicker(time.Second / time.Duration(*qps**numChannels))
|
||||
defer ticker.Stop()
|
||||
sendRPCs(clients, ticker)
|
||||
}
|
||||
|
||||
func makeOneRPC(c testgrpc.TestServiceClient, cfg *rpcConfig) (*peer.Peer, *rpcInfo, error) {
|
||||
timeout := *rpcTimeout
|
||||
if cfg.timeout != 0 {
|
||||
timeout = time.Duration(cfg.timeout) * time.Second
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
if len(cfg.md) != 0 {
|
||||
ctx = metadata.NewOutgoingContext(ctx, cfg.md)
|
||||
}
|
||||
info := rpcInfo{typ: cfg.typ}
|
||||
|
||||
var (
|
||||
p peer.Peer
|
||||
header metadata.MD
|
||||
err error
|
||||
)
|
||||
accStats.startRPC(cfg.typ)
|
||||
switch cfg.typ {
|
||||
case unaryCall:
|
||||
sr := &testpb.SimpleRequest{FillServerId: true}
|
||||
if *requestPayloadSize > 0 {
|
||||
sr.Payload = &testpb.Payload{Body: make([]byte, *requestPayloadSize)}
|
||||
}
|
||||
if *responsePayloadSize > 0 {
|
||||
sr.ResponseSize = int32(*responsePayloadSize)
|
||||
}
|
||||
sr.ResponseSize = int32(*responsePayloadSize)
|
||||
var resp *testpb.SimpleResponse
|
||||
resp, err = c.UnaryCall(ctx, sr, grpc.Peer(&p), grpc.Header(&header))
|
||||
// For UnaryCall, also read hostname from response, in case the server
|
||||
// isn't updated to send headers.
|
||||
if resp != nil {
|
||||
info.hostname = resp.Hostname
|
||||
}
|
||||
case emptyCall:
|
||||
_, err = c.EmptyCall(ctx, &testpb.Empty{}, grpc.Peer(&p), grpc.Header(&header))
|
||||
}
|
||||
accStats.finishRPC(cfg.typ, err)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
hosts := header["hostname"]
|
||||
if len(hosts) > 0 {
|
||||
info.hostname = hosts[0]
|
||||
}
|
||||
return &p, &info, err
|
||||
}
|
||||
|
||||
func sendRPCs(clients []testgrpc.TestServiceClient, ticker *time.Ticker) {
|
||||
var i int
|
||||
for range ticker.C {
|
||||
// Get and increment request ID, and save a list of watchers that are
|
||||
// interested in this RPC.
|
||||
mu.Lock()
|
||||
savedRequestID := currentRequestID
|
||||
currentRequestID++
|
||||
savedWatchers := []*statsWatcher{}
|
||||
for key, value := range watchers {
|
||||
if key.startID <= savedRequestID && savedRequestID < key.endID {
|
||||
savedWatchers = append(savedWatchers, value)
|
||||
}
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
// Get the RPC metadata configurations from the Configure RPC.
|
||||
cfgs := rpcCfgs.Load().([]*rpcConfig)
|
||||
|
||||
c := clients[i]
|
||||
for _, cfg := range cfgs {
|
||||
go func(cfg *rpcConfig) {
|
||||
p, info, err := makeOneRPC(c, cfg)
|
||||
|
||||
for _, watcher := range savedWatchers {
|
||||
// This sends an empty string if the RPC failed.
|
||||
watcher.chanHosts <- info
|
||||
}
|
||||
if err != nil && *failOnFailedRPC && hasRPCSucceeded() {
|
||||
logger.Fatalf("RPC failed: %v", err)
|
||||
}
|
||||
if err == nil {
|
||||
setRPCSucceeded()
|
||||
}
|
||||
if *printResponse {
|
||||
if err == nil {
|
||||
if cfg.typ == unaryCall {
|
||||
// Need to keep this format, because some tests are
|
||||
// relying on stdout.
|
||||
fmt.Printf("Greeting: Hello world, this is %s, from %v\n", info.hostname, p.Addr)
|
||||
} else {
|
||||
fmt.Printf("RPC %q, from host %s, addr %v\n", cfg.typ, info.hostname, p.Addr)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("RPC %q, failed with %v\n", cfg.typ, err)
|
||||
}
|
||||
}
|
||||
}(cfg)
|
||||
}
|
||||
i = (i + 1) % len(clients)
|
||||
}
|
||||
}
|
||||
140
interop/xds/custom_lb.go
Normal file
140
interop/xds/custom_lb.go
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2023 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Package xds contains various xds interop helpers for usage in interop tests.
|
||||
package xds
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/balancer/roundrobin"
|
||||
"google.golang.org/grpc/internal/pretty"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/serviceconfig"
|
||||
)
|
||||
|
||||
func init() {
|
||||
balancer.Register(rpcBehaviorBB{})
|
||||
}
|
||||
|
||||
const name = "test.RpcBehaviorLoadBalancer"
|
||||
|
||||
type rpcBehaviorBB struct{}
|
||||
|
||||
func (rpcBehaviorBB) Build(cc balancer.ClientConn, bOpts balancer.BuildOptions) balancer.Balancer {
|
||||
b := &rpcBehaviorLB{
|
||||
ClientConn: cc,
|
||||
}
|
||||
// round_robin child to complete balancer tree with a usable leaf policy and
|
||||
// have RPCs actually work.
|
||||
builder := balancer.Get(roundrobin.Name)
|
||||
if builder == nil {
|
||||
// Shouldn't happen, defensive programming. Registered from import of
|
||||
// roundrobin package.
|
||||
return nil
|
||||
}
|
||||
rr := builder.Build(b, bOpts)
|
||||
if rr == nil {
|
||||
// Shouldn't happen, defensive programming.
|
||||
return nil
|
||||
}
|
||||
b.Balancer = rr
|
||||
return b
|
||||
}
|
||||
|
||||
func (rpcBehaviorBB) ParseConfig(s json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
|
||||
lbCfg := &lbConfig{}
|
||||
if err := json.Unmarshal(s, lbCfg); err != nil {
|
||||
return nil, fmt.Errorf("rpc-behavior-lb: unable to marshal lbConfig: %s, error: %v", string(s), err)
|
||||
}
|
||||
return lbCfg, nil
|
||||
|
||||
}
|
||||
|
||||
func (rpcBehaviorBB) Name() string {
|
||||
return name
|
||||
}
|
||||
|
||||
type lbConfig struct {
|
||||
serviceconfig.LoadBalancingConfig `json:"-"`
|
||||
RPCBehavior string `json:"rpcBehavior,omitempty"`
|
||||
}
|
||||
|
||||
// rpcBehaviorLB is a load balancer that wraps a round robin balancer and
|
||||
// appends the rpc-behavior metadata field to any metadata in pick results based
|
||||
// on what is specified in configuration.
|
||||
type rpcBehaviorLB struct {
|
||||
// embed a ClientConn to wrap only UpdateState() operation
|
||||
balancer.ClientConn
|
||||
// embed a Balancer to wrap only UpdateClientConnState() operation
|
||||
balancer.Balancer
|
||||
|
||||
mu sync.Mutex
|
||||
cfg *lbConfig
|
||||
}
|
||||
|
||||
func (b *rpcBehaviorLB) UpdateClientConnState(s balancer.ClientConnState) error {
|
||||
lbCfg, ok := s.BalancerConfig.(*lbConfig)
|
||||
if !ok {
|
||||
return fmt.Errorf("test.RpcBehaviorLoadBalancer:received config with unexpected type %T: %s", s.BalancerConfig, pretty.ToJSON(s.BalancerConfig))
|
||||
}
|
||||
b.mu.Lock()
|
||||
b.cfg = lbCfg
|
||||
b.mu.Unlock()
|
||||
return b.Balancer.UpdateClientConnState(balancer.ClientConnState{
|
||||
ResolverState: s.ResolverState,
|
||||
})
|
||||
}
|
||||
|
||||
func (b *rpcBehaviorLB) UpdateState(state balancer.State) {
|
||||
b.mu.Lock()
|
||||
rpcBehavior := b.cfg.RPCBehavior
|
||||
b.mu.Unlock()
|
||||
|
||||
b.ClientConn.UpdateState(balancer.State{
|
||||
ConnectivityState: state.ConnectivityState,
|
||||
Picker: newRPCBehaviorPicker(state.Picker, rpcBehavior),
|
||||
})
|
||||
}
|
||||
|
||||
// rpcBehaviorPicker wraps a picker and adds the rpc-behavior metadata field
|
||||
// into the child pick result's metadata.
|
||||
type rpcBehaviorPicker struct {
|
||||
childPicker balancer.Picker
|
||||
rpcBehavior string
|
||||
}
|
||||
|
||||
// Pick appends the rpc-behavior metadata entry to the pick result of the child.
|
||||
func (p *rpcBehaviorPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
|
||||
pr, err := p.childPicker.Pick(info)
|
||||
if err != nil {
|
||||
return balancer.PickResult{}, err
|
||||
}
|
||||
pr.Metadata = metadata.Join(pr.Metadata, metadata.Pairs("rpc-behavior", p.rpcBehavior))
|
||||
return pr, nil
|
||||
}
|
||||
|
||||
func newRPCBehaviorPicker(childPicker balancer.Picker, rpcBehavior string) *rpcBehaviorPicker {
|
||||
return &rpcBehaviorPicker{
|
||||
childPicker: childPicker,
|
||||
rpcBehavior: rpcBehavior,
|
||||
}
|
||||
}
|
||||
135
interop/xds/custom_lb_test.go
Normal file
135
interop/xds/custom_lb_test.go
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2023 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package xds
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/internal"
|
||||
"google.golang.org/grpc/internal/grpctest"
|
||||
"google.golang.org/grpc/internal/stubserver"
|
||||
"google.golang.org/grpc/internal/testutils"
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
testpb "google.golang.org/grpc/interop/grpc_testing"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/resolver"
|
||||
"google.golang.org/grpc/resolver/manual"
|
||||
"google.golang.org/grpc/serviceconfig"
|
||||
)
|
||||
|
||||
var defaultTestTimeout = 5 * time.Second
|
||||
|
||||
type s struct {
|
||||
grpctest.Tester
|
||||
}
|
||||
|
||||
func Test(t *testing.T) {
|
||||
grpctest.RunSubTests(t, s{})
|
||||
}
|
||||
|
||||
// TestCustomLB tests the Custom LB for the interop client. It configures the
|
||||
// custom lb as the top level Load Balancing policy of the channel, then asserts
|
||||
// it can successfully make an RPC and also that the rpc behavior the Custom LB
|
||||
// is configured with makes it's way to the server in metadata.
|
||||
func (s) TestCustomLB(t *testing.T) {
|
||||
errCh := testutils.NewChannel()
|
||||
// Setup a backend which verifies the expected rpc-behavior metadata is
|
||||
// present in the request.
|
||||
backend := &stubserver.StubServer{
|
||||
UnaryCallF: func(ctx context.Context, _ *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
errCh.Send(errors.New("failed to receive metadata"))
|
||||
return &testpb.SimpleResponse{}, nil
|
||||
}
|
||||
rpcBMD := md.Get("rpc-behavior")
|
||||
if len(rpcBMD) != 1 {
|
||||
errCh.Send(fmt.Errorf("received %d values for metadata key \"rpc-behavior\", want 1", len(rpcBMD)))
|
||||
return &testpb.SimpleResponse{}, nil
|
||||
}
|
||||
wantVal := "error-code-0"
|
||||
if rpcBMD[0] != wantVal {
|
||||
errCh.Send(fmt.Errorf("metadata val for key \"rpc-behavior\": got val %v, want val %v", rpcBMD[0], wantVal))
|
||||
return &testpb.SimpleResponse{}, nil
|
||||
}
|
||||
// Success.
|
||||
errCh.Send(nil)
|
||||
return &testpb.SimpleResponse{}, nil
|
||||
},
|
||||
}
|
||||
if err := backend.StartServer(); err != nil {
|
||||
t.Fatalf("Failed to start backend: %v", err)
|
||||
}
|
||||
t.Logf("Started good TestService backend at: %q", backend.Address)
|
||||
defer backend.Stop()
|
||||
|
||||
lbCfgJSON := `{
|
||||
"loadBalancingConfig": [
|
||||
{
|
||||
"test.RpcBehaviorLoadBalancer": {
|
||||
"rpcBehavior": "error-code-0"
|
||||
}
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
sc := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(lbCfgJSON)
|
||||
mr := manual.NewBuilderWithScheme("customlb-e2e")
|
||||
defer mr.Close()
|
||||
mr.InitialState(resolver.State{
|
||||
Addresses: []resolver.Address{
|
||||
{Addr: backend.Address},
|
||||
},
|
||||
ServiceConfig: sc,
|
||||
})
|
||||
|
||||
cc, err := grpc.NewClient(mr.Scheme()+":///", grpc.WithResolvers(mr), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
t.Fatalf("grpc.NewClient() failed: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
|
||||
defer cancel()
|
||||
testServiceClient := testgrpc.NewTestServiceClient(cc)
|
||||
|
||||
// Make a Unary RPC. This RPC should be successful due to the round_robin
|
||||
// leaf balancer. Also, the custom load balancer should inject the
|
||||
// "rpc-behavior" string it is configured with into the metadata sent to
|
||||
// server.
|
||||
if _, err := testServiceClient.UnaryCall(ctx, &testpb.SimpleRequest{}); err != nil {
|
||||
t.Fatalf("EmptyCall() failed: %v", err)
|
||||
}
|
||||
|
||||
val, err := errCh.Receive(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("error receiving from errCh: %v", err)
|
||||
}
|
||||
|
||||
// Should receive nil on the error channel which implies backend verified it
|
||||
// correctly received the correct "rpc-behavior" metadata.
|
||||
if err, ok := val.(error); ok {
|
||||
t.Fatalf("error in backend verifications on metadata received: %v", err)
|
||||
}
|
||||
}
|
||||
56
interop/xds/go.mod
Normal file
56
interop/xds/go.mod
Normal file
@@ -0,0 +1,56 @@
|
||||
module google.golang.org/grpc/interop/xds
|
||||
|
||||
go 1.25.0
|
||||
|
||||
replace google.golang.org/grpc => ../..
|
||||
|
||||
require (
|
||||
github.com/prometheus/client_golang v1.24.1
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.67.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.45.0
|
||||
google.golang.org/grpc v1.83.0
|
||||
)
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.25.3 // indirect
|
||||
cloud.google.com/go/auth v0.23.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.35.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.39.1-0.20260819172001-e6e3fd93e4be // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect
|
||||
github.com/felixge/httpsnoop v1.1.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
|
||||
github.com/go-logr/logr v1.4.4 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.21 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.23.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.70.1 // indirect
|
||||
github.com/prometheus/otlptranslator v1.0.0 // indirect
|
||||
github.com/prometheus/procfs v0.21.1 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.8.1 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.45.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0 // indirect
|
||||
go.opentelemetry.io/otel v1.45.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.45.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.45.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.45.0 // indirect
|
||||
golang.org/x/crypto v0.55.0 // indirect
|
||||
golang.org/x/net v0.58.0 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
golang.org/x/sync v0.22.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
golang.org/x/text v0.41.0 // indirect
|
||||
google.golang.org/api v0.293.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260817212433-ac3dfec99bb1 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 // indirect
|
||||
google.golang.org/protobuf v1.36.12 // indirect
|
||||
)
|
||||
119
interop/xds/go.sum
Normal file
119
interop/xds/go.sum
Normal file
@@ -0,0 +1,119 @@
|
||||
cel.dev/expr v0.25.3 h1:A2jO8jwOugrrovveCWfj0KEZOfqiLgAcwjpHPhzIGw0=
|
||||
cel.dev/expr v0.25.3/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
|
||||
cloud.google.com/go/auth v0.23.1 h1:1tPpBPG02lQHmoiAvs9egyCASqXP0xgobptjZzov/Jg=
|
||||
cloud.google.com/go/auth v0.23.1/go.mod h1:4DhBRcqvtljQN3dJ57qtqbib5ZGCYE5f2crfiiC2EM0=
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.35.0 h1:bN1gA3of5bXtbnLsRPrwfmbbe7A5UWFlcTHseujLnpc=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.35.0/go.mod h1:Yj5vHEz/aAepZGliRJsA6uvHAVAQyEwajq9ORCHPxzM=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik=
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
|
||||
github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.39.1-0.20260819172001-e6e3fd93e4be h1:SWe0x6yfglnxuvOiYgTTnNq7QD/yvqthOh1RBI8Bj8w=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.39.1-0.20260819172001-e6e3fd93e4be/go.mod h1:PYEOlng9XcrulfyWpm49jECTPV0LT4q8cO7fLW/xwgk=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0=
|
||||
github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc=
|
||||
github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
|
||||
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.21 h1:OFdQ3tnCX/zaQ0Cedur3D3z7kI6HiLX9g3TiAN4/DFU=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.21/go.mod h1:L3D/IQExI6LqEjBdXcZQ1WluSgigQmSwBboFstVPM4w=
|
||||
github.com/googleapis/gax-go/v2 v2.23.0 h1:Tchl7qkvE7Ip3y+ztvNufYFvkfqTe7NfLTYGIdJRLuE=
|
||||
github.com/googleapis/gax-go/v2 v2.23.0/go.mod h1:rBQKOVJCdb8IFEzg+FCwlt1LP/xMDGuqUXhUG+XMXEg=
|
||||
github.com/klauspost/compress v1.19.1 h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk=
|
||||
github.com/klauspost/compress v1.19.1/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU=
|
||||
github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY=
|
||||
github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc=
|
||||
github.com/prometheus/otlptranslator v1.0.0 h1:s0LJW/iN9dkIH+EnhiD3BlkkP5QVIUVEoIwkU+A6qos=
|
||||
github.com/prometheus/otlptranslator v1.0.0/go.mod h1:vRYWnXvI6aWGpsdY/mOT/cbeVRBlPWtBNDb7kGR3uKM=
|
||||
github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI=
|
||||
github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY=
|
||||
github.com/spiffe/go-spiffe/v2 v2.8.1 h1:eXZMLsu+3MLEPJyGJkolqtVrteZfQdUpOWj6LTiDl/E=
|
||||
github.com/spiffe/go-spiffe/v2 v2.8.1/go.mod h1:47Q0Q9/AqGha8QLHp+kxpH4Wca7X7EnOtlIJy3mxZ3U=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.45.0 h1:9jR0ZPRok9ryaOQ2Wx8rg5F7Aon59mxrqbVI60/vlBk=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.45.0/go.mod h1:VSme3o2fvSg5bVg0dRzyHaj4Z5EVhG+g2Fde6LKzmQA=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0 h1:LMuyCAyfalSjDyjdC65nK6N0zoTT63+E/u95X0JovZI=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0/go.mod h1:085m8qbm4hgc8rZWGDEa4vmyyo2c3nPxUslYUKUIU04=
|
||||
go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
|
||||
go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.67.0 h1:7IefDa35e6V3NoiqIeLDMDxMFyZDk5qcoC0Ax4cC16E=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.67.0/go.mod h1:nsPI1awTg5Vmg1YrommL2mVarVGlqc4yXOoKAkPRD0c=
|
||||
go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M=
|
||||
go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s=
|
||||
go.opentelemetry.io/otel/metric/x v0.67.0 h1:PcicCNZFkZ4bXfSooXdo3WN7RBOVOtjVdo1wD358Uns=
|
||||
go.opentelemetry.io/otel/metric/x v0.67.0/go.mod h1:FBjCWZe6wgcqxcMtjdGiClDKXb2YxxXii0CXftE4QtI=
|
||||
go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw=
|
||||
go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA=
|
||||
go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag=
|
||||
go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
|
||||
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
|
||||
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
|
||||
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
|
||||
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
|
||||
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
|
||||
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/api v0.293.0 h1:p9XIWOf63U4OgYx120ZwVU8+vl4XTPmWfgVPnmOAS9w=
|
||||
google.golang.org/api v0.293.0/go.mod h1:6n5tjEB1gzwniZTepZ0g5u+wM7Bof5GeULCx/zh8ZE0=
|
||||
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0=
|
||||
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260817212433-ac3dfec99bb1 h1:lrupDmKL3p5kEX1M92oan027eCKcouzjuPbH6YBK+Rs=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260817212433-ac3dfec99bb1/go.mod h1:q/3oV3jAi5vwelxsVAprMBC8BcM2zmNe+IjRGd+9/ks=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 h1:utmqiUzZAEINbLmRyAt1QjVkMyLQ+lvIwCpLaB1rZfw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
|
||||
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
37
interop/xds/server/Dockerfile
Normal file
37
interop/xds/server/Dockerfile
Normal file
@@ -0,0 +1,37 @@
|
||||
# Copyright 2021 gRPC authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Dockerfile for building the xDS interop server. To build the image, run the
|
||||
# following command from grpc-go directory:
|
||||
# docker build -t <TAG> -f interop/xds/server/Dockerfile .
|
||||
|
||||
FROM golang:1.25-alpine as build
|
||||
|
||||
# Make a grpc-go directory and copy the repo into it.
|
||||
WORKDIR /go/src/grpc-go
|
||||
COPY . .
|
||||
|
||||
# Build a static binary without cgo so that we can copy just the binary in the
|
||||
# final image, and can get rid of the Go compiler and gRPC-Go dependencies.
|
||||
RUN cd interop/xds/server && go build -tags osusergo,netgo .
|
||||
|
||||
# Second stage of the build which copies over only the server binary and skips
|
||||
# the Go compiler and gRPC repo from the earlier stage. This significantly
|
||||
# reduces the docker image size.
|
||||
FROM alpine
|
||||
COPY --from=build /go/src/grpc-go/interop/xds/server/server .
|
||||
ENV GRPC_GO_LOG_VERBOSITY_LEVEL=99
|
||||
ENV GRPC_GO_LOG_SEVERITY_LEVEL="info"
|
||||
ENV GRPC_GO_LOG_FORMATTER="json"
|
||||
ENTRYPOINT ["./server"]
|
||||
342
interop/xds/server/server.go
Normal file
342
interop/xds/server/server.go
Normal file
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2021 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Binary server is the server used for xDS interop tests.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/admin"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/health"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/reflection"
|
||||
"google.golang.org/grpc/stats/opentelemetry"
|
||||
"google.golang.org/grpc/stats/opentelemetry/csm"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/grpc/xds"
|
||||
|
||||
xdscreds "google.golang.org/grpc/credentials/xds"
|
||||
healthgrpc "google.golang.org/grpc/health/grpc_health_v1"
|
||||
healthpb "google.golang.org/grpc/health/grpc_health_v1"
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
testpb "google.golang.org/grpc/interop/grpc_testing"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"go.opentelemetry.io/otel/exporters/prometheus"
|
||||
"go.opentelemetry.io/otel/sdk/metric"
|
||||
)
|
||||
|
||||
var (
|
||||
port = flag.Int("port", 8080, "Listening port for test service")
|
||||
maintenancePort = flag.Int("maintenance_port", 8081, "Listening port for maintenance services like health, reflection, channelz etc when -secure_mode is true. When -secure_mode is false, all these services will be registered on -port")
|
||||
serverID = flag.String("server_id", "go_server", "Server ID included in response")
|
||||
secureMode = flag.Bool("secure_mode", false, "If true, retrieve security configuration from the management server. Else, use insecure credentials.")
|
||||
hostNameOverride = flag.String("host_name_override", "", "If set, use this as the hostname instead of the real hostname")
|
||||
enableCSMObservability = flag.Bool("enable_csm_observability", false, "Whether to enable CSM Observability")
|
||||
|
||||
logger = grpclog.Component("interop")
|
||||
)
|
||||
|
||||
const (
|
||||
rpcBehaviorMDKey = "rpc-behavior"
|
||||
grpcPreviousRPCAttemptsMDKey = "grpc-previous-rpc-attempts"
|
||||
sleepPfx = "sleep-"
|
||||
keepOpenVal = "keep-open"
|
||||
errorCodePfx = "error-code-"
|
||||
succeedOnRetryPfx = "succeed-on-retry-attempt-"
|
||||
hostnamePfx = "hostname="
|
||||
)
|
||||
|
||||
func getHostname() string {
|
||||
if *hostNameOverride != "" {
|
||||
return *hostNameOverride
|
||||
}
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to get hostname: %v", err)
|
||||
}
|
||||
return hostname
|
||||
}
|
||||
|
||||
// testServiceImpl provides an implementation of the TestService defined in
|
||||
// grpc.testing package.
|
||||
type testServiceImpl struct {
|
||||
testgrpc.UnimplementedTestServiceServer
|
||||
hostname string
|
||||
serverID string
|
||||
}
|
||||
|
||||
func (s *testServiceImpl) EmptyCall(ctx context.Context, _ *testpb.Empty) (*testpb.Empty, error) {
|
||||
grpc.SetHeader(ctx, metadata.Pairs("hostname", s.hostname))
|
||||
return &testpb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *testServiceImpl) UnaryCall(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
|
||||
response := &testpb.SimpleResponse{ServerId: s.serverID, Hostname: s.hostname}
|
||||
if in.ResponseSize > 0 {
|
||||
response.Payload = &testpb.Payload{
|
||||
Body: make([]byte, in.ResponseSize),
|
||||
}
|
||||
}
|
||||
|
||||
forLoop:
|
||||
for _, headerVal := range getRPCBehaviorMetadata(ctx) {
|
||||
// A value can have a prefix "hostname=<string>" followed by a space.
|
||||
// In that case, the rest of the value should only be applied
|
||||
// if the specified hostname matches the server's hostname.
|
||||
if strings.HasPrefix(headerVal, hostnamePfx) {
|
||||
splitVal := strings.Split(headerVal, " ")
|
||||
if len(splitVal) <= 1 {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid format for rpc-behavior header %v, must be 'hostname=<string> <header>=<value>' instead", headerVal)
|
||||
}
|
||||
|
||||
if s.hostname != splitVal[0][len(hostnamePfx):] {
|
||||
continue forLoop
|
||||
}
|
||||
headerVal = splitVal[1]
|
||||
}
|
||||
|
||||
switch {
|
||||
// If the value matches "sleep-<int>", the server should wait
|
||||
// the specified number of seconds before resuming
|
||||
// behavior matching and RPC processing.
|
||||
case strings.HasPrefix(headerVal, sleepPfx):
|
||||
sleep, err := strconv.Atoi(headerVal[len(sleepPfx):])
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid format for rpc-behavior header %v, must be 'sleep-<int>' instead", headerVal)
|
||||
}
|
||||
time.Sleep(time.Duration(sleep) * time.Second)
|
||||
|
||||
// If the value matches "keep-open", the server should
|
||||
// never respond to the request and behavior matching ends.
|
||||
case strings.HasPrefix(headerVal, keepOpenVal):
|
||||
<-ctx.Done()
|
||||
return nil, nil
|
||||
|
||||
// If the value matches "error-code-<int>", the server should
|
||||
// respond with the specified status code and behavior matching ends.
|
||||
case strings.HasPrefix(headerVal, errorCodePfx):
|
||||
code, err := strconv.Atoi(headerVal[len(errorCodePfx):])
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid format for rpc-behavior header %v, must be 'error-code-<int>' instead", headerVal)
|
||||
}
|
||||
return nil, status.Errorf(codes.Code(code), "rpc failed as per the rpc-behavior header value: %v", headerVal)
|
||||
|
||||
// If the value matches "success-on-retry-attempt-<int>", and the
|
||||
// value of the "grpc-previous-rpc-attempts" metadata field is equal to
|
||||
// the specified number, the normal RPC processing should resume
|
||||
// and behavior matching ends.
|
||||
case strings.HasPrefix(headerVal, succeedOnRetryPfx):
|
||||
wantRetry, err := strconv.Atoi(headerVal[len(succeedOnRetryPfx):])
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid format for rpc-behavior header %v, must be 'success-on-retry-attempt-<int>' instead", headerVal)
|
||||
}
|
||||
|
||||
mdRetry := getMetadataValues(ctx, grpcPreviousRPCAttemptsMDKey)
|
||||
curRetry, err := strconv.Atoi(mdRetry[0])
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid format for grpc-previous-rpc-attempts header: %v", mdRetry[0])
|
||||
}
|
||||
|
||||
if curRetry == wantRetry {
|
||||
break forLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grpc.SetHeader(ctx, metadata.Pairs("hostname", s.hostname))
|
||||
return response, status.Error(codes.OK, "")
|
||||
}
|
||||
|
||||
func getRPCBehaviorMetadata(ctx context.Context) []string {
|
||||
mdRPCBehavior := getMetadataValues(ctx, rpcBehaviorMDKey)
|
||||
var rpcBehaviorMetadata []string
|
||||
for _, mdVal := range mdRPCBehavior {
|
||||
splitVals := strings.Split(mdVal, ",")
|
||||
|
||||
for _, val := range splitVals {
|
||||
headerVal := strings.TrimSpace(val)
|
||||
if headerVal == "" {
|
||||
continue
|
||||
}
|
||||
rpcBehaviorMetadata = append(rpcBehaviorMetadata, headerVal)
|
||||
}
|
||||
}
|
||||
return rpcBehaviorMetadata
|
||||
}
|
||||
|
||||
func getMetadataValues(ctx context.Context, metadataKey string) []string {
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
logger.Error("Failed to retrieve metadata from incoming RPC context")
|
||||
return nil
|
||||
}
|
||||
return md.Get(metadataKey)
|
||||
}
|
||||
|
||||
// xdsUpdateHealthServiceImpl provides an implementation of the
|
||||
// XdsUpdateHealthService defined in grpc.testing package.
|
||||
type xdsUpdateHealthServiceImpl struct {
|
||||
testgrpc.UnimplementedXdsUpdateHealthServiceServer
|
||||
healthServer *health.Server
|
||||
}
|
||||
|
||||
func (x *xdsUpdateHealthServiceImpl) SetServing(_ context.Context, _ *testpb.Empty) (*testpb.Empty, error) {
|
||||
x.healthServer.SetServingStatus("", healthpb.HealthCheckResponse_SERVING)
|
||||
return &testpb.Empty{}, nil
|
||||
|
||||
}
|
||||
|
||||
func (x *xdsUpdateHealthServiceImpl) SetNotServing(_ context.Context, _ *testpb.Empty) (*testpb.Empty, error) {
|
||||
x.healthServer.SetServingStatus("", healthpb.HealthCheckResponse_NOT_SERVING)
|
||||
return &testpb.Empty{}, nil
|
||||
}
|
||||
|
||||
func xdsServingModeCallback(addr net.Addr, args xds.ServingModeChangeArgs) {
|
||||
logger.Infof("Serving mode callback for xDS server at %q invoked with mode: %q, err: %v", addr.String(), args.Mode, args.Err)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *enableCSMObservability {
|
||||
exporter, err := prometheus.New()
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to start prometheus exporter: %v", err)
|
||||
}
|
||||
var addr string
|
||||
var ok bool
|
||||
if addr, ok = os.LookupEnv("OTEL_EXPORTER_PROMETHEUS_HOST"); !ok {
|
||||
addr = ""
|
||||
}
|
||||
var port string
|
||||
if port, ok = os.LookupEnv("OTEL_EXPORTER_PROMETHEUS_PORT"); !ok {
|
||||
port = "9464"
|
||||
}
|
||||
go func() {
|
||||
if err := http.ListenAndServe(addr+":"+port, promhttp.Handler()); err != nil {
|
||||
logger.Fatalf("error listening: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
provider := metric.NewMeterProvider(
|
||||
metric.WithReader(exporter),
|
||||
)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
cleanup := csm.EnableObservability(ctx, opentelemetry.Options{MetricsOptions: opentelemetry.MetricsOptions{MeterProvider: provider}})
|
||||
defer cleanup()
|
||||
}
|
||||
|
||||
if *secureMode && *port == *maintenancePort {
|
||||
logger.Fatal("-port and -maintenance_port must be different when -secure_mode is set")
|
||||
}
|
||||
|
||||
testService := &testServiceImpl{hostname: getHostname(), serverID: *serverID}
|
||||
healthServer := health.NewServer()
|
||||
updateHealthService := &xdsUpdateHealthServiceImpl{healthServer: healthServer}
|
||||
|
||||
// If -secure_mode is not set, expose all services on -port with a regular
|
||||
// gRPC server.
|
||||
if !*secureMode {
|
||||
addr := fmt.Sprintf(":%d", *port)
|
||||
lis, err := net.Listen("tcp4", addr)
|
||||
if err != nil {
|
||||
logger.Fatalf("net.Listen(%s) failed: %v", addr, err)
|
||||
}
|
||||
|
||||
server := grpc.NewServer()
|
||||
testgrpc.RegisterTestServiceServer(server, testService)
|
||||
healthServer.SetServingStatus("", healthpb.HealthCheckResponse_SERVING)
|
||||
healthgrpc.RegisterHealthServer(server, healthServer)
|
||||
testgrpc.RegisterXdsUpdateHealthServiceServer(server, updateHealthService)
|
||||
reflection.Register(server)
|
||||
cleanup, err := admin.Register(server)
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to register admin services: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
if err := server.Serve(lis); err != nil {
|
||||
logger.Errorf("Serve() failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create a listener on -port to expose the test service.
|
||||
addr := fmt.Sprintf(":%d", *port)
|
||||
testLis, err := net.Listen("tcp4", addr)
|
||||
if err != nil {
|
||||
logger.Fatalf("net.Listen(%s) failed: %v", addr, err)
|
||||
}
|
||||
|
||||
// Create server-side xDS credentials with a plaintext fallback.
|
||||
creds, err := xdscreds.NewServerCredentials(xdscreds.ServerOptions{FallbackCreds: insecure.NewCredentials()})
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to create xDS credentials: %v", err)
|
||||
}
|
||||
|
||||
// Create an xDS enabled gRPC server, register the test service
|
||||
// implementation and start serving.
|
||||
testServer, err := xds.NewGRPCServer(grpc.Creds(creds), xds.ServingModeCallback(xdsServingModeCallback))
|
||||
if err != nil {
|
||||
logger.Fatal("Failed to create an xDS enabled gRPC server: %v", err)
|
||||
}
|
||||
testgrpc.RegisterTestServiceServer(testServer, testService)
|
||||
go func() {
|
||||
if err := testServer.Serve(testLis); err != nil {
|
||||
logger.Errorf("test server Serve() failed: %v", err)
|
||||
}
|
||||
}()
|
||||
defer testServer.Stop()
|
||||
|
||||
// Create a listener on -maintenance_port to expose other services.
|
||||
addr = fmt.Sprintf(":%d", *maintenancePort)
|
||||
maintenanceLis, err := net.Listen("tcp4", addr)
|
||||
if err != nil {
|
||||
logger.Fatalf("net.Listen(%s) failed: %v", addr, err)
|
||||
}
|
||||
|
||||
// Create a regular gRPC server and register the maintenance services on
|
||||
// it and start serving.
|
||||
maintenanceServer := grpc.NewServer()
|
||||
healthServer.SetServingStatus("", healthpb.HealthCheckResponse_SERVING)
|
||||
healthgrpc.RegisterHealthServer(maintenanceServer, healthServer)
|
||||
testgrpc.RegisterXdsUpdateHealthServiceServer(maintenanceServer, updateHealthService)
|
||||
reflection.Register(maintenanceServer)
|
||||
cleanup, err := admin.Register(maintenanceServer)
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to register admin services: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
if err := maintenanceServer.Serve(maintenanceLis); err != nil {
|
||||
logger.Errorf("maintenance server Serve() failed: %v", err)
|
||||
}
|
||||
}
|
||||
152
interop/xds_federation/client.go
Normal file
152
interop/xds_federation/client.go
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2014 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Binary client is an interop client.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/google"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/interop"
|
||||
|
||||
_ "google.golang.org/grpc/balancer/grpclb" // Register the grpclb load balancing policy.
|
||||
_ "google.golang.org/grpc/balancer/rls" // Register the RLS load balancing policy.
|
||||
_ "google.golang.org/grpc/xds/googledirectpath" // Register xDS resolver required for c2p directpath.
|
||||
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
)
|
||||
|
||||
const (
|
||||
computeEngineCredsName = "compute_engine_channel_creds"
|
||||
insecureCredsName = "INSECURE_CREDENTIALS"
|
||||
)
|
||||
|
||||
var (
|
||||
serverURIs = flag.String("server_uris", "", "Comma-separated list of sever URIs to make RPCs to")
|
||||
credentialsTypes = flag.String("credentials_types", "", "Comma-separated list of credentials, each entry is used for the server of the corresponding index in server_uris. Supported values: compute_engine_channel_creds, INSECURE_CREDENTIALS")
|
||||
soakIterations = flag.Int("soak_iterations", 10, "The number of iterations to use for the two soak tests: rpc_soak and channel_soak")
|
||||
soakMaxFailures = flag.Int("soak_max_failures", 0, "The number of iterations in soak tests that are allowed to fail (either due to non-OK status code or exceeding the per-iteration max acceptable latency).")
|
||||
soakPerIterationMaxAcceptableLatencyMs = flag.Int("soak_per_iteration_max_acceptable_latency_ms", 1000, "The number of milliseconds a single iteration in the two soak tests (rpc_soak and channel_soak) should take.")
|
||||
soakOverallTimeoutSeconds = flag.Int("soak_overall_timeout_seconds", 10, "The overall number of seconds after which a soak test should stop and fail, if the desired number of iterations have not yet completed.")
|
||||
soakMinTimeMsBetweenRPCs = flag.Int("soak_min_time_ms_between_rpcs", 0, "The minimum time in milliseconds between consecutive RPCs in a soak test (rpc_soak or channel_soak), useful for limiting QPS")
|
||||
soakRequestSize = flag.Int("soak_request_size", 271828, "The request size in a soak RPC. The default value is set based on the interop large unary test case.")
|
||||
soakResponseSize = flag.Int("soak_response_size", 314159, "The response size in a soak RPC. The default value is set based on the interop large unary test case.")
|
||||
soakNumThreads = flag.Int("soak_num_threads", 1, "The number of threads for concurrent execution of the soak tests (rpc_soak or channel_soak). The default value is set based on the interop large unary test case.")
|
||||
testCase = flag.String("test_case", "rpc_soak",
|
||||
`Configure different test cases. Valid options are:
|
||||
rpc_soak: sends --soak_iterations large_unary RPCs;
|
||||
channel_soak: sends --soak_iterations RPCs, rebuilding the channel each time`)
|
||||
|
||||
logger = grpclog.Component("interop")
|
||||
)
|
||||
|
||||
type clientConfig struct {
|
||||
conn *grpc.ClientConn
|
||||
tc testgrpc.TestServiceClient
|
||||
opts []grpc.DialOption
|
||||
uri string
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
// validate flags
|
||||
uris := strings.Split(*serverURIs, ",")
|
||||
creds := strings.Split(*credentialsTypes, ",")
|
||||
if len(uris) != len(creds) {
|
||||
logger.Fatalf("Number of entries in --server_uris (%d) != number of entries in --credentials_types (%d)", len(uris), len(creds))
|
||||
}
|
||||
for _, c := range creds {
|
||||
if c != computeEngineCredsName && c != insecureCredsName {
|
||||
logger.Fatalf("Unsupported credentials type: %v", c)
|
||||
}
|
||||
}
|
||||
var clients []clientConfig
|
||||
for i := range uris {
|
||||
var opts []grpc.DialOption
|
||||
switch creds[i] {
|
||||
case computeEngineCredsName:
|
||||
opts = append(opts, grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()))
|
||||
case insecureCredsName:
|
||||
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
}
|
||||
cc, err := grpc.NewClient(uris[i], opts...)
|
||||
if err != nil {
|
||||
logger.Fatalf("grpc.NewClient(%q) = %v", uris[i], err)
|
||||
}
|
||||
defer cc.Close()
|
||||
clients = append(clients, clientConfig{
|
||||
conn: cc,
|
||||
tc: testgrpc.NewTestServiceClient(cc),
|
||||
opts: opts,
|
||||
uri: uris[i],
|
||||
})
|
||||
}
|
||||
|
||||
// run soak tests with the different clients
|
||||
logger.Infof("Clients running with test case %q", *testCase)
|
||||
var wg sync.WaitGroup
|
||||
var channelForTest func() (*grpc.ClientConn, func())
|
||||
ctx := context.Background()
|
||||
for i := range clients {
|
||||
wg.Add(1)
|
||||
go func(c clientConfig) {
|
||||
ctxWithDeadline, cancel := context.WithTimeout(ctx, time.Duration(*soakOverallTimeoutSeconds)*time.Second)
|
||||
defer cancel()
|
||||
switch *testCase {
|
||||
case "rpc_soak":
|
||||
channelForTest = func() (*grpc.ClientConn, func()) { return c.conn, func() {} }
|
||||
case "channel_soak":
|
||||
channelForTest = func() (*grpc.ClientConn, func()) {
|
||||
cc, err := grpc.NewClient(c.uri, c.opts...)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create shared channel: %v", err)
|
||||
}
|
||||
return cc, func() { cc.Close() }
|
||||
}
|
||||
default:
|
||||
logger.Fatal("Unsupported test case: ", *testCase)
|
||||
}
|
||||
soakConfig := interop.SoakTestConfig{
|
||||
RequestSize: *soakRequestSize,
|
||||
ResponseSize: *soakResponseSize,
|
||||
PerIterationMaxAcceptableLatency: time.Duration(*soakPerIterationMaxAcceptableLatencyMs) * time.Millisecond,
|
||||
MinTimeBetweenRPCs: time.Duration(*soakMinTimeMsBetweenRPCs) * time.Millisecond,
|
||||
OverallTimeout: time.Duration(*soakOverallTimeoutSeconds) * time.Second,
|
||||
ServerAddr: c.uri,
|
||||
NumWorkers: *soakNumThreads,
|
||||
Iterations: *soakIterations,
|
||||
MaxFailures: *soakMaxFailures,
|
||||
ChannelForTest: channelForTest,
|
||||
}
|
||||
interop.DoSoakTest(ctxWithDeadline, soakConfig)
|
||||
logger.Infof("%s test done for server: %s", *testCase, c.uri)
|
||||
wg.Done()
|
||||
}(clients[i])
|
||||
}
|
||||
wg.Wait()
|
||||
logger.Infoln("All clients done!")
|
||||
}
|
||||
Reference in New Issue
Block a user