diff --git a/CMakeLists.txt b/CMakeLists.txt index 992ef86b8df..bb91cf8b6d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13604,6 +13604,10 @@ add_executable(http2_client ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.h test/cpp/interop/http2_client.cc third_party/googletest/googletest/src/gtest-all.cc third_party/googletest/googlemock/src/gmock-all.cc @@ -14103,7 +14107,12 @@ add_executable(interop_client ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.h test/core/security/oauth2_utils.cc + test/cpp/interop/backend_metrics_lb_policy.cc test/cpp/interop/client.cc test/cpp/interop/client_helper.cc test/cpp/interop/interop_client.cc @@ -14156,6 +14165,10 @@ add_executable(interop_server ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/test.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.h test/cpp/interop/interop_server.cc test/cpp/interop/interop_server_bootstrap.cc test/cpp/interop/server_helper.cc @@ -16905,6 +16918,10 @@ add_executable(qps_json_driver ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/worker_service.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/worker_service.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/worker_service.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.h test/cpp/qps/benchmark_config.cc test/cpp/qps/client_async.cc test/cpp/qps/client_callback.cc @@ -16980,6 +16997,10 @@ add_executable(qps_worker ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/worker_service.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/worker_service.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/worker_service.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.h test/cpp/qps/client_async.cc test/cpp/qps/client_callback.cc test/cpp/qps/client_sync.cc @@ -24342,6 +24363,10 @@ add_executable(xds_interop_client ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/csds.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/csds.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/csds.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/percent.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/percent.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/percent.pb.h @@ -24417,6 +24442,10 @@ add_executable(xds_interop_server ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/csds.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/csds.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/csds.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/percent.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/percent.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/percent.pb.h diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index 1049d1ec704..744fd06f252 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -8714,6 +8714,7 @@ targets: - src/proto/grpc/testing/empty.proto - src/proto/grpc/testing/messages.proto - src/proto/grpc/testing/test.proto + - src/proto/grpc/testing/xds/v3/orca_load_report.proto - test/cpp/interop/http2_client.cc deps: - grpc++_test_config @@ -8960,13 +8961,16 @@ targets: language: c++ headers: - test/core/security/oauth2_utils.h + - test/cpp/interop/backend_metrics_lb_policy.h - test/cpp/interop/client_helper.h - test/cpp/interop/interop_client.h src: - src/proto/grpc/testing/empty.proto - src/proto/grpc/testing/messages.proto - src/proto/grpc/testing/test.proto + - src/proto/grpc/testing/xds/v3/orca_load_report.proto - test/core/security/oauth2_utils.cc + - test/cpp/interop/backend_metrics_lb_policy.cc - test/cpp/interop/client.cc - test/cpp/interop/client_helper.cc - test/cpp/interop/interop_client.cc @@ -8983,6 +8987,7 @@ targets: - src/proto/grpc/testing/empty.proto - src/proto/grpc/testing/messages.proto - src/proto/grpc/testing/test.proto + - src/proto/grpc/testing/xds/v3/orca_load_report.proto - test/cpp/interop/interop_server.cc - test/cpp/interop/interop_server_bootstrap.cc - test/cpp/interop/server_helper.cc @@ -10191,6 +10196,7 @@ targets: - src/proto/grpc/testing/report_qps_scenario_service.proto - src/proto/grpc/testing/stats.proto - src/proto/grpc/testing/worker_service.proto + - src/proto/grpc/testing/xds/v3/orca_load_report.proto - test/cpp/qps/benchmark_config.cc - test/cpp/qps/client_async.cc - test/cpp/qps/client_callback.cc @@ -10228,6 +10234,7 @@ targets: - src/proto/grpc/testing/payloads.proto - src/proto/grpc/testing/stats.proto - src/proto/grpc/testing/worker_service.proto + - src/proto/grpc/testing/xds/v3/orca_load_report.proto - test/cpp/qps/client_async.cc - test/cpp/qps/client_callback.cc - test/cpp/qps/client_sync.cc @@ -13326,6 +13333,7 @@ targets: - src/proto/grpc/testing/xds/v3/base.proto - src/proto/grpc/testing/xds/v3/config_dump.proto - src/proto/grpc/testing/xds/v3/csds.proto + - src/proto/grpc/testing/xds/v3/orca_load_report.proto - src/proto/grpc/testing/xds/v3/percent.proto - src/cpp/server/admin/admin_services.cc - src/cpp/server/csds/csds.cc @@ -13351,6 +13359,7 @@ targets: - src/proto/grpc/testing/xds/v3/base.proto - src/proto/grpc/testing/xds/v3/config_dump.proto - src/proto/grpc/testing/xds/v3/csds.proto + - src/proto/grpc/testing/xds/v3/orca_load_report.proto - src/proto/grpc/testing/xds/v3/percent.proto - src/cpp/server/admin/admin_services.cc - src/cpp/server/csds/csds.cc diff --git a/src/android/test/interop/app/CMakeLists.txt b/src/android/test/interop/app/CMakeLists.txt index 2690bdce091..e19c79e2674 100644 --- a/src/android/test/interop/app/CMakeLists.txt +++ b/src/android/test/interop/app/CMakeLists.txt @@ -63,7 +63,9 @@ set(PROTO_BASE_DIR ${GRPC_SRC_DIR}/examples/protos) android_protobuf_grpc_generate_cpp( MESSAGES_PROTO_SRCS MESSAGES_PROTO_HDRS - ${GRPC_SRC_DIR} ${GRPC_SRC_DIR}/src/proto/grpc/testing/messages.proto) + ${GRPC_SRC_DIR} + ${GRPC_SRC_DIR}/src/proto/grpc/testing/messages.proto + ${GRPC_SRC_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.proto) add_library(messages_proto_lib SHARED ${MESSAGES_PROTO_SRCS} ${MESSAGES_PROTO_HDRS}) @@ -88,7 +90,8 @@ target_link_libraries(empty_proto_lib log) android_protobuf_grpc_generate_cpp( - TEST_PROTO_SRCS TEST_PROTO_HDRS ${GRPC_SRC_DIR} ${GRPC_SRC_DIR}/src/proto/grpc/testing/test.proto) + TEST_PROTO_SRCS TEST_PROTO_HDRS + ${GRPC_SRC_DIR} ${GRPC_SRC_DIR}/src/proto/grpc/testing/test.proto) add_library(test_proto_lib SHARED ${TEST_PROTO_SRCS} ${TEST_PROTO_HDRS}) @@ -107,10 +110,14 @@ find_library(log-lib add_library(grpc-interop SHARED src/main/cpp/grpc-interop.cc + ${GRPC_SRC_DIR}/test/cpp/interop/backend_metrics_lb_policy.h + ${GRPC_SRC_DIR}/test/cpp/interop/backend_metrics_lb_policy.cc ${GRPC_SRC_DIR}/test/cpp/interop/interop_client.h ${GRPC_SRC_DIR}/test/cpp/interop/interop_client.cc ${GRPC_SRC_DIR}/test/core/util/histogram.h - ${GRPC_SRC_DIR}/test/core/util/histogram.cc) + ${GRPC_SRC_DIR}/test/core/util/histogram.cc + ${GRPC_SRC_DIR}/test/core/util/test_lb_policies.h + ${GRPC_SRC_DIR}/test/core/util/test_lb_policies.cc) target_link_libraries(grpc-interop messages_proto_lib diff --git a/src/proto/grpc/testing/BUILD b/src/proto/grpc/testing/BUILD index 0f7bbbecf63..980989298cb 100644 --- a/src/proto/grpc/testing/BUILD +++ b/src/proto/grpc/testing/BUILD @@ -93,11 +93,17 @@ grpc_proto_library( name = "messages_proto", srcs = ["messages.proto"], has_services = False, + deps = [ + "//src/proto/grpc/testing/xds/v3:orca_load_report_proto", + ], ) proto_library( name = "messages_proto_descriptor", srcs = ["messages.proto"], + deps = [ + "//src/proto/grpc/testing/xds/v3:orca_load_report_proto_descriptor", + ], ) py_proto_library( @@ -188,7 +194,7 @@ genrule( name = "messages_gen_proto_file", srcs = ["messages.proto"], outs = ["messages_gen.proto"], - cmd = "cp $< $@", + cmd = "sed '/orca_/d' $< > $@", ) grpc_proto_library( diff --git a/src/proto/grpc/testing/messages.proto b/src/proto/grpc/testing/messages.proto index 1a03a6fd6ee..ed7f8ead793 100644 --- a/src/proto/grpc/testing/messages.proto +++ b/src/proto/grpc/testing/messages.proto @@ -17,6 +17,8 @@ syntax = "proto3"; +import "src/proto/grpc/testing/xds/v3/orca_load_report.proto"; + package grpc.testing; // TODO(dgq): Go back to using well-known types once @@ -98,6 +100,9 @@ message SimpleRequest { // Whether SimpleResponse should include grpclb_route_type. bool fill_grpclb_route_type = 10; + + // Orca report for this RPC + xds.data.orca.v3.OrcaLoadReport orca_per_rpc_report = 11; } // Unary response, as configured by the request. diff --git a/src/proto/grpc/testing/xds/v3/BUILD b/src/proto/grpc/testing/xds/v3/BUILD index 9ac92f32df9..17c71390835 100644 --- a/src/proto/grpc/testing/xds/v3/BUILD +++ b/src/proto/grpc/testing/xds/v3/BUILD @@ -182,6 +182,11 @@ grpc_proto_library( ], ) +proto_library( + name = "orca_load_report_proto_descriptor", + srcs = ["orca_load_report.proto"], +) + grpc_proto_library( name = "orca_service_proto", srcs = [ diff --git a/test/cpp/interop/BUILD b/test/cpp/interop/BUILD index 72571090e44..7b64a4e7ac9 100644 --- a/test/cpp/interop/BUILD +++ b/test/cpp/interop/BUILD @@ -86,6 +86,7 @@ grpc_cc_library( "//src/proto/grpc/testing:empty_proto", "//src/proto/grpc/testing:messages_proto", "//src/proto/grpc/testing:test_proto", + "//src/proto/grpc/testing/xds/v3:orca_load_report_proto", "//test/cpp/util:test_config", ], ) @@ -105,6 +106,7 @@ grpc_cc_library( ], language = "C++", deps = [ + ":backend_metrics_lb_policy", "//src/proto/grpc/testing:empty_proto", "//src/proto/grpc/testing:messages_proto", "//src/proto/grpc/testing:test_proto", @@ -425,3 +427,22 @@ grpc_cc_binary( "//test/cpp/util:test_util", ], ) + +grpc_cc_library( + name = "backend_metrics_lb_policy", + srcs = [ + "backend_metrics_lb_policy.cc", + ], + hdrs = [ + "backend_metrics_lb_policy.h", + ], + external_deps = [ + ], + language = "C++", + tags = ["nobuilder"], + deps = [ + "//:grpc", + "//:grpc++", + "//src/proto/grpc/testing/xds/v3:orca_load_report_proto", + ], +) diff --git a/test/cpp/interop/README.md b/test/cpp/interop/README.md new file mode 100644 index 00000000000..e6c78f6d6f8 --- /dev/null +++ b/test/cpp/interop/README.md @@ -0,0 +1,12 @@ +# Running a test locally during development + +To start a server during development: +1. Choose an available port number. +2. Start the server: +``` +GRPC_VERBOSITY=DEBUG ibazel run --compilation_mode=dbg //test/cpp/interop:interop_server -- --port={port_number} +``` +3. Start the client: +``` +GRPC_VERBOSITY=DEBUG ibazel run --test_output=streamed //test/cpp/interop:interop_client -- --server_port={port_number} --test_case={test_case} +``` diff --git a/test/cpp/interop/backend_metrics_lb_policy.cc b/test/cpp/interop/backend_metrics_lb_policy.cc new file mode 100644 index 00000000000..93eb7caf43b --- /dev/null +++ b/test/cpp/interop/backend_metrics_lb_policy.cc @@ -0,0 +1,247 @@ +// +// +// 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. +// +// + +#include + +#include "test/cpp/interop/backend_metrics_lb_policy.h" + +#include "src/core/lib/iomgr/pollset_set.h" + +namespace grpc { +namespace testing { + +namespace { + +using grpc_core::CoreConfiguration; +using grpc_core::LoadBalancingPolicy; +using grpc_core::MakeRefCounted; +using grpc_core::OrphanablePtr; +using grpc_core::RefCountedPtr; + +constexpr absl::string_view kBackendMetricsLbPolicyName = + "test_backend_metrics_load_balancer"; +constexpr absl::string_view kMetricsTrackerArgument = "orca_metrics_tracker"; + +LoadReportTracker::LoadReportEntry BackendMetricDataToOrcaLoadReport( + const grpc_core::BackendMetricData* backend_metric_data) { + if (backend_metric_data == nullptr) { + return absl::nullopt; + } + xds::data::orca::v3::OrcaLoadReport load_report; + load_report.set_cpu_utilization(backend_metric_data->cpu_utilization); + load_report.set_mem_utilization(backend_metric_data->mem_utilization); + load_report.set_rps_fractional(backend_metric_data->qps); + for (const auto& p : backend_metric_data->request_cost) { + std::string name(p.first); + (*load_report.mutable_request_cost())[name] = p.second; + } + for (const auto& p : backend_metric_data->utilization) { + std::string name(p.first); + (*load_report.mutable_utilization())[name] = p.second; + } + return load_report; +} + +class BackendMetricsLbPolicy : public LoadBalancingPolicy { + public: + explicit BackendMetricsLbPolicy(Args args) + : LoadBalancingPolicy(std::move(args), /*initial_refcount=*/2) { + load_report_tracker_ = + channel_args().GetPointer(kMetricsTrackerArgument); + GPR_ASSERT(load_report_tracker_ != nullptr); + Args delegate_args; + delegate_args.work_serializer = work_serializer(); + delegate_args.args = channel_args(); + delegate_args.channel_control_helper = + std::make_unique(RefCountedPtr(this)); + delegate_ = + CoreConfiguration::Get().lb_policy_registry().CreateLoadBalancingPolicy( + "pick_first", std::move(delegate_args)); + grpc_pollset_set_add_pollset_set(delegate_->interested_parties(), + interested_parties()); + } + + ~BackendMetricsLbPolicy() override = default; + + absl::string_view name() const override { + return kBackendMetricsLbPolicyName; + } + + absl::Status UpdateLocked(UpdateArgs args) override { + return delegate_->UpdateLocked(std::move(args)); + } + + void ExitIdleLocked() override { delegate_->ExitIdleLocked(); } + + void ResetBackoffLocked() override { delegate_->ResetBackoffLocked(); } + + private: + class Picker : public SubchannelPicker { + public: + Picker(RefCountedPtr delegate_picker, + LoadReportTracker* load_report_tracker) + : delegate_picker_(std::move(delegate_picker)), + load_report_tracker_(load_report_tracker) {} + + PickResult Pick(PickArgs args) override { + // Do pick. + PickResult result = delegate_picker_->Pick(args); + // Intercept trailing metadata. + auto* complete_pick = absl::get_if(&result.result); + if (complete_pick != nullptr) { + complete_pick->subchannel_call_tracker = + std::make_unique(load_report_tracker_); + } + return result; + } + + private: + RefCountedPtr delegate_picker_; + LoadReportTracker* load_report_tracker_; + }; + + class Helper : public ChannelControlHelper { + public: + explicit Helper(RefCountedPtr parent) + : parent_(std::move(parent)) {} + + RefCountedPtr CreateSubchannel( + grpc_core::ServerAddress address, + const grpc_core::ChannelArgs& args) override { + return parent_->channel_control_helper()->CreateSubchannel( + std::move(address), args); + } + + void UpdateState(grpc_connectivity_state state, const absl::Status& status, + RefCountedPtr picker) override { + parent_->channel_control_helper()->UpdateState( + state, status, + MakeRefCounted(std::move(picker), + parent_->load_report_tracker_)); + } + + void RequestReresolution() override { + parent_->channel_control_helper()->RequestReresolution(); + } + + absl::string_view GetAuthority() override { + return parent_->channel_control_helper()->GetAuthority(); + } + + grpc_event_engine::experimental::EventEngine* GetEventEngine() override { + return parent_->channel_control_helper()->GetEventEngine(); + } + + void AddTraceEvent(TraceSeverity severity, + absl::string_view message) override { + parent_->channel_control_helper()->AddTraceEvent(severity, message); + } + + private: + RefCountedPtr parent_; + }; + + class SubchannelCallTracker : public SubchannelCallTrackerInterface { + public: + explicit SubchannelCallTracker(LoadReportTracker* load_report_tracker) + : load_report_tracker_(load_report_tracker) {} + + void Start() override {} + + void Finish(FinishArgs args) override { + load_report_tracker_->RecordPerRpcLoadReport( + args.backend_metric_accessor->GetBackendMetricData()); + } + + private: + LoadReportTracker* load_report_tracker_; + }; + + void ShutdownLocked() override { + grpc_pollset_set_del_pollset_set(delegate_->interested_parties(), + interested_parties()); + delegate_.reset(); + } + + OrphanablePtr delegate_; + LoadReportTracker* load_report_tracker_; +}; + +class BackendMetricsLbPolicyFactory + : public grpc_core::LoadBalancingPolicyFactory { + private: + class BackendMetricsLbPolicyFactoryConfig + : public LoadBalancingPolicy::Config { + private: + absl::string_view name() const override { + return kBackendMetricsLbPolicyName; + } + }; + + absl::string_view name() const override { + return kBackendMetricsLbPolicyName; + } + + OrphanablePtr CreateLoadBalancingPolicy( + LoadBalancingPolicy::Args args) const override { + return grpc_core::MakeOrphanable(std::move(args)); + } + + absl::StatusOr> + ParseLoadBalancingConfig(const grpc_core::Json& /*json*/) const override { + return MakeRefCounted(); + } +}; +} // namespace + +void RegisterBackendMetricsLbPolicy(CoreConfiguration::Builder* builder) { + builder->lb_policy_registry()->RegisterLoadBalancingPolicyFactory( + std::make_unique()); +} + +void LoadReportTracker::RecordPerRpcLoadReport( + const grpc_core::BackendMetricData* backend_metric_data) { + absl::MutexLock lock(&per_rpc_load_reports_mu_); + per_rpc_load_reports_.emplace_back( + BackendMetricDataToOrcaLoadReport(backend_metric_data)); +} + +absl::optional +LoadReportTracker::GetNextLoadReport() { + absl::MutexLock lock(&per_rpc_load_reports_mu_); + if (per_rpc_load_reports_.empty()) { + return absl::nullopt; + } + auto report = std::move(per_rpc_load_reports_.front()); + per_rpc_load_reports_.pop_front(); + return report; +} + +void LoadReportTracker::ResetCollectedLoadReports() { + absl::MutexLock lock(&per_rpc_load_reports_mu_); + per_rpc_load_reports_.clear(); +} + +ChannelArguments LoadReportTracker::GetChannelArguments() { + ChannelArguments arguments; + arguments.SetPointer(std::string(kMetricsTrackerArgument), this); + return arguments; +} + +} // namespace testing +} // namespace grpc diff --git a/test/cpp/interop/backend_metrics_lb_policy.h b/test/cpp/interop/backend_metrics_lb_policy.h new file mode 100644 index 00000000000..9853f3279a1 --- /dev/null +++ b/test/cpp/interop/backend_metrics_lb_policy.h @@ -0,0 +1,54 @@ +// +// +// 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. +// +// + +#ifndef GRPC_TEST_CPP_INTEROP_BACKEND_METRICS_LB_POLICY_H +#define GRPC_TEST_CPP_INTEROP_BACKEND_METRICS_LB_POLICY_H + +#include + +#include + +#include "src/core/lib/config/core_configuration.h" +#include "src/proto/grpc/testing/xds/v3/orca_load_report.pb.h" + +namespace grpc { +namespace testing { +class LoadReportTracker { + public: + // A load report, or nullopt if the call had no load report. + using LoadReportEntry = absl::optional; + + ChannelArguments GetChannelArguments(); + void ResetCollectedLoadReports(); + void RecordPerRpcLoadReport( + const grpc_core::BackendMetricData* backend_metric_data); + // Returns the next load report, or nullopt if the queue is empty. + absl::optional GetNextLoadReport(); + + private: + std::deque per_rpc_load_reports_ + ABSL_GUARDED_BY(per_rpc_load_reports_mu_); + absl::Mutex per_rpc_load_reports_mu_; +}; + +void RegisterBackendMetricsLbPolicy( + grpc_core::CoreConfiguration::Builder* builder); +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_INTEROP_BACKEND_METRICS_LB_POLICY_H \ No newline at end of file diff --git a/test/cpp/interop/client.cc b/test/cpp/interop/client.cc index bf3b86a41fa..874e7bc7b7a 100644 --- a/test/cpp/interop/client.cc +++ b/test/cpp/interop/client.cc @@ -26,6 +26,7 @@ #include #include #include +#include #include "src/core/lib/gpr/string.h" #include "src/core/lib/gprpp/crash.h" @@ -68,6 +69,7 @@ ABSL_FLAG( //"large_unary : single request and (large) response;\n" //"long_lived_channel: sends large_unary rpcs over a long-lived channel;\n" //"oauth2_auth_token: raw oauth2 access token auth;\n" + //"orca_per_rpc: custom LB policy receives per-query metric reports;\n" //"per_rpc_creds: raw oauth2 access token on a single rpc;\n" //"ping_pong : full-duplex streaming;\n" //"response streaming;\n" @@ -124,6 +126,9 @@ ABSL_FLAG( bool, log_metadata_and_status, false, "If set to 'true', will print received initial and trailing metadata, " "grpc-status and error message to the console, in a stable format."); +ABSL_FLAG(std::string, service_config_json, "", + "Disables service config lookups and sets the provided string as the " + "default service config"); using grpc::testing::CreateChannelForTestCase; using grpc::testing::GetServiceAccountJsonKey; @@ -135,16 +140,18 @@ namespace { // alphanumeric characters and dashes in keys, and any character but semicolons // in values. Convert keys to lowercase. On failure, log an error and return // false. -bool ParseAdditionalMetadataFlag( - const std::string& flag, - std::multimap* additional_metadata) { +absl::StatusOr> +ParseAdditionalMetadataFlag(const std::string& flag) { + std::multimap additional_metadata; + if (flag.empty()) { + return additional_metadata; + } size_t start_pos = 0; while (start_pos < flag.length()) { size_t colon_pos = flag.find(':', start_pos); if (colon_pos == std::string::npos) { - gpr_log(GPR_ERROR, - "Couldn't parse metadata flag: extra characters at end of flag"); - return false; + return absl::InvalidArgumentError( + "Couldn't parse metadata flag: extra characters at end of flag"); } size_t semicolon_pos = flag.find(';', colon_pos); @@ -157,11 +164,10 @@ bool ParseAdditionalMetadataFlag( "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; if (key.find_first_not_of(alphanum_and_hyphen) != std::string::npos) { - gpr_log(GPR_ERROR, - "Couldn't parse metadata flag: key contains characters other " - "than alphanumeric and hyphens: %s", - key.c_str()); - return false; + return absl::InvalidArgumentError(absl::StrCat( + "Couldn't parse metadata flag: key contains characters other " + "than alphanumeric and hyphens: ", + key)); } // Convert to lowercase. @@ -173,7 +179,7 @@ bool ParseAdditionalMetadataFlag( gpr_log(GPR_INFO, "Adding additional metadata with key %s and value %s", key.c_str(), value.c_str()); - additional_metadata->insert({key, value}); + additional_metadata.insert({key, value}); if (semicolon_pos == std::string::npos) { break; @@ -182,7 +188,7 @@ bool ParseAdditionalMetadataFlag( } } - return true; + return additional_metadata; } } // namespace @@ -194,40 +200,36 @@ int main(int argc, char** argv) { absl::GetFlag(FLAGS_test_case).c_str()); int ret = 0; - grpc::testing::ChannelCreationFunc channel_creation_func; std::string test_case = absl::GetFlag(FLAGS_test_case); - if (absl::GetFlag(FLAGS_additional_metadata).empty()) { - channel_creation_func = [test_case]() { - std::vector> - factories; - if (absl::GetFlag(FLAGS_log_metadata_and_status)) { - factories.emplace_back( - new grpc::testing::MetadataAndStatusLoggerInterceptorFactory()); - } - return CreateChannelForTestCase(test_case, std::move(factories)); - }; - } else { - std::multimap additional_metadata; - if (!ParseAdditionalMetadataFlag(absl::GetFlag(FLAGS_additional_metadata), - &additional_metadata)) { - return 1; - } - - channel_creation_func = [test_case, additional_metadata]() { - std::vector> - factories; - factories.emplace_back( - new grpc::testing::AdditionalMetadataInterceptorFactory( - additional_metadata)); - if (absl::GetFlag(FLAGS_log_metadata_and_status)) { - factories.emplace_back( - new grpc::testing::MetadataAndStatusLoggerInterceptorFactory()); - } - return CreateChannelForTestCase(test_case, std::move(factories)); - }; + auto additional_metadata = + ParseAdditionalMetadataFlag(absl::GetFlag(FLAGS_additional_metadata)); + if (!additional_metadata.ok()) { + gpr_log(GPR_ERROR, "%s", + std::string(additional_metadata.status().message()).c_str()); + return 1; } + grpc::testing::ChannelCreationFunc channel_creation_func = + [test_case, &additional_metadata](grpc::ChannelArguments arguments) { + std::vector> + factories; + if (!additional_metadata->empty()) { + factories.emplace_back( + new grpc::testing::AdditionalMetadataInterceptorFactory( + *additional_metadata)); + } + if (absl::GetFlag(FLAGS_log_metadata_and_status)) { + factories.emplace_back( + new grpc::testing::MetadataAndStatusLoggerInterceptorFactory()); + } + std::string service_config_json = + absl::GetFlag(FLAGS_service_config_json); + if (!service_config_json.empty()) { + arguments.SetServiceConfigJSON(service_config_json); + } + return CreateChannelForTestCase(test_case, std::move(factories), + arguments); + }; grpc::testing::InteropClient client( channel_creation_func, true, @@ -267,6 +269,8 @@ int main(int argc, char** argv) { std::bind(&grpc::testing::InteropClient::DoEmptyStream, &client); actions["pick_first_unary"] = std::bind(&grpc::testing::InteropClient::DoPickFirstUnary, &client); + actions["orca_per_rpc"] = + std::bind(&grpc::testing::InteropClient::DoOrcaPerRpc, &client); if (absl::GetFlag(FLAGS_use_tls)) { actions["compute_engine_creds"] = std::bind(&grpc::testing::InteropClient::DoComputeEngineCreds, &client, diff --git a/test/cpp/interop/client_helper.cc b/test/cpp/interop/client_helper.cc index 1ffec938c04..225d598caae 100644 --- a/test/cpp/interop/client_helper.cc +++ b/test/cpp/interop/client_helper.cc @@ -40,17 +40,17 @@ #include "test/cpp/util/create_test_channel.h" #include "test/cpp/util/test_credentials_provider.h" -ABSL_DECLARE_FLAG(bool, use_alts); -ABSL_DECLARE_FLAG(bool, use_tls); ABSL_DECLARE_FLAG(std::string, custom_credentials_type); -ABSL_DECLARE_FLAG(bool, use_test_ca); -ABSL_DECLARE_FLAG(int32_t, server_port); +ABSL_DECLARE_FLAG(std::string, default_service_account); +ABSL_DECLARE_FLAG(std::string, oauth_scope); +ABSL_DECLARE_FLAG(std::string, service_account_key_file); ABSL_DECLARE_FLAG(std::string, server_host); ABSL_DECLARE_FLAG(std::string, server_host_override); +ABSL_DECLARE_FLAG(int32_t, server_port); ABSL_DECLARE_FLAG(std::string, test_case); -ABSL_DECLARE_FLAG(std::string, default_service_account); -ABSL_DECLARE_FLAG(std::string, service_account_key_file); -ABSL_DECLARE_FLAG(std::string, oauth_scope); +ABSL_DECLARE_FLAG(bool, use_alts); +ABSL_DECLARE_FLAG(bool, use_test_ca); +ABSL_DECLARE_FLAG(bool, use_tls); namespace grpc { namespace testing { @@ -87,7 +87,8 @@ std::shared_ptr CreateChannelForTestCase( const std::string& test_case, std::vector< std::unique_ptr> - interceptor_creators) { + interceptor_creators, + ChannelArguments channel_args) { std::string server_uri = absl::GetFlag(FLAGS_server_host); int32_t port = absl::GetFlag(FLAGS_server_port); if (port != 0) { @@ -113,7 +114,6 @@ std::shared_ptr CreateChannelForTestCase( ? nullptr : AccessTokenCredentials(GetOauth2AccessToken()); } else if (test_case == "pick_first_unary") { - ChannelArguments channel_args; // allow the LB policy to be configured with service config channel_args.SetInt(GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION, 0); return CreateTestChannel( @@ -126,18 +126,19 @@ std::shared_ptr CreateChannelForTestCase( absl::GetFlag(FLAGS_use_alts) ? ALTS : (absl::GetFlag(FLAGS_use_tls) ? TLS : INSECURE); - return CreateTestChannel(server_uri, - absl::GetFlag(FLAGS_server_host_override), - security_type, !absl::GetFlag(FLAGS_use_test_ca), - creds, std::move(interceptor_creators)); + return CreateTestChannel( + server_uri, absl::GetFlag(FLAGS_server_host_override), security_type, + !absl::GetFlag(FLAGS_use_test_ca), creds, channel_args, + std::move(interceptor_creators)); } else { if (interceptor_creators.empty()) { - return CreateTestChannel( - server_uri, absl::GetFlag(FLAGS_custom_credentials_type), creds); - } else { return CreateTestChannel(server_uri, - absl::GetFlag(FLAGS_custom_credentials_type), - creds, std::move(interceptor_creators)); + absl::GetFlag(FLAGS_custom_credentials_type), "", + false, creds, channel_args); + } else { + return CreateTestChannel( + server_uri, absl::GetFlag(FLAGS_custom_credentials_type), creds, + std::move(interceptor_creators), channel_args); } } } diff --git a/test/cpp/interop/client_helper.h b/test/cpp/interop/client_helper.h index dd80ee77c0b..35bd62f5a58 100644 --- a/test/cpp/interop/client_helper.h +++ b/test/cpp/interop/client_helper.h @@ -25,6 +25,7 @@ #include #include +#include #include "src/core/lib/surface/call_test_only.h" #include "src/core/lib/transport/transport.h" @@ -43,7 +44,8 @@ std::shared_ptr CreateChannelForTestCase( const std::string& test_case, std::vector< std::unique_ptr> - interceptor_creators = {}); + interceptor_creators = {}, + ChannelArguments channel_args = ChannelArguments()); class InteropClientContextInspector { public: diff --git a/test/cpp/interop/interop_client.cc b/test/cpp/interop/interop_client.cc index ccab7a36273..87721d3e253 100644 --- a/test/cpp/interop/interop_client.cc +++ b/test/cpp/interop/interop_client.cc @@ -37,11 +37,13 @@ #include #include +#include "src/core/lib/config/core_configuration.h" #include "src/core/lib/gprpp/crash.h" #include "src/proto/grpc/testing/empty.pb.h" #include "src/proto/grpc/testing/messages.pb.h" #include "src/proto/grpc/testing/test.grpc.pb.h" #include "test/core/util/histogram.h" +#include "test/cpp/interop/backend_metrics_lb_policy.h" #include "test/cpp/interop/client_helper.h" namespace grpc { @@ -79,6 +81,56 @@ void UnaryCompressionChecks(const InteropClientContextInspector& inspector, GPR_ASSERT(!(inspector.WasCompressed())); } } + +void InitializeCustomLbPolicyIfNeeded() { + // Load balancing policy builder is global. For now, all instances of the + // LB policy will store data in the same collection. All interop_clients in + // the same process will also share the collection + // Realistically, we do not yet need synchronization as only a single test is + // running at a time. + static bool initialized = false; + if (!initialized) { + grpc_core::CoreConfiguration::RegisterBuilder( + [](grpc_core::CoreConfiguration::Builder* builder) { + RegisterBackendMetricsLbPolicy(builder); + }); + initialized = true; + } +} + +template +bool SameMaps(const std::string& path, const Map& expected, const Map& actual) { + if (expected.size() != actual.size()) { + gpr_log(GPR_ERROR, "Field %s does not match: expected %lu entries, got %lu", + path.c_str(), expected.size(), actual.size()); + return false; + } + for (const auto& key_value : expected) { + auto it = actual.find(key_value.first); + if (it == actual.end()) { + gpr_log(GPR_ERROR, "In field %s, key %s was not found", path.c_str(), + key_value.first.c_str()); + return false; + } + if (key_value.second != it->second) { + gpr_log( + GPR_ERROR, "In field %s, value %s mismatch: expected %f, actual: %f", + path.c_str(), key_value.first.c_str(), key_value.second, it->second); + return false; + } + } + return true; +} + +void SameOrcaLoadReports(const xds::data::orca::v3::OrcaLoadReport& expected, + const xds::data::orca::v3::OrcaLoadReport& actual) { + GPR_ASSERT(expected.cpu_utilization() == actual.cpu_utilization()); + GPR_ASSERT(expected.mem_utilization() == actual.mem_utilization()); + GPR_ASSERT( + SameMaps("request_cost", expected.request_cost(), actual.request_cost())); + GPR_ASSERT( + SameMaps("utilization", expected.utilization(), actual.utilization())); +} } // namespace InteropClient::ServiceStub::ServiceStub( @@ -119,7 +171,13 @@ void InteropClient::ServiceStub::ResetChannel() { InteropClient::InteropClient(ChannelCreationFunc channel_creation_func, bool new_stub_every_test_case, bool do_not_abort_on_transient_failures) - : serviceStub_(std::move(channel_creation_func), new_stub_every_test_case), + : serviceStub_( + [&]() { + InitializeCustomLbPolicyIfNeeded(); + return channel_creation_func( + load_report_tracker_.GetChannelArguments()); + }, + new_stub_every_test_case), do_not_abort_on_transient_failures_(do_not_abort_on_transient_failures) {} bool InteropClient::AssertStatusOk(const Status& s, @@ -930,6 +988,30 @@ bool InteropClient::DoPickFirstUnary() { return true; } +bool InteropClient::DoOrcaPerRpc() { + load_report_tracker_.ResetCollectedLoadReports(); + gpr_log(GPR_DEBUG, "testing orca per rpc"); + SimpleRequest request; + SimpleResponse response; + ClientContext context; + auto orca_report = request.mutable_orca_per_rpc_report(); + orca_report->set_cpu_utilization(0.8210); + orca_report->set_mem_utilization(0.5847); + orca_report->mutable_request_cost()->emplace("cost", 3456.32); + orca_report->mutable_utilization()->emplace("util", 0.30499); + auto status = serviceStub_.Get()->UnaryCall(&context, request, &response); + if (!AssertStatusOk(status, context.debug_error_string())) { + return false; + } + auto report = load_report_tracker_.GetNextLoadReport(); + GPR_ASSERT(report.has_value()); + GPR_ASSERT(report->has_value()); + SameOrcaLoadReports(report->value(), *orca_report); + GPR_ASSERT(!load_report_tracker_.GetNextLoadReport().has_value()); + gpr_log(GPR_DEBUG, "orca per rpc successfully finished"); + return true; +} + bool InteropClient::DoCustomMetadata() { const std::string kEchoInitialMetadataKey("x-grpc-test-echo-initial"); const std::string kInitialMetadataValue("test_initial_metadata_value"); diff --git a/test/cpp/interop/interop_client.h b/test/cpp/interop/interop_client.h index 27c276290a7..4fc7d79dd82 100644 --- a/test/cpp/interop/interop_client.h +++ b/test/cpp/interop/interop_client.h @@ -21,11 +21,14 @@ #include +#include "absl/types/optional.h" + #include #include #include "src/proto/grpc/testing/messages.pb.h" #include "src/proto/grpc/testing/test.grpc.pb.h" +#include "test/cpp/interop/backend_metrics_lb_policy.h" namespace grpc { namespace testing { @@ -35,14 +38,15 @@ typedef std::function CheckerFn; -typedef std::function(void)> ChannelCreationFunc; +typedef std::function(ChannelArguments)> + ChannelCreationFunc; class InteropClient { public: /// If new_stub_every_test_case is true, a new TestService::Stub object is /// created for every test case - /// If do_not_abort_on_transient_failures is true, abort() is not called in - /// case of transient failures (like connection failures) + /// If do_not_abort_on_transient_failures is true, abort() is not called + /// in case of transient failures (like connection failures) explicit InteropClient(ChannelCreationFunc channel_creation_func, bool new_stub_every_test_case, bool do_not_abort_on_transient_failures); @@ -73,6 +77,7 @@ class InteropClient { bool DoUnimplementedService(); // all requests are sent to one server despite multiple servers are resolved bool DoPickFirstUnary(); + bool DoOrcaPerRpc(); // The following interop test are not yet part of the interop spec, and are // not implemented cross-language. They are considered experimental for now, @@ -107,6 +112,7 @@ class InteropClient { private: class ServiceStub { public: + typedef std::function()> ChannelCreationFunc; // If new_stub_every_call = true, pointer to a new instance of // TestServce::Stub is returned by Get() everytime it is called ServiceStub(ChannelCreationFunc channel_creation_func, @@ -154,6 +160,8 @@ class InteropClient { ServiceStub serviceStub_; /// If true, abort() is not called for transient failures bool do_not_abort_on_transient_failures_; + // Load Orca metrics captured by the custom LB policy. + LoadReportTracker load_report_tracker_; }; } // namespace testing diff --git a/test/cpp/interop/interop_server.cc b/test/cpp/interop/interop_server.cc index 94610a4f8f9..7383c68d7c4 100644 --- a/test/cpp/interop/interop_server.cc +++ b/test/cpp/interop/interop_server.cc @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include #include @@ -36,6 +38,7 @@ #include "src/proto/grpc/testing/empty.pb.h" #include "src/proto/grpc/testing/messages.pb.h" #include "src/proto/grpc/testing/test.grpc.pb.h" +#include "src/proto/grpc/testing/xds/v3/orca_load_report.pb.h" #include "test/cpp/interop/server_helper.h" #include "test/cpp/util/test_config.h" @@ -136,6 +139,35 @@ bool CheckExpectedCompression(const ServerContext& context, return true; } +void RecordMetrics(ServerContext* context, + const xds::data::orca::v3::OrcaLoadReport& request_metrics) { + auto recorder = context->ExperimentalGetCallMetricRecorder(); + // Do not record when zero since it indicates no test per-call report. + if (request_metrics.cpu_utilization() > 0) { + recorder->RecordCpuUtilizationMetric(request_metrics.cpu_utilization()); + } + if (request_metrics.mem_utilization() > 0) { + recorder->RecordMemoryUtilizationMetric(request_metrics.mem_utilization()); + } + if (request_metrics.rps_fractional() > 0) { + recorder->RecordQpsMetric(request_metrics.rps_fractional()); + } + for (const auto& p : request_metrics.request_cost()) { + char* key = static_cast( + grpc_call_arena_alloc(context->c_call(), p.first.size() + 1)); + strncpy(key, p.first.data(), p.first.size()); + key[p.first.size()] = '\0'; + recorder->RecordRequestCostMetric(key, p.second); + } + for (const auto& p : request_metrics.utilization()) { + char* key = static_cast( + grpc_call_arena_alloc(context->c_call(), p.first.size() + 1)); + strncpy(key, p.first.data(), p.first.size()); + key[p.first.size()] = '\0'; + recorder->RecordUtilizationMetric(key, p.second); + } +} + class TestServiceImpl : public TestService::Service { public: Status EmptyCall(ServerContext* context, @@ -187,7 +219,9 @@ class TestServiceImpl : public TestService::Service { static_cast(request->response_status().code()), request->response_status().message()); } - + if (request->has_orca_per_rpc_report()) { + RecordMetrics(context, request->orca_per_rpc_report()); + } return Status::OK; } @@ -360,6 +394,8 @@ void grpc::testing::interop::RunServer( if (absl::GetFlag(FLAGS_max_send_message_size) >= 0) { builder.SetMaxSendMessageSize(absl::GetFlag(FLAGS_max_send_message_size)); } + grpc::ServerBuilder::experimental_type(&builder).EnableCallMetricRecording( + nullptr); std::unique_ptr server(builder.BuildAndStart()); gpr_log(GPR_INFO, "Server listening on %s", server_address.str().c_str()); diff --git a/test/cpp/interop/observability_client.cc b/test/cpp/interop/observability_client.cc index 051c6d5ff61..38d23955d40 100644 --- a/test/cpp/interop/observability_client.cc +++ b/test/cpp/interop/observability_client.cc @@ -209,7 +209,7 @@ int main(int argc, char** argv) { grpc::testing::ChannelCreationFunc channel_creation_func; std::string test_case = absl::GetFlag(FLAGS_test_case); if (absl::GetFlag(FLAGS_additional_metadata).empty()) { - channel_creation_func = [test_case]() { + channel_creation_func = [test_case](auto arguments) { std::vector> factories; @@ -217,7 +217,8 @@ int main(int argc, char** argv) { factories.emplace_back( new grpc::testing::MetadataAndStatusLoggerInterceptorFactory()); } - return CreateChannelForTestCase(test_case, std::move(factories)); + return CreateChannelForTestCase(test_case, std::move(factories), + arguments); }; } else { std::multimap additional_metadata; @@ -226,7 +227,7 @@ int main(int argc, char** argv) { return 1; } - channel_creation_func = [test_case, additional_metadata]() { + channel_creation_func = [test_case, additional_metadata](auto arguments) { std::vector> factories; @@ -237,7 +238,8 @@ int main(int argc, char** argv) { factories.emplace_back( new grpc::testing::MetadataAndStatusLoggerInterceptorFactory()); } - return CreateChannelForTestCase(test_case, std::move(factories)); + return CreateChannelForTestCase(test_case, std::move(factories), + arguments); }; } diff --git a/test/cpp/interop/xds_federation_client.cc b/test/cpp/interop/xds_federation_client.cc index 9af58c2fcc3..1a0dd63118c 100644 --- a/test/cpp/interop/xds_federation_client.cc +++ b/test/cpp/interop/xds_federation_client.cc @@ -88,7 +88,7 @@ int main(int argc, char** argv) { std::vector threads; for (size_t i = 0; i < uris.size(); i++) { threads.push_back(std::thread([uris, creds, i, test_case]() { - auto channel_creation_func = [uris, creds, i]() { + auto channel_creation_func = [uris, creds, i](grpc::ChannelArguments) { return grpc::CreateTestChannel(uris[i], creds[i], nullptr /* call creds */); }; diff --git a/test/cpp/util/create_test_channel.cc b/test/cpp/util/create_test_channel.cc index 570fbdb49e9..6f5ad07ce7e 100644 --- a/test/cpp/util/create_test_channel.cc +++ b/test/cpp/util/create_test_channel.cc @@ -236,7 +236,18 @@ std::shared_ptr CreateTestChannel( std::vector< std::unique_ptr> interceptor_creators) { - ChannelArguments channel_args; + return CreateTestChannel(server, credential_type, creds, + std::move(interceptor_creators), + {} /* channel_args */); +} + +std::shared_ptr CreateTestChannel( + const std::string& server, const std::string& credential_type, + const std::shared_ptr& creds, + std::vector< + std::unique_ptr> + interceptor_creators, + ChannelArguments channel_args) { MaybeSetCustomChannelArgs(&channel_args); std::shared_ptr channel_creds = testing::GetCredentialsProvider()->GetChannelCredentials(credential_type, diff --git a/test/cpp/util/create_test_channel.h b/test/cpp/util/create_test_channel.h index 8d04668213b..1ed9c44e491 100644 --- a/test/cpp/util/create_test_channel.h +++ b/test/cpp/util/create_test_channel.h @@ -94,6 +94,14 @@ std::shared_ptr CreateTestChannel( std::unique_ptr> interceptor_creators); +std::shared_ptr CreateTestChannel( + const std::string& server, const std::string& credential_type, + const std::shared_ptr& creds, + std::vector< + std::unique_ptr> + interceptor_creators, + ChannelArguments channel_args); + } // namespace grpc #endif // GRPC_TEST_CPP_UTIL_CREATE_TEST_CHANNEL_H