From 22a6e7dcd0a51d35a414a5aea5438d8ee4d16517 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 23 Apr 2024 22:33:54 +0000 Subject: [PATCH 01/77] Add support for boolean type to Envoy::Json::Streamer Signed-off-by: Demitri Swan --- diff.sh | 23 ++++++++++++++++++++ envoy-demo.yaml | 58 +++++++++++++++++++++++++++++++++++++++++++++++++ file1 | 1 + file2 | 1 + file3 | 1 + 5 files changed, 84 insertions(+) create mode 100644 diff.sh create mode 100644 envoy-demo.yaml create mode 100644 file1 create mode 100644 file2 create mode 100644 file3 diff --git a/diff.sh b/diff.sh new file mode 100644 index 000000000000..23e829f5f10e --- /dev/null +++ b/diff.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +file_1="$(<$1)" +file_2="$(<$2)" + +declare -i word_count_1=$(echo "$file_1" | wc -c) +declare -i word_count_2=$(echo "$file_2" | wc -c) +declare -i exit_code + +if (( word_count_1 != word_count_2 )); then + echo "word_count_1: $word_count_1 word_count_2: $word_count_2" >&2 + exit_code=1 +fi + +for ((i=0; i < word_count_1; i++)) { + if [[ ${file_1:$i:1} != ${file_2:$i:1} ]]; then + echo "mismatch at index $i: first ${file_1:$i:30} | second ${file_2:$i:30}" >&2 + exit_code=1 + exit 11 + fi +} + +exit $exit_code diff --git a/envoy-demo.yaml b/envoy-demo.yaml new file mode 100644 index 000000000000..40e12b2206b2 --- /dev/null +++ b/envoy-demo.yaml @@ -0,0 +1,58 @@ +admin: + address: + socket_address: { address: 127.0.0.1, port_value: 9000 } + +static_resources: + + listeners: + - name: listener_0 + address: + socket_address: + address: 0.0.0.0 + port_value: 10000 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + access_log: + - name: envoy.access_loggers.stdout + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog + http_filters: + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: ["*"] + routes: + - match: + prefix: "/" + route: + host_rewrite_literal: www.envoyproxy.io + cluster: service_envoyproxy_io + + clusters: + - name: service_envoyproxy_io + type: LOGICAL_DNS + # Comment out the following line to test on v6 networks + dns_lookup_family: V4_ONLY + load_assignment: + cluster_name: service_envoyproxy_io + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: www.envoyproxy.io + port_value: 443 + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + sni: www.envoyproxy.io + diff --git a/file1 b/file1 new file mode 100644 index 000000000000..dd4b4fe675d1 --- /dev/null +++ b/file1 @@ -0,0 +1 @@ +"{\"cluster_statuses\":[{\"name\":\"test_cluster\",\"observability_name\":\"observability_name\",\"eds_service_name\":\"potato_launcher\",\"circuit_breakers\":{\"thresholds\":[{\"priority\":\"DEFAULT\",\"max_connections\":1024,\"max_pending_requests\":1024,\"max_requests\":1024,\"max_retries\":16},{\"priority\":\"HIGH\",\"max_connections\":4096,\"max_pending_requests\":4096,\"max_requests\":4096,\"max_retries\":16}]},\"success_rate_ejection_threshold\":1.1,\"local_success_rate_ejection_threshold\":1.1,\"added_via_api\":true,\"host_statuses\":[{\"address\":{\"socket_address\":{\"address\":\"1.2.3.4\",\"port\":80}},\"hostname\":\"test_hostname\",\"locality\":{\"region\":\"test_region\",\"zone\":\"test_zone\",\"sub_zone\":\"test_sub_zone\"},\"stats\":[{\"type\":\"COUNTER\",\"name\":\"test_counter\",\"value\":10},{\"type\":\"GAUGE\",\"name\":\"test_gauge\",\"value\":11}],\"health_status\":{\"failed_active_health_check\":true,\"failed_outlier_check\":true,\"eds_health_status\":\"DEGRADED\",\"failed_active_degraded_check\":true,\"eds_health_status\":\"DEGRADED\",\"pending_dynamic_removal\":true,\"pending_active_hc\":false,\"excluded_via_immediate_hc_fail\":false,\"active_hc_timeout\":false,\"eds_health_status\":\"DRAINING\"}}]},{\"name\":\"test_cluster\",\"observability_name\":\"observability_name\",\"eds_service_name\":\"potato_launcher\",\"circuit_breakers\":{\"thresholds\":[{\"priority\":\"DEFAULT\",\"max_connections\":1024,\"max_pending_requests\":1024,\"max_requests\":1024,\"max_retries\":16},{\"priority\":\"HIGH\",\"max_connections\":4096,\"max_pending_requests\":4096,\"max_requests\":4096,\"max_retries\":16}]},\"success_rate_ejection_threshold\":1.1,\"local_success_rate_ejection_threshold\":1.1,\"added_via_api\":true,\"host_statuses\":[{\"address\":{\"socket_address\":{\"address\":\"1.2.3.4\",\"port\":80}},\"hostname\":\"test_hostname\",\"locality\":{\"region\":\"test_region\",\"zone\":\"test_zone\",\"sub_zone\":\"test_sub_zone\"},\"stats\":[{\"type\":\"COUNTER\",\"name\":\"test_counter\",\"value\":10},{\"type\":\"GAUGE\",\"name\":\"test_gauge\",\"value\":11}],\"health_status\":{\"failed_active_health_check\":true,\"failed_outlier_check\":true,\"eds_health_status\":\"DEGRADED\",\"failed_active_degraded_check\":true,\"eds_health_status\":\"DEGRADED\",\"pending_dynamic_removal\":true,\"pending_active_hc\":false,\"excluded_via_immediate_hc_fail\":false,\"active_hc_timeout\":false,\"eds_health_status\":\"DRAINING\"}}]}]}" diff --git a/file2 b/file2 new file mode 100644 index 000000000000..9b39817672a0 --- /dev/null +++ b/file2 @@ -0,0 +1 @@ +"{\"cluster_statuses\":[{\"name\":\"test_cluster\",\"observability_name\":\"observability_name\",\"eds_service_name\":\"potato_launcher\",\"circuit_breakers\":{\"thresholds\":[{\"priority\":\"DEFAULT\",\"max_connections\":1024,\"max_pending_requests\":1024,\"max_requests\":1024,\"max_retries\":16},{\"priority\":\"HIGH\",\"max_connections\":4096,\"max_pending_requests\":4096,\"max_requests\":4096,\"max_retries\":16}]},\"success_rate_ejection_threshold\":1.1,\"local_success_rate_ejection_threshold\":1.1,\"added_via_api\":true,\"host_statuses\":[{\"address\":{\"socket_address\":{\"address\":\"1.2.3.4\",\"port\":80}},\"hostname\":\"test_hostname\",\"locality\":{\"region\":\"test_region\",\"zone\":\"test_zone\",\"sub_zone\":\"test_sub_zone\"},\"stats\":[{\"type\":\"COUNTER\",\"name\":\"test_counter\",\"value\":10},{\"type\":\"GAUGE\",\"name\":\"test_gauge\",\"value\":11}],\"health_status\":{\"eds_health_status\":\"DRAINING\",\"failed_active_health_check\":true,\"failed_outlier_check\":true,\"failed_active_degraded_check\":true,\"pending_dynamic_removal\":true}}],},{\"name\":\"test_cluster\",\"observability_name\":\"observability_name\",\"eds_service_name\":\"potato_launcher\",\"circuit_breakers\":{\"thresholds\":[{\"priority\":\"DEFAULT\",\"max_connections\":1024,\"max_pending_requests\":1024,\"max_requests\":1024,\"max_retries\":16},{\"priority\":\"HIGH\",\"max_connections\":4096,\"max_pending_requests\":4096,\"max_requests\":4096,\"max_retries\":16}]},\"success_rate_ejection_threshold\":1.1,\"local_success_rate_ejection_threshold\":1.1,\"added_via_api\":true,\"host_statuses\":[{\"address\":{\"socket_address\":{\"address\":\"1.2.3.4\",\"port\":80}},\"hostname\":\"test_hostname\",\"locality\":{\"region\":\"test_region\",\"zone\":\"test_zone\",\"sub_zone\":\"test_sub_zone\"},\"stats\":[{\"type\":\"COUNTER\",\"name\":\"test_counter\",\"value\":10},{\"type\":\"GAUGE\",\"name\":\"test_gauge\",\"value\":11}],\"health_status\":{\"failed_active_health_check\":false,\"failed_outlier_check\":false,\"eds_health_status\":\"HEALTHY\",\"failed_active_degraded_check\":false,\"pending_dynamic_removal\":false,\"pending_active_hc\":false,\"excluded_via_immediate_hc_fail\":false,\"active_hc_timeout\":false}}]}]}" diff --git a/file3 b/file3 new file mode 100644 index 000000000000..b04802db73f4 --- /dev/null +++ b/file3 @@ -0,0 +1 @@ +"{\"cluster_statuses\":[{\"circuit_breakers\":{\"thresholds\":[{\"priority\":\"DEFAULT\",\"max_connections\":1024,\"max_pending_requests\":1024,\"max_requests\":1024,\"max_retries\":1024},{\"priority\":\"HIGH\",\"max_connections\":4096,\"max_pending_requests\":4096,\"max_requests\":4096,\"max_retries\":4096}]},\"success_rate_ejection_threshold\":1.1,\"local_success_rate_ejection_threshold\":1.1,\"added_via_api\":true,\"host_statuses\":[{\"address\":{\"socket_address\":{\"address\":\"1.2.3.4\",\"port\":80}},\"locality\":{\"region\":\"test_region\",\"zone\":\"test_zone\",\"sub_zone\":\"test_sub_zone\"},\"stats\":[{\"type\":\"COUNTER\",\"name\":\"test_counter\",\"value\":10},{\"type\":\"GAUGE\",\"name\":\"test_gauge\",\"value\":11}],\"hostname\":\"\\000\\000\\000\\000\\000\\000\\000\\250TI&\\376\\177\",\"success_rate\":1,\"weight\":1,\"priority\":1,\"local_origin_success_rate\":1}]},{\"circuit_breakers\":{\"thresholds\":[{\"priority\":\"DEFAULT\",\"max_connections\":1024,\"max_pending_requests\":1024,\"max_requests\":1024,\"max_retries\":1024},{\"priority\":\"HIGH\",\"max_connections\":4096,\"max_pending_requests\":4096,\"max_requests\":4096,\"max_retries\":4096}]},\"success_rate_ejection_threshold\":1.1,\"local_success_rate_ejection_threshold\":1.1,\"added_via_api\":true,\"host_statuses\":[{\"address\":{\"socket_address\":{\"address\":\"1.2.3.4\",\"port\":80}},\"locality\":{\"region\":\"test_region\",\"zone\":\"test_zone\",\"sub_zone\":\"test_sub_zone\"},\"stats\":[{\"type\":\"COUNTER\",\"name\":\"test_counter\",\"value\":10},{\"type\":\"GAUGE\",\"name\":\"test_gauge\",\"value\":11}],\"hostname\":\"\\000\\000\\000\\000\\000\\000\\000\\250TI&\\376\\177\",\"success_rate\":1,\"weight\":1,\"priority\":1,\"local_origin_success_rate\":1}]}]}" From 797c8ca0b5553431a010ec05b04d954ef4dc2b60 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 24 Apr 2024 02:35:46 +0000 Subject: [PATCH 02/77] Harden variant selection with static_assert Signed-off-by: Demitri Swan --- source/common/json/json_streamer.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/common/json/json_streamer.cc b/source/common/json/json_streamer.cc index c67f43076e0f..aa616ecdd57f 100644 --- a/source/common/json/json_streamer.cc +++ b/source/common/json/json_streamer.cc @@ -137,7 +137,7 @@ void Streamer::Map::addEntries(const Entries& entries) { } void Streamer::Level::addValue(const Value& value) { - switch (value.index()) { + switch (int idx = value.index()) { case 0: static_assert(std::is_same(value)), const absl::string_view&>::value, "value at index 0 must be an absl::string_vlew"); From b2dc336b6d393fd15087c26283cb3801ae3a7a86 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 24 Apr 2024 03:30:19 +0000 Subject: [PATCH 03/77] Remove unused variable Signed-off-by: Demitri Swan --- source/common/json/json_streamer.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/common/json/json_streamer.cc b/source/common/json/json_streamer.cc index aa616ecdd57f..c67f43076e0f 100644 --- a/source/common/json/json_streamer.cc +++ b/source/common/json/json_streamer.cc @@ -137,7 +137,7 @@ void Streamer::Map::addEntries(const Entries& entries) { } void Streamer::Level::addValue(const Value& value) { - switch (int idx = value.index()) { + switch (value.index()) { case 0: static_assert(std::is_same(value)), const absl::string_view&>::value, "value at index 0 must be an absl::string_vlew"); From 85275f815ec322a78453e8316acdc0cd3314483b Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 2 Apr 2024 00:51:11 +0000 Subject: [PATCH 04/77] Setup new request route for /clusters Signed-off-by: Demitri Swan --- envoy/server/admin.h | 7 +++++++ source/server/admin/admin.cc | 21 +++++++++++++++++++-- source/server/admin/clusters_handler.cc | 7 +++++++ source/server/admin/clusters_handler.h | 2 ++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/envoy/server/admin.h b/envoy/server/admin.h index 48c1629fef44..7ec50cd82180 100644 --- a/envoy/server/admin.h +++ b/envoy/server/admin.h @@ -77,6 +77,13 @@ class AdminStream { return X(response_headers, data, admin_stream); \ } +/** + * This macro is used to add streaming handlers to the Admin HTTP Endpoint. It builds + * a callback that executes X when the specified admin handler is hit. + */ +#define MAKE_STREAMING_HANDLER(X) \ + [this](AdminStream& admin_stream) -> Admin::RequestPtr { return X(admin_stream); } + /** * Global admin HTTP endpoint for the server, holding a map from URL prefixes to * handlers. When an HTTP request arrives at the admin port, the URL is linearly diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index 72f71f29d6f2..346550432b16 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -9,6 +9,7 @@ #include "envoy/extensions/http/header_validators/envoy_default/v3/header_validator.pb.h" #include "envoy/http/header_validator_factory.h" +#include "envoy/server/admin.h" #include "envoy/server/hot_restart.h" #include "envoy/server/instance.h" #include "envoy/server/options.h" @@ -127,8 +128,24 @@ AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server, makeHandler("/", "Admin home page", MAKE_ADMIN_HANDLER(handlerAdminHome), false, false), makeHandler("/certs", "print certs on machine", MAKE_ADMIN_HANDLER(server_info_handler_.handlerCerts), false, false), - makeHandler("/clusters", "upstream cluster status", - MAKE_ADMIN_HANDLER(clusters_handler_.handlerClusters), false, false), + { + // Using designated initialization to improve readability of request routing + // configuration. + .prefix_ = "/clusters", + .help_text_ = "retrieve clusters data", + .handler_ = MAKE_STREAMING_HANDLER(clusters_handler_.makeRequest), + .removable_ = false, + .mutates_server_state_ = false, + .params_ = + { + { + .type_ = Admin::ParamDescriptor::Type::Enum, + .id_ = "format", + .help_ = "the output format", + .enum_choices_ = {"text", "json"}, + }, + }, + }, makeHandler( "/config_dump", "dump current Envoy configs (experimental)", MAKE_ADMIN_HANDLER(config_dump_handler_.handlerConfigDump), false, false, diff --git a/source/server/admin/clusters_handler.cc b/source/server/admin/clusters_handler.cc index abf3f63430c6..c40910cba238 100644 --- a/source/server/admin/clusters_handler.cc +++ b/source/server/admin/clusters_handler.cc @@ -2,6 +2,7 @@ #include "envoy/admin/v3/clusters.pb.h" +#include "source/common/common/macros.h" #include "source/common/http/headers.h" #include "source/common/http/utility.h" #include "source/common/network/utility.h" @@ -56,6 +57,12 @@ Http::Code ClustersHandler::handlerClusters(Http::ResponseHeaderMap& response_he return Http::Code::OK; } +// TODO(demitriswan) Implement this member function. +Admin::RequestPtr ClustersHandler::makeRequest(AdminStream& admin_stream) { + UNREFERENCED_PARAMETER(admin_stream); + return nullptr; +} + // Helper method that ensures that we've setting flags based on all the health flag values on the // host. void setHealthFlag(Upstream::Host::HealthFlag flag, const Upstream::Host& host, diff --git a/source/server/admin/clusters_handler.h b/source/server/admin/clusters_handler.h index 14bc570dedb6..c6feacbd5f89 100644 --- a/source/server/admin/clusters_handler.h +++ b/source/server/admin/clusters_handler.h @@ -31,6 +31,8 @@ class ClustersHandler : public HandlerContextBase { Http::Code handlerClusters(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream&); + // TODO(demitriswan) Document this member function. + Admin::RequestPtr makeRequest(AdminStream& admin_stream); private: void addOutlierInfo(const std::string& cluster_name, From 0a0245eb7dd62a4eda948e4ca1c40428b4907101 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 2 Apr 2024 01:00:05 +0000 Subject: [PATCH 05/77] Updating documentation comments. Signed-off-by: Demitri Swan --- envoy/server/admin.h | 5 ++++- source/server/admin/admin.cc | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/envoy/server/admin.h b/envoy/server/admin.h index 7ec50cd82180..d5ec4b60b675 100644 --- a/envoy/server/admin.h +++ b/envoy/server/admin.h @@ -79,7 +79,10 @@ class AdminStream { /** * This macro is used to add streaming handlers to the Admin HTTP Endpoint. It builds - * a callback that executes X when the specified admin handler is hit. + * a callback that executes X when the specified admin handler is hit. Member + * functions must be called when referenced, so this macro DRYs up the process of + * passing member functions to route-handling configuration within + * Envoy::Server::AdminImpl. */ #define MAKE_STREAMING_HANDLER(X) \ [this](AdminStream& admin_stream) -> Admin::RequestPtr { return X(admin_stream); } diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index 346550432b16..da64be2de6c6 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -130,7 +130,8 @@ AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server, MAKE_ADMIN_HANDLER(server_info_handler_.handlerCerts), false, false), { // Using designated initialization to improve readability of request routing - // configuration. + // configuration. See + // https://en.cppreference.com/w/cpp/language/aggregate_initialization#Designated_initializers. .prefix_ = "/clusters", .help_text_ = "retrieve clusters data", .handler_ = MAKE_STREAMING_HANDLER(clusters_handler_.makeRequest), From 2ed400a59da5666ac86e5ea930d0b369916d2a9d Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 2 Apr 2024 03:14:51 +0000 Subject: [PATCH 06/77] Adding boilerplate for clusters_request.cc Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 12 ++++++++++++ source/server/admin/clusters_request.cc | 20 ++++++++++++++++++++ source/server/admin/clusters_request.h | 25 +++++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 source/server/admin/clusters_request.cc create mode 100644 source/server/admin/clusters_request.h diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 1c453e712a8e..2a613abd0195 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -410,3 +410,15 @@ envoy_cc_library( "//source/common/common:macros", ], ) + +envoy_cc_library( + name = "clusters_request_lib", + srcs = ["clusters_request.cc"], + hdrs = ["clusters_request.h"], + deps = [ + "//envoy/buffer:buffer_interface", + "//envoy/http:codes_interface", + "//envoy/http:header_map_interface", + "//envoy/server:admin_interface", + ], +) diff --git a/source/server/admin/clusters_request.cc b/source/server/admin/clusters_request.cc new file mode 100644 index 000000000000..b901397e652d --- /dev/null +++ b/source/server/admin/clusters_request.cc @@ -0,0 +1,20 @@ +#include "source/server/admin/clusters_request.h" + +namespace Envoy { +namespace Server { + +// TODO(demitriswan) Implement this member function. +Http::Code ClustersRequest::start(Http::ResponseHeaderMap& response_headers) { + UNREFERENCED_PARAMETER(response_headers); + return Http::Code::OK; +} + +// TODO(demitriswan) Implement this member function. +bool ClustersRequest::nextChunk(Buffer::Instance& response) { + UNREFERENCED_PARAMETER(response); + UNREFERENCED_PARAMETER(chunk_size_); + return false; +} + +} // namespace Server +} // namespace Envoy diff --git a/source/server/admin/clusters_request.h b/source/server/admin/clusters_request.h new file mode 100644 index 000000000000..a0bd74ecaf1e --- /dev/null +++ b/source/server/admin/clusters_request.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include "envoy/buffer/buffer.h" +#include "envoy/http/codes.h" +#include "envoy/server/admin.h" + +namespace Envoy { +namespace Server { + +// Captures context for a streaming request, implementing the Admin::Request interface. +class ClustersRequest : public Admin::Request { +public: + static constexpr uint64_t DefaultChunkSize = 2 * 1000 * 1000; + + Http::Code start(Http::ResponseHeaderMap& response_headers) override; + bool nextChunk(Buffer::Instance& response) override; + +private: + uint64_t chunk_size_{DefaultChunkSize}; +}; + +} // namespace Server +} // namespace Envoy From ecec95d4374bafa37fe0c0c5c62d173004f34472 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 2 Apr 2024 05:41:07 +0000 Subject: [PATCH 07/77] Small update set * Using more accurate calculation for 2 MB * Adding clusters_request_test.cc * Changing the route configuration to match expected output in test Signed-off-by: Demitri Swan --- source/server/admin/admin.cc | 4 +-- source/server/admin/clusters_request.h | 2 +- test/server/admin/BUILD | 11 ++++++ test/server/admin/admin_test.cc | 1 + test/server/admin/clusters_request_test.cc | 39 ++++++++++++++++++++++ 5 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 test/server/admin/clusters_request_test.cc diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index da64be2de6c6..2181c651055e 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -133,7 +133,7 @@ AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server, // configuration. See // https://en.cppreference.com/w/cpp/language/aggregate_initialization#Designated_initializers. .prefix_ = "/clusters", - .help_text_ = "retrieve clusters data", + .help_text_ = "upstream clusters status", .handler_ = MAKE_STREAMING_HANDLER(clusters_handler_.makeRequest), .removable_ = false, .mutates_server_state_ = false, @@ -142,7 +142,7 @@ AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server, { .type_ = Admin::ParamDescriptor::Type::Enum, .id_ = "format", - .help_ = "the output format", + .help_ = "The output format", .enum_choices_ = {"text", "json"}, }, }, diff --git a/source/server/admin/clusters_request.h b/source/server/admin/clusters_request.h index a0bd74ecaf1e..ed424358fe36 100644 --- a/source/server/admin/clusters_request.h +++ b/source/server/admin/clusters_request.h @@ -12,7 +12,7 @@ namespace Server { // Captures context for a streaming request, implementing the Admin::Request interface. class ClustersRequest : public Admin::Request { public: - static constexpr uint64_t DefaultChunkSize = 2 * 1000 * 1000; + static constexpr uint64_t DefaultChunkSize = 2 << 20; // 2 MB Http::Code start(Http::ResponseHeaderMap& response_headers) override; bool nextChunk(Buffer::Instance& response) override; diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index 9df87ce7e4ca..0c208535f800 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -263,3 +263,14 @@ envoy_cc_test( "//test/mocks:common_lib", ], ) + +envoy_cc_test( + name = "clusters_request_test", + srcs = envoy_select_admin_functionality(["clusters_request_test.cc"]), + deps = [ + "//envoy/http:codes_interface", + "//source/common/buffer:buffer_lib", + "//source/server/admin:clusters_request_lib", + "//test/test_common:utility_lib", + ], +) diff --git a/test/server/admin/admin_test.cc b/test/server/admin/admin_test.cc index 0315397f86d6..3c57a8fcd337 100644 --- a/test/server/admin/admin_test.cc +++ b/test/server/admin/admin_test.cc @@ -144,6 +144,7 @@ TEST_P(AdminInstanceTest, Help) { /: Admin home page /certs: print certs on machine /clusters: upstream cluster status + format: The output format; One of (text, json) /config_dump: dump current Envoy configs (experimental) resource: The resource to dump mask: The mask to apply. When both resource and mask are specified, the mask is applied to every element in the desired repeated field so that only a subset of fields are returned. The mask is parsed as a ProtobufWkt::FieldMask diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc new file mode 100644 index 000000000000..b3aca1aa72e6 --- /dev/null +++ b/test/server/admin/clusters_request_test.cc @@ -0,0 +1,39 @@ +#include + +#include "envoy/http/codes.h" + +#include "source/common/buffer/buffer_impl.h" +#include "source/server/admin/clusters_request.h" + +#include "test/test_common/utility.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Server { + +class ClustersRequestTest : public testing::Test { +protected: + std::unique_ptr makeRequest() { return std::make_unique(); } + + struct ResponseResult { + Http::Code code_; + Buffer::OwnedImpl data_; + }; + + // Executes a request, returning the rendered buffer as a string. + ResponseResult response(ClustersRequest& request) { + Http::TestResponseHeaderMapImpl response_headers; + Http::Code code = request.start(response_headers); + Buffer::OwnedImpl data; + while (request.nextChunk(data)) { + } + return { + .code_ = code, + .data_ = std::move(data), + }; + } +}; + +} // namespace Server +} // namespace Envoy From 6f36f5c624c6e464d9fa9be55fabc159c37c6e1c Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 3 Apr 2024 04:18:38 +0000 Subject: [PATCH 08/77] Adding preliminary unit testing for clusters_request.cc Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 1 + source/server/admin/clusters_request.cc | 5 +++ source/server/admin/clusters_request.h | 4 ++ test/server/admin/BUILD | 2 + test/server/admin/clusters_request_test.cc | 46 +++++++++++++++++++--- 5 files changed, 52 insertions(+), 6 deletions(-) diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 2a613abd0195..1c7101dae140 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -420,5 +420,6 @@ envoy_cc_library( "//envoy/http:codes_interface", "//envoy/http:header_map_interface", "//envoy/server:admin_interface", + "//envoy/server:instance_interface", ], ) diff --git a/source/server/admin/clusters_request.cc b/source/server/admin/clusters_request.cc index b901397e652d..cc04ad605545 100644 --- a/source/server/admin/clusters_request.cc +++ b/source/server/admin/clusters_request.cc @@ -1,8 +1,12 @@ #include "source/server/admin/clusters_request.h" +#include "envoy/server/instance.h" + namespace Envoy { namespace Server { +ClustersRequest::ClustersRequest(Instance& server) : server_(server) {} + // TODO(demitriswan) Implement this member function. Http::Code ClustersRequest::start(Http::ResponseHeaderMap& response_headers) { UNREFERENCED_PARAMETER(response_headers); @@ -13,6 +17,7 @@ Http::Code ClustersRequest::start(Http::ResponseHeaderMap& response_headers) { bool ClustersRequest::nextChunk(Buffer::Instance& response) { UNREFERENCED_PARAMETER(response); UNREFERENCED_PARAMETER(chunk_size_); + UNREFERENCED_PARAMETER(server_); return false; } diff --git a/source/server/admin/clusters_request.h b/source/server/admin/clusters_request.h index ed424358fe36..1f14e89f14b4 100644 --- a/source/server/admin/clusters_request.h +++ b/source/server/admin/clusters_request.h @@ -5,6 +5,7 @@ #include "envoy/buffer/buffer.h" #include "envoy/http/codes.h" #include "envoy/server/admin.h" +#include "envoy/server/instance.h" namespace Envoy { namespace Server { @@ -14,11 +15,14 @@ class ClustersRequest : public Admin::Request { public: static constexpr uint64_t DefaultChunkSize = 2 << 20; // 2 MB + ClustersRequest(Instance& server); + Http::Code start(Http::ResponseHeaderMap& response_headers) override; bool nextChunk(Buffer::Instance& response) override; private: uint64_t chunk_size_{DefaultChunkSize}; + Server::Instance& server_; }; } // namespace Server diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index 0c208535f800..0ab61877510a 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -269,8 +269,10 @@ envoy_cc_test( srcs = envoy_select_admin_functionality(["clusters_request_test.cc"]), deps = [ "//envoy/http:codes_interface", + "//envoy/server:instance_interface", "//source/common/buffer:buffer_lib", "//source/server/admin:clusters_request_lib", + "//test/mocks/server:instance_mocks", "//test/test_common:utility_lib", ], ) diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index b3aca1aa72e6..41bab039da61 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -1,10 +1,12 @@ #include #include "envoy/http/codes.h" +#include "envoy/server/instance.h" #include "source/common/buffer/buffer_impl.h" #include "source/server/admin/clusters_request.h" +#include "test/mocks/server/instance.h" #include "test/test_common/utility.h" #include "gtest/gtest.h" @@ -12,28 +14,60 @@ namespace Envoy { namespace Server { -class ClustersRequestTest : public testing::Test { +class BaseClustersRequestFixture : public testing::Test { protected: - std::unique_ptr makeRequest() { return std::make_unique(); } + using ClustersRequestPtr = std::unique_ptr; + + ClustersRequestPtr makeRequest(Instance& server) { + return std::make_unique(server); + } struct ResponseResult { Http::Code code_; Buffer::OwnedImpl data_; }; - // Executes a request, returning the rendered buffer as a string. - ResponseResult response(ClustersRequest& request) { + ResponseResult response(ClustersRequest& request, bool drain_after_next_chunk) { Http::TestResponseHeaderMapImpl response_headers; Http::Code code = request.start(response_headers); Buffer::OwnedImpl data; + Buffer::OwnedImpl result_data; while (request.nextChunk(data)) { + if (drain_after_next_chunk) { + result_data.add(data); + data.drain(data.length()); + } } return { - .code_ = code, - .data_ = std::move(data), + /* code=*/code, + /* data=*/drain_after_next_chunk ? std::move(result_data) : std::move(data), }; } }; +struct VerifyJsonOutputParameters { + bool drain_; +}; + +class VerifyJsonOutputFixture : public BaseClustersRequestFixture, + public testing::WithParamInterface {}; + +TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { + VerifyJsonOutputParameters params = GetParam(); + MockInstance mock_server; + ResponseResult result = response(*makeRequest(mock_server), params.drain_); + EXPECT_EQ(result.code_, Http::Code::OK); + // TODO(demtiriswan) add expection for JSON here based on mock function results + // EXPECT_EQ(result.data_.toString(), "{}"); +} + +constexpr VerifyJsonOutputParameters VERIFY_JSON_CASES[] = { + {/* drain_=*/true}, + {/* drain_=*/false}, +}; + +INSTANTIATE_TEST_SUITE_P(VerifyJsonOutput, VerifyJsonOutputFixture, + testing::ValuesIn(VERIFY_JSON_CASES)); + } // namespace Server } // namespace Envoy From eeb5fbca4c228ef1e1963e98bd1b36abef9ca823 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 3 Apr 2024 17:07:39 +0000 Subject: [PATCH 09/77] Adding ClustersParams struct Signed-off-by: Demitri Swan --- envoy/http/BUILD | 1 + source/server/admin/BUILD | 15 +++++++++++++ source/server/admin/clusters_params.cc | 29 ++++++++++++++++++++++++++ source/server/admin/clusters_params.h | 21 +++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 source/server/admin/clusters_params.cc create mode 100644 source/server/admin/clusters_params.h diff --git a/envoy/http/BUILD b/envoy/http/BUILD index eea971bfec65..944ad8660f99 100644 --- a/envoy/http/BUILD +++ b/envoy/http/BUILD @@ -165,6 +165,7 @@ envoy_cc_library( name = "query_params_interface", hdrs = ["query_params.h"], external_deps = ["abseil_btree"], + deps = [":header_map_interface"], ) envoy_cc_library( diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 1c7101dae140..c249a3e4eefe 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -423,3 +423,18 @@ envoy_cc_library( "//envoy/server:instance_interface", ], ) + +envoy_cc_library( + name = "clusters_params_lib", + srcs = ["clusters_params.cc"], + hdrs = ["clusters_params.h"], + external_deps = [ + "abseil_optional", + "abseil_strings", + ], + deps = [ + "//envoy/buffer:buffer_interface", + "//envoy/http:codes_interface", + "//envoy/http:query_params_interface", + ], +) diff --git a/source/server/admin/clusters_params.cc b/source/server/admin/clusters_params.cc new file mode 100644 index 000000000000..4bfc80cf49f8 --- /dev/null +++ b/source/server/admin/clusters_params.cc @@ -0,0 +1,29 @@ +#include "source/server/admin/clusters_params.h" + +#include "envoy/http/codes.h" +#include "envoy/http/query_params.h" + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" + +namespace Envoy { +namespace Server { + +Http::Code ClustersParams::parse(absl::string_view url, Buffer::Instance& response) { + UNREFERENCED_PARAMETER(response); + + Http::Utility::QueryParamsMulti query = + Http::Utility::QueryParamsMulti::parseAndDecodeQueryString(url); + absl::optional optional_format = query.getFirstValue("format"); + if (optional_format.has_value()) { + if (*optional_format == "text") { + format_ = Format::Text; + } else { + format_ = Format::Json; + } + } + return Http::Code::OK; +} + +} // namespace Server +} // namespace Envoy diff --git a/source/server/admin/clusters_params.h b/source/server/admin/clusters_params.h new file mode 100644 index 000000000000..925bf9d761ef --- /dev/null +++ b/source/server/admin/clusters_params.h @@ -0,0 +1,21 @@ +#pragma once + +#include "envoy/buffer/buffer.h" +#include "envoy/http/codes.h" + +namespace Envoy { +namespace Server { + +class ClustersParams { + enum class Format { + Text, + Json, + }; + + Http::Code parse(std::string_view url, Buffer::Instance& response); + + Format format_; +}; + +} // namespace Server +} // namespace Envoy From 0060cce5bcdc9d54398fb767c0b0f5565c1bca95 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 3 Apr 2024 17:26:21 +0000 Subject: [PATCH 10/77] Add dependency on ClustersParams to ClustersRequest Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 1 + source/server/admin/clusters_params.h | 4 +++- source/server/admin/clusters_request.cc | 4 +++- source/server/admin/clusters_request.h | 5 ++++- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index c249a3e4eefe..e85f23918ae6 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -416,6 +416,7 @@ envoy_cc_library( srcs = ["clusters_request.cc"], hdrs = ["clusters_request.h"], deps = [ + ":clusters_params_lib", "//envoy/buffer:buffer_interface", "//envoy/http:codes_interface", "//envoy/http:header_map_interface", diff --git a/source/server/admin/clusters_params.h b/source/server/admin/clusters_params.h index 925bf9d761ef..bf50fa2a4851 100644 --- a/source/server/admin/clusters_params.h +++ b/source/server/admin/clusters_params.h @@ -3,6 +3,8 @@ #include "envoy/buffer/buffer.h" #include "envoy/http/codes.h" +#include "absl/strings/string_view.h" + namespace Envoy { namespace Server { @@ -12,7 +14,7 @@ class ClustersParams { Json, }; - Http::Code parse(std::string_view url, Buffer::Instance& response); + Http::Code parse(absl::string_view url, Buffer::Instance& response); Format format_; }; diff --git a/source/server/admin/clusters_request.cc b/source/server/admin/clusters_request.cc index cc04ad605545..1767af5bca62 100644 --- a/source/server/admin/clusters_request.cc +++ b/source/server/admin/clusters_request.cc @@ -5,7 +5,8 @@ namespace Envoy { namespace Server { -ClustersRequest::ClustersRequest(Instance& server) : server_(server) {} +ClustersRequest::ClustersRequest(Instance& server, const ClustersParams& params) + : server_(server), params_(params) {} // TODO(demitriswan) Implement this member function. Http::Code ClustersRequest::start(Http::ResponseHeaderMap& response_headers) { @@ -18,6 +19,7 @@ bool ClustersRequest::nextChunk(Buffer::Instance& response) { UNREFERENCED_PARAMETER(response); UNREFERENCED_PARAMETER(chunk_size_); UNREFERENCED_PARAMETER(server_); + UNREFERENCED_PARAMETER(params_); return false; } diff --git a/source/server/admin/clusters_request.h b/source/server/admin/clusters_request.h index 1f14e89f14b4..ac3a17991cde 100644 --- a/source/server/admin/clusters_request.h +++ b/source/server/admin/clusters_request.h @@ -7,6 +7,8 @@ #include "envoy/server/admin.h" #include "envoy/server/instance.h" +#include "source/server/admin/clusters_params.h" + namespace Envoy { namespace Server { @@ -15,7 +17,7 @@ class ClustersRequest : public Admin::Request { public: static constexpr uint64_t DefaultChunkSize = 2 << 20; // 2 MB - ClustersRequest(Instance& server); + ClustersRequest(Instance& server, const ClustersParams& params); Http::Code start(Http::ResponseHeaderMap& response_headers) override; bool nextChunk(Buffer::Instance& response) override; @@ -23,6 +25,7 @@ class ClustersRequest : public Admin::Request { private: uint64_t chunk_size_{DefaultChunkSize}; Server::Instance& server_; + const ClustersParams& params_; }; } // namespace Server From c104f43957a5160a33a8525f37a3dc9a7e88b2cf Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 3 Apr 2024 18:52:06 +0000 Subject: [PATCH 11/77] Adding the interface for ClustersRenderer. Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 10 +++++ source/server/admin/clusters_renderer.h | 54 +++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 source/server/admin/clusters_renderer.h diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index e85f23918ae6..d00e941d0ef8 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -439,3 +439,13 @@ envoy_cc_library( "//envoy/http:query_params_interface", ], ) + +envoy_cc_library( + name = "clusters_renderer_lib", + hdrs = ["clusters_renderer.h"], + deps = [ + "//envoy/buffer:buffer_interface", + "//envoy/common:pure_lib", + "//envoy/upstream:cluster_manager_interface", + ], +) diff --git a/source/server/admin/clusters_renderer.h b/source/server/admin/clusters_renderer.h new file mode 100644 index 000000000000..362bda62709f --- /dev/null +++ b/source/server/admin/clusters_renderer.h @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include "envoy/buffer/buffer.h" +#include "envoy/common/pure.h" +#include "envoy/upstream/cluster_manager.h" + +namespace Envoy { +namespace Server { + +class ClustersRenderer { +public: + virtual bool nextChunk() PURE; + // TODO(demitriswan) make sure this is the best way to handle destruction + // in a virtual base class such as this. + virtual ~ClustersRenderer() = default; + +protected: + virtual void render(std::reference_wrapper cluster) PURE; +}; + +class ClustersTextRenderer : ClustersRenderer { +public: + ClustersTextRenderer(Buffer::Instance& response, + const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map, + uint64_t chunk_limit); + bool nextChunk() override; + +private: + void render(std::reference_wrapper cluster) override; + + Buffer::Instance& response_; + const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map_; + const uint64_t chunk_limit_; +}; + +class ClustersJsonRenderer : ClustersRenderer { +public: + ClustersJsonRenderer(Buffer::Instance& response, + const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map, + uint64_t chunk_limit); + bool nextChunk() override; + +private: + void render(std::reference_wrapper cluster) override; + + Buffer::Instance& response_; + const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map_; + const uint16_t chunk_limit_; +}; + +} // namespace Server +} // namespace Envoy From 233def0331a190ab0060b283e5d4a59e61e116ca Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 3 Apr 2024 19:14:01 +0000 Subject: [PATCH 12/77] Adding ClustersRenderer implementation boilerplate Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 1 + source/server/admin/clusters_renderer.cc | 46 ++++++++++++++++++++++++ source/server/admin/clusters_renderer.h | 5 ++- 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 source/server/admin/clusters_renderer.cc diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index d00e941d0ef8..45709bc37113 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -442,6 +442,7 @@ envoy_cc_library( envoy_cc_library( name = "clusters_renderer_lib", + srcs = ["clusters_renderer.cc"], hdrs = ["clusters_renderer.h"], deps = [ "//envoy/buffer:buffer_interface", diff --git a/source/server/admin/clusters_renderer.cc b/source/server/admin/clusters_renderer.cc new file mode 100644 index 000000000000..373001e9dab7 --- /dev/null +++ b/source/server/admin/clusters_renderer.cc @@ -0,0 +1,46 @@ +#include "source/server/admin/clusters_renderer.h" + +#include "envoy/buffer/buffer.h" +#include "envoy/upstream/cluster_manager.h" + +namespace Envoy { +namespace Server { + +ClustersJsonRenderer::ClustersJsonRenderer( + Buffer::Instance& response, const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map, + uint64_t chunk_limit) + : response_{response}, cluster_info_map_{cluster_info_map}, chunk_limit_{chunk_limit} {} + +// TODO(demitriswan) implement using iterator state. +bool ClustersJsonRenderer::nextChunk() { + UNREFERENCED_PARAMETER(response_); + UNREFERENCED_PARAMETER(chunk_limit_); + UNREFERENCED_PARAMETER(cluster_info_map_); + return false; +} + +// TODO(demitriswan) implement. See clusters_handler.cc. +void ClustersJsonRenderer::render(std::reference_wrapper cluster) { + UNREFERENCED_PARAMETER(cluster); +} + +ClustersTextRenderer::ClustersTextRenderer( + Buffer::Instance& response, const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map, + uint64_t chunk_limit) + : response_{response}, cluster_info_map_{cluster_info_map}, chunk_limit_{chunk_limit} {} + +// TODO(demitriswan) implement using iterator state. +bool ClustersTextRenderer::nextChunk() { + UNREFERENCED_PARAMETER(response_); + UNREFERENCED_PARAMETER(chunk_limit_); + UNREFERENCED_PARAMETER(cluster_info_map_); + return false; +} + +// TODO(demitriswan) implement. See clusters_handler.cc. +void ClustersTextRenderer::render(std::reference_wrapper cluster) { + UNREFERENCED_PARAMETER(cluster); +} + +} // namespace Server +} // namespace Envoy diff --git a/source/server/admin/clusters_renderer.h b/source/server/admin/clusters_renderer.h index 362bda62709f..e1569d2856d5 100644 --- a/source/server/admin/clusters_renderer.h +++ b/source/server/admin/clusters_renderer.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "envoy/buffer/buffer.h" #include "envoy/common/pure.h" @@ -33,6 +34,7 @@ class ClustersTextRenderer : ClustersRenderer { Buffer::Instance& response_; const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map_; const uint64_t chunk_limit_; + Upstream::ClusterManager::ClusterInfoMap::const_iterator it_; }; class ClustersJsonRenderer : ClustersRenderer { @@ -47,7 +49,8 @@ class ClustersJsonRenderer : ClustersRenderer { Buffer::Instance& response_; const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map_; - const uint16_t chunk_limit_; + const uint64_t chunk_limit_; + Upstream::ClusterManager::ClusterInfoMap::const_iterator it_; }; } // namespace Server From 71c3b3e8a00422425b7e6e6ccf91b3f7e1a508f9 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 3 Apr 2024 21:09:39 +0000 Subject: [PATCH 13/77] Implemented ClustersRequest Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 1 + source/server/admin/clusters_params.h | 4 ++- source/server/admin/clusters_renderer.cc | 16 ++++++---- source/server/admin/clusters_renderer.h | 24 +++++++------- source/server/admin/clusters_request.cc | 40 ++++++++++++++++++------ source/server/admin/clusters_request.h | 12 +++++-- 6 files changed, 66 insertions(+), 31 deletions(-) diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 45709bc37113..064cdf8413a8 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -417,6 +417,7 @@ envoy_cc_library( hdrs = ["clusters_request.h"], deps = [ ":clusters_params_lib", + ":clusters_renderer_lib", "//envoy/buffer:buffer_interface", "//envoy/http:codes_interface", "//envoy/http:header_map_interface", diff --git a/source/server/admin/clusters_params.h b/source/server/admin/clusters_params.h index bf50fa2a4851..d09dc9da439f 100644 --- a/source/server/admin/clusters_params.h +++ b/source/server/admin/clusters_params.h @@ -8,12 +8,14 @@ namespace Envoy { namespace Server { -class ClustersParams { +struct ClustersParams { enum class Format { Text, Json, }; + ClustersParams() = default; + Http::Code parse(absl::string_view url, Buffer::Instance& response); Format format_; diff --git a/source/server/admin/clusters_renderer.cc b/source/server/admin/clusters_renderer.cc index 373001e9dab7..7876a8a8da8a 100644 --- a/source/server/admin/clusters_renderer.cc +++ b/source/server/admin/clusters_renderer.cc @@ -7,12 +7,14 @@ namespace Envoy { namespace Server { ClustersJsonRenderer::ClustersJsonRenderer( - Buffer::Instance& response, const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map, - uint64_t chunk_limit) - : response_{response}, cluster_info_map_{cluster_info_map}, chunk_limit_{chunk_limit} {} + uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, + const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map) + : chunk_limit_{chunk_limit}, response_headers_{response_headers}, response_{response}, + cluster_info_map_{cluster_info_map}, it_{cluster_info_map.begin()} {} // TODO(demitriswan) implement using iterator state. bool ClustersJsonRenderer::nextChunk() { + UNREFERENCED_PARAMETER(response_headers_); UNREFERENCED_PARAMETER(response_); UNREFERENCED_PARAMETER(chunk_limit_); UNREFERENCED_PARAMETER(cluster_info_map_); @@ -25,12 +27,14 @@ void ClustersJsonRenderer::render(std::reference_wrapper cluster) PURE; }; -class ClustersTextRenderer : ClustersRenderer { +class ClustersTextRenderer : public ClustersRenderer { public: - ClustersTextRenderer(Buffer::Instance& response, - const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map, - uint64_t chunk_limit); + ClustersTextRenderer(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, + Buffer::Instance& response, + const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map); bool nextChunk() override; private: void render(std::reference_wrapper cluster) override; - + const uint64_t chunk_limit_; + Http::ResponseHeaderMap& response_headers_; Buffer::Instance& response_; const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map_; - const uint64_t chunk_limit_; Upstream::ClusterManager::ClusterInfoMap::const_iterator it_; }; -class ClustersJsonRenderer : ClustersRenderer { +class ClustersJsonRenderer : public ClustersRenderer { public: - ClustersJsonRenderer(Buffer::Instance& response, - const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map, - uint64_t chunk_limit); + ClustersJsonRenderer(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, + Buffer::Instance& response, + const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map); bool nextChunk() override; private: void render(std::reference_wrapper cluster) override; - + const uint64_t chunk_limit_; + Http::ResponseHeaderMap& response_headers_; Buffer::Instance& response_; const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map_; - const uint64_t chunk_limit_; Upstream::ClusterManager::ClusterInfoMap::const_iterator it_; }; diff --git a/source/server/admin/clusters_request.cc b/source/server/admin/clusters_request.cc index 1767af5bca62..2a83912d00ed 100644 --- a/source/server/admin/clusters_request.cc +++ b/source/server/admin/clusters_request.cc @@ -1,26 +1,48 @@ #include "source/server/admin/clusters_request.h" +#include +#include + #include "envoy/server/instance.h" +#include "source/server/admin/clusters_renderer.h" + +#include "clusters_params.h" + namespace Envoy { namespace Server { -ClustersRequest::ClustersRequest(Instance& server, const ClustersParams& params) - : server_(server), params_(params) {} +ClustersRequest::ClustersRequest(uint64_t chunk_limit, Instance& server, Buffer::Instance& response, + const ClustersParams& params) + : chunk_limit_(chunk_limit), server_(server), response_(response), params_(params) {} -// TODO(demitriswan) Implement this member function. Http::Code ClustersRequest::start(Http::ResponseHeaderMap& response_headers) { - UNREFERENCED_PARAMETER(response_headers); + switch (params_.format_) { + case ClustersParams::Format::Text: + renderer_ = std::make_unique( + chunk_limit_, response_headers, response_, + server_.clusterManager().clusters().active_clusters_); + break; + case ClustersParams::Format::Json: + renderer_ = std::make_unique( + chunk_limit_, response_headers, response_, + server_.clusterManager().clusters().active_clusters_); + break; + default: + // TODO(demitriswan) handle this case properly. + return Http::Code::BadRequest; + } return Http::Code::OK; } -// TODO(demitriswan) Implement this member function. bool ClustersRequest::nextChunk(Buffer::Instance& response) { + // When reading the documentation for Request, I noted that it was + // a mistake to add the buffer to the call to nextChunk since we + // should always process the same buffer on each call. So, in this + // implementation I ignore the parameter here and use the buffer + // associated with this request from cosntruction. UNREFERENCED_PARAMETER(response); - UNREFERENCED_PARAMETER(chunk_size_); - UNREFERENCED_PARAMETER(server_); - UNREFERENCED_PARAMETER(params_); - return false; + return renderer_->nextChunk(); } } // namespace Server diff --git a/source/server/admin/clusters_request.h b/source/server/admin/clusters_request.h index ac3a17991cde..06973c6ddaa5 100644 --- a/source/server/admin/clusters_request.h +++ b/source/server/admin/clusters_request.h @@ -8,6 +8,7 @@ #include "envoy/server/instance.h" #include "source/server/admin/clusters_params.h" +#include "source/server/admin/clusters_renderer.h" namespace Envoy { namespace Server { @@ -15,17 +16,22 @@ namespace Server { // Captures context for a streaming request, implementing the Admin::Request interface. class ClustersRequest : public Admin::Request { public: - static constexpr uint64_t DefaultChunkSize = 2 << 20; // 2 MB + static constexpr uint64_t DefaultChunkLimit = 2 << 20; // 2 MB - ClustersRequest(Instance& server, const ClustersParams& params); + ClustersRequest(uint64_t chunk_limit, Instance& server, Buffer::Instance& response, + const ClustersParams& params); Http::Code start(Http::ResponseHeaderMap& response_headers) override; bool nextChunk(Buffer::Instance& response) override; private: - uint64_t chunk_size_{DefaultChunkSize}; + using ClustersRendererPtr = std::unique_ptr; + + uint64_t chunk_limit_{DefaultChunkLimit}; Server::Instance& server_; + Buffer::Instance& response_; const ClustersParams& params_; + ClustersRendererPtr renderer_; }; } // namespace Server From a6658f6902bbfed9c06de49f8dbc9439bd05c533 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 3 Apr 2024 22:48:17 +0000 Subject: [PATCH 14/77] Updating unit tests to accommodate new dependencies Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 1 + source/server/admin/clusters_handler.cc | 10 +++++-- source/server/admin/stats_handler.cc | 1 + test/server/admin/clusters_request_test.cc | 35 ++++++++++++++++------ 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 064cdf8413a8..ea25d758f372 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -343,6 +343,7 @@ envoy_cc_library( "//source/common/http:codes_lib", "//source/common/http:header_map_lib", "//source/common/upstream:host_utility_lib", + "//source/server/admin:clusters_params_lib", "@envoy_api//envoy/admin/v3:pkg_cc_proto", ], ) diff --git a/source/server/admin/clusters_handler.cc b/source/server/admin/clusters_handler.cc index c40910cba238..0e5d67cb8c74 100644 --- a/source/server/admin/clusters_handler.cc +++ b/source/server/admin/clusters_handler.cc @@ -2,11 +2,14 @@ #include "envoy/admin/v3/clusters.pb.h" +#include "source/common/buffer/buffer_impl.h" #include "source/common/common/macros.h" #include "source/common/http/headers.h" #include "source/common/http/utility.h" #include "source/common/network/utility.h" #include "source/common/upstream/host_utility.h" +#include "source/server/admin/clusters_params.h" +#include "source/server/admin/clusters_request.h" #include "source/server/admin/utils.h" namespace Envoy { @@ -59,8 +62,11 @@ Http::Code ClustersHandler::handlerClusters(Http::ResponseHeaderMap& response_he // TODO(demitriswan) Implement this member function. Admin::RequestPtr ClustersHandler::makeRequest(AdminStream& admin_stream) { - UNREFERENCED_PARAMETER(admin_stream); - return nullptr; + Buffer::OwnedImpl response; + ClustersParams params; + params.parse(admin_stream.getRequestHeaders().getPathValue(), response); + return std::make_unique(ClustersRequest::DefaultChunkLimit, server_, response, + params); } // Helper method that ensures that we've setting flags based on all the health flag values on the diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 7430ef103c58..97a7295ca01f 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -10,6 +10,7 @@ #include "source/common/common/empty_string.h" #include "source/common/http/headers.h" #include "source/common/http/utility.h" +#include "source/server/admin/clusters_request.h" #include "source/server/admin/prometheus_stats.h" #include "source/server/admin/stats_request.h" diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index 41bab039da61..36d7370fd39e 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -1,12 +1,15 @@ #include +#include "envoy/buffer/buffer.h" #include "envoy/http/codes.h" #include "envoy/server/instance.h" #include "source/common/buffer/buffer_impl.h" +#include "source/server/admin/clusters_params.h" #include "source/server/admin/clusters_request.h" #include "test/mocks/server/instance.h" +#include "test/mocks/upstream/cluster_manager.h" #include "test/test_common/utility.h" #include "gtest/gtest.h" @@ -14,12 +17,16 @@ namespace Envoy { namespace Server { +using testing::Return; +using testing::ReturnRef; + class BaseClustersRequestFixture : public testing::Test { protected: using ClustersRequestPtr = std::unique_ptr; - ClustersRequestPtr makeRequest(Instance& server) { - return std::make_unique(server); + ClustersRequestPtr makeRequest(uint64_t chunk_limit, Instance& server, Buffer::Instance& buffer, + ClustersParams& params) { + return std::make_unique(chunk_limit, server, buffer, params); } struct ResponseResult { @@ -27,20 +34,20 @@ class BaseClustersRequestFixture : public testing::Test { Buffer::OwnedImpl data_; }; - ResponseResult response(ClustersRequest& request, bool drain_after_next_chunk) { + ResponseResult response(ClustersRequest& request, bool drain_after_next_chunk, + Buffer::Instance& buffer) { Http::TestResponseHeaderMapImpl response_headers; Http::Code code = request.start(response_headers); - Buffer::OwnedImpl data; Buffer::OwnedImpl result_data; - while (request.nextChunk(data)) { + while (request.nextChunk(buffer)) { if (drain_after_next_chunk) { - result_data.add(data); - data.drain(data.length()); + result_data.add(buffer); + buffer.drain(buffer.length()); } } return { /* code=*/code, - /* data=*/drain_after_next_chunk ? std::move(result_data) : std::move(data), + /* data=*/drain_after_next_chunk ? std::move(result_data) : std::move(buffer), }; } }; @@ -55,7 +62,17 @@ class VerifyJsonOutputFixture : public BaseClustersRequestFixture, TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { VerifyJsonOutputParameters params = GetParam(); MockInstance mock_server; - ResponseResult result = response(*makeRequest(mock_server), params.drain_); + Upstream::MockClusterManager mock_cluster_manager; + Buffer::OwnedImpl buffer; + ClustersParams clusters_params; + Upstream::ClusterManager::ClusterInfoMaps cluster_info_maps; + clusters_params.format_ = ClustersParams::Format::Json; + EXPECT_CALL(mock_server, clusterManager()).WillOnce(ReturnRef(mock_cluster_manager)); + EXPECT_CALL(mock_cluster_manager, clusters()).WillOnce(Return(cluster_info_maps)); + + ResponseResult result = + response(*makeRequest(1, mock_server, buffer, clusters_params), params.drain_, buffer); + EXPECT_EQ(result.code_, Http::Code::OK); // TODO(demtiriswan) add expection for JSON here based on mock function results // EXPECT_EQ(result.data_.toString(), "{}"); From c9c7d3a08d77d58273f0355271e1c7b06c2eb1fb Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Thu, 4 Apr 2024 06:38:21 +0000 Subject: [PATCH 15/77] Implement ClustersTextRenderer Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 3 + source/server/admin/clusters_renderer.cc | 113 ++++++++++++++++++++- source/server/admin/clusters_renderer.h | 12 +++ source/server/admin/stats_handler.cc | 1 - test/server/admin/clusters_request_test.cc | 18 ++++ 5 files changed, 142 insertions(+), 5 deletions(-) diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index ea25d758f372..890ad24af5fd 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -336,6 +336,7 @@ envoy_cc_library( deps = [ ":handler_ctx_lib", ":utils_lib", + "//envoy/buffer:buffer_interface", "//envoy/http:codes_interface", "//envoy/server:admin_interface", "//envoy/server:instance_interface", @@ -344,6 +345,7 @@ envoy_cc_library( "//source/common/http:header_map_lib", "//source/common/upstream:host_utility_lib", "//source/server/admin:clusters_params_lib", + "//source/server/admin:clusters_request_lib", "@envoy_api//envoy/admin/v3:pkg_cc_proto", ], ) @@ -450,5 +452,6 @@ envoy_cc_library( "//envoy/buffer:buffer_interface", "//envoy/common:pure_lib", "//envoy/upstream:cluster_manager_interface", + "//source/common/upstream:host_utility_lib", ], ) diff --git a/source/server/admin/clusters_renderer.cc b/source/server/admin/clusters_renderer.cc index 7876a8a8da8a..bbf48ae17fdb 100644 --- a/source/server/admin/clusters_renderer.cc +++ b/source/server/admin/clusters_renderer.cc @@ -1,7 +1,12 @@ #include "source/server/admin/clusters_renderer.h" +#include + #include "envoy/buffer/buffer.h" #include "envoy/upstream/cluster_manager.h" +#include "envoy/upstream/resource_manager.h" + +#include "source/common/upstream/host_utility.h" namespace Envoy { namespace Server { @@ -32,12 +37,75 @@ ClustersTextRenderer::ClustersTextRenderer( : chunk_limit_{chunk_limit}, response_headers_{response_headers}, response_{response}, cluster_info_map_{cluster_info_map}, it_{cluster_info_map.begin()} {} -// TODO(demitriswan) implement using iterator state. bool ClustersTextRenderer::nextChunk() { + const uint64_t original_request_size = response_.length(); + for (; + it_ != cluster_info_map_.end() && response_.length() - original_request_size < chunk_limit_; + it_++) { + const Upstream::Cluster& cluster = it_->second.get(); + const std::string& cluster_name = cluster.info()->name(); + response_.add(fmt::format("{}::observability_name::{}\n", cluster_name, + cluster.info()->observabilityName())); + addOutlierInfo(cluster_name, cluster.outlierDetector(), response_); + + addCircuitBreakerSettings(cluster_name, "default", + cluster.info()->resourceManager(Upstream::ResourcePriority::Default), + response_); + addCircuitBreakerSettings(cluster_name, "high", + cluster.info()->resourceManager(Upstream::ResourcePriority::High), + response_); + + response_.add( + fmt::format("{}::added_via_api::{}\n", cluster_name, cluster.info()->addedViaApi())); + if (const auto& name = cluster.info()->edsServiceName(); !name.empty()) { + response_.add(fmt::format("{}::eds_service_name::{}\n", cluster_name, name)); + } + for (auto& host_set : cluster.prioritySet().hostSetsPerPriority()) { + for (auto& host : host_set->hosts()) { + const std::string& host_address = host->address()->asString(); + std::map all_stats; + for (const auto& [counter_name, counter] : host->counters()) { + all_stats[counter_name] = counter.get().value(); + } + + for (const auto& [gauge_name, gauge] : host->gauges()) { + all_stats[gauge_name] = gauge.get().value(); + } + + for (const auto& [stat_name, stat] : all_stats) { + response_.add( + fmt::format("{}::{}::{}::{}\n", cluster_name, host_address, stat_name, stat)); + } + + response_.add( + fmt::format("{}::{}::hostname::{}\n", cluster_name, host_address, host->hostname())); + response_.add(fmt::format("{}::{}::health_flags::{}\n", cluster_name, host_address, + Upstream::HostUtility::healthFlagsToString(*host))); + response_.add( + fmt::format("{}::{}::weight::{}\n", cluster_name, host_address, host->weight())); + response_.add(fmt::format("{}::{}::region::{}\n", cluster_name, host_address, + host->locality().region())); + response_.add( + fmt::format("{}::{}::zone::{}\n", cluster_name, host_address, host->locality().zone())); + response_.add(fmt::format("{}::{}::sub_zone::{}\n", cluster_name, host_address, + host->locality().sub_zone())); + response_.add( + fmt::format("{}::{}::canary::{}\n", cluster_name, host_address, host->canary())); + response_.add( + fmt::format("{}::{}::priority::{}\n", cluster_name, host_address, host->priority())); + response_.add(fmt::format( + "{}::{}::success_rate::{}\n", cluster_name, host_address, + host->outlierDetector().successRate( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))); + response_.add(fmt::format( + "{}::{}::local_origin_success_rate::{}\n", cluster_name, host_address, + host->outlierDetector().successRate( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))); + } + } + } + // TODO(demitriswan) See if these need to be updated here. UNREFERENCED_PARAMETER(response_headers_); - UNREFERENCED_PARAMETER(response_); - UNREFERENCED_PARAMETER(chunk_limit_); - UNREFERENCED_PARAMETER(cluster_info_map_); return false; } @@ -46,5 +114,42 @@ void ClustersTextRenderer::render(std::reference_wrappersuccessRateAverage( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))); + response.add(fmt::format( + "{}::outlier::success_rate_ejection_threshold::{:g}\n", cluster_name, + outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))); + response.add(fmt::format( + "{}::outlier::local_origin_success_rate_average::{:g}\n", cluster_name, + outlier_detector->successRateAverage( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))); + response.add(fmt::format( + "{}::outlier::local_origin_success_rate_ejection_threshold::{:g}\n", cluster_name, + outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))); + } +} + +void ClustersTextRenderer::addCircuitBreakerSettings(const std::string& cluster_name, + const std::string& priority_str, + Upstream::ResourceManager& resource_manager, + Buffer::Instance& response) { + response.add(fmt::format("{}::{}_priority::max_connections::{}\n", cluster_name, priority_str, + resource_manager.connections().max())); + response.add(fmt::format("{}::{}_priority::max_pending_requests::{}\n", cluster_name, + priority_str, resource_manager.pendingRequests().max())); + response.add(fmt::format("{}::{}_priority::max_requests::{}\n", cluster_name, priority_str, + resource_manager.requests().max())); + response.add(fmt::format("{}::{}_priority::max_retries::{}\n", cluster_name, priority_str, + resource_manager.retries().max())); +} + } // namespace Server } // namespace Envoy diff --git a/source/server/admin/clusters_renderer.h b/source/server/admin/clusters_renderer.h index 0d83bd020311..54b5eb301e1f 100644 --- a/source/server/admin/clusters_renderer.h +++ b/source/server/admin/clusters_renderer.h @@ -2,10 +2,13 @@ #include #include +#include #include "envoy/buffer/buffer.h" #include "envoy/common/pure.h" #include "envoy/upstream/cluster_manager.h" +#include "envoy/upstream/outlier_detection.h" +#include "envoy/upstream/resource_manager.h" namespace Envoy { namespace Server { @@ -18,6 +21,8 @@ class ClustersRenderer { virtual ~ClustersRenderer() = default; protected: + // render is protected to suggest to implementations that this method should be + // implemented to properly implement nextChunk. virtual void render(std::reference_wrapper cluster) PURE; }; @@ -30,6 +35,13 @@ class ClustersTextRenderer : public ClustersRenderer { private: void render(std::reference_wrapper cluster) override; + static void addOutlierInfo(const std::string& cluster_name, + const Upstream::Outlier::Detector* outlier_detector, + Buffer::Instance& response); + static void addCircuitBreakerSettings(const std::string& cluster_name, + const std::string& priority_str, + Upstream::ResourceManager& resource_manager, + Buffer::Instance& response); const uint64_t chunk_limit_; Http::ResponseHeaderMap& response_headers_; Buffer::Instance& response_; diff --git a/source/server/admin/stats_handler.cc b/source/server/admin/stats_handler.cc index 97a7295ca01f..7430ef103c58 100644 --- a/source/server/admin/stats_handler.cc +++ b/source/server/admin/stats_handler.cc @@ -10,7 +10,6 @@ #include "source/common/common/empty_string.h" #include "source/common/http/headers.h" #include "source/common/http/utility.h" -#include "source/server/admin/clusters_request.h" #include "source/server/admin/prometheus_stats.h" #include "source/server/admin/stats_request.h" diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index 36d7370fd39e..b47db6e8d9af 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -86,5 +86,23 @@ constexpr VerifyJsonOutputParameters VERIFY_JSON_CASES[] = { INSTANTIATE_TEST_SUITE_P(VerifyJsonOutput, VerifyJsonOutputFixture, testing::ValuesIn(VERIFY_JSON_CASES)); +struct VerifyTextOutputParameters { + bool drain_; +}; + +class VerifyTextOutputFixture : public BaseClustersRequestFixture, + public testing::WithParamInterface {}; + +// TODO(demitriswan) Implement test for text output verification. +TEST_P(VerifyTextOutputFixture, VerifyTextOutput) {} + +constexpr VerifyTextOutputParameters VERIFY_TEXT_CASES[] = { + {/* drain_=*/true}, + {/* drain_=*/false}, +}; + +INSTANTIATE_TEST_SUITE_P(VerifyTextOutput, VerifyTextOutputFixture, + testing::ValuesIn(VERIFY_TEXT_CASES)); + } // namespace Server } // namespace Envoy From 1638f2e99e9ee04e196e120787e97a5c409374d7 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Thu, 4 Apr 2024 22:17:41 +0000 Subject: [PATCH 16/77] Modify iteration strategy for the text renderer Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 2 + source/server/admin/admin_filter.cc | 1 + source/server/admin/clusters_handler.cc | 3 +- source/server/admin/clusters_renderer.cc | 79 ++++++++++++---------- source/server/admin/clusters_renderer.h | 14 ++-- source/server/admin/clusters_request.cc | 20 ++---- source/server/admin/clusters_request.h | 6 +- test/server/admin/clusters_request_test.cc | 12 ++-- 8 files changed, 65 insertions(+), 72 deletions(-) diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 890ad24af5fd..fad9d34249a4 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -426,6 +426,7 @@ envoy_cc_library( "//envoy/http:header_map_interface", "//envoy/server:admin_interface", "//envoy/server:instance_interface", + "//source/common/common:logger_lib", ], ) @@ -452,6 +453,7 @@ envoy_cc_library( "//envoy/buffer:buffer_interface", "//envoy/common:pure_lib", "//envoy/upstream:cluster_manager_interface", + "//source/common/common:logger_lib", "//source/common/upstream:host_utility_lib", ], ) diff --git a/source/server/admin/admin_filter.cc b/source/server/admin/admin_filter.cc index d9dee2576612..3b885c34cabe 100644 --- a/source/server/admin/admin_filter.cc +++ b/source/server/admin/admin_filter.cc @@ -96,6 +96,7 @@ void AdminFilter::onComplete() { bool more_data; do { Buffer::OwnedImpl response; + ENVOY_LOG_MISC(debug, "about to call next chunk"); more_data = handler->nextChunk(response); bool end_stream = end_stream_on_complete_ && !more_data; ENVOY_LOG_MISC(debug, "nextChunk: response.length={} more_data={} end_stream={}", diff --git a/source/server/admin/clusters_handler.cc b/source/server/admin/clusters_handler.cc index 0e5d67cb8c74..f508ac68c970 100644 --- a/source/server/admin/clusters_handler.cc +++ b/source/server/admin/clusters_handler.cc @@ -65,8 +65,7 @@ Admin::RequestPtr ClustersHandler::makeRequest(AdminStream& admin_stream) { Buffer::OwnedImpl response; ClustersParams params; params.parse(admin_stream.getRequestHeaders().getPathValue(), response); - return std::make_unique(ClustersRequest::DefaultChunkLimit, server_, response, - params); + return std::make_unique(ClustersRequest::DefaultChunkLimit, server_, params); } // Helper method that ensures that we've setting flags based on all the health flag values on the diff --git a/source/server/admin/clusters_renderer.cc b/source/server/admin/clusters_renderer.cc index bbf48ae17fdb..c546ab892b46 100644 --- a/source/server/admin/clusters_renderer.cc +++ b/source/server/admin/clusters_renderer.cc @@ -1,26 +1,28 @@ #include "source/server/admin/clusters_renderer.h" #include +#include #include "envoy/buffer/buffer.h" #include "envoy/upstream/cluster_manager.h" #include "envoy/upstream/resource_manager.h" +#include "source/common/common/logger.h" #include "source/common/upstream/host_utility.h" namespace Envoy { namespace Server { ClustersJsonRenderer::ClustersJsonRenderer( - uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, + uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map) - : chunk_limit_{chunk_limit}, response_headers_{response_headers}, response_{response}, - cluster_info_map_{cluster_info_map}, it_{cluster_info_map.begin()} {} + : chunk_limit_(chunk_limit), response_headers_(response_headers), + cluster_info_map_(cluster_info_map), it_(cluster_info_map.cbegin()) {} // TODO(demitriswan) implement using iterator state. -bool ClustersJsonRenderer::nextChunk() { +bool ClustersJsonRenderer::nextChunk(Buffer::Instance& response) { + UNREFERENCED_PARAMETER(response); UNREFERENCED_PARAMETER(response_headers_); - UNREFERENCED_PARAMETER(response_); UNREFERENCED_PARAMETER(chunk_limit_); UNREFERENCED_PARAMETER(cluster_info_map_); return false; @@ -32,33 +34,36 @@ void ClustersJsonRenderer::render(std::reference_wrappersecond.get(); + : chunk_limit_(chunk_limit), response_headers_(response_headers), idx_(0) { + std::vector> v; + for (const auto& pair : cluster_info_map) { + clusters_.push_back(pair.second); + } +} + +bool ClustersTextRenderer::nextChunk(Buffer::Instance& response) { + const uint64_t original_request_size = response.length(); + for (; idx_ < clusters_.size() && response.length() - original_request_size < chunk_limit_; + idx_++) { + const Upstream::Cluster& cluster = clusters_[idx_].get(); const std::string& cluster_name = cluster.info()->name(); - response_.add(fmt::format("{}::observability_name::{}\n", cluster_name, - cluster.info()->observabilityName())); - addOutlierInfo(cluster_name, cluster.outlierDetector(), response_); + response.add(fmt::format("{}::observability_name::{}\n", cluster_name, + cluster.info()->observabilityName())); + addOutlierInfo(cluster_name, cluster.outlierDetector(), response); addCircuitBreakerSettings(cluster_name, "default", cluster.info()->resourceManager(Upstream::ResourcePriority::Default), - response_); + response); addCircuitBreakerSettings(cluster_name, "high", cluster.info()->resourceManager(Upstream::ResourcePriority::High), - response_); + response); - response_.add( + response.add( fmt::format("{}::added_via_api::{}\n", cluster_name, cluster.info()->addedViaApi())); if (const auto& name = cluster.info()->edsServiceName(); !name.empty()) { - response_.add(fmt::format("{}::eds_service_name::{}\n", cluster_name, name)); + response.add(fmt::format("{}::eds_service_name::{}\n", cluster_name, name)); } for (auto& host_set : cluster.prioritySet().hostSetsPerPriority()) { for (auto& host : host_set->hosts()) { @@ -73,31 +78,31 @@ bool ClustersTextRenderer::nextChunk() { } for (const auto& [stat_name, stat] : all_stats) { - response_.add( + response.add( fmt::format("{}::{}::{}::{}\n", cluster_name, host_address, stat_name, stat)); } - response_.add( + response.add( fmt::format("{}::{}::hostname::{}\n", cluster_name, host_address, host->hostname())); - response_.add(fmt::format("{}::{}::health_flags::{}\n", cluster_name, host_address, - Upstream::HostUtility::healthFlagsToString(*host))); - response_.add( + response.add(fmt::format("{}::{}::health_flags::{}\n", cluster_name, host_address, + Upstream::HostUtility::healthFlagsToString(*host))); + response.add( fmt::format("{}::{}::weight::{}\n", cluster_name, host_address, host->weight())); - response_.add(fmt::format("{}::{}::region::{}\n", cluster_name, host_address, - host->locality().region())); - response_.add( + response.add(fmt::format("{}::{}::region::{}\n", cluster_name, host_address, + host->locality().region())); + response.add( fmt::format("{}::{}::zone::{}\n", cluster_name, host_address, host->locality().zone())); - response_.add(fmt::format("{}::{}::sub_zone::{}\n", cluster_name, host_address, - host->locality().sub_zone())); - response_.add( + response.add(fmt::format("{}::{}::sub_zone::{}\n", cluster_name, host_address, + host->locality().sub_zone())); + response.add( fmt::format("{}::{}::canary::{}\n", cluster_name, host_address, host->canary())); - response_.add( + response.add( fmt::format("{}::{}::priority::{}\n", cluster_name, host_address, host->priority())); - response_.add(fmt::format( + response.add(fmt::format( "{}::{}::success_rate::{}\n", cluster_name, host_address, host->outlierDetector().successRate( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))); - response_.add(fmt::format( + response.add(fmt::format( "{}::{}::local_origin_success_rate::{}\n", cluster_name, host_address, host->outlierDetector().successRate( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))); @@ -106,7 +111,7 @@ bool ClustersTextRenderer::nextChunk() { } // TODO(demitriswan) See if these need to be updated here. UNREFERENCED_PARAMETER(response_headers_); - return false; + return idx_ < clusters_.size() ? true : false; } // TODO(demitriswan) implement. See clusters_handler.cc. diff --git a/source/server/admin/clusters_renderer.h b/source/server/admin/clusters_renderer.h index 54b5eb301e1f..852761b8b9e1 100644 --- a/source/server/admin/clusters_renderer.h +++ b/source/server/admin/clusters_renderer.h @@ -15,7 +15,7 @@ namespace Server { class ClustersRenderer { public: - virtual bool nextChunk() PURE; + virtual bool nextChunk(Buffer::Instance& response) PURE; // TODO(demitriswan) make sure this is the best way to handle destruction // in a virtual base class such as this. virtual ~ClustersRenderer() = default; @@ -29,9 +29,8 @@ class ClustersRenderer { class ClustersTextRenderer : public ClustersRenderer { public: ClustersTextRenderer(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, - Buffer::Instance& response, const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map); - bool nextChunk() override; + bool nextChunk(Buffer::Instance& response) override; private: void render(std::reference_wrapper cluster) override; @@ -44,23 +43,20 @@ class ClustersTextRenderer : public ClustersRenderer { Buffer::Instance& response); const uint64_t chunk_limit_; Http::ResponseHeaderMap& response_headers_; - Buffer::Instance& response_; - const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map_; - Upstream::ClusterManager::ClusterInfoMap::const_iterator it_; + std::vector> clusters_; + uint64_t idx_; }; class ClustersJsonRenderer : public ClustersRenderer { public: ClustersJsonRenderer(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, - Buffer::Instance& response, const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map); - bool nextChunk() override; + bool nextChunk(Buffer::Instance& response) override; private: void render(std::reference_wrapper cluster) override; const uint64_t chunk_limit_; Http::ResponseHeaderMap& response_headers_; - Buffer::Instance& response_; const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map_; Upstream::ClusterManager::ClusterInfoMap::const_iterator it_; }; diff --git a/source/server/admin/clusters_request.cc b/source/server/admin/clusters_request.cc index 2a83912d00ed..155f8cdc9d4d 100644 --- a/source/server/admin/clusters_request.cc +++ b/source/server/admin/clusters_request.cc @@ -1,10 +1,12 @@ #include "source/server/admin/clusters_request.h" #include +#include #include #include "envoy/server/instance.h" +#include "source/common/common/logger.h" #include "source/server/admin/clusters_renderer.h" #include "clusters_params.h" @@ -12,21 +14,19 @@ namespace Envoy { namespace Server { -ClustersRequest::ClustersRequest(uint64_t chunk_limit, Instance& server, Buffer::Instance& response, +ClustersRequest::ClustersRequest(uint64_t chunk_limit, Instance& server, const ClustersParams& params) - : chunk_limit_(chunk_limit), server_(server), response_(response), params_(params) {} + : chunk_limit_(chunk_limit), server_(server), params_(params) {} Http::Code ClustersRequest::start(Http::ResponseHeaderMap& response_headers) { switch (params_.format_) { case ClustersParams::Format::Text: renderer_ = std::make_unique( - chunk_limit_, response_headers, response_, - server_.clusterManager().clusters().active_clusters_); + chunk_limit_, response_headers, server_.clusterManager().clusters().active_clusters_); break; case ClustersParams::Format::Json: renderer_ = std::make_unique( - chunk_limit_, response_headers, response_, - server_.clusterManager().clusters().active_clusters_); + chunk_limit_, response_headers, server_.clusterManager().clusters().active_clusters_); break; default: // TODO(demitriswan) handle this case properly. @@ -36,13 +36,7 @@ Http::Code ClustersRequest::start(Http::ResponseHeaderMap& response_headers) { } bool ClustersRequest::nextChunk(Buffer::Instance& response) { - // When reading the documentation for Request, I noted that it was - // a mistake to add the buffer to the call to nextChunk since we - // should always process the same buffer on each call. So, in this - // implementation I ignore the parameter here and use the buffer - // associated with this request from cosntruction. - UNREFERENCED_PARAMETER(response); - return renderer_->nextChunk(); + return renderer_->nextChunk(response); } } // namespace Server diff --git a/source/server/admin/clusters_request.h b/source/server/admin/clusters_request.h index 06973c6ddaa5..55ac8d92acb3 100644 --- a/source/server/admin/clusters_request.h +++ b/source/server/admin/clusters_request.h @@ -18,8 +18,7 @@ class ClustersRequest : public Admin::Request { public: static constexpr uint64_t DefaultChunkLimit = 2 << 20; // 2 MB - ClustersRequest(uint64_t chunk_limit, Instance& server, Buffer::Instance& response, - const ClustersParams& params); + ClustersRequest(uint64_t chunk_limit, Instance& server, const ClustersParams& params); Http::Code start(Http::ResponseHeaderMap& response_headers) override; bool nextChunk(Buffer::Instance& response) override; @@ -29,9 +28,8 @@ class ClustersRequest : public Admin::Request { uint64_t chunk_limit_{DefaultChunkLimit}; Server::Instance& server_; - Buffer::Instance& response_; const ClustersParams& params_; - ClustersRendererPtr renderer_; + ClustersRendererPtr renderer_ = nullptr; }; } // namespace Server diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index b47db6e8d9af..c7314f9431fe 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -24,9 +24,8 @@ class BaseClustersRequestFixture : public testing::Test { protected: using ClustersRequestPtr = std::unique_ptr; - ClustersRequestPtr makeRequest(uint64_t chunk_limit, Instance& server, Buffer::Instance& buffer, - ClustersParams& params) { - return std::make_unique(chunk_limit, server, buffer, params); + ClustersRequestPtr makeRequest(uint64_t chunk_limit, Instance& server, ClustersParams& params) { + return std::make_unique(chunk_limit, server, params); } struct ResponseResult { @@ -34,10 +33,10 @@ class BaseClustersRequestFixture : public testing::Test { Buffer::OwnedImpl data_; }; - ResponseResult response(ClustersRequest& request, bool drain_after_next_chunk, - Buffer::Instance& buffer) { + ResponseResult response(ClustersRequest& request, bool drain_after_next_chunk) { Http::TestResponseHeaderMapImpl response_headers; Http::Code code = request.start(response_headers); + Buffer::OwnedImpl buffer; Buffer::OwnedImpl result_data; while (request.nextChunk(buffer)) { if (drain_after_next_chunk) { @@ -70,8 +69,7 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { EXPECT_CALL(mock_server, clusterManager()).WillOnce(ReturnRef(mock_cluster_manager)); EXPECT_CALL(mock_cluster_manager, clusters()).WillOnce(Return(cluster_info_maps)); - ResponseResult result = - response(*makeRequest(1, mock_server, buffer, clusters_params), params.drain_, buffer); + ResponseResult result = response(*makeRequest(1, mock_server, clusters_params), params.drain_); EXPECT_EQ(result.code_, Http::Code::OK); // TODO(demtiriswan) add expection for JSON here based on mock function results From 253b0d2d7fc8860b0f5eed479abfc9477f9a9f44 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Fri, 19 Apr 2024 21:14:22 +0000 Subject: [PATCH 17/77] Implement render to simplify nextChunk Signed-off-by: Demitri Swan --- source/server/admin/clusters_renderer.cc | 130 ++++++++++++----------- source/server/admin/clusters_renderer.h | 6 +- 2 files changed, 69 insertions(+), 67 deletions(-) diff --git a/source/server/admin/clusters_renderer.cc b/source/server/admin/clusters_renderer.cc index c546ab892b46..ddf9276e5db3 100644 --- a/source/server/admin/clusters_renderer.cc +++ b/source/server/admin/clusters_renderer.cc @@ -29,8 +29,9 @@ bool ClustersJsonRenderer::nextChunk(Buffer::Instance& response) { } // TODO(demitriswan) implement. See clusters_handler.cc. -void ClustersJsonRenderer::render(std::reference_wrapper cluster) { +void ClustersJsonRenderer::render(std::reference_wrapper cluster, Buffer::Instance& response) { UNREFERENCED_PARAMETER(cluster); + UNREFERENCED_PARAMETER(response); } ClustersTextRenderer::ClustersTextRenderer( @@ -47,78 +48,79 @@ bool ClustersTextRenderer::nextChunk(Buffer::Instance& response) { const uint64_t original_request_size = response.length(); for (; idx_ < clusters_.size() && response.length() - original_request_size < chunk_limit_; idx_++) { - const Upstream::Cluster& cluster = clusters_[idx_].get(); - const std::string& cluster_name = cluster.info()->name(); - response.add(fmt::format("{}::observability_name::{}\n", cluster_name, - cluster.info()->observabilityName())); - addOutlierInfo(cluster_name, cluster.outlierDetector(), response); - - addCircuitBreakerSettings(cluster_name, "default", - cluster.info()->resourceManager(Upstream::ResourcePriority::Default), - response); - addCircuitBreakerSettings(cluster_name, "high", - cluster.info()->resourceManager(Upstream::ResourcePriority::High), - response); - - response.add( - fmt::format("{}::added_via_api::{}\n", cluster_name, cluster.info()->addedViaApi())); - if (const auto& name = cluster.info()->edsServiceName(); !name.empty()) { - response.add(fmt::format("{}::eds_service_name::{}\n", cluster_name, name)); - } - for (auto& host_set : cluster.prioritySet().hostSetsPerPriority()) { - for (auto& host : host_set->hosts()) { - const std::string& host_address = host->address()->asString(); - std::map all_stats; - for (const auto& [counter_name, counter] : host->counters()) { - all_stats[counter_name] = counter.get().value(); - } - - for (const auto& [gauge_name, gauge] : host->gauges()) { - all_stats[gauge_name] = gauge.get().value(); - } - - for (const auto& [stat_name, stat] : all_stats) { - response.add( - fmt::format("{}::{}::{}::{}\n", cluster_name, host_address, stat_name, stat)); - } - response.add( - fmt::format("{}::{}::hostname::{}\n", cluster_name, host_address, host->hostname())); - response.add(fmt::format("{}::{}::health_flags::{}\n", cluster_name, host_address, - Upstream::HostUtility::healthFlagsToString(*host))); - response.add( - fmt::format("{}::{}::weight::{}\n", cluster_name, host_address, host->weight())); - response.add(fmt::format("{}::{}::region::{}\n", cluster_name, host_address, - host->locality().region())); - response.add( - fmt::format("{}::{}::zone::{}\n", cluster_name, host_address, host->locality().zone())); - response.add(fmt::format("{}::{}::sub_zone::{}\n", cluster_name, host_address, - host->locality().sub_zone())); - response.add( - fmt::format("{}::{}::canary::{}\n", cluster_name, host_address, host->canary())); - response.add( - fmt::format("{}::{}::priority::{}\n", cluster_name, host_address, host->priority())); - response.add(fmt::format( - "{}::{}::success_rate::{}\n", cluster_name, host_address, - host->outlierDetector().successRate( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))); - response.add(fmt::format( - "{}::{}::local_origin_success_rate::{}\n", cluster_name, host_address, - host->outlierDetector().successRate( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))); - } - } + render(clusters_[idx_], response); } // TODO(demitriswan) See if these need to be updated here. UNREFERENCED_PARAMETER(response_headers_); return idx_ < clusters_.size() ? true : false; } -// TODO(demitriswan) implement. See clusters_handler.cc. -void ClustersTextRenderer::render(std::reference_wrapper cluster) { - UNREFERENCED_PARAMETER(cluster); +void ClustersTextRenderer::render(std::reference_wrapper cluster, Buffer::Instance& response) { + const Upstream::Cluster& unwrapped_cluster = cluster.get(); + const std::string& cluster_name = unwrapped_cluster.info()->name(); + response.add(fmt::format("{}::observability_name::{}\n", cluster_name, + unwrapped_cluster.info()->observabilityName())); + addOutlierInfo(cluster_name, unwrapped_cluster.outlierDetector(), response); + + addCircuitBreakerSettings(cluster_name, "default", + unwrapped_cluster.info()->resourceManager(Upstream::ResourcePriority::Default), + response); + addCircuitBreakerSettings(cluster_name, "high", + unwrapped_cluster.info()->resourceManager(Upstream::ResourcePriority::High), + response); + + response.add( + fmt::format("{}::added_via_api::{}\n", cluster_name, unwrapped_cluster.info()->addedViaApi())); + if (const auto& name = unwrapped_cluster.info()->edsServiceName(); !name.empty()) { + response.add(fmt::format("{}::eds_service_name::{}\n", cluster_name, name)); + } + for (auto& host_set : unwrapped_cluster.prioritySet().hostSetsPerPriority()) { + for (auto& host : host_set->hosts()) { + const std::string& host_address = host->address()->asString(); + std::map all_stats; + for (const auto& [counter_name, counter] : host->counters()) { + all_stats[counter_name] = counter.get().value(); + } + + for (const auto& [gauge_name, gauge] : host->gauges()) { + all_stats[gauge_name] = gauge.get().value(); + } + + for (const auto& [stat_name, stat] : all_stats) { + response.add( + fmt::format("{}::{}::{}::{}\n", cluster_name, host_address, stat_name, stat)); + } + + response.add( + fmt::format("{}::{}::hostname::{}\n", cluster_name, host_address, host->hostname())); + response.add(fmt::format("{}::{}::health_flags::{}\n", cluster_name, host_address, + Upstream::HostUtility::healthFlagsToString(*host))); + response.add( + fmt::format("{}::{}::weight::{}\n", cluster_name, host_address, host->weight())); + response.add(fmt::format("{}::{}::region::{}\n", cluster_name, host_address, + host->locality().region())); + response.add( + fmt::format("{}::{}::zone::{}\n", cluster_name, host_address, host->locality().zone())); + response.add(fmt::format("{}::{}::sub_zone::{}\n", cluster_name, host_address, + host->locality().sub_zone())); + response.add( + fmt::format("{}::{}::canary::{}\n", cluster_name, host_address, host->canary())); + response.add( + fmt::format("{}::{}::priority::{}\n", cluster_name, host_address, host->priority())); + response.add(fmt::format( + "{}::{}::success_rate::{}\n", cluster_name, host_address, + host->outlierDetector().successRate( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))); + response.add(fmt::format( + "{}::{}::local_origin_success_rate::{}\n", cluster_name, host_address, + host->outlierDetector().successRate( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))); + } + } } + void ClustersTextRenderer::addOutlierInfo(const std::string& cluster_name, const Upstream::Outlier::Detector* outlier_detector, Buffer::Instance& response) { diff --git a/source/server/admin/clusters_renderer.h b/source/server/admin/clusters_renderer.h index 852761b8b9e1..f4e03d2db0a3 100644 --- a/source/server/admin/clusters_renderer.h +++ b/source/server/admin/clusters_renderer.h @@ -23,7 +23,7 @@ class ClustersRenderer { protected: // render is protected to suggest to implementations that this method should be // implemented to properly implement nextChunk. - virtual void render(std::reference_wrapper cluster) PURE; + virtual void render(std::reference_wrapper cluster, Buffer::Instance& response) PURE; }; class ClustersTextRenderer : public ClustersRenderer { @@ -33,7 +33,7 @@ class ClustersTextRenderer : public ClustersRenderer { bool nextChunk(Buffer::Instance& response) override; private: - void render(std::reference_wrapper cluster) override; + void render(std::reference_wrapper cluster, Buffer::Instance& response) override; static void addOutlierInfo(const std::string& cluster_name, const Upstream::Outlier::Detector* outlier_detector, Buffer::Instance& response); @@ -54,7 +54,7 @@ class ClustersJsonRenderer : public ClustersRenderer { bool nextChunk(Buffer::Instance& response) override; private: - void render(std::reference_wrapper cluster) override; + void render(std::reference_wrapper cluster, Buffer::Instance& response) override; const uint64_t chunk_limit_; Http::ResponseHeaderMap& response_headers_; const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map_; From d19238a8f82ec8e7123f83cb9fa6e373460976d0 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Sat, 20 Apr 2024 18:44:49 +0000 Subject: [PATCH 18/77] Add clusters_params_test.cc Signed-off-by: Demitri Swan --- source/server/admin/clusters_params.cc | 6 ++-- source/server/admin/clusters_params.h | 2 +- source/server/admin/clusters_renderer.cc | 32 +++++++++--------- source/server/admin/clusters_renderer.h | 9 +++-- test/server/admin/BUILD | 9 +++++ test/server/admin/clusters_params_test.cc | 41 +++++++++++++++++++++++ 6 files changed, 75 insertions(+), 24 deletions(-) create mode 100644 test/server/admin/clusters_params_test.cc diff --git a/source/server/admin/clusters_params.cc b/source/server/admin/clusters_params.cc index 4bfc80cf49f8..866c49645965 100644 --- a/source/server/admin/clusters_params.cc +++ b/source/server/admin/clusters_params.cc @@ -16,10 +16,10 @@ Http::Code ClustersParams::parse(absl::string_view url, Buffer::Instance& respon Http::Utility::QueryParamsMulti::parseAndDecodeQueryString(url); absl::optional optional_format = query.getFirstValue("format"); if (optional_format.has_value()) { - if (*optional_format == "text") { - format_ = Format::Text; - } else { + if (*optional_format == "json") { format_ = Format::Json; + } else { + format_ = Format::Text; } } return Http::Code::OK; diff --git a/source/server/admin/clusters_params.h b/source/server/admin/clusters_params.h index d09dc9da439f..ae299cfeabfd 100644 --- a/source/server/admin/clusters_params.h +++ b/source/server/admin/clusters_params.h @@ -18,7 +18,7 @@ struct ClustersParams { Http::Code parse(absl::string_view url, Buffer::Instance& response); - Format format_; + Format format_{Format::Text}; }; } // namespace Server diff --git a/source/server/admin/clusters_renderer.cc b/source/server/admin/clusters_renderer.cc index ddf9276e5db3..aba511317531 100644 --- a/source/server/admin/clusters_renderer.cc +++ b/source/server/admin/clusters_renderer.cc @@ -29,7 +29,8 @@ bool ClustersJsonRenderer::nextChunk(Buffer::Instance& response) { } // TODO(demitriswan) implement. See clusters_handler.cc. -void ClustersJsonRenderer::render(std::reference_wrapper cluster, Buffer::Instance& response) { +void ClustersJsonRenderer::render(std::reference_wrapper cluster, + Buffer::Instance& response) { UNREFERENCED_PARAMETER(cluster); UNREFERENCED_PARAMETER(response); } @@ -56,22 +57,23 @@ bool ClustersTextRenderer::nextChunk(Buffer::Instance& response) { return idx_ < clusters_.size() ? true : false; } -void ClustersTextRenderer::render(std::reference_wrapper cluster, Buffer::Instance& response) { +void ClustersTextRenderer::render(std::reference_wrapper cluster, + Buffer::Instance& response) { const Upstream::Cluster& unwrapped_cluster = cluster.get(); const std::string& cluster_name = unwrapped_cluster.info()->name(); response.add(fmt::format("{}::observability_name::{}\n", cluster_name, unwrapped_cluster.info()->observabilityName())); addOutlierInfo(cluster_name, unwrapped_cluster.outlierDetector(), response); - addCircuitBreakerSettings(cluster_name, "default", - unwrapped_cluster.info()->resourceManager(Upstream::ResourcePriority::Default), - response); - addCircuitBreakerSettings(cluster_name, "high", - unwrapped_cluster.info()->resourceManager(Upstream::ResourcePriority::High), - response); + addCircuitBreakerSettings( + cluster_name, "default", + unwrapped_cluster.info()->resourceManager(Upstream::ResourcePriority::Default), response); + addCircuitBreakerSettings( + cluster_name, "high", + unwrapped_cluster.info()->resourceManager(Upstream::ResourcePriority::High), response); - response.add( - fmt::format("{}::added_via_api::{}\n", cluster_name, unwrapped_cluster.info()->addedViaApi())); + response.add(fmt::format("{}::added_via_api::{}\n", cluster_name, + unwrapped_cluster.info()->addedViaApi())); if (const auto& name = unwrapped_cluster.info()->edsServiceName(); !name.empty()) { response.add(fmt::format("{}::eds_service_name::{}\n", cluster_name, name)); } @@ -88,24 +90,21 @@ void ClustersTextRenderer::render(std::reference_wrapperhostname())); response.add(fmt::format("{}::{}::health_flags::{}\n", cluster_name, host_address, Upstream::HostUtility::healthFlagsToString(*host))); - response.add( - fmt::format("{}::{}::weight::{}\n", cluster_name, host_address, host->weight())); + response.add(fmt::format("{}::{}::weight::{}\n", cluster_name, host_address, host->weight())); response.add(fmt::format("{}::{}::region::{}\n", cluster_name, host_address, host->locality().region())); response.add( fmt::format("{}::{}::zone::{}\n", cluster_name, host_address, host->locality().zone())); response.add(fmt::format("{}::{}::sub_zone::{}\n", cluster_name, host_address, host->locality().sub_zone())); - response.add( - fmt::format("{}::{}::canary::{}\n", cluster_name, host_address, host->canary())); + response.add(fmt::format("{}::{}::canary::{}\n", cluster_name, host_address, host->canary())); response.add( fmt::format("{}::{}::priority::{}\n", cluster_name, host_address, host->priority())); response.add(fmt::format( @@ -120,7 +119,6 @@ void ClustersTextRenderer::render(std::reference_wrapper cluster, Buffer::Instance& response) PURE; + virtual void render(std::reference_wrapper cluster, + Buffer::Instance& response) PURE; }; class ClustersTextRenderer : public ClustersRenderer { @@ -33,7 +34,8 @@ class ClustersTextRenderer : public ClustersRenderer { bool nextChunk(Buffer::Instance& response) override; private: - void render(std::reference_wrapper cluster, Buffer::Instance& response) override; + void render(std::reference_wrapper cluster, + Buffer::Instance& response) override; static void addOutlierInfo(const std::string& cluster_name, const Upstream::Outlier::Detector* outlier_detector, Buffer::Instance& response); @@ -54,7 +56,8 @@ class ClustersJsonRenderer : public ClustersRenderer { bool nextChunk(Buffer::Instance& response) override; private: - void render(std::reference_wrapper cluster, Buffer::Instance& response) override; + void render(std::reference_wrapper cluster, + Buffer::Instance& response) override; const uint64_t chunk_limit_; Http::ResponseHeaderMap& response_headers_; const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map_; diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index 0ab61877510a..bca0c4d7bf3d 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -264,6 +264,15 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "clusters_params_test", + srcs = envoy_select_admin_functionality(["clusters_params_test.cc"]), + deps = [ + "//source/common/buffer:buffer_lib", + "//source/server/admin:clusters_params_lib", + ], +) + envoy_cc_test( name = "clusters_request_test", srcs = envoy_select_admin_functionality(["clusters_request_test.cc"]), diff --git a/test/server/admin/clusters_params_test.cc b/test/server/admin/clusters_params_test.cc new file mode 100644 index 000000000000..9676cf4ff9aa --- /dev/null +++ b/test/server/admin/clusters_params_test.cc @@ -0,0 +1,41 @@ +#include "source/common/buffer/buffer_impl.h" +#include "source/server/admin/clusters_params.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Server { + +struct ParamsCase { + std::string url_; + ClustersParams::Format expected_format_; + Http::Code expected_code_; +}; + +class ParamsFixture : public testing::TestWithParam {}; + +TEST(ClustersParamsTest, ClustersParamsHasExpectedDefaultValue) { + ClustersParams params; + EXPECT_EQ(params.format_, ClustersParams::Format::Text); +} + +TEST_P(ParamsFixture, ClustersParamsHasExpectedFormatAndStatusCode) { + ClustersParams params; + Buffer::OwnedImpl buffer; + ParamsCase test_case = GetParam(); + Http::Code code = params.parse(test_case.url_, buffer); + + EXPECT_EQ(params.format_, test_case.expected_format_); + EXPECT_EQ(code, test_case.expected_code_); +} + +INSTANTIATE_TEST_SUITE_P( + AllCases, ParamsFixture, + testing::ValuesIn({ + {"localhost:1337/clusters", ClustersParams::Format::Text, Http::Code::OK}, + {"localhsot:1337/clusters?format=text", ClustersParams::Format::Text, Http::Code::OK}, + {"localhost:1337/clusters?format=json", ClustersParams::Format::Json, Http::Code::OK}, + })); + +} // namespace Server +} // namespace Envoy From f4600bad89dd1266930efbc707075ae86c02c3a3 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Sat, 20 Apr 2024 22:33:02 +0000 Subject: [PATCH 19/77] Refactor of rendering. Separate cluster rendering from iteration. Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 1 + source/server/admin/clusters_renderer.cc | 83 ++++++++++++++---------- source/server/admin/clusters_renderer.h | 57 ++++++++++------ source/server/admin/clusters_request.cc | 6 +- source/server/admin/clusters_request.h | 4 +- 5 files changed, 89 insertions(+), 62 deletions(-) diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index fad9d34249a4..4c37d126a4db 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -454,6 +454,7 @@ envoy_cc_library( "//envoy/common:pure_lib", "//envoy/upstream:cluster_manager_interface", "//source/common/common:logger_lib", + "//source/common/json:json_streamer_lib", "//source/common/upstream:host_utility_lib", ], ) diff --git a/source/server/admin/clusters_renderer.cc b/source/server/admin/clusters_renderer.cc index aba511317531..a463b04f13bf 100644 --- a/source/server/admin/clusters_renderer.cc +++ b/source/server/admin/clusters_renderer.cc @@ -2,63 +2,41 @@ #include #include +#include #include "envoy/buffer/buffer.h" #include "envoy/upstream/cluster_manager.h" #include "envoy/upstream/resource_manager.h" -#include "source/common/common/logger.h" #include "source/common/upstream/host_utility.h" namespace Envoy { namespace Server { -ClustersJsonRenderer::ClustersJsonRenderer( +TextClustersChunkProcessor::TextClustersChunkProcessor( uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map) : chunk_limit_(chunk_limit), response_headers_(response_headers), - cluster_info_map_(cluster_info_map), it_(cluster_info_map.cbegin()) {} - -// TODO(demitriswan) implement using iterator state. -bool ClustersJsonRenderer::nextChunk(Buffer::Instance& response) { - UNREFERENCED_PARAMETER(response); - UNREFERENCED_PARAMETER(response_headers_); - UNREFERENCED_PARAMETER(chunk_limit_); - UNREFERENCED_PARAMETER(cluster_info_map_); - return false; -} - -// TODO(demitriswan) implement. See clusters_handler.cc. -void ClustersJsonRenderer::render(std::reference_wrapper cluster, - Buffer::Instance& response) { - UNREFERENCED_PARAMETER(cluster); - UNREFERENCED_PARAMETER(response); -} - -ClustersTextRenderer::ClustersTextRenderer( - uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, - const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map) - : chunk_limit_(chunk_limit), response_headers_(response_headers), idx_(0) { - std::vector> v; + renderer_(std::make_unique()), idx_(0) { for (const auto& pair : cluster_info_map) { clusters_.push_back(pair.second); } } -bool ClustersTextRenderer::nextChunk(Buffer::Instance& response) { +bool TextClustersChunkProcessor::nextChunk(Buffer::Instance& response) { const uint64_t original_request_size = response.length(); for (; idx_ < clusters_.size() && response.length() - original_request_size < chunk_limit_; idx_++) { - render(clusters_[idx_], response); + renderer_->render(clusters_[idx_], response); } // TODO(demitriswan) See if these need to be updated here. UNREFERENCED_PARAMETER(response_headers_); return idx_ < clusters_.size() ? true : false; } -void ClustersTextRenderer::render(std::reference_wrapper cluster, - Buffer::Instance& response) { +void TextClusterRenderer::render(std::reference_wrapper cluster, + Buffer::Instance& response) { const Upstream::Cluster& unwrapped_cluster = cluster.get(); const std::string& cluster_name = unwrapped_cluster.info()->name(); response.add(fmt::format("{}::observability_name::{}\n", cluster_name, @@ -119,9 +97,9 @@ void ClustersTextRenderer::render(std::reference_wrapper()), idx_(0) { + for (const auto& pair : cluster_info_map) { + clusters_.push_back(pair.second); + } +} + +void JsonClusterRenderer::render(std::reference_wrapper cluster, + Buffer::Instance& response) { + UNREFERENCED_PARAMETER(cluster); + UNREFERENCED_PARAMETER(response); +} + +bool JsonClustersChunkProcessor::nextChunk(Buffer::Instance& response) { + // TODO(demitriswan) See if these need to be updated here. + UNREFERENCED_PARAMETER(response_headers_); + + const uint64_t original_request_size = response.length(); + response.add("["); + for (; idx_ < clusters_.size() && response.length() - original_request_size < chunk_limit_; + idx_++) { + + renderer_->render(clusters_[idx_], response); + } + if (idx_ < clusters_.size()) { + return true; + } + response.add("]"); + return false; +} } // namespace Server } // namespace Envoy diff --git a/source/server/admin/clusters_renderer.h b/source/server/admin/clusters_renderer.h index 2ca38eaf12fd..8177aa345903 100644 --- a/source/server/admin/clusters_renderer.h +++ b/source/server/admin/clusters_renderer.h @@ -10,32 +10,31 @@ #include "envoy/upstream/outlier_detection.h" #include "envoy/upstream/resource_manager.h" +#include "source/common/json/json_streamer.h" + namespace Envoy { namespace Server { -class ClustersRenderer { +class ClustersChunkProcessor { public: virtual bool nextChunk(Buffer::Instance& response) PURE; - // TODO(demitriswan) make sure this is the best way to handle destruction - // in a virtual base class such as this. - virtual ~ClustersRenderer() = default; + virtual ~ClustersChunkProcessor() = default; +}; -protected: - // render is protected to suggest to implementations that this method should be - // implemented to properly implement nextChunk. +class ClusterRenderer { +public: virtual void render(std::reference_wrapper cluster, Buffer::Instance& response) PURE; + virtual ~ClusterRenderer() = default; }; -class ClustersTextRenderer : public ClustersRenderer { +class TextClusterRenderer : public ClusterRenderer { public: - ClustersTextRenderer(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, - const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map); - bool nextChunk(Buffer::Instance& response) override; - -private: + TextClusterRenderer() = default; void render(std::reference_wrapper cluster, Buffer::Instance& response) override; + +private: static void addOutlierInfo(const std::string& cluster_name, const Upstream::Outlier::Detector* outlier_detector, Buffer::Instance& response); @@ -43,25 +42,43 @@ class ClustersTextRenderer : public ClustersRenderer { const std::string& priority_str, Upstream::ResourceManager& resource_manager, Buffer::Instance& response); +}; + +class TextClustersChunkProcessor : public ClustersChunkProcessor { +public: + TextClustersChunkProcessor(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, + const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map); + + bool nextChunk(Buffer::Instance& response) override; + +private: const uint64_t chunk_limit_; Http::ResponseHeaderMap& response_headers_; std::vector> clusters_; + std::unique_ptr renderer_; uint64_t idx_; }; -class ClustersJsonRenderer : public ClustersRenderer { +class JsonClusterRenderer : public ClusterRenderer { +public: + JsonClusterRenderer() = default; + void render(std::reference_wrapper cluster, + Buffer::Instance& response) override; +}; + +class JsonClustersChunkProcessor : public ClustersChunkProcessor { public: - ClustersJsonRenderer(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, - const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map); + JsonClustersChunkProcessor(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, + const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map); + bool nextChunk(Buffer::Instance& response) override; private: - void render(std::reference_wrapper cluster, - Buffer::Instance& response) override; const uint64_t chunk_limit_; Http::ResponseHeaderMap& response_headers_; - const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map_; - Upstream::ClusterManager::ClusterInfoMap::const_iterator it_; + std::vector> clusters_; + std::unique_ptr renderer_; + uint64_t idx_; }; } // namespace Server diff --git a/source/server/admin/clusters_request.cc b/source/server/admin/clusters_request.cc index 155f8cdc9d4d..a025b5cf0f18 100644 --- a/source/server/admin/clusters_request.cc +++ b/source/server/admin/clusters_request.cc @@ -21,11 +21,11 @@ ClustersRequest::ClustersRequest(uint64_t chunk_limit, Instance& server, Http::Code ClustersRequest::start(Http::ResponseHeaderMap& response_headers) { switch (params_.format_) { case ClustersParams::Format::Text: - renderer_ = std::make_unique( + chunk_processor_ = std::make_unique( chunk_limit_, response_headers, server_.clusterManager().clusters().active_clusters_); break; case ClustersParams::Format::Json: - renderer_ = std::make_unique( + chunk_processor_ = std::make_unique( chunk_limit_, response_headers, server_.clusterManager().clusters().active_clusters_); break; default: @@ -36,7 +36,7 @@ Http::Code ClustersRequest::start(Http::ResponseHeaderMap& response_headers) { } bool ClustersRequest::nextChunk(Buffer::Instance& response) { - return renderer_->nextChunk(response); + return chunk_processor_->nextChunk(response); } } // namespace Server diff --git a/source/server/admin/clusters_request.h b/source/server/admin/clusters_request.h index 55ac8d92acb3..a11db69c91d0 100644 --- a/source/server/admin/clusters_request.h +++ b/source/server/admin/clusters_request.h @@ -24,12 +24,10 @@ class ClustersRequest : public Admin::Request { bool nextChunk(Buffer::Instance& response) override; private: - using ClustersRendererPtr = std::unique_ptr; - uint64_t chunk_limit_{DefaultChunkLimit}; Server::Instance& server_; const ClustersParams& params_; - ClustersRendererPtr renderer_ = nullptr; + std::unique_ptr chunk_processor_ = nullptr; }; } // namespace Server From 6b5cc1dbe4eaf39164ea4cf3e3331aabe5196af9 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Mon, 22 Apr 2024 14:52:59 +0000 Subject: [PATCH 20/77] refactor and start Json implementation Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 10 ++- ...enderer.cc => clusters_chunk_processor.cc} | 57 ++++++++++---- ..._renderer.h => clusters_chunk_processor.h} | 61 +++++++-------- source/server/admin/clusters_request.cc | 2 +- source/server/admin/clusters_request.h | 2 +- test/server/admin/BUILD | 3 + test/server/admin/clusters_request_test.cc | 75 ++++++++++++++----- 7 files changed, 141 insertions(+), 69 deletions(-) rename source/server/admin/{clusters_renderer.cc => clusters_chunk_processor.cc} (75%) rename source/server/admin/{clusters_renderer.h => clusters_chunk_processor.h} (72%) diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 4c37d126a4db..807b9d6b672b 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -420,12 +420,13 @@ envoy_cc_library( hdrs = ["clusters_request.h"], deps = [ ":clusters_params_lib", - ":clusters_renderer_lib", + ":clusters_chunk_processor_lib", "//envoy/buffer:buffer_interface", "//envoy/http:codes_interface", "//envoy/http:header_map_interface", "//envoy/server:admin_interface", "//envoy/server:instance_interface", + "//source/common/buffer:buffer_lib", "//source/common/common:logger_lib", ], ) @@ -446,9 +447,9 @@ envoy_cc_library( ) envoy_cc_library( - name = "clusters_renderer_lib", - srcs = ["clusters_renderer.cc"], - hdrs = ["clusters_renderer.h"], + name = "clusters_chunk_processor_lib", + srcs = ["clusters_chunk_processor.cc"], + hdrs = ["clusters_chunk_processor.h"], deps = [ "//envoy/buffer:buffer_interface", "//envoy/common:pure_lib", @@ -456,5 +457,6 @@ envoy_cc_library( "//source/common/common:logger_lib", "//source/common/json:json_streamer_lib", "//source/common/upstream:host_utility_lib", + "@envoy_api//envoy/admin/v3:pkg_cc_proto", ], ) diff --git a/source/server/admin/clusters_renderer.cc b/source/server/admin/clusters_chunk_processor.cc similarity index 75% rename from source/server/admin/clusters_renderer.cc rename to source/server/admin/clusters_chunk_processor.cc index a463b04f13bf..d63353584b1c 100644 --- a/source/server/admin/clusters_renderer.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -1,14 +1,16 @@ -#include "source/server/admin/clusters_renderer.h" +#include "source/server/admin/clusters_chunk_processor.h" #include #include #include +#include "envoy/admin/v3/clusters.pb.h" #include "envoy/buffer/buffer.h" #include "envoy/upstream/cluster_manager.h" #include "envoy/upstream/resource_manager.h" #include "source/common/upstream/host_utility.h" +#include "source/common/buffer/buffer_impl.h" namespace Envoy { namespace Server { @@ -17,7 +19,7 @@ TextClustersChunkProcessor::TextClustersChunkProcessor( uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map) : chunk_limit_(chunk_limit), response_headers_(response_headers), - renderer_(std::make_unique()), idx_(0) { + idx_(0) { for (const auto& pair : cluster_info_map) { clusters_.push_back(pair.second); } @@ -28,15 +30,15 @@ bool TextClustersChunkProcessor::nextChunk(Buffer::Instance& response) { for (; idx_ < clusters_.size() && response.length() - original_request_size < chunk_limit_; idx_++) { - renderer_->render(clusters_[idx_], response); + render(clusters_[idx_], response); } // TODO(demitriswan) See if these need to be updated here. UNREFERENCED_PARAMETER(response_headers_); return idx_ < clusters_.size() ? true : false; } -void TextClusterRenderer::render(std::reference_wrapper cluster, - Buffer::Instance& response) { +void TextClustersChunkProcessor::render(std::reference_wrapper cluster, + Buffer::Instance& response) { const Upstream::Cluster& unwrapped_cluster = cluster.get(); const std::string& cluster_name = unwrapped_cluster.info()->name(); response.add(fmt::format("{}::observability_name::{}\n", cluster_name, @@ -97,7 +99,7 @@ void TextClusterRenderer::render(std::reference_wrapper } } -void TextClusterRenderer::addOutlierInfo(const std::string& cluster_name, +void TextClustersChunkProcessor::addOutlierInfo(const std::string& cluster_name, const Upstream::Outlier::Detector* outlier_detector, Buffer::Instance& response) { if (outlier_detector) { @@ -120,7 +122,7 @@ void TextClusterRenderer::addOutlierInfo(const std::string& cluster_name, } } -void TextClusterRenderer::addCircuitBreakerSettings(const std::string& cluster_name, +void TextClustersChunkProcessor::addCircuitBreakerSettings(const std::string& cluster_name, const std::string& priority_str, Upstream::ResourceManager& resource_manager, Buffer::Instance& response) { @@ -133,20 +135,32 @@ void TextClusterRenderer::addCircuitBreakerSettings(const std::string& cluster_n response.add(fmt::format("{}::{}_priority::max_retries::{}\n", cluster_name, priority_str, resource_manager.retries().max())); } + JsonClustersChunkProcessor::JsonClustersChunkProcessor( uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map) : chunk_limit_(chunk_limit), response_headers_(response_headers), - renderer_(std::make_unique()), idx_(0) { + idx_(0) { for (const auto& pair : cluster_info_map) { clusters_.push_back(pair.second); } + std::unique_ptr streamer = std::make_unique(buffer_); + Json::Streamer::MapPtr root_map = streamer->makeRootMap(); + root_map->addKey("cluster_statuses"); + Json::Streamer::ArrayPtr clusters = root_map->addArray(); + json_context_holder_.push_back(std::make_unique( + std::move(streamer), buffer_, + std::move(root_map), std::move(clusters))); } -void JsonClusterRenderer::render(std::reference_wrapper cluster, - Buffer::Instance& response) { +void JsonClustersChunkProcessor::render(std::reference_wrapper cluster, + Buffer::Instance& response) { UNREFERENCED_PARAMETER(cluster); UNREFERENCED_PARAMETER(response); + Json::Streamer::MapPtr cluster_map = json_context_holder_.back()->clusters_->addMap(); + const Upstream::Cluster& unwrapped_cluster = cluster.get(); + Upstream::ClusterInfoConstSharedPtr cluster_info = unwrapped_cluster.info(); + cluster_map->addEntries({{"name", cluster_info->name()}}); } bool JsonClustersChunkProcessor::nextChunk(Buffer::Instance& response) { @@ -154,18 +168,35 @@ bool JsonClustersChunkProcessor::nextChunk(Buffer::Instance& response) { UNREFERENCED_PARAMETER(response_headers_); const uint64_t original_request_size = response.length(); - response.add("["); for (; idx_ < clusters_.size() && response.length() - original_request_size < chunk_limit_; idx_++) { - renderer_->render(clusters_[idx_], response); + render(clusters_[idx_], response); } if (idx_ < clusters_.size()) { return true; } - response.add("]"); + finalize(response); return false; } +// Json::Streamer holds a reference to a Buffer::Instance reference but the API for Request +// takes a Buffer::Instance reference on each call to nextChunk. So, at the end of each +// Json::Streamer function invocation, call drainBufferIntoResponse to ensure that the +// contents written to its buffer gets moved and appended to the response. +void JsonClustersChunkProcessor::drainBufferIntoResponse(Buffer::Instance& response) { + if (&response != &buffer_) { + response.move(buffer_); + } +} + +// Start destruction of the ClustersJsonContext to render the closing tokens and push to the +// buffer. Since we've pushed data into the buffer in the Json::Streamer, we'll need to drain +// the contents into the response. +void JsonClustersChunkProcessor::finalize(Buffer::Instance& response) { + json_context_holder_.pop_back(); + drainBufferIntoResponse(response); +} + } // namespace Server } // namespace Envoy diff --git a/source/server/admin/clusters_renderer.h b/source/server/admin/clusters_chunk_processor.h similarity index 72% rename from source/server/admin/clusters_renderer.h rename to source/server/admin/clusters_chunk_processor.h index 8177aa345903..9370699cb661 100644 --- a/source/server/admin/clusters_renderer.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -9,32 +9,41 @@ #include "envoy/upstream/cluster_manager.h" #include "envoy/upstream/outlier_detection.h" #include "envoy/upstream/resource_manager.h" - +#include "source/common/buffer/buffer_impl.h" #include "source/common/json/json_streamer.h" namespace Envoy { namespace Server { +struct ClustersJsonContext { + ClustersJsonContext( + std::unique_ptr streamer, + std::reference_wrapper buffer, + Json::Streamer::MapPtr root_map, + Json::Streamer::ArrayPtr clusters) : + streamer_(std::move(streamer)), buffer_(buffer.get()), + root_map_(std::move(root_map)), clusters_(std::move(clusters)) {} + std::unique_ptr streamer_; + Buffer::Instance& buffer_; + Json::Streamer::MapPtr root_map_; + Json::Streamer::ArrayPtr clusters_; +}; + class ClustersChunkProcessor { public: virtual bool nextChunk(Buffer::Instance& response) PURE; virtual ~ClustersChunkProcessor() = default; }; -class ClusterRenderer { -public: - virtual void render(std::reference_wrapper cluster, - Buffer::Instance& response) PURE; - virtual ~ClusterRenderer() = default; -}; - -class TextClusterRenderer : public ClusterRenderer { +class TextClustersChunkProcessor : public ClustersChunkProcessor { public: - TextClusterRenderer() = default; + TextClustersChunkProcessor(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, + const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map); + bool nextChunk(Buffer::Instance& response) override; +private: void render(std::reference_wrapper cluster, - Buffer::Instance& response) override; + Buffer::Instance& response); -private: static void addOutlierInfo(const std::string& cluster_name, const Upstream::Outlier::Detector* outlier_detector, Buffer::Instance& response); @@ -42,42 +51,30 @@ class TextClusterRenderer : public ClusterRenderer { const std::string& priority_str, Upstream::ResourceManager& resource_manager, Buffer::Instance& response); -}; - -class TextClustersChunkProcessor : public ClustersChunkProcessor { -public: - TextClustersChunkProcessor(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, - const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map); - - bool nextChunk(Buffer::Instance& response) override; - -private: const uint64_t chunk_limit_; Http::ResponseHeaderMap& response_headers_; std::vector> clusters_; - std::unique_ptr renderer_; uint64_t idx_; }; -class JsonClusterRenderer : public ClusterRenderer { -public: - JsonClusterRenderer() = default; - void render(std::reference_wrapper cluster, - Buffer::Instance& response) override; -}; class JsonClustersChunkProcessor : public ClustersChunkProcessor { public: JsonClustersChunkProcessor(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map); - bool nextChunk(Buffer::Instance& response) override; - private: + void render(std::reference_wrapper cluster, + Buffer::Instance& response); + + void drainBufferIntoResponse(Buffer::Instance& response); + void finalize(Buffer::Instance& response); + const uint64_t chunk_limit_; Http::ResponseHeaderMap& response_headers_; std::vector> clusters_; - std::unique_ptr renderer_; + Buffer::OwnedImpl buffer_; + std::vector> json_context_holder_; uint64_t idx_; }; diff --git a/source/server/admin/clusters_request.cc b/source/server/admin/clusters_request.cc index a025b5cf0f18..b427bcab8180 100644 --- a/source/server/admin/clusters_request.cc +++ b/source/server/admin/clusters_request.cc @@ -7,7 +7,7 @@ #include "envoy/server/instance.h" #include "source/common/common/logger.h" -#include "source/server/admin/clusters_renderer.h" +#include "source/server/admin/clusters_chunk_processor.h" #include "clusters_params.h" diff --git a/source/server/admin/clusters_request.h b/source/server/admin/clusters_request.h index a11db69c91d0..5e73cbdb407c 100644 --- a/source/server/admin/clusters_request.h +++ b/source/server/admin/clusters_request.h @@ -8,7 +8,7 @@ #include "envoy/server/instance.h" #include "source/server/admin/clusters_params.h" -#include "source/server/admin/clusters_renderer.h" +#include "source/server/admin/clusters_chunk_processor.h" namespace Envoy { namespace Server { diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index bca0c4d7bf3d..2a4f2d59d014 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -280,8 +280,11 @@ envoy_cc_test( "//envoy/http:codes_interface", "//envoy/server:instance_interface", "//source/common/buffer:buffer_lib", + "//source/server/admin:clusters_chunk_processor_lib", + "//source/server/admin:clusters_params_lib", "//source/server/admin:clusters_request_lib", "//test/mocks/server:instance_mocks", + "//test/mocks/upstream:upstream_mocks", "//test/test_common:utility_lib", ], ) diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index c7314f9431fe..5fd1d6a62908 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -1,3 +1,4 @@ +#include #include #include "envoy/buffer/buffer.h" @@ -9,6 +10,7 @@ #include "source/server/admin/clusters_request.h" #include "test/mocks/server/instance.h" +#include "test/mocks/upstream/cluster.h" #include "test/mocks/upstream/cluster_manager.h" #include "test/test_common/utility.h" @@ -21,6 +23,9 @@ using testing::Return; using testing::ReturnRef; class BaseClustersRequestFixture : public testing::Test { +public: + BaseClustersRequestFixture() : + cluster_info_name_("") {} protected: using ClustersRequestPtr = std::unique_ptr; @@ -49,6 +54,13 @@ class BaseClustersRequestFixture : public testing::Test { /* data=*/drain_after_next_chunk ? std::move(result_data) : std::move(buffer), }; } + +const std::string& getClusterInfoName() { + return cluster_info_name_; +} + +private: + const std::string cluster_info_name_; }; struct VerifyJsonOutputParameters { @@ -61,6 +73,7 @@ class VerifyJsonOutputFixture : public BaseClustersRequestFixture, TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { VerifyJsonOutputParameters params = GetParam(); MockInstance mock_server; + Upstream::MockCluster mock_cluster; Upstream::MockClusterManager mock_cluster_manager; Buffer::OwnedImpl buffer; ClustersParams clusters_params; @@ -76,31 +89,57 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { // EXPECT_EQ(result.data_.toString(), "{}"); } -constexpr VerifyJsonOutputParameters VERIFY_JSON_CASES[] = { - {/* drain_=*/true}, - {/* drain_=*/false}, -}; +// constexpr VerifyJsonOutputParameters VERIFY_JSON_CASES[] = { +// {/* drain_=*/true}, +// {/* drain_=*/false}, +// }; -INSTANTIATE_TEST_SUITE_P(VerifyJsonOutput, VerifyJsonOutputFixture, - testing::ValuesIn(VERIFY_JSON_CASES)); +// INSTANTIATE_TEST_SUITE_P(VerifyJsonOutput, VerifyJsonOutputFixture, +// testing::ValuesIn(VERIFY_JSON_CASES)); -struct VerifyTextOutputParameters { - bool drain_; -}; +// struct VerifyTextOutputParameters { +// bool drain_; +// }; + +// class VerifyTextOutputFixture : public BaseClustersRequestFixture, +// public testing::WithParamInterface {}; -class VerifyTextOutputFixture : public BaseClustersRequestFixture, - public testing::WithParamInterface {}; +// // TODO(demitriswan) Implement test for text output verification. +// TEST_P(VerifyTextOutputFixture, VerifyTextOutput) {} -// TODO(demitriswan) Implement test for text output verification. -TEST_P(VerifyTextOutputFixture, VerifyTextOutput) {} +// constexpr VerifyTextOutputParameters VERIFY_TEXT_CASES[] = { +// {/* drain_=*/true}, +// {/* drain_=*/false}, +// }; -constexpr VerifyTextOutputParameters VERIFY_TEXT_CASES[] = { - {/* drain_=*/true}, - {/* drain_=*/false}, +// INSTANTIATE_TEST_SUITE_P(VerifyTextOutput, VerifyTextOutputFixture, +// testing::ValuesIn(VERIFY_TEXT_CASES)); + +class Foo { +public: + Foo(std::unique_ptr streamer, Buffer::Instance& buffer) : streamer_(std::move(streamer)), buffer_(buffer) { + array_ = streamer_->makeRootArray(); + } + void foo(Buffer::Instance& buffer) { + array_->addNumber(int64_t(1)); + buffer.move(buffer_); + } + std::unique_ptr streamer_; + Buffer::Instance& buffer_; + Json::Streamer::ArrayPtr array_; }; -INSTANTIATE_TEST_SUITE_P(VerifyTextOutput, VerifyTextOutputFixture, - testing::ValuesIn(VERIFY_TEXT_CASES)); +TEST(Json, Verify) { + Buffer::OwnedImpl request_buffer; + Buffer::OwnedImpl buffer; + { + Foo foo(std::make_unique(buffer), buffer); + foo.foo(request_buffer); + foo.foo(request_buffer); + } + request_buffer.move(buffer); + EXPECT_EQ(request_buffer.toString(), "[1,1]"); +} } // namespace Server } // namespace Envoy From 1d9ba05f7881b0d7a3a6e97788bab0cd07df9e24 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Mon, 22 Apr 2024 21:31:44 +0000 Subject: [PATCH 21/77] Json processing working Signed-off-by: Demitri Swan --- .../server/admin/clusters_chunk_processor.cc | 14 +- .../server/admin/clusters_chunk_processor.h | 5 +- test/server/admin/BUILD | 1 + test/server/admin/clusters_request_test.cc | 130 ++++++++++-------- 4 files changed, 87 insertions(+), 63 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index d63353584b1c..1b053c62c239 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -155,12 +155,15 @@ JsonClustersChunkProcessor::JsonClustersChunkProcessor( void JsonClustersChunkProcessor::render(std::reference_wrapper cluster, Buffer::Instance& response) { - UNREFERENCED_PARAMETER(cluster); - UNREFERENCED_PARAMETER(response); Json::Streamer::MapPtr cluster_map = json_context_holder_.back()->clusters_->addMap(); const Upstream::Cluster& unwrapped_cluster = cluster.get(); Upstream::ClusterInfoConstSharedPtr cluster_info = unwrapped_cluster.info(); - cluster_map->addEntries({{"name", cluster_info->name()}}); + std::vector top_level_entries = { + {"name", cluster_info->name()}, + {"observability_name", cluster_info->observabilityName()}, + }; + addMapEntries(cluster_map.get(), response, top_level_entries); + drainBufferIntoResponse(response); } bool JsonClustersChunkProcessor::nextChunk(Buffer::Instance& response) { @@ -190,6 +193,11 @@ void JsonClustersChunkProcessor::drainBufferIntoResponse(Buffer::Instance& respo } } +void JsonClustersChunkProcessor::addMapEntries(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, std::vector& entries) { + raw_map_ptr->addEntries(entries); + drainBufferIntoResponse(response); +} + // Start destruction of the ClustersJsonContext to render the closing tokens and push to the // buffer. Since we've pushed data into the buffer in the Json::Streamer, we'll need to drain // the contents into the response. diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index 9370699cb661..4f031a2bb7f3 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -66,9 +66,9 @@ class JsonClustersChunkProcessor : public ClustersChunkProcessor { private: void render(std::reference_wrapper cluster, Buffer::Instance& response); - void drainBufferIntoResponse(Buffer::Instance& response); void finalize(Buffer::Instance& response); + void addMapEntries(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, std::vector& entries); const uint64_t chunk_limit_; Http::ResponseHeaderMap& response_headers_; @@ -78,5 +78,8 @@ class JsonClustersChunkProcessor : public ClustersChunkProcessor { uint64_t idx_; }; + + + } // namespace Server } // namespace Envoy diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index 2a4f2d59d014..2ccb1906af7d 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -279,6 +279,7 @@ envoy_cc_test( deps = [ "//envoy/http:codes_interface", "//envoy/server:instance_interface", + "//envoy/stream_info:stream_info_interface", "//source/common/buffer:buffer_lib", "//source/server/admin:clusters_chunk_processor_lib", "//source/server/admin:clusters_params_lib", diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index 5fd1d6a62908..e5ced1dfcec4 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -1,9 +1,12 @@ #include +#include #include +#include #include "envoy/buffer/buffer.h" #include "envoy/http/codes.h" #include "envoy/server/instance.h" +#include "envoy/stream_info/stream_info.h" #include "source/common/buffer/buffer_impl.h" #include "source/server/admin/clusters_params.h" @@ -19,18 +22,21 @@ namespace Envoy { namespace Server { -using testing::Return; +using testing::NiceMock; +using testing::ReturnPointee; using testing::ReturnRef; class BaseClustersRequestFixture : public testing::Test { -public: - BaseClustersRequestFixture() : - cluster_info_name_("") {} protected: + BaseClustersRequestFixture() { + ON_CALL(mock_server_, clusterManager()).WillByDefault(ReturnRef(mock_cluster_manager_)); + ON_CALL(mock_cluster_manager_, clusters()).WillByDefault(ReturnPointee(&cluster_info_maps_)); + } + using ClustersRequestPtr = std::unique_ptr; - ClustersRequestPtr makeRequest(uint64_t chunk_limit, Instance& server, ClustersParams& params) { - return std::make_unique(chunk_limit, server, params); + ClustersRequestPtr makeRequest(uint64_t chunk_limit, ClustersParams& params) { + return std::make_unique(chunk_limit, mock_server_, params); } struct ResponseResult { @@ -45,22 +51,26 @@ class BaseClustersRequestFixture : public testing::Test { Buffer::OwnedImpl result_data; while (request.nextChunk(buffer)) { if (drain_after_next_chunk) { - result_data.add(buffer); - buffer.drain(buffer.length()); + result_data.move(buffer); } } + if (drain_after_next_chunk) { + result_data.move(buffer); + } return { /* code=*/code, /* data=*/drain_after_next_chunk ? std::move(result_data) : std::move(buffer), }; } -const std::string& getClusterInfoName() { - return cluster_info_name_; -} + void loadNewMockClusterByName(NiceMock& mock_cluster, std::string_view name) { + mock_cluster.info_->name_ = name; + cluster_info_maps_.active_clusters_.emplace(name, std::ref(mock_cluster)); + } -private: - const std::string cluster_info_name_; + NiceMock mock_server_; + NiceMock mock_cluster_manager_; + Upstream::ClusterManager::ClusterInfoMaps cluster_info_maps_; }; struct VerifyJsonOutputParameters { @@ -71,74 +81,76 @@ class VerifyJsonOutputFixture : public BaseClustersRequestFixture, public testing::WithParamInterface {}; TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { + constexpr int chunk_limit = 1; // Small chunk limit will force next chunk to be called for each Cluster. VerifyJsonOutputParameters params = GetParam(); - MockInstance mock_server; - Upstream::MockCluster mock_cluster; - Upstream::MockClusterManager mock_cluster_manager; Buffer::OwnedImpl buffer; - ClustersParams clusters_params; - Upstream::ClusterManager::ClusterInfoMaps cluster_info_maps; + ClustersParams clusters_params; clusters_params.format_ = ClustersParams::Format::Json; - EXPECT_CALL(mock_server, clusterManager()).WillOnce(ReturnRef(mock_cluster_manager)); - EXPECT_CALL(mock_cluster_manager, clusters()).WillOnce(Return(cluster_info_maps)); - ResponseResult result = response(*makeRequest(1, mock_server, clusters_params), params.drain_); + NiceMock test_cluster; + loadNewMockClusterByName(test_cluster, "test_cluster"); + + NiceMock test_cluster2; + loadNewMockClusterByName(test_cluster2, "test_cluster2"); + + + ResponseResult result = response(*makeRequest(chunk_limit, clusters_params), params.drain_); EXPECT_EQ(result.code_, Http::Code::OK); - // TODO(demtiriswan) add expection for JSON here based on mock function results - // EXPECT_EQ(result.data_.toString(), "{}"); + EXPECT_EQ(result.data_.toString(), R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name"},{"name":"test_cluster2","observability_name":"observability_name"}]})EOF"); } -// constexpr VerifyJsonOutputParameters VERIFY_JSON_CASES[] = { -// {/* drain_=*/true}, -// {/* drain_=*/false}, -// }; +constexpr VerifyJsonOutputParameters VERIFY_JSON_CASES[] = { + {/* drain_=*/false}, + {/* drain_=*/true}, +}; + +INSTANTIATE_TEST_SUITE_P(VerifyJsonOutput, VerifyJsonOutputFixture, + testing::ValuesIn(VERIFY_JSON_CASES)); -// INSTANTIATE_TEST_SUITE_P(VerifyJsonOutput, VerifyJsonOutputFixture, -// testing::ValuesIn(VERIFY_JSON_CASES)); +struct VerifyTextOutputParameters { + bool drain_; +}; -// struct VerifyTextOutputParameters { -// bool drain_; -// }; +class VerifyTextOutputFixture : public BaseClustersRequestFixture, + public testing::WithParamInterface {}; -// class VerifyTextOutputFixture : public BaseClustersRequestFixture, -// public testing::WithParamInterface {}; +// TODO(demitriswan) Implement test for text output verification. +TEST_P(VerifyTextOutputFixture, VerifyTextOutput) {} -// // TODO(demitriswan) Implement test for text output verification. -// TEST_P(VerifyTextOutputFixture, VerifyTextOutput) {} +constexpr VerifyTextOutputParameters VERIFY_TEXT_CASES[] = { + {/* drain_=*/true}, + {/* drain_=*/false}, +}; -// constexpr VerifyTextOutputParameters VERIFY_TEXT_CASES[] = { -// {/* drain_=*/true}, -// {/* drain_=*/false}, -// }; +INSTANTIATE_TEST_SUITE_P(VerifyTextOutput, VerifyTextOutputFixture, + testing::ValuesIn(VERIFY_TEXT_CASES)); -// INSTANTIATE_TEST_SUITE_P(VerifyTextOutput, VerifyTextOutputFixture, -// testing::ValuesIn(VERIFY_TEXT_CASES)); -class Foo { -public: - Foo(std::unique_ptr streamer, Buffer::Instance& buffer) : streamer_(std::move(streamer)), buffer_(buffer) { - array_ = streamer_->makeRootArray(); - } - void foo(Buffer::Instance& buffer) { - array_->addNumber(int64_t(1)); - buffer.move(buffer_); - } - std::unique_ptr streamer_; - Buffer::Instance& buffer_; - Json::Streamer::ArrayPtr array_; -}; +TEST(Json, VerifyArrayPtrDestructionTerminatesJsonArray) { + class Foo { + public: + Foo(std::unique_ptr streamer, Buffer::Instance& buffer) : streamer_(std::move(streamer)), buffer_(buffer) { + array_ = streamer_->makeRootArray(); + } + void foo(Buffer::Instance& buffer, int64_t number) { + array_->addNumber(number); + buffer.move(buffer_); + } + std::unique_ptr streamer_; + Buffer::Instance& buffer_; + Json::Streamer::ArrayPtr array_; + }; -TEST(Json, Verify) { Buffer::OwnedImpl request_buffer; Buffer::OwnedImpl buffer; { Foo foo(std::make_unique(buffer), buffer); - foo.foo(request_buffer); - foo.foo(request_buffer); + foo.foo(request_buffer, 1); + foo.foo(request_buffer, 2); } request_buffer.move(buffer); - EXPECT_EQ(request_buffer.toString(), "[1,1]"); + EXPECT_EQ(request_buffer.toString(), "[1,2]"); } } // namespace Server From 3aadf0dd071d8df7a7f27794b49dc46488a31efa Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 23 Apr 2024 03:08:58 +0000 Subject: [PATCH 22/77] Adding addCircuitBreakerSettingsAsJson Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 3 +- .../server/admin/clusters_chunk_processor.cc | 88 +++++++++++++------ .../server/admin/clusters_chunk_processor.h | 34 +++---- source/server/admin/clusters_request.h | 2 +- test/server/admin/clusters_request_test.cc | 38 ++++++-- 5 files changed, 111 insertions(+), 54 deletions(-) diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 807b9d6b672b..a627858b14cb 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -419,8 +419,8 @@ envoy_cc_library( srcs = ["clusters_request.cc"], hdrs = ["clusters_request.h"], deps = [ - ":clusters_params_lib", ":clusters_chunk_processor_lib", + ":clusters_params_lib", "//envoy/buffer:buffer_interface", "//envoy/http:codes_interface", "//envoy/http:header_map_interface", @@ -458,5 +458,6 @@ envoy_cc_library( "//source/common/json:json_streamer_lib", "//source/common/upstream:host_utility_lib", "@envoy_api//envoy/admin/v3:pkg_cc_proto", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], ) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 1b053c62c239..41cb83a5258c 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -6,11 +6,12 @@ #include "envoy/admin/v3/clusters.pb.h" #include "envoy/buffer/buffer.h" +#include "envoy/config/core/v3/base.pb.h" #include "envoy/upstream/cluster_manager.h" #include "envoy/upstream/resource_manager.h" -#include "source/common/upstream/host_utility.h" #include "source/common/buffer/buffer_impl.h" +#include "source/common/upstream/host_utility.h" namespace Envoy { namespace Server { @@ -18,8 +19,7 @@ namespace Server { TextClustersChunkProcessor::TextClustersChunkProcessor( uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map) - : chunk_limit_(chunk_limit), response_headers_(response_headers), - idx_(0) { + : chunk_limit_(chunk_limit), response_headers_(response_headers), idx_(0) { for (const auto& pair : cluster_info_map) { clusters_.push_back(pair.second); } @@ -100,8 +100,8 @@ void TextClustersChunkProcessor::render(std::reference_wrapperaddKey("cluster_statuses"); Json::Streamer::ArrayPtr clusters = root_map->addArray(); json_context_holder_.push_back(std::make_unique( - std::move(streamer), buffer_, - std::move(root_map), std::move(clusters))); -} - -void JsonClustersChunkProcessor::render(std::reference_wrapper cluster, - Buffer::Instance& response) { - Json::Streamer::MapPtr cluster_map = json_context_holder_.back()->clusters_->addMap(); - const Upstream::Cluster& unwrapped_cluster = cluster.get(); - Upstream::ClusterInfoConstSharedPtr cluster_info = unwrapped_cluster.info(); - std::vector top_level_entries = { - {"name", cluster_info->name()}, - {"observability_name", cluster_info->observabilityName()}, - }; - addMapEntries(cluster_map.get(), response, top_level_entries); - drainBufferIntoResponse(response); + std::move(streamer), buffer_, std::move(root_map), std::move(clusters))); } bool JsonClustersChunkProcessor::nextChunk(Buffer::Instance& response) { @@ -183,6 +167,54 @@ bool JsonClustersChunkProcessor::nextChunk(Buffer::Instance& response) { return false; } +void JsonClustersChunkProcessor::render(std::reference_wrapper cluster, + Buffer::Instance& response) { + Json::Streamer::MapPtr cluster_map = json_context_holder_.back()->clusters_->addMap(); + const Upstream::Cluster& unwrapped_cluster = cluster.get(); + Upstream::ClusterInfoConstSharedPtr cluster_info = unwrapped_cluster.info(); + + Json::Streamer::Map::Entries top_level_entries = + std::vector{ + {"name", cluster_info->name()}, + {"observability_name", cluster_info->observabilityName()}, + }; + addMapEntries(cluster_map.get(), response, top_level_entries); + + if (const std::string& name = cluster_info->edsServiceName(); !name.empty()) { + Json::Streamer::Map::Entries eds_service_name_entry = + std::vector{ + {"eds_service_name", name}, + }; + addMapEntries(cluster_map.get(), response, eds_service_name_entry); + } + + { + Json::Streamer::MapPtr circuit_breaker_settings = + json_context_holder_.back()->clusters_->addMap(); + addCircuitBreakerSettingsAsJson( + circuit_breaker_settings.get(), response, envoy::config::core::v3::RoutingPriority::DEFAULT, + cluster_info->resourceManager(Upstream::ResourcePriority::Default)); + addCircuitBreakerSettingsAsJson( + circuit_breaker_settings.get(), response, envoy::config::core::v3::RoutingPriority::HIGH, + cluster_info->resourceManager(Upstream::ResourcePriority::High)); + } // Terminate the map. +} + +void JsonClustersChunkProcessor::addCircuitBreakerSettingsAsJson( + Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, + const envoy::config::core::v3::RoutingPriority& priority, + Upstream::ResourceManager& resource_manager) { + Json::Streamer::Map::Entries config = std::vector{ + {"priority", + priority == envoy::config::core::v3::RoutingPriority::DEFAULT ? "DEFAULT" : "HIGH"}, + {"max_connections", resource_manager.connections().max()}, + {"max_pending_requests", resource_manager.pendingRequests().max()}, + {"max_requests", resource_manager.requests().max()}, + {"max_retries", resource_manager.retries().max()}, + }; + addMapEntries(raw_map_ptr, response, config); +} + // Json::Streamer holds a reference to a Buffer::Instance reference but the API for Request // takes a Buffer::Instance reference on each call to nextChunk. So, at the end of each // Json::Streamer function invocation, call drainBufferIntoResponse to ensure that the @@ -193,7 +225,9 @@ void JsonClustersChunkProcessor::drainBufferIntoResponse(Buffer::Instance& respo } } -void JsonClustersChunkProcessor::addMapEntries(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, std::vector& entries) { +void JsonClustersChunkProcessor::addMapEntries(Json::Streamer::Map* raw_map_ptr, + Buffer::Instance& response, + Json::Streamer::Map::Entries& entries) { raw_map_ptr->addEntries(entries); drainBufferIntoResponse(response); } diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index 4f031a2bb7f3..63f969c5c85b 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -4,11 +4,13 @@ #include #include +#include "envoy/admin/v3/clusters.pb.h" #include "envoy/buffer/buffer.h" #include "envoy/common/pure.h" #include "envoy/upstream/cluster_manager.h" #include "envoy/upstream/outlier_detection.h" #include "envoy/upstream/resource_manager.h" + #include "source/common/buffer/buffer_impl.h" #include "source/common/json/json_streamer.h" @@ -16,13 +18,11 @@ namespace Envoy { namespace Server { struct ClustersJsonContext { - ClustersJsonContext( - std::unique_ptr streamer, - std::reference_wrapper buffer, - Json::Streamer::MapPtr root_map, - Json::Streamer::ArrayPtr clusters) : - streamer_(std::move(streamer)), buffer_(buffer.get()), - root_map_(std::move(root_map)), clusters_(std::move(clusters)) {} + ClustersJsonContext(std::unique_ptr streamer, + std::reference_wrapper buffer, + Json::Streamer::MapPtr root_map, Json::Streamer::ArrayPtr clusters) + : streamer_(std::move(streamer)), buffer_(buffer.get()), root_map_(std::move(root_map)), + clusters_(std::move(clusters)) {} std::unique_ptr streamer_; Buffer::Instance& buffer_; Json::Streamer::MapPtr root_map_; @@ -40,9 +40,9 @@ class TextClustersChunkProcessor : public ClustersChunkProcessor { TextClustersChunkProcessor(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map); bool nextChunk(Buffer::Instance& response) override; + private: - void render(std::reference_wrapper cluster, - Buffer::Instance& response); + void render(std::reference_wrapper cluster, Buffer::Instance& response); static void addOutlierInfo(const std::string& cluster_name, const Upstream::Outlier::Detector* outlier_detector, @@ -57,29 +57,29 @@ class TextClustersChunkProcessor : public ClustersChunkProcessor { uint64_t idx_; }; - class JsonClustersChunkProcessor : public ClustersChunkProcessor { public: JsonClustersChunkProcessor(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map); bool nextChunk(Buffer::Instance& response) override; + private: - void render(std::reference_wrapper cluster, - Buffer::Instance& response); + void render(std::reference_wrapper cluster, Buffer::Instance& response); void drainBufferIntoResponse(Buffer::Instance& response); void finalize(Buffer::Instance& response); - void addMapEntries(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, std::vector& entries); + void addMapEntries(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, + Json::Streamer::Map::Entries& entries); + void addCircuitBreakerSettingsAsJson(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, + const envoy::config::core::v3::RoutingPriority& priority, + Upstream::ResourceManager& resource_manager); const uint64_t chunk_limit_; Http::ResponseHeaderMap& response_headers_; std::vector> clusters_; Buffer::OwnedImpl buffer_; - std::vector> json_context_holder_; + std::vector> json_context_holder_; uint64_t idx_; }; - - - } // namespace Server } // namespace Envoy diff --git a/source/server/admin/clusters_request.h b/source/server/admin/clusters_request.h index 5e73cbdb407c..65b617328105 100644 --- a/source/server/admin/clusters_request.h +++ b/source/server/admin/clusters_request.h @@ -7,8 +7,8 @@ #include "envoy/server/admin.h" #include "envoy/server/instance.h" -#include "source/server/admin/clusters_params.h" #include "source/server/admin/clusters_chunk_processor.h" +#include "source/server/admin/clusters_params.h" namespace Envoy { namespace Server { diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index e5ced1dfcec4..d7008b44f379 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -1,7 +1,7 @@ #include #include -#include #include +#include #include "envoy/buffer/buffer.h" #include "envoy/http/codes.h" @@ -9,6 +9,7 @@ #include "envoy/stream_info/stream_info.h" #include "source/common/buffer/buffer_impl.h" +#include "source/common/upstream/resource_manager_impl.h" #include "source/server/admin/clusters_params.h" #include "source/server/admin/clusters_request.h" @@ -23,6 +24,7 @@ namespace Envoy { namespace Server { using testing::NiceMock; +using testing::Return; using testing::ReturnPointee; using testing::ReturnRef; @@ -31,6 +33,12 @@ class BaseClustersRequestFixture : public testing::Test { BaseClustersRequestFixture() { ON_CALL(mock_server_, clusterManager()).WillByDefault(ReturnRef(mock_cluster_manager_)); ON_CALL(mock_cluster_manager_, clusters()).WillByDefault(ReturnPointee(&cluster_info_maps_)); + resource_manager_default_ = std::make_unique( + runtime_, resource_manager_key_, 1024, 1024, 1024, 16, 4, 512, circuit_breaker_stats_, + std::nullopt, std::nullopt); + resource_manager_high_ = std::make_unique( + runtime_, resource_manager_key_, 4096, 4096, 4096, 16, 4, 1024, circuit_breaker_stats_, + std::nullopt, std::nullopt); } using ClustersRequestPtr = std::unique_ptr; @@ -63,14 +71,25 @@ class BaseClustersRequestFixture : public testing::Test { }; } - void loadNewMockClusterByName(NiceMock& mock_cluster, std::string_view name) { + void loadNewMockClusterByName(NiceMock& mock_cluster, + absl::string_view name) { mock_cluster.info_->name_ = name; + ON_CALL(*mock_cluster.info_, edsServiceName()).WillByDefault(ReturnRef("potato_launcher")); + ON_CALL(*mock_cluster.info_, resourceManager(Upstream::ResourcePriority::Default)) + .WillByDefault(ReturnRef(std::ref(*resource_manager_default_).get())); + ON_CALL(*mock_cluster.info_, resourceManager(Upstream::ResourcePriority::High)) + .WillByDefault(ReturnRef(std::ref(*resource_manager_default_).get())); cluster_info_maps_.active_clusters_.emplace(name, std::ref(mock_cluster)); } NiceMock mock_server_; NiceMock mock_cluster_manager_; Upstream::ClusterManager::ClusterInfoMaps cluster_info_maps_; + NiceMock runtime_; + const std::string resource_manager_key_{"test_resource_manager_key"}; + Upstream::ClusterCircuitBreakersStats circuit_breaker_stats_; + std::unique_ptr resource_manager_default_; + std::unique_ptr resource_manager_high_; }; struct VerifyJsonOutputParameters { @@ -81,10 +100,11 @@ class VerifyJsonOutputFixture : public BaseClustersRequestFixture, public testing::WithParamInterface {}; TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { - constexpr int chunk_limit = 1; // Small chunk limit will force next chunk to be called for each Cluster. + // Small chunk limit will force next chunk to be called for each Cluster. + constexpr int chunk_limit = 1; VerifyJsonOutputParameters params = GetParam(); Buffer::OwnedImpl buffer; - ClustersParams clusters_params; + ClustersParams clusters_params; clusters_params.format_ = ClustersParams::Format::Json; NiceMock test_cluster; @@ -92,12 +112,14 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { NiceMock test_cluster2; loadNewMockClusterByName(test_cluster2, "test_cluster2"); - ResponseResult result = response(*makeRequest(chunk_limit, clusters_params), params.drain_); EXPECT_EQ(result.code_, Http::Code::OK); - EXPECT_EQ(result.data_.toString(), R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name"},{"name":"test_cluster2","observability_name":"observability_name"}]})EOF"); + EXPECT_EQ( + result.data_.toString(), + R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":"1024","max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":"4096","max_retries":16}]}}," + "{"name":"test_cluster2","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":"1024","max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":"4096","max_retries":16}]}]})EOF"); } constexpr VerifyJsonOutputParameters VERIFY_JSON_CASES[] = { @@ -126,11 +148,11 @@ constexpr VerifyTextOutputParameters VERIFY_TEXT_CASES[] = { INSTANTIATE_TEST_SUITE_P(VerifyTextOutput, VerifyTextOutputFixture, testing::ValuesIn(VERIFY_TEXT_CASES)); - TEST(Json, VerifyArrayPtrDestructionTerminatesJsonArray) { class Foo { public: - Foo(std::unique_ptr streamer, Buffer::Instance& buffer) : streamer_(std::move(streamer)), buffer_(buffer) { + Foo(std::unique_ptr streamer, Buffer::Instance& buffer) + : streamer_(std::move(streamer)), buffer_(buffer) { array_ = streamer_->makeRootArray(); } void foo(Buffer::Instance& buffer, int64_t number) { From 8715ec02e7a3c0b5972c51fe1f5c00876b58e9e3 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 23 Apr 2024 07:16:29 +0000 Subject: [PATCH 23/77] Fixing test with addCircuitBreakerSettingsAsJson Signed-off-by: Demitri Swan --- .../server/admin/clusters_chunk_processor.cc | 37 ++++++++++--------- .../server/admin/clusters_chunk_processor.h | 4 +- test/server/admin/clusters_request_test.cc | 19 +++++----- 3 files changed, 31 insertions(+), 29 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 41cb83a5258c..d25533ef1121 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -173,38 +173,39 @@ void JsonClustersChunkProcessor::render(std::reference_wrapper{ - {"name", cluster_info->name()}, - {"observability_name", cluster_info->observabilityName()}, - }; + std::vector top_level_entries{ + {"name", cluster_info->name()}, + {"observability_name", cluster_info->observabilityName()}, + }; addMapEntries(cluster_map.get(), response, top_level_entries); if (const std::string& name = cluster_info->edsServiceName(); !name.empty()) { - Json::Streamer::Map::Entries eds_service_name_entry = - std::vector{ - {"eds_service_name", name}, - }; + std::vector eds_service_name_entry{ + {"eds_service_name", name}, + }; addMapEntries(cluster_map.get(), response, eds_service_name_entry); } - { - Json::Streamer::MapPtr circuit_breaker_settings = - json_context_holder_.back()->clusters_->addMap(); + { + cluster_map->addKey("circuit_breakers"); + Json::Streamer::MapPtr circuit_breakers = cluster_map->addMap(); + circuit_breakers->addKey("thresholds"); + Json::Streamer::ArrayPtr thresholds = circuit_breakers->addArray(); addCircuitBreakerSettingsAsJson( - circuit_breaker_settings.get(), response, envoy::config::core::v3::RoutingPriority::DEFAULT, + thresholds.get(), response, envoy::config::core::v3::RoutingPriority::DEFAULT, cluster_info->resourceManager(Upstream::ResourcePriority::Default)); addCircuitBreakerSettingsAsJson( - circuit_breaker_settings.get(), response, envoy::config::core::v3::RoutingPriority::HIGH, + thresholds.get(), response, envoy::config::core::v3::RoutingPriority::HIGH, cluster_info->resourceManager(Upstream::ResourcePriority::High)); } // Terminate the map. } void JsonClustersChunkProcessor::addCircuitBreakerSettingsAsJson( - Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, + Json::Streamer::Array* raw_thresholds_ptr, Buffer::Instance& response, const envoy::config::core::v3::RoutingPriority& priority, Upstream::ResourceManager& resource_manager) { - Json::Streamer::Map::Entries config = std::vector{ + Json::Streamer::MapPtr threshold = raw_thresholds_ptr->addMap(); + std::vector entries{ {"priority", priority == envoy::config::core::v3::RoutingPriority::DEFAULT ? "DEFAULT" : "HIGH"}, {"max_connections", resource_manager.connections().max()}, @@ -212,7 +213,7 @@ void JsonClustersChunkProcessor::addCircuitBreakerSettingsAsJson( {"max_requests", resource_manager.requests().max()}, {"max_retries", resource_manager.retries().max()}, }; - addMapEntries(raw_map_ptr, response, config); + addMapEntries(threshold.get(), response, entries); } // Json::Streamer holds a reference to a Buffer::Instance reference but the API for Request @@ -227,7 +228,7 @@ void JsonClustersChunkProcessor::drainBufferIntoResponse(Buffer::Instance& respo void JsonClustersChunkProcessor::addMapEntries(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, - Json::Streamer::Map::Entries& entries) { + std::vector& entries) { raw_map_ptr->addEntries(entries); drainBufferIntoResponse(response); } diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index 63f969c5c85b..8a7b591e6b09 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -68,8 +68,8 @@ class JsonClustersChunkProcessor : public ClustersChunkProcessor { void drainBufferIntoResponse(Buffer::Instance& response); void finalize(Buffer::Instance& response); void addMapEntries(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, - Json::Streamer::Map::Entries& entries); - void addCircuitBreakerSettingsAsJson(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, + std::vector& entries); + void addCircuitBreakerSettingsAsJson(Json::Streamer::Array* raw_map_ptr, Buffer::Instance& response, const envoy::config::core::v3::RoutingPriority& priority, Upstream::ResourceManager& resource_manager); diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index d7008b44f379..e8e4499216bd 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -2,6 +2,7 @@ #include #include #include +#include #include "envoy/buffer/buffer.h" #include "envoy/http/codes.h" @@ -24,7 +25,6 @@ namespace Envoy { namespace Server { using testing::NiceMock; -using testing::Return; using testing::ReturnPointee; using testing::ReturnRef; @@ -34,10 +34,10 @@ class BaseClustersRequestFixture : public testing::Test { ON_CALL(mock_server_, clusterManager()).WillByDefault(ReturnRef(mock_cluster_manager_)); ON_CALL(mock_cluster_manager_, clusters()).WillByDefault(ReturnPointee(&cluster_info_maps_)); resource_manager_default_ = std::make_unique( - runtime_, resource_manager_key_, 1024, 1024, 1024, 16, 4, 512, circuit_breaker_stats_, + runtime_, resource_manager_key_, 1024, 1024, 1024, 16, 4, 512, mock_cluster_info_.circuit_breakers_stats_, std::nullopt, std::nullopt); resource_manager_high_ = std::make_unique( - runtime_, resource_manager_key_, 4096, 4096, 4096, 16, 4, 1024, circuit_breaker_stats_, + runtime_, resource_manager_key_, 4096, 4096, 4096, 16, 4, 1024, mock_cluster_info_.circuit_breakers_stats_, std::nullopt, std::nullopt); } @@ -78,16 +78,16 @@ class BaseClustersRequestFixture : public testing::Test { ON_CALL(*mock_cluster.info_, resourceManager(Upstream::ResourcePriority::Default)) .WillByDefault(ReturnRef(std::ref(*resource_manager_default_).get())); ON_CALL(*mock_cluster.info_, resourceManager(Upstream::ResourcePriority::High)) - .WillByDefault(ReturnRef(std::ref(*resource_manager_default_).get())); + .WillByDefault(ReturnRef(std::ref(*resource_manager_high_).get())); cluster_info_maps_.active_clusters_.emplace(name, std::ref(mock_cluster)); } + NiceMock mock_cluster_info_; NiceMock mock_server_; NiceMock mock_cluster_manager_; Upstream::ClusterManager::ClusterInfoMaps cluster_info_maps_; NiceMock runtime_; const std::string resource_manager_key_{"test_resource_manager_key"}; - Upstream::ClusterCircuitBreakersStats circuit_breaker_stats_; std::unique_ptr resource_manager_default_; std::unique_ptr resource_manager_high_; }; @@ -100,7 +100,7 @@ class VerifyJsonOutputFixture : public BaseClustersRequestFixture, public testing::WithParamInterface {}; TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { - // Small chunk limit will force next chunk to be called for each Cluster. + // Small chunk limit will force Request::nextChunk to be called for each Cluster. constexpr int chunk_limit = 1; VerifyJsonOutputParameters params = GetParam(); Buffer::OwnedImpl buffer; @@ -116,10 +116,11 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { ResponseResult result = response(*makeRequest(chunk_limit, clusters_params), params.drain_); EXPECT_EQ(result.code_, Http::Code::OK); + // The order of clusters is non-deterministic so strip the 2 from test_cluster2 and expect both + // clusters to be identical. EXPECT_EQ( - result.data_.toString(), - R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":"1024","max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":"4096","max_retries":16}]}}," - "{"name":"test_cluster2","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":"1024","max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":"4096","max_retries":16}]}]})EOF"); + std::regex_replace(result.data_.toString(), std::regex("test_cluster2"), "test_cluster"), + R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]}},{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]}}]})EOF"); } constexpr VerifyJsonOutputParameters VERIFY_JSON_CASES[] = { From 423d769eefb4bc38423bca50cccc1ce4c1d683bb Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 23 Apr 2024 07:22:05 +0000 Subject: [PATCH 24/77] Format update Signed-off-by: Demitri Swan --- source/server/admin/clusters_chunk_processor.cc | 12 ++++++------ source/server/admin/clusters_chunk_processor.h | 3 ++- test/server/admin/clusters_request_test.cc | 10 +++++----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index d25533ef1121..4e79507fb3fb 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -174,8 +174,8 @@ void JsonClustersChunkProcessor::render(std::reference_wrapper top_level_entries{ - {"name", cluster_info->name()}, - {"observability_name", cluster_info->observabilityName()}, + {"name", cluster_info->name()}, + {"observability_name", cluster_info->observabilityName()}, }; addMapEntries(cluster_map.get(), response, top_level_entries); @@ -186,7 +186,7 @@ void JsonClustersChunkProcessor::render(std::reference_wrapperaddKey("circuit_breakers"); Json::Streamer::MapPtr circuit_breakers = cluster_map->addMap(); circuit_breakers->addKey("thresholds"); @@ -226,9 +226,9 @@ void JsonClustersChunkProcessor::drainBufferIntoResponse(Buffer::Instance& respo } } -void JsonClustersChunkProcessor::addMapEntries(Json::Streamer::Map* raw_map_ptr, - Buffer::Instance& response, - std::vector& entries) { +void JsonClustersChunkProcessor::addMapEntries( + Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, + std::vector& entries) { raw_map_ptr->addEntries(entries); drainBufferIntoResponse(response); } diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index 8a7b591e6b09..5e0bc4459f1c 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -69,7 +69,8 @@ class JsonClustersChunkProcessor : public ClustersChunkProcessor { void finalize(Buffer::Instance& response); void addMapEntries(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, std::vector& entries); - void addCircuitBreakerSettingsAsJson(Json::Streamer::Array* raw_map_ptr, Buffer::Instance& response, + void addCircuitBreakerSettingsAsJson(Json::Streamer::Array* raw_map_ptr, + Buffer::Instance& response, const envoy::config::core::v3::RoutingPriority& priority, Upstream::ResourceManager& resource_manager); diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index e8e4499216bd..47af301a8fed 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -34,11 +34,11 @@ class BaseClustersRequestFixture : public testing::Test { ON_CALL(mock_server_, clusterManager()).WillByDefault(ReturnRef(mock_cluster_manager_)); ON_CALL(mock_cluster_manager_, clusters()).WillByDefault(ReturnPointee(&cluster_info_maps_)); resource_manager_default_ = std::make_unique( - runtime_, resource_manager_key_, 1024, 1024, 1024, 16, 4, 512, mock_cluster_info_.circuit_breakers_stats_, - std::nullopt, std::nullopt); + runtime_, resource_manager_key_, 1024, 1024, 1024, 16, 4, 512, + mock_cluster_info_.circuit_breakers_stats_, std::nullopt, std::nullopt); resource_manager_high_ = std::make_unique( - runtime_, resource_manager_key_, 4096, 4096, 4096, 16, 4, 1024, mock_cluster_info_.circuit_breakers_stats_, - std::nullopt, std::nullopt); + runtime_, resource_manager_key_, 4096, 4096, 4096, 16, 4, 1024, + mock_cluster_info_.circuit_breakers_stats_, std::nullopt, std::nullopt); } using ClustersRequestPtr = std::unique_ptr; @@ -119,7 +119,7 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { // The order of clusters is non-deterministic so strip the 2 from test_cluster2 and expect both // clusters to be identical. EXPECT_EQ( - std::regex_replace(result.data_.toString(), std::regex("test_cluster2"), "test_cluster"), + std::regex_replace(result.data_.toString(), std::regex("test_cluster2"), "test_cluster"), R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]}},{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]}}]})EOF"); } From f37ef5a63646b59ced1226d642e1fbccda947bb2 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 23 Apr 2024 14:35:33 +0000 Subject: [PATCH 25/77] Small refactor for circuit breaker rendering Signed-off-by: Demitri Swan --- .../server/admin/clusters_chunk_processor.cc | 34 +++++++++++-------- .../server/admin/clusters_chunk_processor.h | 10 +++--- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 4e79507fb3fb..97ecadd8741b 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -186,23 +186,29 @@ void JsonClustersChunkProcessor::render(std::reference_wrapperaddKey("circuit_breakers"); - Json::Streamer::MapPtr circuit_breakers = cluster_map->addMap(); - circuit_breakers->addKey("thresholds"); - Json::Streamer::ArrayPtr thresholds = circuit_breakers->addArray(); - addCircuitBreakerSettingsAsJson( - thresholds.get(), response, envoy::config::core::v3::RoutingPriority::DEFAULT, - cluster_info->resourceManager(Upstream::ResourcePriority::Default)); - addCircuitBreakerSettingsAsJson( - thresholds.get(), response, envoy::config::core::v3::RoutingPriority::HIGH, - cluster_info->resourceManager(Upstream::ResourcePriority::High)); - } // Terminate the map. + addCircuitBreakers(cluster_map.get(), cluster_info, response); + + // const Upstream::Outlier::Detector* outlier_detector = unwrapped_cluster.outlierDetector(); } -void JsonClustersChunkProcessor::addCircuitBreakerSettingsAsJson( - Json::Streamer::Array* raw_thresholds_ptr, Buffer::Instance& response, +void JsonClustersChunkProcessor::addCircuitBreakers( + Json::Streamer::Map* raw_clusters_map_ptr, Upstream::ClusterInfoConstSharedPtr cluster_info, + Buffer::Instance& response) { + raw_clusters_map_ptr->addKey("circuit_breakers"); + Json::Streamer::MapPtr circuit_breakers = raw_clusters_map_ptr->addMap(); + circuit_breakers->addKey("thresholds"); + Json::Streamer::ArrayPtr thresholds = circuit_breakers->addArray(); + addCircuitBreakerForPriority(envoy::config::core::v3::RoutingPriority::DEFAULT, thresholds.get(), + response, + cluster_info->resourceManager(Upstream::ResourcePriority::Default)); + addCircuitBreakerForPriority(envoy::config::core::v3::RoutingPriority::HIGH, thresholds.get(), + response, + cluster_info->resourceManager(Upstream::ResourcePriority::High)); +} + +void JsonClustersChunkProcessor::addCircuitBreakerForPriority( const envoy::config::core::v3::RoutingPriority& priority, + Json::Streamer::Array* raw_thresholds_ptr, Buffer::Instance& response, Upstream::ResourceManager& resource_manager) { Json::Streamer::MapPtr threshold = raw_thresholds_ptr->addMap(); std::vector entries{ diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index 5e0bc4459f1c..f3d53a122957 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -69,10 +69,12 @@ class JsonClustersChunkProcessor : public ClustersChunkProcessor { void finalize(Buffer::Instance& response); void addMapEntries(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, std::vector& entries); - void addCircuitBreakerSettingsAsJson(Json::Streamer::Array* raw_map_ptr, - Buffer::Instance& response, - const envoy::config::core::v3::RoutingPriority& priority, - Upstream::ResourceManager& resource_manager); + void addCircuitBreakers(Json::Streamer::Map* raw_clusters_map_ptr, + Upstream::ClusterInfoConstSharedPtr cluster_info, + Buffer::Instance& response); + void addCircuitBreakerForPriority(const envoy::config::core::v3::RoutingPriority& priority, + Json::Streamer::Array* raw_map_ptr, Buffer::Instance& response, + Upstream::ResourceManager& resource_manager); const uint64_t chunk_limit_; Http::ResponseHeaderMap& response_headers_; From 7bf62d49b6ca31e810cebd5672340d9394e9cf14 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 23 Apr 2024 21:06:19 +0000 Subject: [PATCH 26/77] Fixed mocking for Upstream::Cluster Signed-off-by: Demitri Swan --- .../server/admin/clusters_chunk_processor.cc | 30 +++++++++++++++++-- .../server/admin/clusters_chunk_processor.h | 3 ++ test/server/admin/BUILD | 1 + test/server/admin/clusters_request_test.cc | 20 ++++++++++--- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 97ecadd8741b..dca72eeb3c85 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -170,8 +170,7 @@ bool JsonClustersChunkProcessor::nextChunk(Buffer::Instance& response) { void JsonClustersChunkProcessor::render(std::reference_wrapper cluster, Buffer::Instance& response) { Json::Streamer::MapPtr cluster_map = json_context_holder_.back()->clusters_->addMap(); - const Upstream::Cluster& unwrapped_cluster = cluster.get(); - Upstream::ClusterInfoConstSharedPtr cluster_info = unwrapped_cluster.info(); + Upstream::ClusterInfoConstSharedPtr cluster_info = cluster.get().info(); std::vector top_level_entries{ {"name", cluster_info->name()}, @@ -187,8 +186,33 @@ void JsonClustersChunkProcessor::render(std::reference_wrappersuccessRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin) > 0.0) { + std::vector success_rate_ejection_threshold{ + {"success_rate_ejection_threshold", double(outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))}, + }; + addMapEntries(raw_clusters_map_ptr, response, success_rate_ejection_threshold); + } + if (outlier_detector != nullptr && + outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin) > 0.0) { + std::vector local_success_rate_ejection_threshold{ + {"local_success_rate_ejection_threshold", double(outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))}, + }; + addMapEntries(raw_clusters_map_ptr, response, local_success_rate_ejection_threshold); + } } void JsonClustersChunkProcessor::addCircuitBreakers( diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index f3d53a122957..1222aa1d9e06 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -75,6 +75,9 @@ class JsonClustersChunkProcessor : public ClustersChunkProcessor { void addCircuitBreakerForPriority(const envoy::config::core::v3::RoutingPriority& priority, Json::Streamer::Array* raw_map_ptr, Buffer::Instance& response, Upstream::ResourceManager& resource_manager); + void addEjectionThresholds(Json::Streamer::Map* raw_clusters_map_ptr, + const Upstream::Cluster& unwrapped_cluster, + Buffer::Instance& response); const uint64_t chunk_limit_; Http::ResponseHeaderMap& response_headers_; diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index 2ccb1906af7d..348fd25fcdab 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -286,6 +286,7 @@ envoy_cc_test( "//source/server/admin:clusters_request_lib", "//test/mocks/server:instance_mocks", "//test/mocks/upstream:upstream_mocks", + "//test/mocks/upstream:host_mocks", "//test/test_common:utility_lib", ], ) diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index 47af301a8fed..aa2360c71be1 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -17,6 +18,7 @@ #include "test/mocks/server/instance.h" #include "test/mocks/upstream/cluster.h" #include "test/mocks/upstream/cluster_manager.h" +#include "test/mocks/upstream/host.h" #include "test/test_common/utility.h" #include "gtest/gtest.h" @@ -24,10 +26,14 @@ namespace Envoy { namespace Server { +using testing::AtLeast; +using testing::Const; using testing::NiceMock; +using testing::Return; using testing::ReturnPointee; using testing::ReturnRef; + class BaseClustersRequestFixture : public testing::Test { protected: BaseClustersRequestFixture() { @@ -71,7 +77,7 @@ class BaseClustersRequestFixture : public testing::Test { }; } - void loadNewMockClusterByName(NiceMock& mock_cluster, + void loadNewMockClusterByName(NiceMock& mock_cluster, absl::string_view name) { mock_cluster.info_->name_ = name; ON_CALL(*mock_cluster.info_, edsServiceName()).WillByDefault(ReturnRef("potato_launcher")); @@ -79,6 +85,11 @@ class BaseClustersRequestFixture : public testing::Test { .WillByDefault(ReturnRef(std::ref(*resource_manager_default_).get())); ON_CALL(*mock_cluster.info_, resourceManager(Upstream::ResourcePriority::High)) .WillByDefault(ReturnRef(std::ref(*resource_manager_high_).get())); + ON_CALL(Const(mock_cluster), outlierDetector()).WillByDefault(Return(&detector_)); + ON_CALL(Const(detector_), successRateEjectionThreshold(Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin)) + .WillByDefault(Return(double(1.1))); + ON_CALL(Const(detector_), successRateEjectionThreshold(Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin)) + .WillByDefault(Return(double(1.1))); cluster_info_maps_.active_clusters_.emplace(name, std::ref(mock_cluster)); } @@ -87,6 +98,7 @@ class BaseClustersRequestFixture : public testing::Test { NiceMock mock_cluster_manager_; Upstream::ClusterManager::ClusterInfoMaps cluster_info_maps_; NiceMock runtime_; + NiceMock detector_; const std::string resource_manager_key_{"test_resource_manager_key"}; std::unique_ptr resource_manager_default_; std::unique_ptr resource_manager_high_; @@ -107,10 +119,10 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { ClustersParams clusters_params; clusters_params.format_ = ClustersParams::Format::Json; - NiceMock test_cluster; + NiceMock test_cluster; loadNewMockClusterByName(test_cluster, "test_cluster"); - NiceMock test_cluster2; + NiceMock test_cluster2; loadNewMockClusterByName(test_cluster2, "test_cluster2"); ResponseResult result = response(*makeRequest(chunk_limit, clusters_params), params.drain_); @@ -120,7 +132,7 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { // clusters to be identical. EXPECT_EQ( std::regex_replace(result.data_.toString(), std::regex("test_cluster2"), "test_cluster"), - R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]}},{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]}}]})EOF"); + R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1},{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1}]})EOF"); } constexpr VerifyJsonOutputParameters VERIFY_JSON_CASES[] = { From d2e6ddb78b4be8e925f0f13baba544407dfb6a69 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 23 Apr 2024 21:07:12 +0000 Subject: [PATCH 27/77] Fix formatting Signed-off-by: Demitri Swan --- source/server/admin/clusters_chunk_processor.cc | 12 ++++++------ test/server/admin/BUILD | 2 +- test/server/admin/clusters_request_test.cc | 11 +++++++---- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index dca72eeb3c85..74359f61ded9 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -187,10 +187,8 @@ void JsonClustersChunkProcessor::render(std::reference_wrappersuccessRateEjectionThreshold( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin) > 0.0) { std::vector success_rate_ejection_threshold{ - {"success_rate_ejection_threshold", double(outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))}, + {"success_rate_ejection_threshold", + double(outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))}, }; addMapEntries(raw_clusters_map_ptr, response, success_rate_ejection_threshold); } @@ -208,8 +207,9 @@ void JsonClustersChunkProcessor::addEjectionThresholds(Json::Streamer::Map* raw_ outlier_detector->successRateEjectionThreshold( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin) > 0.0) { std::vector local_success_rate_ejection_threshold{ - {"local_success_rate_ejection_threshold", double(outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))}, + {"local_success_rate_ejection_threshold", + double(outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))}, }; addMapEntries(raw_clusters_map_ptr, response, local_success_rate_ejection_threshold); } diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index 348fd25fcdab..0dd826e09813 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -285,8 +285,8 @@ envoy_cc_test( "//source/server/admin:clusters_params_lib", "//source/server/admin:clusters_request_lib", "//test/mocks/server:instance_mocks", - "//test/mocks/upstream:upstream_mocks", "//test/mocks/upstream:host_mocks", + "//test/mocks/upstream:upstream_mocks", "//test/test_common:utility_lib", ], ) diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index aa2360c71be1..dbec9916ef0b 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -1,5 +1,5 @@ -#include #include +#include #include #include #include @@ -33,7 +33,6 @@ using testing::Return; using testing::ReturnPointee; using testing::ReturnRef; - class BaseClustersRequestFixture : public testing::Test { protected: BaseClustersRequestFixture() { @@ -86,9 +85,13 @@ class BaseClustersRequestFixture : public testing::Test { ON_CALL(*mock_cluster.info_, resourceManager(Upstream::ResourcePriority::High)) .WillByDefault(ReturnRef(std::ref(*resource_manager_high_).get())); ON_CALL(Const(mock_cluster), outlierDetector()).WillByDefault(Return(&detector_)); - ON_CALL(Const(detector_), successRateEjectionThreshold(Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin)) + ON_CALL(Const(detector_), + successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin)) .WillByDefault(Return(double(1.1))); - ON_CALL(Const(detector_), successRateEjectionThreshold(Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin)) + ON_CALL(Const(detector_), + successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin)) .WillByDefault(Return(double(1.1))); cluster_info_maps_.active_clusters_.emplace(name, std::ref(mock_cluster)); } From 4c1ce2f8ac28f58c7c69d3620ce24247f06f4a90 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 24 Apr 2024 14:58:17 +0000 Subject: [PATCH 28/77] Add added_via_api Signed-off-by: Demitri Swan --- source/server/admin/clusters_chunk_processor.cc | 4 ++++ test/server/admin/clusters_request_test.cc | 6 ++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 74359f61ded9..483356a36ac3 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -187,6 +187,10 @@ void JsonClustersChunkProcessor::render(std::reference_wrapper added_via_api{ + {"added_via_api", cluster_info->addedViaApi()}, + }; + addMapEntries(cluster_map.get(), response, added_via_api); } void JsonClustersChunkProcessor::addEjectionThresholds(Json::Streamer::Map* raw_clusters_map_ptr, diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index dbec9916ef0b..f1f3e580fc4b 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -1,7 +1,5 @@ -#include #include #include -#include #include #include @@ -26,7 +24,6 @@ namespace Envoy { namespace Server { -using testing::AtLeast; using testing::Const; using testing::NiceMock; using testing::Return; @@ -93,6 +90,7 @@ class BaseClustersRequestFixture : public testing::Test { successRateEjectionThreshold( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin)) .WillByDefault(Return(double(1.1))); + ON_CALL(*mock_cluster.info_, addedViaApi()).WillByDefault(Return(true)); cluster_info_maps_.active_clusters_.emplace(name, std::ref(mock_cluster)); } @@ -135,7 +133,7 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { // clusters to be identical. EXPECT_EQ( std::regex_replace(result.data_.toString(), std::regex("test_cluster2"), "test_cluster"), - R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1},{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1}]})EOF"); + R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1,"added_via_api":true},{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1,"added_via_api":true}]})EOF"); } constexpr VerifyJsonOutputParameters VERIFY_JSON_CASES[] = { From 9f9152f4acf64b5923a48a8c1ac4df811afba66a Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 24 Apr 2024 19:02:43 +0000 Subject: [PATCH 29/77] Save progress on host config Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 2 + .../server/admin/clusters_chunk_processor.cc | 167 +++++++++++++++++- .../server/admin/clusters_chunk_processor.h | 8 + 3 files changed, 176 insertions(+), 1 deletion(-) diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index a627858b14cb..20ff1d54e416 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -454,8 +454,10 @@ envoy_cc_library( "//envoy/buffer:buffer_interface", "//envoy/common:pure_lib", "//envoy/upstream:cluster_manager_interface", + "//envoy/upstream:upstream_lib", "//source/common/common:logger_lib", "//source/common/json:json_streamer_lib", + "//source/common/network:utility_lib", "//source/common/upstream:host_utility_lib", "@envoy_api//envoy/admin/v3:pkg_cc_proto", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 483356a36ac3..198595d49d4c 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -4,18 +4,22 @@ #include #include +#include "clusters_chunk_processor.h" #include "envoy/admin/v3/clusters.pb.h" #include "envoy/buffer/buffer.h" #include "envoy/config/core/v3/base.pb.h" #include "envoy/upstream/cluster_manager.h" #include "envoy/upstream/resource_manager.h" +#include "envoy/upstream/upstream.h" +#include "source/common/network/utility.h" -#include "source/common/buffer/buffer_impl.h" #include "source/common/upstream/host_utility.h" namespace Envoy { namespace Server { +void setHealthFlag(Json::Streamer::Map* raw_host_ptr, Upstream::Host::HealthFlag flag, const Upstream::Host& host); + TextClustersChunkProcessor::TextClustersChunkProcessor( uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map) @@ -193,6 +197,167 @@ void JsonClustersChunkProcessor::render(std::reference_wrapperaddArray(); + for (const Upstream::HostSetPtr& host_set : unwrapped_cluster.prioritySet().hostSetsPerPriority()) { + Json::Streamer::ArrayPtr hosts = raw_clusters_map_ptr->addArray(); + processHostSet(host_sets.get(), host_set, response); + } +} + +void JsonClustersChunkProcessor::processHostSet(Json::Streamer::Array* raw_hosts_statuses_ptr, const Upstream::HostSetPtr& host_set, Buffer::Instance& response) { + Json::Streamer::ArrayPtr host_statuses_ptr = raw_hosts_statuses_ptr->addArray(); + for (const Upstream::HostSharedPtr& host : host_set->hosts()) { + processHost(host_statuses_ptr.get(), host, response); + } +} + + +void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_statuses_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { + Json::Streamer::MapPtr host_ptr = raw_host_statuses_ptr->addMap(); + std::vector hostname{ + {"hostname", host->hostname()}, + }; + addMapEntries(host_ptr.get(), response, hostname); + { + host_ptr->addKey("locality"); + Json::Streamer::MapPtr locality_ptr = host_ptr->addMap(); + std::vector locality{ + {"region", host->locality().region()}, + {"zone", host->locality().zone()}, + {"sub_zone", host->locality().sub_zone()}, + }; + addMapEntries(locality_ptr.get(), response, locality); + } + + setHealthFlags(host_ptr.get(), host, response); + + double success_rate = host->outlierDetector().successRate( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin); + if (success_rate >= 0.0) { + std::vector success_rate_property{ + {"suceess_rate", success_rate}, + }; + addMapEntries(host_ptr.get(), response, success_rate_property); + } + + std::vector weight_and_priority{ + {"weight", uint64_t(host->weight())}, + {"priority", uint64_t(host->priority())}, + }; + addMapEntries(host_ptr.get(), response, weight_and_priority); + success_rate = host->outlierDetector().successRate( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin); + if (success_rate >= 0.0) { + std::vector success_rate_property{ + {"local_origin_success_rate", success_rate}, + }; + addMapEntries(host_ptr.get(), response, success_rate_property); + } +} + + +void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { + Json::Streamer::ArrayPtr stats = raw_host_ptr->addArray(); + for (const auto& [counter_name, counter] : host->counters()) { + Json::Streamer::MapPtr stat_obj = stats->addMap(); + // TODO(demitriswan) Make sure this name conversion works as expected + std::vector stats_properties{ + {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::COUNTER)}, + {"name", counter_name}, + {"value", counter.get().value()}, + }; + addMapEntries(raw_host_ptr, response, stats_properties); + } + for (const auto& [gauge_name, gauge] : host->gauges()) { + Json::Streamer::MapPtr stat_obj = stats->addMap(); + // TODO(demitriswan) Make sure this name conversion works as expected + std::vector stats_properties{ + {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::GAUGE)}, + {"name", gauge_name}, + {"value", gauge.get().value()}, + }; + addMapEntries(raw_host_ptr, response, stats_properties); + } +} + +void JsonClusterChunkProcessor::setHealthFlags(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { + raw_host_ptr->addKey("health_stats"); + Json::Streamer::MapPtr heath_status_ptr = raw_host_ptr->addMap(); + // Invokes setHealthFlag for each health flag. + #define SET_HEALTH_FLAG(name, notused) \ + setHealthFlag(map_status_ptr.get(), Upstream::Host::HealthFlag::name, host, response); + HEALTH_FLAG_ENUM_VALUES(SET_HEALTH_FLAG) + #undef SET_HEALTH_FLAG +} + +void JsonClustersChunkProcessor::setHealthFlag(Json::Streamer::Map* health_status_ptr, Upstream::Host::HealthFlag flag, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { + switch (flag) { + case Upstream::Host::HealthFlag::FAILED_ACTIVE_HC: { + std::vector status{ + {"failed_active_health_check", host.get()->healthFlagGet(flag)}, + }; + addMapEntries(health_status_ptr, response, status); + break; + } + case Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK: { + std::vector status{ + {"failed_outlier_check", host.get()->healthFlagGet(flag)}, + }; + addMapEntries(health_status_ptr, response, status); + break; + } + case Upstream::Host::HealthFlag::FAILED_EDS_HEALTH: + case Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH: + case Upstream::Host::HealthFlag::EDS_STATUS_DRAINING: { + // TODO(demitriswan) make sure this name conversion works as expected. + std::vector status{ + {"eds_health_status", envoy::config::core::v3::HealthStatus_Name(host.get()->healthFlagGet(flag))}, + }; + addMapEntries(health_status_ptr, response, status); + break; + } + case Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC: { + std::vector status{ + {"failed_active_degraded_check", host.get()->healthFlagGet(flag)}, + }; + addMapEntries(health_status_ptr, response, status); + break; + } + case Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL: { + std::vector status{ + {"pending_dynamic_removal", host.get()->healthFlagGet(flag)}, + }; + addMapEntries(health_status_ptr, response, status); + break; + } + case Upstream::Host::HealthFlag::PENDING_ACTIVE_HC: { + std::vector status{ + {"pending_active_hc", host.get()->healthFlagGet(flag)}, + }; + addMapEntries(health_status_ptr, response, status); + break; + } + case Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL: { + std::vector status{ + {"excluded_via_immediate_hc_fail", host.get()->healthFlagGet(flag)}, + }; + addMapEntries(health_status_ptr, response, status); + break; + } + case Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT: { + std::vector status{ + {"active_hc_timeout", host.get()->healthFlagGet(flag)}, + }; + addMapEntries(health_status_ptr, response, status); + break; + } +} + void JsonClustersChunkProcessor::addEjectionThresholds(Json::Streamer::Map* raw_clusters_map_ptr, const Upstream::Cluster& unwrapped_cluster, Buffer::Instance& response) { diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index 1222aa1d9e06..ef5d9829834c 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -78,6 +78,14 @@ class JsonClustersChunkProcessor : public ClustersChunkProcessor { void addEjectionThresholds(Json::Streamer::Map* raw_clusters_map_ptr, const Upstream::Cluster& unwrapped_cluster, Buffer::Instance& response); + void addHostStatuses(Json::Streamer::Map* raw_clusters_map_ptr, + const Upstream::Cluster& unwrapped_cluster, + Buffer::Instance& response); + void processHostSet(Json::Streamer::Array* raw_hosts_statuses_ptr, const Upstream::HostSetPtr& host_set, Buffer::Instance& response); + void processHost(Json::Streamer::Array* raw_host_statuses_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response); + void buildHostStats(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response); + void setHealthFlags(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response); + void setHealthFlag(Json::Streamer::Map* raw_host_ptr, Upstream::Host::HealthFlag flag, const Upstream::HostSharedPtr& host, Buffer::Instance& response); const uint64_t chunk_limit_; Http::ResponseHeaderMap& response_headers_; From d25966f77bfc477ed960f1f02b517ffcc2d0bf7c Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 24 Apr 2024 19:03:23 +0000 Subject: [PATCH 30/77] Fix formatting Signed-off-by: Demitri Swan --- .../server/admin/clusters_chunk_processor.cc | 249 +++++++++--------- .../server/admin/clusters_chunk_processor.h | 18 +- 2 files changed, 141 insertions(+), 126 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 198595d49d4c..1eb27203c784 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -1,24 +1,25 @@ +#include "clusters_chunk_processor.h" #include "source/server/admin/clusters_chunk_processor.h" #include #include #include -#include "clusters_chunk_processor.h" #include "envoy/admin/v3/clusters.pb.h" #include "envoy/buffer/buffer.h" #include "envoy/config/core/v3/base.pb.h" #include "envoy/upstream/cluster_manager.h" #include "envoy/upstream/resource_manager.h" #include "envoy/upstream/upstream.h" -#include "source/common/network/utility.h" +#include "source/common/network/utility.h" #include "source/common/upstream/host_utility.h" namespace Envoy { namespace Server { -void setHealthFlag(Json::Streamer::Map* raw_host_ptr, Upstream::Host::HealthFlag flag, const Upstream::Host& host); +void setHealthFlag(Json::Streamer::Map* raw_host_ptr, Upstream::Host::HealthFlag flag, + const Upstream::Host& host); TextClustersChunkProcessor::TextClustersChunkProcessor( uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, @@ -197,39 +198,42 @@ void JsonClustersChunkProcessor::render(std::reference_wrapperaddArray(); - for (const Upstream::HostSetPtr& host_set : unwrapped_cluster.prioritySet().hostSetsPerPriority()) { - Json::Streamer::ArrayPtr hosts = raw_clusters_map_ptr->addArray(); + for (const Upstream::HostSetPtr& host_set : + unwrapped_cluster.prioritySet().hostSetsPerPriority()) { + Json::Streamer::ArrayPtr hosts = raw_clusters_map_ptr->addArray(); processHostSet(host_sets.get(), host_set, response); } } -void JsonClustersChunkProcessor::processHostSet(Json::Streamer::Array* raw_hosts_statuses_ptr, const Upstream::HostSetPtr& host_set, Buffer::Instance& response) { +void JsonClustersChunkProcessor::processHostSet(Json::Streamer::Array* raw_hosts_statuses_ptr, + const Upstream::HostSetPtr& host_set, + Buffer::Instance& response) { Json::Streamer::ArrayPtr host_statuses_ptr = raw_hosts_statuses_ptr->addArray(); for (const Upstream::HostSharedPtr& host : host_set->hosts()) { processHost(host_statuses_ptr.get(), host, response); } } - -void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_statuses_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { +void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_statuses_ptr, + const Upstream::HostSharedPtr& host, + Buffer::Instance& response) { Json::Streamer::MapPtr host_ptr = raw_host_statuses_ptr->addMap(); std::vector hostname{ - {"hostname", host->hostname()}, + {"hostname", host->hostname()}, }; addMapEntries(host_ptr.get(), response, hostname); { host_ptr->addKey("locality"); Json::Streamer::MapPtr locality_ptr = host_ptr->addMap(); std::vector locality{ - {"region", host->locality().region()}, - {"zone", host->locality().zone()}, - {"sub_zone", host->locality().sub_zone()}, + {"region", host->locality().region()}, + {"zone", host->locality().zone()}, + {"sub_zone", host->locality().sub_zone()}, }; addMapEntries(locality_ptr.get(), response, locality); } @@ -237,39 +241,40 @@ void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_sta setHealthFlags(host_ptr.get(), host, response); double success_rate = host->outlierDetector().successRate( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin); + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin); if (success_rate >= 0.0) { std::vector success_rate_property{ - {"suceess_rate", success_rate}, + {"suceess_rate", success_rate}, }; addMapEntries(host_ptr.get(), response, success_rate_property); } std::vector weight_and_priority{ - {"weight", uint64_t(host->weight())}, - {"priority", uint64_t(host->priority())}, + {"weight", uint64_t(host->weight())}, + {"priority", uint64_t(host->priority())}, }; addMapEntries(host_ptr.get(), response, weight_and_priority); success_rate = host->outlierDetector().successRate( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin); + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin); if (success_rate >= 0.0) { std::vector success_rate_property{ - {"local_origin_success_rate", success_rate}, + {"local_origin_success_rate", success_rate}, }; addMapEntries(host_ptr.get(), response, success_rate_property); - } + } } - -void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { +void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_ptr, + const Upstream::HostSharedPtr& host, + Buffer::Instance& response) { Json::Streamer::ArrayPtr stats = raw_host_ptr->addArray(); for (const auto& [counter_name, counter] : host->counters()) { Json::Streamer::MapPtr stat_obj = stats->addMap(); // TODO(demitriswan) Make sure this name conversion works as expected std::vector stats_properties{ - {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::COUNTER)}, - {"name", counter_name}, - {"value", counter.get().value()}, + {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::COUNTER)}, + {"name", counter_name}, + {"value", counter.get().value()}, }; addMapEntries(raw_host_ptr, response, stats_properties); } @@ -277,36 +282,41 @@ void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_pt Json::Streamer::MapPtr stat_obj = stats->addMap(); // TODO(demitriswan) Make sure this name conversion works as expected std::vector stats_properties{ - {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::GAUGE)}, - {"name", gauge_name}, - {"value", gauge.get().value()}, + {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::GAUGE)}, + {"name", gauge_name}, + {"value", gauge.get().value()}, }; addMapEntries(raw_host_ptr, response, stats_properties); } } -void JsonClusterChunkProcessor::setHealthFlags(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { +void JsonClusterChunkProcessor::setHealthFlags(Json::Streamer::Map* raw_host_ptr, + const Upstream::HostSharedPtr& host, + Buffer::Instance& response) { raw_host_ptr->addKey("health_stats"); Json::Streamer::MapPtr heath_status_ptr = raw_host_ptr->addMap(); - // Invokes setHealthFlag for each health flag. - #define SET_HEALTH_FLAG(name, notused) \ - setHealthFlag(map_status_ptr.get(), Upstream::Host::HealthFlag::name, host, response); - HEALTH_FLAG_ENUM_VALUES(SET_HEALTH_FLAG) - #undef SET_HEALTH_FLAG +// Invokes setHealthFlag for each health flag. +#define SET_HEALTH_FLAG(name, notused) \ + setHealthFlag(map_status_ptr.get(), Upstream::Host::HealthFlag::name, host, response); + HEALTH_FLAG_ENUM_VALUES(SET_HEALTH_FLAG) +#undef SET_HEALTH_FLAG } -void JsonClustersChunkProcessor::setHealthFlag(Json::Streamer::Map* health_status_ptr, Upstream::Host::HealthFlag flag, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { +void JsonClustersChunkProcessor::setHealthFlag(Json::Streamer::Map* health_status_ptr, + Upstream::Host::HealthFlag flag, + const Upstream::HostSharedPtr& host, + Buffer::Instance& response) { switch (flag) { case Upstream::Host::HealthFlag::FAILED_ACTIVE_HC: { std::vector status{ - {"failed_active_health_check", host.get()->healthFlagGet(flag)}, + {"failed_active_health_check", host.get()->healthFlagGet(flag)}, }; addMapEntries(health_status_ptr, response, status); break; } case Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK: { std::vector status{ - {"failed_outlier_check", host.get()->healthFlagGet(flag)}, + {"failed_outlier_check", host.get()->healthFlagGet(flag)}, }; addMapEntries(health_status_ptr, response, status); break; @@ -316,129 +326,130 @@ void JsonClustersChunkProcessor::setHealthFlag(Json::Streamer::Map* health_statu case Upstream::Host::HealthFlag::EDS_STATUS_DRAINING: { // TODO(demitriswan) make sure this name conversion works as expected. std::vector status{ - {"eds_health_status", envoy::config::core::v3::HealthStatus_Name(host.get()->healthFlagGet(flag))}, + {"eds_health_status", + envoy::config::core::v3::HealthStatus_Name(host.get()->healthFlagGet(flag))}, }; addMapEntries(health_status_ptr, response, status); break; } case Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC: { std::vector status{ - {"failed_active_degraded_check", host.get()->healthFlagGet(flag)}, + {"failed_active_degraded_check", host.get()->healthFlagGet(flag)}, }; addMapEntries(health_status_ptr, response, status); break; } case Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL: { std::vector status{ - {"pending_dynamic_removal", host.get()->healthFlagGet(flag)}, + {"pending_dynamic_removal", host.get()->healthFlagGet(flag)}, }; addMapEntries(health_status_ptr, response, status); break; } case Upstream::Host::HealthFlag::PENDING_ACTIVE_HC: { std::vector status{ - {"pending_active_hc", host.get()->healthFlagGet(flag)}, + {"pending_active_hc", host.get()->healthFlagGet(flag)}, }; addMapEntries(health_status_ptr, response, status); break; } case Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL: { std::vector status{ - {"excluded_via_immediate_hc_fail", host.get()->healthFlagGet(flag)}, + {"excluded_via_immediate_hc_fail", host.get()->healthFlagGet(flag)}, }; addMapEntries(health_status_ptr, response, status); break; } case Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT: { std::vector status{ - {"active_hc_timeout", host.get()->healthFlagGet(flag)}, + {"active_hc_timeout", host.get()->healthFlagGet(flag)}, }; addMapEntries(health_status_ptr, response, status); break; } -} - -void JsonClustersChunkProcessor::addEjectionThresholds(Json::Streamer::Map* raw_clusters_map_ptr, - const Upstream::Cluster& unwrapped_cluster, - Buffer::Instance& response) { - const Upstream::Outlier::Detector* outlier_detector = unwrapped_cluster.outlierDetector(); - if (outlier_detector != nullptr && - outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin) > 0.0) { - std::vector success_rate_ejection_threshold{ - {"success_rate_ejection_threshold", - double(outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))}, - }; - addMapEntries(raw_clusters_map_ptr, response, success_rate_ejection_threshold); } - if (outlier_detector != nullptr && - outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin) > 0.0) { - std::vector local_success_rate_ejection_threshold{ - {"local_success_rate_ejection_threshold", - double(outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))}, - }; - addMapEntries(raw_clusters_map_ptr, response, local_success_rate_ejection_threshold); + + void JsonClustersChunkProcessor::addEjectionThresholds(Json::Streamer::Map * raw_clusters_map_ptr, + const Upstream::Cluster& unwrapped_cluster, + Buffer::Instance& response) { + const Upstream::Outlier::Detector* outlier_detector = unwrapped_cluster.outlierDetector(); + if (outlier_detector != nullptr && + outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin) > 0.0) { + std::vector success_rate_ejection_threshold{ + {"success_rate_ejection_threshold", + double(outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))}, + }; + addMapEntries(raw_clusters_map_ptr, response, success_rate_ejection_threshold); + } + if (outlier_detector != nullptr && + outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin) > 0.0) { + std::vector local_success_rate_ejection_threshold{ + {"local_success_rate_ejection_threshold", + double(outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))}, + }; + addMapEntries(raw_clusters_map_ptr, response, local_success_rate_ejection_threshold); + } } -} -void JsonClustersChunkProcessor::addCircuitBreakers( - Json::Streamer::Map* raw_clusters_map_ptr, Upstream::ClusterInfoConstSharedPtr cluster_info, - Buffer::Instance& response) { - raw_clusters_map_ptr->addKey("circuit_breakers"); - Json::Streamer::MapPtr circuit_breakers = raw_clusters_map_ptr->addMap(); - circuit_breakers->addKey("thresholds"); - Json::Streamer::ArrayPtr thresholds = circuit_breakers->addArray(); - addCircuitBreakerForPriority(envoy::config::core::v3::RoutingPriority::DEFAULT, thresholds.get(), - response, - cluster_info->resourceManager(Upstream::ResourcePriority::Default)); - addCircuitBreakerForPriority(envoy::config::core::v3::RoutingPriority::HIGH, thresholds.get(), - response, - cluster_info->resourceManager(Upstream::ResourcePriority::High)); -} + void JsonClustersChunkProcessor::addCircuitBreakers( + Json::Streamer::Map * raw_clusters_map_ptr, Upstream::ClusterInfoConstSharedPtr cluster_info, + Buffer::Instance & response) { + raw_clusters_map_ptr->addKey("circuit_breakers"); + Json::Streamer::MapPtr circuit_breakers = raw_clusters_map_ptr->addMap(); + circuit_breakers->addKey("thresholds"); + Json::Streamer::ArrayPtr thresholds = circuit_breakers->addArray(); + addCircuitBreakerForPriority( + envoy::config::core::v3::RoutingPriority::DEFAULT, thresholds.get(), response, + cluster_info->resourceManager(Upstream::ResourcePriority::Default)); + addCircuitBreakerForPriority(envoy::config::core::v3::RoutingPriority::HIGH, thresholds.get(), + response, + cluster_info->resourceManager(Upstream::ResourcePriority::High)); + } -void JsonClustersChunkProcessor::addCircuitBreakerForPriority( - const envoy::config::core::v3::RoutingPriority& priority, - Json::Streamer::Array* raw_thresholds_ptr, Buffer::Instance& response, - Upstream::ResourceManager& resource_manager) { - Json::Streamer::MapPtr threshold = raw_thresholds_ptr->addMap(); - std::vector entries{ - {"priority", - priority == envoy::config::core::v3::RoutingPriority::DEFAULT ? "DEFAULT" : "HIGH"}, - {"max_connections", resource_manager.connections().max()}, - {"max_pending_requests", resource_manager.pendingRequests().max()}, - {"max_requests", resource_manager.requests().max()}, - {"max_retries", resource_manager.retries().max()}, - }; - addMapEntries(threshold.get(), response, entries); -} + void JsonClustersChunkProcessor::addCircuitBreakerForPriority( + const envoy::config::core::v3::RoutingPriority& priority, + Json::Streamer::Array* raw_thresholds_ptr, Buffer::Instance& response, + Upstream::ResourceManager& resource_manager) { + Json::Streamer::MapPtr threshold = raw_thresholds_ptr->addMap(); + std::vector entries{ + {"priority", + priority == envoy::config::core::v3::RoutingPriority::DEFAULT ? "DEFAULT" : "HIGH"}, + {"max_connections", resource_manager.connections().max()}, + {"max_pending_requests", resource_manager.pendingRequests().max()}, + {"max_requests", resource_manager.requests().max()}, + {"max_retries", resource_manager.retries().max()}, + }; + addMapEntries(threshold.get(), response, entries); + } -// Json::Streamer holds a reference to a Buffer::Instance reference but the API for Request -// takes a Buffer::Instance reference on each call to nextChunk. So, at the end of each -// Json::Streamer function invocation, call drainBufferIntoResponse to ensure that the -// contents written to its buffer gets moved and appended to the response. -void JsonClustersChunkProcessor::drainBufferIntoResponse(Buffer::Instance& response) { - if (&response != &buffer_) { - response.move(buffer_); + // Json::Streamer holds a reference to a Buffer::Instance reference but the API for Request + // takes a Buffer::Instance reference on each call to nextChunk. So, at the end of each + // Json::Streamer function invocation, call drainBufferIntoResponse to ensure that the + // contents written to its buffer gets moved and appended to the response. + void JsonClustersChunkProcessor::drainBufferIntoResponse(Buffer::Instance & response) { + if (&response != &buffer_) { + response.move(buffer_); + } } -} -void JsonClustersChunkProcessor::addMapEntries( - Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, - std::vector& entries) { - raw_map_ptr->addEntries(entries); - drainBufferIntoResponse(response); -} + void JsonClustersChunkProcessor::addMapEntries( + Json::Streamer::Map * raw_map_ptr, Buffer::Instance & response, + std::vector & entries) { + raw_map_ptr->addEntries(entries); + drainBufferIntoResponse(response); + } -// Start destruction of the ClustersJsonContext to render the closing tokens and push to the -// buffer. Since we've pushed data into the buffer in the Json::Streamer, we'll need to drain -// the contents into the response. -void JsonClustersChunkProcessor::finalize(Buffer::Instance& response) { - json_context_holder_.pop_back(); - drainBufferIntoResponse(response); -} + // Start destruction of the ClustersJsonContext to render the closing tokens and push to the + // buffer. Since we've pushed data into the buffer in the Json::Streamer, we'll need to drain + // the contents into the response. + void JsonClustersChunkProcessor::finalize(Buffer::Instance & response) { + json_context_holder_.pop_back(); + drainBufferIntoResponse(response); + } } // namespace Server } // namespace Envoy diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index ef5d9829834c..c66b6443c1b3 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -79,13 +79,17 @@ class JsonClustersChunkProcessor : public ClustersChunkProcessor { const Upstream::Cluster& unwrapped_cluster, Buffer::Instance& response); void addHostStatuses(Json::Streamer::Map* raw_clusters_map_ptr, - const Upstream::Cluster& unwrapped_cluster, - Buffer::Instance& response); - void processHostSet(Json::Streamer::Array* raw_hosts_statuses_ptr, const Upstream::HostSetPtr& host_set, Buffer::Instance& response); - void processHost(Json::Streamer::Array* raw_host_statuses_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response); - void buildHostStats(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response); - void setHealthFlags(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response); - void setHealthFlag(Json::Streamer::Map* raw_host_ptr, Upstream::Host::HealthFlag flag, const Upstream::HostSharedPtr& host, Buffer::Instance& response); + const Upstream::Cluster& unwrapped_cluster, Buffer::Instance& response); + void processHostSet(Json::Streamer::Array* raw_hosts_statuses_ptr, + const Upstream::HostSetPtr& host_set, Buffer::Instance& response); + void processHost(Json::Streamer::Array* raw_host_statuses_ptr, + const Upstream::HostSharedPtr& host, Buffer::Instance& response); + void buildHostStats(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, + Buffer::Instance& response); + void setHealthFlags(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, + Buffer::Instance& response); + void setHealthFlag(Json::Streamer::Map* raw_host_ptr, Upstream::Host::HealthFlag flag, + const Upstream::HostSharedPtr& host, Buffer::Instance& response); const uint64_t chunk_limit_; Http::ResponseHeaderMap& response_headers_; From 0dd628fda75852f54391f808178ed851245c07e9 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 24 Apr 2024 19:55:33 +0000 Subject: [PATCH 31/77] Fix formatting Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 20ff1d54e416..a20c793f9706 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -454,7 +454,7 @@ envoy_cc_library( "//envoy/buffer:buffer_interface", "//envoy/common:pure_lib", "//envoy/upstream:cluster_manager_interface", - "//envoy/upstream:upstream_lib", + "//envoy/upstream:upstream_interface", "//source/common/common:logger_lib", "//source/common/json:json_streamer_lib", "//source/common/network:utility_lib", From a82222c54bcca29c1ea0d205508670aafbcf45b2 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 24 Apr 2024 20:00:05 +0000 Subject: [PATCH 32/77] Host config code building Signed-off-by: Demitri Swan --- source/server/admin/clusters_chunk_processor.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 1eb27203c784..34a71cce99f1 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -290,14 +290,14 @@ void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_pt } } -void JsonClusterChunkProcessor::setHealthFlags(Json::Streamer::Map* raw_host_ptr, +void JsonClustersChunkProcessor::setHealthFlags(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { raw_host_ptr->addKey("health_stats"); - Json::Streamer::MapPtr heath_status_ptr = raw_host_ptr->addMap(); + Json::Streamer::MapPtr health_status_ptr = raw_host_ptr->addMap(); // Invokes setHealthFlag for each health flag. #define SET_HEALTH_FLAG(name, notused) \ - setHealthFlag(map_status_ptr.get(), Upstream::Host::HealthFlag::name, host, response); + setHealthFlag(health_status_ptr.get(), Upstream::Host::HealthFlag::name, host, response); HEALTH_FLAG_ENUM_VALUES(SET_HEALTH_FLAG) #undef SET_HEALTH_FLAG } @@ -368,6 +368,7 @@ void JsonClustersChunkProcessor::setHealthFlag(Json::Streamer::Map* health_statu break; } } +} void JsonClustersChunkProcessor::addEjectionThresholds(Json::Streamer::Map * raw_clusters_map_ptr, const Upstream::Cluster& unwrapped_cluster, From 4b0daf68d73259b5ff380be83b3eefd79ec248bb Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 24 Apr 2024 23:46:57 +0000 Subject: [PATCH 33/77] Saving progress on testing host config Signed-off-by: Demitri Swan --- .../server/admin/clusters_chunk_processor.cc | 83 +++++++++---------- test/server/admin/clusters_request_test.cc | 60 ++++++++++++-- 2 files changed, 94 insertions(+), 49 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 34a71cce99f1..e801e4d13fd3 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -196,26 +196,25 @@ void JsonClustersChunkProcessor::render(std::reference_wrapperaddedViaApi()}, }; addMapEntries(cluster_map.get(), response, added_via_api); + addHostStatuses(cluster_map.get(), cluster, response); } void JsonClustersChunkProcessor::addHostStatuses(Json::Streamer::Map* raw_clusters_map_ptr, const Upstream::Cluster& unwrapped_cluster, Buffer::Instance& response) { - - Json::Streamer::ArrayPtr host_sets = raw_clusters_map_ptr->addArray(); + raw_clusters_map_ptr->addKey("host_statuses"); + Json::Streamer::ArrayPtr host_statuses_ptr = raw_clusters_map_ptr->addArray(); for (const Upstream::HostSetPtr& host_set : unwrapped_cluster.prioritySet().hostSetsPerPriority()) { - Json::Streamer::ArrayPtr hosts = raw_clusters_map_ptr->addArray(); - processHostSet(host_sets.get(), host_set, response); + processHostSet(host_statuses_ptr.get(), host_set, response); } } -void JsonClustersChunkProcessor::processHostSet(Json::Streamer::Array* raw_hosts_statuses_ptr, +void JsonClustersChunkProcessor::processHostSet(Json::Streamer::Array* raw_host_statuses_ptr, const Upstream::HostSetPtr& host_set, Buffer::Instance& response) { - Json::Streamer::ArrayPtr host_statuses_ptr = raw_hosts_statuses_ptr->addArray(); for (const Upstream::HostSharedPtr& host : host_set->hosts()) { - processHost(host_statuses_ptr.get(), host, response); + processHost(raw_host_statuses_ptr, host, response); } } @@ -227,41 +226,41 @@ void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_sta {"hostname", host->hostname()}, }; addMapEntries(host_ptr.get(), response, hostname); - { - host_ptr->addKey("locality"); - Json::Streamer::MapPtr locality_ptr = host_ptr->addMap(); - std::vector locality{ - {"region", host->locality().region()}, - {"zone", host->locality().zone()}, - {"sub_zone", host->locality().sub_zone()}, - }; - addMapEntries(locality_ptr.get(), response, locality); - } - - setHealthFlags(host_ptr.get(), host, response); - - double success_rate = host->outlierDetector().successRate( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin); - if (success_rate >= 0.0) { - std::vector success_rate_property{ - {"suceess_rate", success_rate}, - }; - addMapEntries(host_ptr.get(), response, success_rate_property); - } - - std::vector weight_and_priority{ - {"weight", uint64_t(host->weight())}, - {"priority", uint64_t(host->priority())}, - }; - addMapEntries(host_ptr.get(), response, weight_and_priority); - success_rate = host->outlierDetector().successRate( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin); - if (success_rate >= 0.0) { - std::vector success_rate_property{ - {"local_origin_success_rate", success_rate}, - }; - addMapEntries(host_ptr.get(), response, success_rate_property); - } + // { + // host_ptr->addKey("locality"); + // Json::Streamer::MapPtr locality_ptr = host_ptr->addMap(); + // std::vector locality{ + // {"region", host->locality().region()}, + // {"zone", host->locality().zone()}, + // {"sub_zone", host->locality().sub_zone()}, + // }; + // addMapEntries(locality_ptr.get(), response, locality); + // } + // buildHostStats(host_ptr.get(), host, response); + // setHealthFlags(host_ptr.get(), host, response); + + // double success_rate = host->outlierDetector().successRate( + // Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin); + // if (success_rate >= 0.0) { + // std::vector success_rate_property{ + // {"suceess_rate", success_rate}, + // }; + // addMapEntries(host_ptr.get(), response, success_rate_property); + // } + + // std::vector weight_and_priority{ + // {"weight", uint64_t(host->weight())}, + // {"priority", uint64_t(host->priority())}, + // }; + // addMapEntries(host_ptr.get(), response, weight_and_priority); + // success_rate = host->outlierDetector().successRate( + // Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin); + // if (success_rate >= 0.0) { + // std::vector success_rate_property{ + // {"local_origin_success_rate", success_rate}, + // }; + // addMapEntries(host_ptr.get(), response, success_rate_property); + // } } void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_ptr, diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index f1f3e580fc4b..2735d55b700b 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -5,8 +5,6 @@ #include "envoy/buffer/buffer.h" #include "envoy/http/codes.h" -#include "envoy/server/instance.h" -#include "envoy/stream_info/stream_info.h" #include "source/common/buffer/buffer_impl.h" #include "source/common/upstream/resource_manager_impl.h" @@ -14,9 +12,10 @@ #include "source/server/admin/clusters_request.h" #include "test/mocks/server/instance.h" -#include "test/mocks/upstream/cluster.h" +#include "test/mocks/upstream/cluster_priority_set.h" #include "test/mocks/upstream/cluster_manager.h" #include "test/mocks/upstream/host.h" +#include "test/mocks/upstream/priority_set.h" #include "test/test_common/utility.h" #include "gtest/gtest.h" @@ -41,6 +40,9 @@ class BaseClustersRequestFixture : public testing::Test { resource_manager_high_ = std::make_unique( runtime_, resource_manager_key_, 4096, 4096, 4096, 16, 4, 1024, mock_cluster_info_.circuit_breakers_stats_, std::nullopt, std::nullopt); + locality_.set_region("test_region"); + locality_.set_zone("test_zone"); + locality_.set_sub_zone("test_sub_zone"); } using ClustersRequestPtr = std::unique_ptr; @@ -73,7 +75,7 @@ class BaseClustersRequestFixture : public testing::Test { }; } - void loadNewMockClusterByName(NiceMock& mock_cluster, + void loadNewMockClusterByName(NiceMock& mock_cluster, absl::string_view name) { mock_cluster.info_->name_ = name; ON_CALL(*mock_cluster.info_, edsServiceName()).WillByDefault(ReturnRef("potato_launcher")); @@ -92,6 +94,43 @@ class BaseClustersRequestFixture : public testing::Test { .WillByDefault(Return(double(1.1))); ON_CALL(*mock_cluster.info_, addedViaApi()).WillByDefault(Return(true)); cluster_info_maps_.active_clusters_.emplace(name, std::ref(mock_cluster)); + + Upstream::MockHostSet* host_set = mock_cluster.priority_set_.getMockHostSet(0); + + Stats::PrimitiveCounter test_counter; + test_counter.add(10); + std::vector> counters{ + {"test_counter", test_counter}, + }; + Stats::PrimitiveGauge test_gauge; + test_gauge.set(11); + std::vector> gauges{ + {"test_gauge", test_gauge}, + }; + ON_CALL(*mock_host_, counters()).WillByDefault(Invoke([&counters]() { return counters; })); + ON_CALL(*mock_host_, gauges()).WillByDefault(Invoke([&gauges]() { return gauges; })); + + ON_CALL(*mock_host_, hostname()).WillByDefault(ReturnRef("test_hostname")); + ON_CALL(*mock_host_, locality()).WillByDefault(ReturnRef(locality_)); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::FAILED_ACTIVE_HC)).WillByDefault(Return(false)); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK)).WillByDefault(Return(false)); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH)).WillByDefault(Return("HEALTHY")); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH)).WillByDefault(Return("HEALTHY")); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::EDS_STATUS_DRAINING)).WillByDefault(Return("HEALTHY")); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC)).WillByDefault(Return(false)); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL)).WillByDefault(Return(false)); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::PENDING_ACTIVE_HC)).WillByDefault(Return(false)); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL)).WillByDefault(Return(false)); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT)).WillByDefault(Return(false)); + ON_CALL(*mock_host_, outlierDetector()).WillByDefault(ReturnRef(detector_host_monitor_)); + ON_CALL(Const(detector_host_monitor_), successRate(Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin)) + .WillByDefault(Return(double(1.0))); + ON_CALL(*mock_host_, weight()).WillByDefault(Return(uint64_t(1))); + ON_CALL(*mock_host_, priority()).WillByDefault(Return(uint64_t(1))); + ON_CALL(Const(detector_host_monitor_), successRate(Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin)) + .WillByDefault(Return(double(1.0))); + + host_set->hosts_.emplace_back(mock_host_); } NiceMock mock_cluster_info_; @@ -103,6 +142,13 @@ class BaseClustersRequestFixture : public testing::Test { const std::string resource_manager_key_{"test_resource_manager_key"}; std::unique_ptr resource_manager_default_; std::unique_ptr resource_manager_high_; + NiceMock mock_priority_set_; + std::unique_ptr mock_host_set_{new NiceMock()}; + std::vector> mock_host_sets_; + std::shared_ptr mock_host_{new NiceMock()}; + std::vector> mock_hosts_; + envoy::config::core::v3::Locality locality_; + NiceMock detector_host_monitor_; }; struct VerifyJsonOutputParameters { @@ -120,10 +166,10 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { ClustersParams clusters_params; clusters_params.format_ = ClustersParams::Format::Json; - NiceMock test_cluster; + NiceMock test_cluster; loadNewMockClusterByName(test_cluster, "test_cluster"); - NiceMock test_cluster2; + NiceMock test_cluster2; loadNewMockClusterByName(test_cluster2, "test_cluster2"); ResponseResult result = response(*makeRequest(chunk_limit, clusters_params), params.drain_); @@ -133,7 +179,7 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { // clusters to be identical. EXPECT_EQ( std::regex_replace(result.data_.toString(), std::regex("test_cluster2"), "test_cluster"), - R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1,"added_via_api":true},{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1,"added_via_api":true}]})EOF"); + R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1,"added_via_api":true,"host_statuses":[{"hostname":"test_hostname"}]},{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1,"added_via_api":true,"host_statuses":[{"hostname":"test_hostname"}]}]})EOF"); } constexpr VerifyJsonOutputParameters VERIFY_JSON_CASES[] = { From 84e92bd7009391bf7ae0fb78424bf18cd7ce8f7e Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 24 Apr 2024 23:50:59 +0000 Subject: [PATCH 34/77] Saving progress on testing host config Signed-off-by: Demitri Swan --- .../server/admin/clusters_chunk_processor.cc | 20 +++++++++---------- test/server/admin/clusters_request_test.cc | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index e801e4d13fd3..3f0f6f944b29 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -226,16 +226,16 @@ void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_sta {"hostname", host->hostname()}, }; addMapEntries(host_ptr.get(), response, hostname); - // { - // host_ptr->addKey("locality"); - // Json::Streamer::MapPtr locality_ptr = host_ptr->addMap(); - // std::vector locality{ - // {"region", host->locality().region()}, - // {"zone", host->locality().zone()}, - // {"sub_zone", host->locality().sub_zone()}, - // }; - // addMapEntries(locality_ptr.get(), response, locality); - // } + { + host_ptr->addKey("locality"); + Json::Streamer::MapPtr locality_ptr = host_ptr->addMap(); + std::vector locality{ + {"region", host->locality().region()}, + {"zone", host->locality().zone()}, + {"sub_zone", host->locality().sub_zone()}, + }; + addMapEntries(locality_ptr.get(), response, locality); + } // buildHostStats(host_ptr.get(), host, response); // setHealthFlags(host_ptr.get(), host, response); diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index 2735d55b700b..1f3b6e9dc608 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -179,7 +179,7 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { // clusters to be identical. EXPECT_EQ( std::regex_replace(result.data_.toString(), std::regex("test_cluster2"), "test_cluster"), - R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1,"added_via_api":true,"host_statuses":[{"hostname":"test_hostname"}]},{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1,"added_via_api":true,"host_statuses":[{"hostname":"test_hostname"}]}]})EOF"); + R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1,"added_via_api":true,"host_statuses":[{"hostname":"test_hostname","locality":{"region":"test_region","zone":"test_zone","sub_zone":"test_sub_zone"}}]},{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1,"added_via_api":true,"host_statuses":[{"hostname":"test_hostname","locality":{"region":"test_region","zone":"test_zone","sub_zone":"test_sub_zone"}}]}]})EOF"); } constexpr VerifyJsonOutputParameters VERIFY_JSON_CASES[] = { From ed7e066063b8bc298659056fdacfe5e9984a1847 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Thu, 25 Apr 2024 05:19:43 +0000 Subject: [PATCH 35/77] Saving progress on testing host config Signed-off-by: Demitri Swan --- .../server/admin/clusters_chunk_processor.cc | 45 +++++++++++++++++++ .../server/admin/clusters_chunk_processor.h | 1 + test/server/admin/clusters_request_test.cc | 5 ++- 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 3f0f6f944b29..d3c40731bdaa 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -8,6 +8,7 @@ #include "envoy/admin/v3/clusters.pb.h" #include "envoy/buffer/buffer.h" #include "envoy/config/core/v3/base.pb.h" +#include "envoy/network/address.h" #include "envoy/upstream/cluster_manager.h" #include "envoy/upstream/resource_manager.h" #include "envoy/upstream/upstream.h" @@ -222,6 +223,7 @@ void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_sta const Upstream::HostSharedPtr& host, Buffer::Instance& response) { Json::Streamer::MapPtr host_ptr = raw_host_statuses_ptr->addMap(); + addAddress(host_ptr.get(), host, response); std::vector hostname{ {"hostname", host->hostname()}, }; @@ -263,6 +265,49 @@ void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_sta // } } +void JsonClustersChunkProcessor::addAddress(Json::Streamer::Map* raw_host_ptr, + const Upstream::HostSharedPtr& host, + Buffer::Instance& response) { + // Referenced Network::Utility::addressToProtobufAddress as used in the + // original admin clusters handler for json responses; however, the + // config.core.v3.Address has a slightly different structure according to the + // documentation. + // + // TODO(demitriswan) find out why this is the case. + raw_host_ptr->addKey("address"); + Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); + switch (host->address()->type()) { + case Network::Address::Type::Pipe: { + Json::Streamer::MapPtr pipe_ptr = address_ptr->addMap(); + std::vector pipe{ + {"pipe", host->address()->asString()}, + }; + addMapEntries(pipe_ptr.get(), response, pipe); + break; + } + case Network::Address::Type::Ip: { + address_ptr->addKey("socket_address"); + Json::Streamer::MapPtr socket_address_ptr = address_ptr->addMap(); + std::vector socket_address{ + {"address", host->address()->ip()->addressAsString()}, + {"port", uint64_t(host->address()->ip()->port())}, + }; + addMapEntries(socket_address_ptr.get(), response, socket_address); + break; + } + case Network::Address::Type::EnvoyInternal: { + raw_host_ptr->addKey("envoy_internal_address"); + Json::Streamer::MapPtr envoy_internal_address_ptr = raw_host_ptr->addMap(); + std::vector envoy_internal_address{ + {"server_listerner_name", host->address()->envoyInternalAddress()->addressId()}, + {"endpoint_id", host->address()->envoyInternalAddress()->endpointId()}, + }; + addMapEntries(envoy_internal_address_ptr.get(), response, envoy_internal_address); + break; + } + } +} + void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index c66b6443c1b3..386de87f80da 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -67,6 +67,7 @@ class JsonClustersChunkProcessor : public ClustersChunkProcessor { void render(std::reference_wrapper cluster, Buffer::Instance& response); void drainBufferIntoResponse(Buffer::Instance& response); void finalize(Buffer::Instance& response); + void addAddress(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response); void addMapEntries(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, std::vector& entries); void addCircuitBreakers(Json::Streamer::Map* raw_clusters_map_ptr, diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index 1f3b6e9dc608..2b202552d7ef 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -95,6 +95,9 @@ class BaseClustersRequestFixture : public testing::Test { ON_CALL(*mock_cluster.info_, addedViaApi()).WillByDefault(Return(true)); cluster_info_maps_.active_clusters_.emplace(name, std::ref(mock_cluster)); + Network::Address::InstanceConstSharedPtr address = + Network::Utility::resolveUrl("tcp://1.2.3.4:80"); + ON_CALL(*mock_host_, address()).WillByDefault(Return(address)); Upstream::MockHostSet* host_set = mock_cluster.priority_set_.getMockHostSet(0); Stats::PrimitiveCounter test_counter; @@ -179,7 +182,7 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { // clusters to be identical. EXPECT_EQ( std::regex_replace(result.data_.toString(), std::regex("test_cluster2"), "test_cluster"), - R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1,"added_via_api":true,"host_statuses":[{"hostname":"test_hostname","locality":{"region":"test_region","zone":"test_zone","sub_zone":"test_sub_zone"}}]},{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1,"added_via_api":true,"host_statuses":[{"hostname":"test_hostname","locality":{"region":"test_region","zone":"test_zone","sub_zone":"test_sub_zone"}}]}]})EOF"); + R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1,"added_via_api":true,"host_statuses":[{"address":{"socket_address":{"address":"1.2.3.4","port":80}},"hostname":"test_hostname","locality":{"region":"test_region","zone":"test_zone","sub_zone":"test_sub_zone"}}]},{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1,"added_via_api":true,"host_statuses":[{"address":{"socket_address":{"address":"1.2.3.4","port":80}},"hostname":"test_hostname","locality":{"region":"test_region","zone":"test_zone","sub_zone":"test_sub_zone"}}]}]})EOF"); } constexpr VerifyJsonOutputParameters VERIFY_JSON_CASES[] = { From 161adcdd45211c0a03710afa77aaaa8793b592a3 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Thu, 25 Apr 2024 09:14:40 +0000 Subject: [PATCH 36/77] Saving progress on testing host config Signed-off-by: Demitri Swan --- .../server/admin/clusters_chunk_processor.cc | 221 +++++++++--------- .../server/admin/clusters_chunk_processor.h | 3 +- test/server/admin/clusters_request_test.cc | 185 ++++++++++++--- 3 files changed, 267 insertions(+), 142 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index d3c40731bdaa..3061d6dbdeff 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -5,7 +5,6 @@ #include #include -#include "envoy/admin/v3/clusters.pb.h" #include "envoy/buffer/buffer.h" #include "envoy/config/core/v3/base.pb.h" #include "envoy/network/address.h" @@ -13,7 +12,6 @@ #include "envoy/upstream/resource_manager.h" #include "envoy/upstream/upstream.h" -#include "source/common/network/utility.h" #include "source/common/upstream/host_utility.h" namespace Envoy { @@ -238,7 +236,7 @@ void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_sta }; addMapEntries(locality_ptr.get(), response, locality); } - // buildHostStats(host_ptr.get(), host, response); + buildHostStats(host_ptr.get(), host, response); // setHealthFlags(host_ptr.get(), host, response); // double success_rate = host->outlierDetector().successRate( @@ -277,67 +275,66 @@ void JsonClustersChunkProcessor::addAddress(Json::Streamer::Map* raw_host_ptr, raw_host_ptr->addKey("address"); Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); switch (host->address()->type()) { - case Network::Address::Type::Pipe: { - Json::Streamer::MapPtr pipe_ptr = address_ptr->addMap(); - std::vector pipe{ + case Network::Address::Type::Pipe: { + Json::Streamer::MapPtr pipe_ptr = address_ptr->addMap(); + std::vector pipe{ {"pipe", host->address()->asString()}, - }; - addMapEntries(pipe_ptr.get(), response, pipe); - break; - } - case Network::Address::Type::Ip: { - address_ptr->addKey("socket_address"); - Json::Streamer::MapPtr socket_address_ptr = address_ptr->addMap(); - std::vector socket_address{ + }; + addMapEntries(pipe_ptr.get(), response, pipe); + break; + } + case Network::Address::Type::Ip: { + address_ptr->addKey("socket_address"); + Json::Streamer::MapPtr socket_address_ptr = address_ptr->addMap(); + std::vector socket_address{ {"address", host->address()->ip()->addressAsString()}, {"port", uint64_t(host->address()->ip()->port())}, - }; - addMapEntries(socket_address_ptr.get(), response, socket_address); - break; - } - case Network::Address::Type::EnvoyInternal: { - raw_host_ptr->addKey("envoy_internal_address"); - Json::Streamer::MapPtr envoy_internal_address_ptr = raw_host_ptr->addMap(); - std::vector envoy_internal_address{ + }; + addMapEntries(socket_address_ptr.get(), response, socket_address); + break; + } + case Network::Address::Type::EnvoyInternal: { + raw_host_ptr->addKey("envoy_internal_address"); + Json::Streamer::MapPtr envoy_internal_address_ptr = raw_host_ptr->addMap(); + std::vector envoy_internal_address{ {"server_listerner_name", host->address()->envoyInternalAddress()->addressId()}, {"endpoint_id", host->address()->envoyInternalAddress()->endpointId()}, - }; - addMapEntries(envoy_internal_address_ptr.get(), response, envoy_internal_address); - break; - } + }; + addMapEntries(envoy_internal_address_ptr.get(), response, envoy_internal_address); + break; + } } } void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { - Json::Streamer::ArrayPtr stats = raw_host_ptr->addArray(); + raw_host_ptr->addKey("stats"); + Json::Streamer::ArrayPtr stats_ptr = raw_host_ptr->addArray(); for (const auto& [counter_name, counter] : host->counters()) { - Json::Streamer::MapPtr stat_obj = stats->addMap(); - // TODO(demitriswan) Make sure this name conversion works as expected - std::vector stats_properties{ + Json::Streamer::MapPtr stats_obj_ptr = stats_ptr->addMap(); + std::vector counter_object{ {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::COUNTER)}, {"name", counter_name}, {"value", counter.get().value()}, }; - addMapEntries(raw_host_ptr, response, stats_properties); + addMapEntries(stats_obj_ptr.get(), response, counter_object); } for (const auto& [gauge_name, gauge] : host->gauges()) { - Json::Streamer::MapPtr stat_obj = stats->addMap(); - // TODO(demitriswan) Make sure this name conversion works as expected - std::vector stats_properties{ + Json::Streamer::MapPtr stats_obj_ptr = stats_ptr->addMap(); + std::vector gauge_object{ {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::GAUGE)}, {"name", gauge_name}, {"value", gauge.get().value()}, }; - addMapEntries(raw_host_ptr, response, stats_properties); + addMapEntries(stats_obj_ptr.get(), response, gauge_object); } } void JsonClustersChunkProcessor::setHealthFlags(Json::Streamer::Map* raw_host_ptr, - const Upstream::HostSharedPtr& host, - Buffer::Instance& response) { - raw_host_ptr->addKey("health_stats"); + const Upstream::HostSharedPtr& host, + Buffer::Instance& response) { + raw_host_ptr->addKey("health_status"); Json::Streamer::MapPtr health_status_ptr = raw_host_ptr->addMap(); // Invokes setHealthFlag for each health flag. #define SET_HEALTH_FLAG(name, notused) \ @@ -414,87 +411,87 @@ void JsonClustersChunkProcessor::setHealthFlag(Json::Streamer::Map* health_statu } } - void JsonClustersChunkProcessor::addEjectionThresholds(Json::Streamer::Map * raw_clusters_map_ptr, - const Upstream::Cluster& unwrapped_cluster, - Buffer::Instance& response) { - const Upstream::Outlier::Detector* outlier_detector = unwrapped_cluster.outlierDetector(); - if (outlier_detector != nullptr && - outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin) > 0.0) { - std::vector success_rate_ejection_threshold{ - {"success_rate_ejection_threshold", - double(outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))}, - }; - addMapEntries(raw_clusters_map_ptr, response, success_rate_ejection_threshold); - } - if (outlier_detector != nullptr && - outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin) > 0.0) { - std::vector local_success_rate_ejection_threshold{ - {"local_success_rate_ejection_threshold", - double(outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))}, - }; - addMapEntries(raw_clusters_map_ptr, response, local_success_rate_ejection_threshold); - } - } - - void JsonClustersChunkProcessor::addCircuitBreakers( - Json::Streamer::Map * raw_clusters_map_ptr, Upstream::ClusterInfoConstSharedPtr cluster_info, - Buffer::Instance & response) { - raw_clusters_map_ptr->addKey("circuit_breakers"); - Json::Streamer::MapPtr circuit_breakers = raw_clusters_map_ptr->addMap(); - circuit_breakers->addKey("thresholds"); - Json::Streamer::ArrayPtr thresholds = circuit_breakers->addArray(); - addCircuitBreakerForPriority( - envoy::config::core::v3::RoutingPriority::DEFAULT, thresholds.get(), response, - cluster_info->resourceManager(Upstream::ResourcePriority::Default)); - addCircuitBreakerForPriority(envoy::config::core::v3::RoutingPriority::HIGH, thresholds.get(), - response, - cluster_info->resourceManager(Upstream::ResourcePriority::High)); +void JsonClustersChunkProcessor::addEjectionThresholds(Json::Streamer::Map* raw_clusters_map_ptr, + const Upstream::Cluster& unwrapped_cluster, + Buffer::Instance& response) { + const Upstream::Outlier::Detector* outlier_detector = unwrapped_cluster.outlierDetector(); + if (outlier_detector != nullptr && + outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin) > 0.0) { + std::vector success_rate_ejection_threshold{ + {"success_rate_ejection_threshold", + double(outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))}, + }; + addMapEntries(raw_clusters_map_ptr, response, success_rate_ejection_threshold); } - - void JsonClustersChunkProcessor::addCircuitBreakerForPriority( - const envoy::config::core::v3::RoutingPriority& priority, - Json::Streamer::Array* raw_thresholds_ptr, Buffer::Instance& response, - Upstream::ResourceManager& resource_manager) { - Json::Streamer::MapPtr threshold = raw_thresholds_ptr->addMap(); - std::vector entries{ - {"priority", - priority == envoy::config::core::v3::RoutingPriority::DEFAULT ? "DEFAULT" : "HIGH"}, - {"max_connections", resource_manager.connections().max()}, - {"max_pending_requests", resource_manager.pendingRequests().max()}, - {"max_requests", resource_manager.requests().max()}, - {"max_retries", resource_manager.retries().max()}, + if (outlier_detector != nullptr && + outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin) > 0.0) { + std::vector local_success_rate_ejection_threshold{ + {"local_success_rate_ejection_threshold", + double(outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))}, }; - addMapEntries(threshold.get(), response, entries); + addMapEntries(raw_clusters_map_ptr, response, local_success_rate_ejection_threshold); } +} - // Json::Streamer holds a reference to a Buffer::Instance reference but the API for Request - // takes a Buffer::Instance reference on each call to nextChunk. So, at the end of each - // Json::Streamer function invocation, call drainBufferIntoResponse to ensure that the - // contents written to its buffer gets moved and appended to the response. - void JsonClustersChunkProcessor::drainBufferIntoResponse(Buffer::Instance & response) { - if (&response != &buffer_) { - response.move(buffer_); - } - } +void JsonClustersChunkProcessor::addCircuitBreakers( + Json::Streamer::Map* raw_clusters_map_ptr, Upstream::ClusterInfoConstSharedPtr cluster_info, + Buffer::Instance& response) { + raw_clusters_map_ptr->addKey("circuit_breakers"); + Json::Streamer::MapPtr circuit_breakers = raw_clusters_map_ptr->addMap(); + circuit_breakers->addKey("thresholds"); + Json::Streamer::ArrayPtr thresholds = circuit_breakers->addArray(); + addCircuitBreakerForPriority(envoy::config::core::v3::RoutingPriority::DEFAULT, thresholds.get(), + response, + cluster_info->resourceManager(Upstream::ResourcePriority::Default)); + addCircuitBreakerForPriority(envoy::config::core::v3::RoutingPriority::HIGH, thresholds.get(), + response, + cluster_info->resourceManager(Upstream::ResourcePriority::High)); +} - void JsonClustersChunkProcessor::addMapEntries( - Json::Streamer::Map * raw_map_ptr, Buffer::Instance & response, - std::vector & entries) { - raw_map_ptr->addEntries(entries); - drainBufferIntoResponse(response); - } +void JsonClustersChunkProcessor::addCircuitBreakerForPriority( + const envoy::config::core::v3::RoutingPriority& priority, + Json::Streamer::Array* raw_thresholds_ptr, Buffer::Instance& response, + Upstream::ResourceManager& resource_manager) { + Json::Streamer::MapPtr threshold = raw_thresholds_ptr->addMap(); + std::vector entries{ + {"priority", + priority == envoy::config::core::v3::RoutingPriority::DEFAULT ? "DEFAULT" : "HIGH"}, + {"max_connections", resource_manager.connections().max()}, + {"max_pending_requests", resource_manager.pendingRequests().max()}, + {"max_requests", resource_manager.requests().max()}, + {"max_retries", resource_manager.retries().max()}, + }; + addMapEntries(threshold.get(), response, entries); +} - // Start destruction of the ClustersJsonContext to render the closing tokens and push to the - // buffer. Since we've pushed data into the buffer in the Json::Streamer, we'll need to drain - // the contents into the response. - void JsonClustersChunkProcessor::finalize(Buffer::Instance & response) { - json_context_holder_.pop_back(); - drainBufferIntoResponse(response); +// Json::Streamer holds a reference to a Buffer::Instance reference but the API for Request +// takes a Buffer::Instance reference on each call to nextChunk. So, at the end of each +// Json::Streamer function invocation, call drainBufferIntoResponse to ensure that the +// contents written to its buffer gets moved and appended to the response. +void JsonClustersChunkProcessor::drainBufferIntoResponse(Buffer::Instance& response) { + if (&response != &buffer_) { + response.move(buffer_); } +} + +void JsonClustersChunkProcessor::addMapEntries( + Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, + std::vector& entries) { + raw_map_ptr->addEntries(entries); + drainBufferIntoResponse(response); +} + +// Start destruction of the ClustersJsonContext to render the closing tokens and push to the +// buffer. Since we've pushed data into the buffer in the Json::Streamer, we'll need to drain +// the contents into the response. +void JsonClustersChunkProcessor::finalize(Buffer::Instance& response) { + json_context_holder_.pop_back(); + drainBufferIntoResponse(response); +} } // namespace Server } // namespace Envoy diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index 386de87f80da..70dfefbec31a 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -67,7 +67,8 @@ class JsonClustersChunkProcessor : public ClustersChunkProcessor { void render(std::reference_wrapper cluster, Buffer::Instance& response); void drainBufferIntoResponse(Buffer::Instance& response); void finalize(Buffer::Instance& response); - void addAddress(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response); + void addAddress(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, + Buffer::Instance& response); void addMapEntries(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, std::vector& entries); void addCircuitBreakers(Json::Streamer::Map* raw_clusters_map_ptr, diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index 2b202552d7ef..afeea4d1b609 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -12,8 +12,8 @@ #include "source/server/admin/clusters_request.h" #include "test/mocks/server/instance.h" -#include "test/mocks/upstream/cluster_priority_set.h" #include "test/mocks/upstream/cluster_manager.h" +#include "test/mocks/upstream/cluster_priority_set.h" #include "test/mocks/upstream/host.h" #include "test/mocks/upstream/priority_set.h" #include "test/test_common/utility.h" @@ -40,9 +40,15 @@ class BaseClustersRequestFixture : public testing::Test { resource_manager_high_ = std::make_unique( runtime_, resource_manager_key_, 4096, 4096, 4096, 16, 4, 1024, mock_cluster_info_.circuit_breakers_stats_, std::nullopt, std::nullopt); + locality_.set_region("test_region"); locality_.set_zone("test_zone"); locality_.set_sub_zone("test_sub_zone"); + + counter_.add(10); + counters_.emplace_back("test_counter", counter_); + gauge_.add(11); + gauges_.emplace_back("test_gauge", gauge_); } using ClustersRequestPtr = std::unique_ptr; @@ -96,42 +102,46 @@ class BaseClustersRequestFixture : public testing::Test { cluster_info_maps_.active_clusters_.emplace(name, std::ref(mock_cluster)); Network::Address::InstanceConstSharedPtr address = - Network::Utility::resolveUrl("tcp://1.2.3.4:80"); + Network::Utility::resolveUrl("tcp://1.2.3.4:80"); ON_CALL(*mock_host_, address()).WillByDefault(Return(address)); Upstream::MockHostSet* host_set = mock_cluster.priority_set_.getMockHostSet(0); - Stats::PrimitiveCounter test_counter; - test_counter.add(10); - std::vector> counters{ - {"test_counter", test_counter}, - }; - Stats::PrimitiveGauge test_gauge; - test_gauge.set(11); - std::vector> gauges{ - {"test_gauge", test_gauge}, - }; - ON_CALL(*mock_host_, counters()).WillByDefault(Invoke([&counters]() { return counters; })); - ON_CALL(*mock_host_, gauges()).WillByDefault(Invoke([&gauges]() { return gauges; })); + ON_CALL(*mock_host_, counters()).WillByDefault(Return(counters_)); + ON_CALL(*mock_host_, gauges()).WillByDefault(Return(gauges_)); ON_CALL(*mock_host_, hostname()).WillByDefault(ReturnRef("test_hostname")); ON_CALL(*mock_host_, locality()).WillByDefault(ReturnRef(locality_)); - ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::FAILED_ACTIVE_HC)).WillByDefault(Return(false)); - ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK)).WillByDefault(Return(false)); - ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH)).WillByDefault(Return("HEALTHY")); - ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH)).WillByDefault(Return("HEALTHY")); - ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::EDS_STATUS_DRAINING)).WillByDefault(Return("HEALTHY")); - ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC)).WillByDefault(Return(false)); - ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL)).WillByDefault(Return(false)); - ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::PENDING_ACTIVE_HC)).WillByDefault(Return(false)); - ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL)).WillByDefault(Return(false)); - ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT)).WillByDefault(Return(false)); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::FAILED_ACTIVE_HC)) + .WillByDefault(Return(false)); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK)) + .WillByDefault(Return(false)); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH)) + .WillByDefault(Return("HEALTHY")); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH)) + .WillByDefault(Return("HEALTHY")); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::EDS_STATUS_DRAINING)) + .WillByDefault(Return("HEALTHY")); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC)) + .WillByDefault(Return(false)); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL)) + .WillByDefault(Return(false)); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::PENDING_ACTIVE_HC)) + .WillByDefault(Return(false)); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL)) + .WillByDefault(Return(false)); + ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT)) + .WillByDefault(Return(false)); ON_CALL(*mock_host_, outlierDetector()).WillByDefault(ReturnRef(detector_host_monitor_)); - ON_CALL(Const(detector_host_monitor_), successRate(Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin)) - .WillByDefault(Return(double(1.0))); + ON_CALL( + Const(detector_host_monitor_), + successRate(Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin)) + .WillByDefault(Return(double(1.0))); ON_CALL(*mock_host_, weight()).WillByDefault(Return(uint64_t(1))); ON_CALL(*mock_host_, priority()).WillByDefault(Return(uint64_t(1))); - ON_CALL(Const(detector_host_monitor_), successRate(Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin)) - .WillByDefault(Return(double(1.0))); + ON_CALL( + Const(detector_host_monitor_), + successRate(Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin)) + .WillByDefault(Return(double(1.0))); host_set->hosts_.emplace_back(mock_host_); } @@ -152,6 +162,10 @@ class BaseClustersRequestFixture : public testing::Test { std::vector> mock_hosts_; envoy::config::core::v3::Locality locality_; NiceMock detector_host_monitor_; + Stats::PrimitiveCounter counter_; + Stats::PrimitiveGauge gauge_; + std::vector> counters_; + std::vector> gauges_; }; struct VerifyJsonOutputParameters { @@ -180,9 +194,122 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { EXPECT_EQ(result.code_, Http::Code::OK); // The order of clusters is non-deterministic so strip the 2 from test_cluster2 and expect both // clusters to be identical. + std::string expected_readable_output = R"EOF( + { + "cluster_statuses": [ + { + "name": "test_cluster", + "observability_name": "observability_name", + "eds_service_name": "potato_launcher", + "circuit_breakers": { + "thresholds": [ + { + "priority": "DEFAULT", + "max_connections": 1024, + "max_pending_requests": 1024, + "max_requests": 1024, + "max_retries": 16 + }, + { + "priority": "HIGH", + "max_connections": 4096, + "max_pending_requests": 4096, + "max_requests": 4096, + "max_retries": 16 + } + ] + }, + "success_rate_ejection_threshold": 1.1, + "local_success_rate_ejection_threshold": 1.1, + "added_via_api": true, + "host_statuses": [ + { + "address": { + "socket_address": { + "address": "1.2.3.4", + "port": 80 + } + }, + "hostname": "test_hostname", + "locality": { + "region": "test_region", + "zone": "test_zone", + "sub_zone": "test_sub_zone" + }, + "stats": [ + { + "type": "COUNTER", + "name": "test_counter", + "value": 10 + }, + { + "type": "GAUGE", + "name": "test_gauge", + "value": 11 + } + ] + } + ] + }, + { + "name": "test_cluster", + "observability_name": "observability_name", + "eds_service_name": "potato_launcher", + "circuit_breakers": { + "thresholds": [ + { + "priority": "DEFAULT", + "max_connections": 1024, + "max_pending_requests": 1024, + "max_requests": 1024, + "max_retries": 16 + }, + { + "priority": "HIGH", + "max_connections": 4096, + "max_pending_requests": 4096, + "max_requests": 4096, + "max_retries": 16 + } + ] + }, + "success_rate_ejection_threshold": 1.1, + "local_success_rate_ejection_threshold": 1.1, + "added_via_api": true, + "host_statuses": [ + { + "address": { + "socket_address": { + "address": "1.2.3.4", + "port": 80 + } + }, + "hostname": "test_hostname", + "locality": { + "region": "test_region", + "zone": "test_zone", + "sub_zone": "test_sub_zone" + }, + "stats": [ + { + "type": "COUNTER", + "name": "test_counter", + "value": 10 + }, + { + "type": "GAUGE", + "name": "test_gauge", + "value": 11 + } + ] + } + ] + } + ] + })EOF"; EXPECT_EQ( std::regex_replace(result.data_.toString(), std::regex("test_cluster2"), "test_cluster"), - R"EOF({"cluster_statuses":[{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1,"added_via_api":true,"host_statuses":[{"address":{"socket_address":{"address":"1.2.3.4","port":80}},"hostname":"test_hostname","locality":{"region":"test_region","zone":"test_zone","sub_zone":"test_sub_zone"}}]},{"name":"test_cluster","observability_name":"observability_name","eds_service_name":"potato_launcher","circuit_breakers":{"thresholds":[{"priority":"DEFAULT","max_connections":1024,"max_pending_requests":1024,"max_requests":1024,"max_retries":16},{"priority":"HIGH","max_connections":4096,"max_pending_requests":4096,"max_requests":4096,"max_retries":16}]},"success_rate_ejection_threshold":1.1,"local_success_rate_ejection_threshold":1.1,"added_via_api":true,"host_statuses":[{"address":{"socket_address":{"address":"1.2.3.4","port":80}},"hostname":"test_hostname","locality":{"region":"test_region","zone":"test_zone","sub_zone":"test_sub_zone"}}]}]})EOF"); + std::regex_replace(expected_readable_output, std::regex(R"(\s)"), "")); } constexpr VerifyJsonOutputParameters VERIFY_JSON_CASES[] = { From fa698e4d4bd45c2691c031ff52bfaa109adf46d6 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Thu, 25 Apr 2024 14:41:59 +0000 Subject: [PATCH 37/77] Save point Signed-off-by: Demitri Swan --- .../server/admin/clusters_chunk_processor.cc | 374 +++++++++++------- .../server/admin/clusters_chunk_processor.h | 11 +- test/server/admin/clusters_request_test.cc | 36 +- 3 files changed, 256 insertions(+), 165 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 3061d6dbdeff..5830e0c9b01f 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -7,11 +7,13 @@ #include "envoy/buffer/buffer.h" #include "envoy/config/core/v3/base.pb.h" +#include "envoy/config/core/v3/health_check.pb.h" #include "envoy/network/address.h" #include "envoy/upstream/cluster_manager.h" #include "envoy/upstream/resource_manager.h" #include "envoy/upstream/upstream.h" +#include "source/common/buffer/buffer_impl.h" #include "source/common/upstream/host_utility.h" namespace Envoy { @@ -176,25 +178,26 @@ void JsonClustersChunkProcessor::render(std::reference_wrapperclusters_->addMap(); Upstream::ClusterInfoConstSharedPtr cluster_info = cluster.get().info(); - std::vector top_level_entries{ - {"name", cluster_info->name()}, - {"observability_name", cluster_info->observabilityName()}, - }; - addMapEntries(cluster_map.get(), response, top_level_entries); + std::vector top_level_entries; + if (const std::string& name = cluster_info->name(); !name.empty()) { + top_level_entries.emplace_back("name", name); + } + if (const std::string& observability_name = cluster_info->observabilityName(); + !observability_name.empty()) { + top_level_entries.emplace_back("observability_name", observability_name); + } - if (const std::string& name = cluster_info->edsServiceName(); !name.empty()) { - std::vector eds_service_name_entry{ - {"eds_service_name", name}, - }; - addMapEntries(cluster_map.get(), response, eds_service_name_entry); + if (const std::string& eds_service_name = cluster_info->edsServiceName(); + !eds_service_name.empty()) { + top_level_entries.push_back({"eds_service_name", eds_service_name}); } addCircuitBreakers(cluster_map.get(), cluster_info, response); addEjectionThresholds(cluster_map.get(), cluster.get(), response); - std::vector added_via_api{ - {"added_via_api", cluster_info->addedViaApi()}, - }; - addMapEntries(cluster_map.get(), response, added_via_api); + if (bool added_via_api = cluster_info->addedViaApi(); added_via_api) { + top_level_entries.emplace_back("added_via_api", added_via_api); + } + addMapEntries(cluster_map.get(), response, top_level_entries); addHostStatuses(cluster_map.get(), cluster, response); } @@ -220,87 +223,134 @@ void JsonClustersChunkProcessor::processHostSet(Json::Streamer::Array* raw_host_ void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_statuses_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { + Buffer::OwnedImpl buffer; Json::Streamer::MapPtr host_ptr = raw_host_statuses_ptr->addMap(); + std::vector host_config; + setHostname(host, host_config); addAddress(host_ptr.get(), host, response); - std::vector hostname{ - {"hostname", host->hostname()}, - }; - addMapEntries(host_ptr.get(), response, hostname); - { - host_ptr->addKey("locality"); - Json::Streamer::MapPtr locality_ptr = host_ptr->addMap(); - std::vector locality{ - {"region", host->locality().region()}, - {"zone", host->locality().zone()}, - {"sub_zone", host->locality().sub_zone()}, - }; - addMapEntries(locality_ptr.get(), response, locality); - } + setLocality(host_ptr.get(), host, response); buildHostStats(host_ptr.get(), host, response); - // setHealthFlags(host_ptr.get(), host, response); - - // double success_rate = host->outlierDetector().successRate( - // Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin); - // if (success_rate >= 0.0) { - // std::vector success_rate_property{ - // {"suceess_rate", success_rate}, - // }; - // addMapEntries(host_ptr.get(), response, success_rate_property); - // } + setHealthFlags(host_ptr.get(), host, response); + setSuccessRate(host, host_config); - // std::vector weight_and_priority{ - // {"weight", uint64_t(host->weight())}, - // {"priority", uint64_t(host->priority())}, - // }; - // addMapEntries(host_ptr.get(), response, weight_and_priority); - // success_rate = host->outlierDetector().successRate( - // Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin); - // if (success_rate >= 0.0) { - // std::vector success_rate_property{ - // {"local_origin_success_rate", success_rate}, - // }; - // addMapEntries(host_ptr.get(), response, success_rate_property); + // TODO(demitriswan) add test to ensure empty object doesn't get written. + // if (buffer.length()) { + // response.move(buffer); // } + if (!host_config.empty()) { + addMapEntries(host_ptr.get(), response, host_config); + } +} +void JsonClustersChunkProcessor::setHostname( + const Upstream::HostSharedPtr& host, + std::vector& host_config) { + // if (const std::string& hostname = host->hostname(); !hostname.empty()) { + host_config.emplace_back("hostname", host->hostname()); + // } +} + +void JsonClustersChunkProcessor::setSuccessRate( + const Upstream::HostSharedPtr& host, + std::vector& host_config) { + + double external_success_rate = host->outlierDetector().successRate( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin); + double local_success_rate = host->outlierDetector().successRate( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin); + if (!external_success_rate && !local_success_rate) { + return; + } + + if (external_success_rate >= 0.0) { + host_config.emplace_back("success_rate", external_success_rate); + } + if (uint64_t weight = host->weight(); weight) { + host_config.emplace_back("weight", weight); + } + if (uint64_t priority = host->priority(); priority) { + host_config.emplace_back("priority", priority); + } + if (local_success_rate >= 0.0) { + host_config.emplace_back("local_origin_success_rate", local_success_rate); + } +} + +void JsonClustersChunkProcessor::setLocality(Json::Streamer::Map* raw_host_ptr, + const Upstream::HostSharedPtr& host, + Buffer::Instance& response) { + + if (host->locality().region().empty() && host->locality().zone().empty() && + host->locality().sub_zone().empty()) { + return; + } + raw_host_ptr->addKey("locality"); + Json::Streamer::MapPtr locality_ptr = raw_host_ptr->addMap(); + std::vector locality; + if (const std::string& region = host->locality().region(); !region.empty()) { + locality.emplace_back("region", region); + } + if (const std::string& zone = host->locality().zone(); !zone.empty()) { + locality.emplace_back("zone", zone); + } + if (const std::string& sub_zone = host->locality().sub_zone(); !sub_zone.empty()) { + locality.emplace_back("sub_zone", sub_zone); + } + addMapEntries(locality_ptr.get(), response, locality); } void JsonClustersChunkProcessor::addAddress(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { - // Referenced Network::Utility::addressToProtobufAddress as used in the - // original admin clusters handler for json responses; however, the - // config.core.v3.Address has a slightly different structure according to the - // documentation. - // - // TODO(demitriswan) find out why this is the case. - raw_host_ptr->addKey("address"); - Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); switch (host->address()->type()) { case Network::Address::Type::Pipe: { - Json::Streamer::MapPtr pipe_ptr = address_ptr->addMap(); - std::vector pipe{ - {"pipe", host->address()->asString()}, - }; - addMapEntries(pipe_ptr.get(), response, pipe); + if (const std::string& path = host->address()->asString(); !path.empty()) { + raw_host_ptr->addKey("address"); + Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); + address_ptr->addKey("pipe"); + Json::Streamer::MapPtr pipe_ptr = address_ptr->addMap(); + std::vector pipe{ + {"pipe", host->address()->asString()}, + }; + addMapEntries(pipe_ptr.get(), response, pipe); + } break; } case Network::Address::Type::Ip: { - address_ptr->addKey("socket_address"); - Json::Streamer::MapPtr socket_address_ptr = address_ptr->addMap(); - std::vector socket_address{ - {"address", host->address()->ip()->addressAsString()}, - {"port", uint64_t(host->address()->ip()->port())}, - }; - addMapEntries(socket_address_ptr.get(), response, socket_address); + if (!host->address()->ip()->addressAsString().empty() || host->address()->ip()->port()) { + raw_host_ptr->addKey("address"); + Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); + address_ptr->addKey("socket_address"); + Json::Streamer::MapPtr socket_address_ptr = address_ptr->addMap(); + std::vector socket_address; + if (const std::string& address = host->address()->ip()->addressAsString(); !address.empty()) { + socket_address.emplace_back("address", address); + } + if (uint64_t port = uint64_t(host->address()->ip()->port()); port) { + socket_address.emplace_back("port", port); + } + addMapEntries(socket_address_ptr.get(), response, socket_address); + } break; } case Network::Address::Type::EnvoyInternal: { - raw_host_ptr->addKey("envoy_internal_address"); - Json::Streamer::MapPtr envoy_internal_address_ptr = raw_host_ptr->addMap(); - std::vector envoy_internal_address{ - {"server_listerner_name", host->address()->envoyInternalAddress()->addressId()}, - {"endpoint_id", host->address()->envoyInternalAddress()->endpointId()}, - }; - addMapEntries(envoy_internal_address_ptr.get(), response, envoy_internal_address); + if (!host->address()->envoyInternalAddress()->addressId().empty() || + !host->address()->envoyInternalAddress()->endpointId().empty()) { + raw_host_ptr->addKey("address"); + Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); + raw_host_ptr->addKey("envoy_internal_address"); + Json::Streamer::MapPtr envoy_internal_address_ptr = raw_host_ptr->addMap(); + std::vector envoy_internal_address; + if (const std::string& server_listener_name = + host->address()->envoyInternalAddress()->addressId(); + !server_listener_name.empty()) { + envoy_internal_address.emplace_back("server_listener_name", server_listener_name); + } + if (const std::string& endpoint_id = host->address()->envoyInternalAddress()->endpointId(); + !endpoint_id.empty()) { + envoy_internal_address.emplace_back("endpoint_id", endpoint_id); + } + addMapEntries(envoy_internal_address_ptr.get(), response, envoy_internal_address); + } break; } } @@ -309,24 +359,34 @@ void JsonClustersChunkProcessor::addAddress(Json::Streamer::Map* raw_host_ptr, void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { + if (host->counters().empty() && host->gauges().empty()) { + return; + } raw_host_ptr->addKey("stats"); Json::Streamer::ArrayPtr stats_ptr = raw_host_ptr->addArray(); for (const auto& [counter_name, counter] : host->counters()) { Json::Streamer::MapPtr stats_obj_ptr = stats_ptr->addMap(); std::vector counter_object{ - {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::COUNTER)}, - {"name", counter_name}, - {"value", counter.get().value()}, - }; + {"type", + envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::COUNTER)}}; + if (!counter_name.empty()) { + counter_object.emplace_back("name", counter_name); + } + if (uint64_t value = counter.get().value(); value) { + counter_object.emplace_back("value", value); + } addMapEntries(stats_obj_ptr.get(), response, counter_object); } for (const auto& [gauge_name, gauge] : host->gauges()) { Json::Streamer::MapPtr stats_obj_ptr = stats_ptr->addMap(); std::vector gauge_object{ - {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::GAUGE)}, - {"name", gauge_name}, - {"value", gauge.get().value()}, - }; + {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::GAUGE)}}; + if (!gauge_name.empty()) { + gauge_object.emplace_back("name", gauge_name); + } + if (uint64_t value = gauge.get().value(); value) { + gauge_object.emplace_back("value", value); + } addMapEntries(stats_obj_ptr.get(), response, gauge_object); } } @@ -334,81 +394,89 @@ void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_pt void JsonClustersChunkProcessor::setHealthFlags(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { - raw_host_ptr->addKey("health_status"); - Json::Streamer::MapPtr health_status_ptr = raw_host_ptr->addMap(); + absl::flat_hash_map<, absl::variant> flag_map; + // Json::Streamer::MapPtr health_status_ptr = raw_host_ptr->addMap(); // Invokes setHealthFlag for each health flag. #define SET_HEALTH_FLAG(name, notused) \ - setHealthFlag(health_status_ptr.get(), Upstream::Host::HealthFlag::name, host, response); + loadHealthFlagMap(flag_map, Upstream::Host::HealthFlag::name, host); HEALTH_FLAG_ENUM_VALUES(SET_HEALTH_FLAG) #undef SET_HEALTH_FLAG + if (!flag_map.empty()) { + return; + } + raw_host_ptr->addKey("health_status"); + Json::Streamer::MapPtr health_flags_ptr = raw_host_ptr->addMap(); + std::vector flags; + for (const auto& [name, flag_value] : flag_map) { + if (name == "eds_health_status") { + flags.emplace_back(name, std::get<>(flag_value)); + } else { + flags.emplace_back(name, std::get(flag_value)); + } + } + addMapEntries(health_flags_ptr.get(), response, flags); } -void JsonClustersChunkProcessor::setHealthFlag(Json::Streamer::Map* health_status_ptr, - Upstream::Host::HealthFlag flag, - const Upstream::HostSharedPtr& host, - Buffer::Instance& response) { +void JsonClustersChunkProcessor::loadHealthFlagMap( + absl::flat_hash_map>& flag_map, + Upstream::Host::HealthFlag flag, const Upstream::HostSharedPtr& host) { switch (flag) { - case Upstream::Host::HealthFlag::FAILED_ACTIVE_HC: { - std::vector status{ - {"failed_active_health_check", host.get()->healthFlagGet(flag)}, - }; - addMapEntries(health_status_ptr, response, status); + case Upstream::Host::HealthFlag::FAILED_ACTIVE_HC: + if (bool value = host.get()->healthFlagGet(flag); value) { + flag_map.emplace("failed_active_health_check", value); + } break; - } - case Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK: { - std::vector status{ - {"failed_outlier_check", host.get()->healthFlagGet(flag)}, - }; - addMapEntries(health_status_ptr, response, status); + case Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK: + if (bool value = host.get()->healthFlagGet(flag); value) { + flag_map.emplace("failed_outlier_check", value); + } break; - } - case Upstream::Host::HealthFlag::FAILED_EDS_HEALTH: case Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH: - case Upstream::Host::HealthFlag::EDS_STATUS_DRAINING: { - // TODO(demitriswan) make sure this name conversion works as expected. - std::vector status{ - {"eds_health_status", - envoy::config::core::v3::HealthStatus_Name(host.get()->healthFlagGet(flag))}, - }; - addMapEntries(health_status_ptr, response, status); + case Upstream::Host::HealthFlag::FAILED_EDS_HEALTH: + if (host->healthFlagGet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH) || + host->healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH)) { + if (host->healthFlagGet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH)) { + flag_map.emplace("eds_health_status", envoy::config::core::v3::HealthStatus_Name( + envoy::config::core::v3::UNHEALTHY)); + } else { + flag_map.emplace("eds_health_status", envoy::config::core::v3::HealthStatus_Name( + envoy::config::core::v3::DEGRADED)); + } + } else { + flag_map.emplace("eds_health_status", envoy::config::core::v3::HealthStatus_Name( + envoy::config::core::v3::HEALTHY)); + } + case Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC: + if (bool value = host.get()->healthFlagGet(flag); value) { + flag_map.emplace("failed_active_degraded_check", value); + } break; - } - case Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC: { - std::vector status{ - {"failed_active_degraded_check", host.get()->healthFlagGet(flag)}, - }; - addMapEntries(health_status_ptr, response, status); + case Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL: + if (bool value = host.get()->healthFlagGet(flag); value) { + flag_map.emplace("pending_dynamic_removal", value); + } break; - } - case Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL: { - std::vector status{ - {"pending_dynamic_removal", host.get()->healthFlagGet(flag)}, - }; - addMapEntries(health_status_ptr, response, status); + case Upstream::Host::HealthFlag::PENDING_ACTIVE_HC: + if (bool value = host.get()->healthFlagGet(flag); value) { + flag_map.emplace("pending_active_hc", value); + } break; - } - case Upstream::Host::HealthFlag::PENDING_ACTIVE_HC: { - std::vector status{ - {"pending_active_hc", host.get()->healthFlagGet(flag)}, - }; - addMapEntries(health_status_ptr, response, status); + case Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL: + if (bool value = host.get()->healthFlagGet(flag); value) { + flag_map.emplace("excluded_via_immediate_hc_fail", value); + } break; - } - case Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL: { - std::vector status{ - {"excluded_via_immediate_hc_fail", host.get()->healthFlagGet(flag)}, - }; - addMapEntries(health_status_ptr, response, status); + case Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT: + if (bool value = host.get()->healthFlagGet(flag); value) { + flag_map.emplace("active_hc_timeout", value); + } break; - } - case Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT: { - std::vector status{ - {"active_hc_timeout", host.get()->healthFlagGet(flag)}, - }; - addMapEntries(health_status_ptr, response, status); + case Upstream::Host::HealthFlag::EDS_STATUS_DRAINING: + if (bool value = host.get()->healthFlagGet(flag); value) { + flag_map.emplace("eds_health_status", value); + } break; } - } } void JsonClustersChunkProcessor::addEjectionThresholds(Json::Streamer::Map* raw_clusters_map_ptr, @@ -459,12 +527,20 @@ void JsonClustersChunkProcessor::addCircuitBreakerForPriority( Json::Streamer::MapPtr threshold = raw_thresholds_ptr->addMap(); std::vector entries{ {"priority", - priority == envoy::config::core::v3::RoutingPriority::DEFAULT ? "DEFAULT" : "HIGH"}, - {"max_connections", resource_manager.connections().max()}, - {"max_pending_requests", resource_manager.pendingRequests().max()}, - {"max_requests", resource_manager.requests().max()}, - {"max_retries", resource_manager.retries().max()}, - }; + priority == envoy::config::core::v3::RoutingPriority::DEFAULT ? "DEFAULT" : "HIGH"}}; + if (uint64_t max_connections = resource_manager.connections().max(); max_connections) { + entries.emplace_back("max_connections", max_connections); + } + if (uint64_t max_pending_requests = resource_manager.pendingRequests().max(); + max_pending_requests) { + entries.emplace_back("max_pending_requests", max_pending_requests); + } + if (uint64_t max_requests = resource_manager.requests().max(); max_requests) { + entries.emplace_back("max_requests", max_requests); + } + if (uint64_t max_retries = resource_manager.requests().max(); max_retries) { + entries.emplace_back("max_retries", max_retries); + } addMapEntries(threshold.get(), response, entries); } diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index 70dfefbec31a..60ede7fb7c32 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -90,8 +90,15 @@ class JsonClustersChunkProcessor : public ClustersChunkProcessor { Buffer::Instance& response); void setHealthFlags(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response); - void setHealthFlag(Json::Streamer::Map* raw_host_ptr, Upstream::Host::HealthFlag flag, - const Upstream::HostSharedPtr& host, Buffer::Instance& response); + void setLocality(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, + Buffer::Instance& response); + void setSuccessRate(const Upstream::HostSharedPtr& host, + std::vector& top_level_entries); + void setHostname(const Upstream::HostSharedPtr& host, + std::vector& top_level_entries); + void loadHealthFlagMap( + absl::flat_hash_map>& flag_map, + Upstream::Host::HealthFlag flag, const Upstream::HostSharedPtr& host); const uint64_t chunk_limit_; Http::ResponseHeaderMap& response_headers_; diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index afeea4d1b609..256d0f6ea3c0 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -112,25 +112,21 @@ class BaseClustersRequestFixture : public testing::Test { ON_CALL(*mock_host_, hostname()).WillByDefault(ReturnRef("test_hostname")); ON_CALL(*mock_host_, locality()).WillByDefault(ReturnRef(locality_)); ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::FAILED_ACTIVE_HC)) - .WillByDefault(Return(false)); + .WillByDefault(Return(true)); ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK)) - .WillByDefault(Return(false)); - ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH)) - .WillByDefault(Return("HEALTHY")); - ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH)) - .WillByDefault(Return("HEALTHY")); + .WillByDefault(Return(true)); ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::EDS_STATUS_DRAINING)) - .WillByDefault(Return("HEALTHY")); + .WillByDefault(Return(true)); ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC)) - .WillByDefault(Return(false)); + .WillByDefault(Return(true)); ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL)) - .WillByDefault(Return(false)); + .WillByDefault(Return(true)); ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::PENDING_ACTIVE_HC)) - .WillByDefault(Return(false)); + .WillByDefault(Return(true)); ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL)) - .WillByDefault(Return(false)); + .WillByDefault(Return(true)); ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT)) - .WillByDefault(Return(false)); + .WillByDefault(Return(true)); ON_CALL(*mock_host_, outlierDetector()).WillByDefault(ReturnRef(detector_host_monitor_)); ON_CALL( Const(detector_host_monitor_), @@ -193,7 +189,8 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { EXPECT_EQ(result.code_, Http::Code::OK); // The order of clusters is non-deterministic so strip the 2 from test_cluster2 and expect both - // clusters to be identical. + // clusters to be identical. We also strip all whitespace when making the expectation since the + // output will not actually have any. std::string expected_readable_output = R"EOF( { "cluster_statuses": [ @@ -248,8 +245,19 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { "value": 11 } ] + "health_status": { + "failed_active_health_check": false, + "failed_outlier_check": false, + "eds_health_status": "HEALTHY", + "failed_active_degraded_check": false, + "pending_dynamic_removal": false, + "pending_active_hc": false, + "excluded_via_immediate_hc_fail": false, + "active_hc_timeout": false + } + } - ] + ], }, { "name": "test_cluster", From ce42c2a8b845a5d856883286711190db5a5d7d71 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 30 Apr 2024 17:51:15 +0000 Subject: [PATCH 38/77] Test passing for json streaming Signed-off-by: Demitri Swan --- .../server/admin/clusters_chunk_processor.cc | 49 ++-- test/server/admin/clusters_request_test.cc | 227 +++++++++--------- 2 files changed, 140 insertions(+), 136 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 5830e0c9b01f..9af68beb1e87 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -189,7 +189,7 @@ void JsonClustersChunkProcessor::render(std::reference_wrapperedsServiceName(); !eds_service_name.empty()) { - top_level_entries.push_back({"eds_service_name", eds_service_name}); + top_level_entries.emplace_back("eds_service_name", eds_service_name); } addCircuitBreakers(cluster_map.get(), cluster_info, response); @@ -244,9 +244,9 @@ void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_sta void JsonClustersChunkProcessor::setHostname( const Upstream::HostSharedPtr& host, std::vector& host_config) { - // if (const std::string& hostname = host->hostname(); !hostname.empty()) { - host_config.emplace_back("hostname", host->hostname()); - // } + if (const std::string& hostname = host->hostname(); !hostname.empty()) { + host_config.emplace_back("hostname", hostname); + } } void JsonClustersChunkProcessor::setSuccessRate( @@ -365,7 +365,9 @@ void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_pt raw_host_ptr->addKey("stats"); Json::Streamer::ArrayPtr stats_ptr = raw_host_ptr->addArray(); for (const auto& [counter_name, counter] : host->counters()) { - Json::Streamer::MapPtr stats_obj_ptr = stats_ptr->addMap(); + if (counter_name.empty() || counter.get().value() == 0) { + continue; + } std::vector counter_object{ {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::COUNTER)}}; @@ -375,10 +377,13 @@ void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_pt if (uint64_t value = counter.get().value(); value) { counter_object.emplace_back("value", value); } + Json::Streamer::MapPtr stats_obj_ptr = stats_ptr->addMap(); addMapEntries(stats_obj_ptr.get(), response, counter_object); } for (const auto& [gauge_name, gauge] : host->gauges()) { - Json::Streamer::MapPtr stats_obj_ptr = stats_ptr->addMap(); + if (gauge_name.empty() || gauge.get().value() == 0 ) { + continue; + } std::vector gauge_object{ {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::GAUGE)}}; if (!gauge_name.empty()) { @@ -387,6 +392,7 @@ void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_pt if (uint64_t value = gauge.get().value(); value) { gauge_object.emplace_back("value", value); } + Json::Streamer::MapPtr stats_obj_ptr = stats_ptr->addMap(); addMapEntries(stats_obj_ptr.get(), response, gauge_object); } } @@ -394,9 +400,8 @@ void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_pt void JsonClustersChunkProcessor::setHealthFlags(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { - absl::flat_hash_map<, absl::variant> flag_map; - // Json::Streamer::MapPtr health_status_ptr = raw_host_ptr->addMap(); -// Invokes setHealthFlag for each health flag. + absl::flat_hash_map> flag_map; + // Invokes setHealthFlag for each health flag. #define SET_HEALTH_FLAG(name, notused) \ loadHealthFlagMap(flag_map, Upstream::Host::HealthFlag::name, host); HEALTH_FLAG_ENUM_VALUES(SET_HEALTH_FLAG) @@ -409,7 +414,7 @@ void JsonClustersChunkProcessor::setHealthFlags(Json::Streamer::Map* raw_host_pt std::vector flags; for (const auto& [name, flag_value] : flag_map) { if (name == "eds_health_status") { - flags.emplace_back(name, std::get<>(flag_value)); + flags.emplace_back(name, std::get(flag_value)); } else { flags.emplace_back(name, std::get(flag_value)); } @@ -418,17 +423,17 @@ void JsonClustersChunkProcessor::setHealthFlags(Json::Streamer::Map* raw_host_pt } void JsonClustersChunkProcessor::loadHealthFlagMap( - absl::flat_hash_map>& flag_map, + absl::flat_hash_map>& flag_map, Upstream::Host::HealthFlag flag, const Upstream::HostSharedPtr& host) { switch (flag) { case Upstream::Host::HealthFlag::FAILED_ACTIVE_HC: if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.emplace("failed_active_health_check", value); + flag_map.insert_or_assign("failed_active_health_check", value); } break; case Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK: if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.emplace("failed_outlier_check", value); + flag_map.insert_or_assign("failed_outlier_check", value); } break; case Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH: @@ -436,44 +441,44 @@ void JsonClustersChunkProcessor::loadHealthFlagMap( if (host->healthFlagGet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH) || host->healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH)) { if (host->healthFlagGet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH)) { - flag_map.emplace("eds_health_status", envoy::config::core::v3::HealthStatus_Name( + flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name( envoy::config::core::v3::UNHEALTHY)); } else { - flag_map.emplace("eds_health_status", envoy::config::core::v3::HealthStatus_Name( + flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name( envoy::config::core::v3::DEGRADED)); } } else { - flag_map.emplace("eds_health_status", envoy::config::core::v3::HealthStatus_Name( + flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name( envoy::config::core::v3::HEALTHY)); } case Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC: if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.emplace("failed_active_degraded_check", value); + flag_map.insert_or_assign("failed_active_degraded_check", value); } break; case Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL: if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.emplace("pending_dynamic_removal", value); + flag_map.insert_or_assign("pending_dynamic_removal", value); } break; case Upstream::Host::HealthFlag::PENDING_ACTIVE_HC: if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.emplace("pending_active_hc", value); + flag_map.insert_or_assign("pending_active_hc", value); } break; case Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL: if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.emplace("excluded_via_immediate_hc_fail", value); + flag_map.insert_or_assign("excluded_via_immediate_hc_fail", value); } break; case Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT: if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.emplace("active_hc_timeout", value); + flag_map.insert_or_assign("active_hc_timeout", value); } break; case Upstream::Host::HealthFlag::EDS_STATUS_DRAINING: if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.emplace("eds_health_status", value); + flag_map.insert_or_assign("eds_health_status", value); } break; } diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index 256d0f6ea3c0..620fc3311cf5 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -84,7 +84,7 @@ class BaseClustersRequestFixture : public testing::Test { void loadNewMockClusterByName(NiceMock& mock_cluster, absl::string_view name) { mock_cluster.info_->name_ = name; - ON_CALL(*mock_cluster.info_, edsServiceName()).WillByDefault(ReturnRef("potato_launcher")); + ON_CALL(*mock_cluster.info_, edsServiceName()).WillByDefault(ReturnRef(eds_service_name_)); ON_CALL(*mock_cluster.info_, resourceManager(Upstream::ResourcePriority::Default)) .WillByDefault(ReturnRef(std::ref(*resource_manager_default_).get())); ON_CALL(*mock_cluster.info_, resourceManager(Upstream::ResourcePriority::High)) @@ -109,7 +109,7 @@ class BaseClustersRequestFixture : public testing::Test { ON_CALL(*mock_host_, counters()).WillByDefault(Return(counters_)); ON_CALL(*mock_host_, gauges()).WillByDefault(Return(gauges_)); - ON_CALL(*mock_host_, hostname()).WillByDefault(ReturnRef("test_hostname")); + ON_CALL(*mock_host_, hostname()).WillByDefault(ReturnRef(hostname_)); ON_CALL(*mock_host_, locality()).WillByDefault(ReturnRef(locality_)); ON_CALL(*mock_host_, healthFlagGet(Upstream::Host::HealthFlag::FAILED_ACTIVE_HC)) .WillByDefault(Return(true)); @@ -142,6 +142,8 @@ class BaseClustersRequestFixture : public testing::Test { host_set->hosts_.emplace_back(mock_host_); } + const std::string hostname_{"test_hostname"}; + const std::string eds_service_name_{"potato_launcher"}; NiceMock mock_cluster_info_; NiceMock mock_server_; NiceMock mock_cluster_manager_; @@ -192,129 +194,126 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { // clusters to be identical. We also strip all whitespace when making the expectation since the // output will not actually have any. std::string expected_readable_output = R"EOF( - { - "cluster_statuses": [ - { - "name": "test_cluster", - "observability_name": "observability_name", - "eds_service_name": "potato_launcher", - "circuit_breakers": { - "thresholds": [ + { + "cluster_statuses": [ + { + "circuit_breakers": { + "thresholds": [ + { + "priority": "DEFAULT", + "max_connections": 1024, + "max_pending_requests": 1024, + "max_requests": 1024, + "max_retries": 1024 + }, + { + "priority": "HIGH", + "max_connections": 4096, + "max_pending_requests": 4096, + "max_requests": 4096, + "max_retries": 4096 + } + ] + }, + "success_rate_ejection_threshold": 1.1, + "local_success_rate_ejection_threshold": 1.1, + "name": "test_cluster", + "observability_name": "observability_name", + "eds_service_name": "potato_launcher", + "added_via_api": true, + "host_statuses": [ + { + "address": { + "socket_address": { + "address": "1.2.3.4", + "port": 80 + } + }, + "locality": { + "region": "test_region", + "zone": "test_zone", + "sub_zone": "test_sub_zone" + }, + "stats": [ { - "priority": "DEFAULT", - "max_connections": 1024, - "max_pending_requests": 1024, - "max_requests": 1024, - "max_retries": 16 + "type": "COUNTER", + "name": "test_counter", + "value": 10 }, { - "priority": "HIGH", - "max_connections": 4096, - "max_pending_requests": 4096, - "max_requests": 4096, - "max_retries": 16 + "type": "GAUGE", + "name": "test_gauge", + "value": 11 } - ] - }, - "success_rate_ejection_threshold": 1.1, - "local_success_rate_ejection_threshold": 1.1, - "added_via_api": true, - "host_statuses": [ + ], + "hostname": "test_hostname", + "success_rate": 1, + "weight": 1, + "priority": 1, + "local_origin_success_rate": 1 + } + ] + }, + { + "circuit_breakers": { + "thresholds": [ { - "address": { - "socket_address": { - "address": "1.2.3.4", - "port": 80 - } - }, - "hostname": "test_hostname", - "locality": { - "region": "test_region", - "zone": "test_zone", - "sub_zone": "test_sub_zone" - }, - "stats": [ - { - "type": "COUNTER", - "name": "test_counter", - "value": 10 - }, - { - "type": "GAUGE", - "name": "test_gauge", - "value": 11 - } - ] - "health_status": { - "failed_active_health_check": false, - "failed_outlier_check": false, - "eds_health_status": "HEALTHY", - "failed_active_degraded_check": false, - "pending_dynamic_removal": false, - "pending_active_hc": false, - "excluded_via_immediate_hc_fail": false, - "active_hc_timeout": false - } - + "priority": "DEFAULT", + "max_connections": 1024, + "max_pending_requests": 1024, + "max_requests": 1024, + "max_retries": 1024 + }, + { + "priority": "HIGH", + "max_connections": 4096, + "max_pending_requests": 4096, + "max_requests": 4096, + "max_retries": 4096 } - ], + ] }, - { - "name": "test_cluster", - "observability_name": "observability_name", - "eds_service_name": "potato_launcher", - "circuit_breakers": { - "thresholds": [ + "success_rate_ejection_threshold": 1.1, + "local_success_rate_ejection_threshold": 1.1, + "name": "test_cluster", + "observability_name": "observability_name", + "eds_service_name": "potato_launcher", + "added_via_api": true, + "host_statuses": [ + { + "address": { + "socket_address": { + "address": "1.2.3.4", + "port": 80 + } + }, + "locality": { + "region": "test_region", + "zone": "test_zone", + "sub_zone": "test_sub_zone" + }, + "stats": [ { - "priority": "DEFAULT", - "max_connections": 1024, - "max_pending_requests": 1024, - "max_requests": 1024, - "max_retries": 16 + "type": "COUNTER", + "name": "test_counter", + "value": 10 }, { - "priority": "HIGH", - "max_connections": 4096, - "max_pending_requests": 4096, - "max_requests": 4096, - "max_retries": 16 + "type": "GAUGE", + "name": "test_gauge", + "value": 11 } - ] - }, - "success_rate_ejection_threshold": 1.1, - "local_success_rate_ejection_threshold": 1.1, - "added_via_api": true, - "host_statuses": [ - { - "address": { - "socket_address": { - "address": "1.2.3.4", - "port": 80 - } - }, - "hostname": "test_hostname", - "locality": { - "region": "test_region", - "zone": "test_zone", - "sub_zone": "test_sub_zone" - }, - "stats": [ - { - "type": "COUNTER", - "name": "test_counter", - "value": 10 - }, - { - "type": "GAUGE", - "name": "test_gauge", - "value": 11 - } - ] - } - ] - } - ] - })EOF"; + ], + "hostname": "test_hostname", + "success_rate": 1, + "weight": 1, + "priority": 1, + "local_origin_success_rate": 1 + } + ] + } + ] + })EOF"; EXPECT_EQ( std::regex_replace(result.data_.toString(), std::regex("test_cluster2"), "test_cluster"), std::regex_replace(expected_readable_output, std::regex(R"(\s)"), "")); From 1db72b467bf316749af514f307aa876dfe89c064 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 30 Apr 2024 20:45:05 +0000 Subject: [PATCH 39/77] Using absl::btree_map instead of absl::flat_hash_map for deterministic output Signed-off-by: Demitri Swan --- diff.sh | 25 +- .../server/admin/clusters_chunk_processor.cc | 17 +- .../server/admin/clusters_chunk_processor.h | 2 +- test/server/admin/clusters_request_test.cc | 239 ++++++++++-------- 4 files changed, 157 insertions(+), 126 deletions(-) mode change 100644 => 100755 diff.sh diff --git a/diff.sh b/diff.sh old mode 100644 new mode 100755 index 23e829f5f10e..1aafeb3aed2d --- a/diff.sh +++ b/diff.sh @@ -1,22 +1,27 @@ #!/usr/bin/env bash -file_1="$(<$1)" -file_2="$(<$2)" +file="$(<$1)" -declare -i word_count_1=$(echo "$file_1" | wc -c) -declare -i word_count_2=$(echo "$file_2" | wc -c) +actual="$(echo "$file" | grep -m 1 Which | tail -n 1)" +expected="$(echo "$file" | grep -m 2 Which | tail -n 1)" + +echo "$actual" +echo "$expected" + +declare -i actual_char_count=$(echo "$actual" | wc -c) +declare -i expected_char_count=$(echo "$expected" | wc -c) declare -i exit_code -if (( word_count_1 != word_count_2 )); then - echo "word_count_1: $word_count_1 word_count_2: $word_count_2" >&2 +if (( actual_char_count != expected_char_count )); then + echo "actual word count: $actual_word_count expected_word_count: $expected_word_count" exit_code=1 fi -for ((i=0; i < word_count_1; i++)) { - if [[ ${file_1:$i:1} != ${file_2:$i:1} ]]; then - echo "mismatch at index $i: first ${file_1:$i:30} | second ${file_2:$i:30}" >&2 +for ((i=0; i < actual_char_count; i++)) { + #echo "${actual:$i:1} ${expected:$i:1}" + if [[ ${actual:$i:1} != ${expected:$i:1} ]]; then + echo "mismatch at index $i: actual ${actual:$i:30} | expected ${expected:$i:30}" exit_code=1 - exit 11 fi } diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 9af68beb1e87..12fd288380a5 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include "envoy/buffer/buffer.h" #include "envoy/config/core/v3/base.pb.h" @@ -400,13 +401,13 @@ void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_pt void JsonClustersChunkProcessor::setHealthFlags(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { - absl::flat_hash_map> flag_map; + absl::btree_map> flag_map; // Invokes setHealthFlag for each health flag. #define SET_HEALTH_FLAG(name, notused) \ loadHealthFlagMap(flag_map, Upstream::Host::HealthFlag::name, host); HEALTH_FLAG_ENUM_VALUES(SET_HEALTH_FLAG) #undef SET_HEALTH_FLAG - if (!flag_map.empty()) { + if (flag_map.empty()) { return; } raw_host_ptr->addKey("health_status"); @@ -414,16 +415,20 @@ void JsonClustersChunkProcessor::setHealthFlags(Json::Streamer::Map* raw_host_pt std::vector flags; for (const auto& [name, flag_value] : flag_map) { if (name == "eds_health_status") { - flags.emplace_back(name, std::get(flag_value)); + if (std::holds_alternative(flag_value)) { + flags.emplace_back(name, std::get(flag_value)); + } } else { - flags.emplace_back(name, std::get(flag_value)); + if (std::holds_alternative(flag_value)) { + flags.emplace_back(name, std::get(flag_value)); + } } } addMapEntries(health_flags_ptr.get(), response, flags); } void JsonClustersChunkProcessor::loadHealthFlagMap( - absl::flat_hash_map>& flag_map, + absl::btree_map>& flag_map, Upstream::Host::HealthFlag flag, const Upstream::HostSharedPtr& host) { switch (flag) { case Upstream::Host::HealthFlag::FAILED_ACTIVE_HC: @@ -478,7 +483,7 @@ void JsonClustersChunkProcessor::loadHealthFlagMap( break; case Upstream::Host::HealthFlag::EDS_STATUS_DRAINING: if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.insert_or_assign("eds_health_status", value); + flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name(envoy::config::core::v3::DRAINING)); } break; } diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index 60ede7fb7c32..9138bcc753ea 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -97,7 +97,7 @@ class JsonClustersChunkProcessor : public ClustersChunkProcessor { void setHostname(const Upstream::HostSharedPtr& host, std::vector& top_level_entries); void loadHealthFlagMap( - absl::flat_hash_map>& flag_map, + absl::btree_map>& flag_map, Upstream::Host::HealthFlag flag, const Upstream::HostSharedPtr& host); const uint64_t chunk_limit_; diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index 620fc3311cf5..9791c9d653e6 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -194,126 +194,147 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { // clusters to be identical. We also strip all whitespace when making the expectation since the // output will not actually have any. std::string expected_readable_output = R"EOF( +{ + "cluster_statuses": [ { - "cluster_statuses": [ + "circuit_breakers": { + "thresholds": [ + { + "priority": "DEFAULT", + "max_connections": 1024, + "max_pending_requests": 1024, + "max_requests": 1024, + "max_retries": 1024 + }, + { + "priority": "HIGH", + "max_connections": 4096, + "max_pending_requests": 4096, + "max_requests": 4096, + "max_retries": 4096 + } + ] + }, + "success_rate_ejection_threshold": 1.1, + "local_success_rate_ejection_threshold": 1.1, + "name": "test_cluster", + "observability_name": "observability_name", + "eds_service_name": "potato_launcher", + "added_via_api": true, + "host_statuses": [ { - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 1024, - "max_pending_requests": 1024, - "max_requests": 1024, - "max_retries": 1024 - }, - { - "priority": "HIGH", - "max_connections": 4096, - "max_pending_requests": 4096, - "max_requests": 4096, - "max_retries": 4096 - } - ] + "address": { + "socket_address": { + "address": "1.2.3.4", + "port": 80 + } }, - "success_rate_ejection_threshold": 1.1, - "local_success_rate_ejection_threshold": 1.1, - "name": "test_cluster", - "observability_name": "observability_name", - "eds_service_name": "potato_launcher", - "added_via_api": true, - "host_statuses": [ + "locality": { + "region": "test_region", + "zone": "test_zone", + "sub_zone": "test_sub_zone" + }, + "stats": [ + { + "type": "COUNTER", + "name": "test_counter", + "value": 10 + }, { - "address": { - "socket_address": { - "address": "1.2.3.4", - "port": 80 - } - }, - "locality": { - "region": "test_region", - "zone": "test_zone", - "sub_zone": "test_sub_zone" - }, - "stats": [ - { - "type": "COUNTER", - "name": "test_counter", - "value": 10 - }, - { - "type": "GAUGE", - "name": "test_gauge", - "value": 11 - } - ], - "hostname": "test_hostname", - "success_rate": 1, - "weight": 1, - "priority": 1, - "local_origin_success_rate": 1 + "type": "GAUGE", + "name": "test_gauge", + "value": 11 } - ] - }, + ], + "health_status": { + "active_hc_timeout": true, + "eds_health_status": "DRAINING", + "excluded_via_immediate_hc_fail": true, + "failed_active_degraded_check": true, + "failed_active_health_check": true, + "failed_outlier_check": true, + "pending_active_hc": true, + "pending_dynamic_removal": true + }, + "hostname": "test_hostname", + "success_rate": 1, + "weight": 1, + "priority": 1, + "local_origin_success_rate": 1 + } + ] + }, + { + "circuit_breakers": { + "thresholds": [ + { + "priority": "DEFAULT", + "max_connections": 1024, + "max_pending_requests": 1024, + "max_requests": 1024, + "max_retries": 1024 + }, + { + "priority": "HIGH", + "max_connections": 4096, + "max_pending_requests": 4096, + "max_requests": 4096, + "max_retries": 4096 + } + ] + }, + "success_rate_ejection_threshold": 1.1, + "local_success_rate_ejection_threshold": 1.1, + "name": "test_cluster", + "observability_name": "observability_name", + "eds_service_name": "potato_launcher", + "added_via_api": true, + "host_statuses": [ { - "circuit_breakers": { - "thresholds": [ - { - "priority": "DEFAULT", - "max_connections": 1024, - "max_pending_requests": 1024, - "max_requests": 1024, - "max_retries": 1024 - }, - { - "priority": "HIGH", - "max_connections": 4096, - "max_pending_requests": 4096, - "max_requests": 4096, - "max_retries": 4096 - } - ] + "address": { + "socket_address": { + "address": "1.2.3.4", + "port": 80 + } }, - "success_rate_ejection_threshold": 1.1, - "local_success_rate_ejection_threshold": 1.1, - "name": "test_cluster", - "observability_name": "observability_name", - "eds_service_name": "potato_launcher", - "added_via_api": true, - "host_statuses": [ + "locality": { + "region": "test_region", + "zone": "test_zone", + "sub_zone": "test_sub_zone" + }, + "stats": [ + { + "type": "COUNTER", + "name": "test_counter", + "value": 10 + }, { - "address": { - "socket_address": { - "address": "1.2.3.4", - "port": 80 - } - }, - "locality": { - "region": "test_region", - "zone": "test_zone", - "sub_zone": "test_sub_zone" - }, - "stats": [ - { - "type": "COUNTER", - "name": "test_counter", - "value": 10 - }, - { - "type": "GAUGE", - "name": "test_gauge", - "value": 11 - } - ], - "hostname": "test_hostname", - "success_rate": 1, - "weight": 1, - "priority": 1, - "local_origin_success_rate": 1 + "type": "GAUGE", + "name": "test_gauge", + "value": 11 } - ] + ], + "health_status": { + "active_hc_timeout": true, + "eds_health_status": "DRAINING", + "excluded_via_immediate_hc_fail": true, + "failed_active_degraded_check": true, + "failed_active_health_check": true, + "failed_outlier_check": true, + "pending_active_hc": true, + "pending_dynamic_removal": true + }, + "hostname": "test_hostname", + "success_rate": 1, + "weight": 1, + "priority": 1, + "local_origin_success_rate": 1 } ] - })EOF"; + } + ] +} + )EOF"; EXPECT_EQ( std::regex_replace(result.data_.toString(), std::regex("test_cluster2"), "test_cluster"), std::regex_replace(expected_readable_output, std::regex(R"(\s)"), "")); From a1717149c0b522548be9021589fde3f8f2db45c7 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 30 Apr 2024 22:03:12 +0000 Subject: [PATCH 40/77] Some minor fixes and formating changes Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 1 + .../server/admin/clusters_chunk_processor.cc | 21 +++++++++++-------- .../server/admin/clusters_chunk_processor.h | 2 ++ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index a20c793f9706..8cc55d70f9f2 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -459,6 +459,7 @@ envoy_cc_library( "//source/common/json:json_streamer_lib", "//source/common/network:utility_lib", "//source/common/upstream:host_utility_lib", + "@com_google_absl//absl/strings:string_view", "@envoy_api//envoy/admin/v3:pkg_cc_proto", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 12fd288380a5..8216e3495a5f 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -17,6 +17,8 @@ #include "source/common/buffer/buffer_impl.h" #include "source/common/upstream/host_utility.h" +#include "absl/strings/string_view.h" + namespace Envoy { namespace Server { @@ -382,7 +384,7 @@ void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_pt addMapEntries(stats_obj_ptr.get(), response, counter_object); } for (const auto& [gauge_name, gauge] : host->gauges()) { - if (gauge_name.empty() || gauge.get().value() == 0 ) { + if (gauge_name.empty() || gauge.get().value() == 0) { continue; } std::vector gauge_object{ @@ -415,11 +417,11 @@ void JsonClustersChunkProcessor::setHealthFlags(Json::Streamer::Map* raw_host_pt std::vector flags; for (const auto& [name, flag_value] : flag_map) { if (name == "eds_health_status") { - if (std::holds_alternative(flag_value)) { - flags.emplace_back(name, std::get(flag_value)); + if (absl::holds_alternative(flag_value)) { + flags.emplace_back(name, std::get(flag_value)); } } else { - if (std::holds_alternative(flag_value)) { + if (absl::holds_alternative(flag_value)) { flags.emplace_back(name, std::get(flag_value)); } } @@ -428,7 +430,7 @@ void JsonClustersChunkProcessor::setHealthFlags(Json::Streamer::Map* raw_host_pt } void JsonClustersChunkProcessor::loadHealthFlagMap( - absl::btree_map>& flag_map, + absl::btree_map>& flag_map, Upstream::Host::HealthFlag flag, const Upstream::HostSharedPtr& host) { switch (flag) { case Upstream::Host::HealthFlag::FAILED_ACTIVE_HC: @@ -447,14 +449,14 @@ void JsonClustersChunkProcessor::loadHealthFlagMap( host->healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH)) { if (host->healthFlagGet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH)) { flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name( - envoy::config::core::v3::UNHEALTHY)); + envoy::config::core::v3::UNHEALTHY)); } else { flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name( - envoy::config::core::v3::DEGRADED)); + envoy::config::core::v3::DEGRADED)); } } else { flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name( - envoy::config::core::v3::HEALTHY)); + envoy::config::core::v3::HEALTHY)); } case Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC: if (bool value = host.get()->healthFlagGet(flag); value) { @@ -483,7 +485,8 @@ void JsonClustersChunkProcessor::loadHealthFlagMap( break; case Upstream::Host::HealthFlag::EDS_STATUS_DRAINING: if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name(envoy::config::core::v3::DRAINING)); + flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name( + envoy::config::core::v3::DRAINING)); } break; } diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index 9138bcc753ea..896aed0f4acc 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -14,6 +14,8 @@ #include "source/common/buffer/buffer_impl.h" #include "source/common/json/json_streamer.h" +#include "absl/strings/string_view.h" + namespace Envoy { namespace Server { From 485dd011bdde5d42b3b882ff9be1eb31cdd00c92 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 30 Apr 2024 23:19:09 +0000 Subject: [PATCH 41/77] VerifyTextOutput done Signed-off-by: Demitri Swan --- test/server/admin/clusters_request_test.cc | 84 +++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index 9791c9d653e6..e107de8aded4 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -356,7 +356,89 @@ class VerifyTextOutputFixture : public BaseClustersRequestFixture, public testing::WithParamInterface {}; // TODO(demitriswan) Implement test for text output verification. -TEST_P(VerifyTextOutputFixture, VerifyTextOutput) {} +TEST_P(VerifyTextOutputFixture, VerifyTextOutput) { + // Small chunk limit will force Request::nextChunk to be called for each Cluster. + constexpr int chunk_limit = 1; + VerifyTextOutputParameters params = GetParam(); + Buffer::OwnedImpl buffer; + ClustersParams clusters_params; + clusters_params.format_ = ClustersParams::Format::Text; + + NiceMock test_cluster; + loadNewMockClusterByName(test_cluster, "test_cluster"); + + NiceMock test_cluster2; + loadNewMockClusterByName(test_cluster2, "test_cluster2"); + + ResponseResult result = response(*makeRequest(chunk_limit, clusters_params), params.drain_); + + EXPECT_EQ(result.code_, Http::Code::OK); + // The order of clusters is non-deterministic so strip the 2 from test_cluster2 and expect both + // clusters to be identical. We also strip all whitespace when making the expectation since the + // output will not actually have any. + std::string expected_readable_output = +R"EOF(test_cluster::observability_name::observability_name +test_cluster::outlier::success_rate_average::0 +test_cluster::outlier::success_rate_ejection_threshold::1.1 +test_cluster::outlier::local_origin_success_rate_average::0 +test_cluster::outlier::local_origin_success_rate_ejection_threshold::1.1 +test_cluster::default_priority::max_connections::1024 +test_cluster::default_priority::max_pending_requests::1024 +test_cluster::default_priority::max_requests::1024 +test_cluster::default_priority::max_retries::16 +test_cluster::high_priority::max_connections::4096 +test_cluster::high_priority::max_pending_requests::4096 +test_cluster::high_priority::max_requests::4096 +test_cluster::high_priority::max_retries::16 +test_cluster::added_via_api::true +test_cluster::eds_service_name::potato_launcher +test_cluster::1.2.3.4:80::test_counter::10 +test_cluster::1.2.3.4:80::test_gauge::11 +test_cluster::1.2.3.4:80::hostname::test_hostname +test_cluster::1.2.3.4:80::health_flags::/failed_active_hc/failed_outlier_check/degraded_active_hc/pending_dynamic_removal/pending_active_hc/excluded_via_immediate_hc_fail/active_hc_timeout/eds_status_draining +test_cluster::1.2.3.4:80::weight::1 +test_cluster::1.2.3.4:80::region::test_region +test_cluster::1.2.3.4:80::zone::test_zone +test_cluster::1.2.3.4:80::sub_zone::test_sub_zone +test_cluster::1.2.3.4:80::canary::false +test_cluster::1.2.3.4:80::priority::1 +test_cluster::1.2.3.4:80::success_rate::1 +test_cluster::1.2.3.4:80::local_origin_success_rate::1 +test_cluster::observability_name::observability_name +test_cluster::outlier::success_rate_average::0 +test_cluster::outlier::success_rate_ejection_threshold::1.1 +test_cluster::outlier::local_origin_success_rate_average::0 +test_cluster::outlier::local_origin_success_rate_ejection_threshold::1.1 +test_cluster::default_priority::max_connections::1024 +test_cluster::default_priority::max_pending_requests::1024 +test_cluster::default_priority::max_requests::1024 +test_cluster::default_priority::max_retries::16 +test_cluster::high_priority::max_connections::4096 +test_cluster::high_priority::max_pending_requests::4096 +test_cluster::high_priority::max_requests::4096 +test_cluster::high_priority::max_retries::16 +test_cluster::added_via_api::true +test_cluster::eds_service_name::potato_launcher +test_cluster::1.2.3.4:80::test_counter::10 +test_cluster::1.2.3.4:80::test_gauge::11 +test_cluster::1.2.3.4:80::hostname::test_hostname +test_cluster::1.2.3.4:80::health_flags::/failed_active_hc/failed_outlier_check/degraded_active_hc/pending_dynamic_removal/pending_active_hc/excluded_via_immediate_hc_fail/active_hc_timeout/eds_status_draining +test_cluster::1.2.3.4:80::weight::1 +test_cluster::1.2.3.4:80::region::test_region +test_cluster::1.2.3.4:80::zone::test_zone +test_cluster::1.2.3.4:80::sub_zone::test_sub_zone +test_cluster::1.2.3.4:80::canary::false +test_cluster::1.2.3.4:80::priority::1 +test_cluster::1.2.3.4:80::success_rate::1 +test_cluster::1.2.3.4:80::local_origin_success_rate::1 +)EOF"; + EXPECT_EQ( + std::regex_replace(result.data_.toString(), std::regex("test_cluster2"), "test_cluster"), + expected_readable_output); + + + +} constexpr VerifyTextOutputParameters VERIFY_TEXT_CASES[] = { {/* drain_=*/true}, From 15c1dd323153541e699767ead265ea5785a705df Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 30 Apr 2024 23:22:06 +0000 Subject: [PATCH 42/77] Format fix Signed-off-by: Demitri Swan --- test/server/admin/clusters_request_test.cc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index e107de8aded4..1723a0b83425 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -377,7 +377,7 @@ TEST_P(VerifyTextOutputFixture, VerifyTextOutput) { // clusters to be identical. We also strip all whitespace when making the expectation since the // output will not actually have any. std::string expected_readable_output = -R"EOF(test_cluster::observability_name::observability_name + R"EOF(test_cluster::observability_name::observability_name test_cluster::outlier::success_rate_average::0 test_cluster::outlier::success_rate_ejection_threshold::1.1 test_cluster::outlier::local_origin_success_rate_average::0 @@ -435,9 +435,6 @@ test_cluster::1.2.3.4:80::local_origin_success_rate::1 EXPECT_EQ( std::regex_replace(result.data_.toString(), std::regex("test_cluster2"), "test_cluster"), expected_readable_output); - - - } constexpr VerifyTextOutputParameters VERIFY_TEXT_CASES[] = { From 75a8590ad4a9d1ffa23da81e6d209f596421f02d Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 30 Apr 2024 23:30:00 +0000 Subject: [PATCH 43/77] Remove temp files Signed-off-by: Demitri Swan --- diff.sh | 28 ------------------------ envoy-demo.yaml | 58 ------------------------------------------------- file1 | 1 - file2 | 1 - file3 | 1 - 5 files changed, 89 deletions(-) delete mode 100755 diff.sh delete mode 100644 envoy-demo.yaml delete mode 100644 file1 delete mode 100644 file2 delete mode 100644 file3 diff --git a/diff.sh b/diff.sh deleted file mode 100755 index 1aafeb3aed2d..000000000000 --- a/diff.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash - -file="$(<$1)" - -actual="$(echo "$file" | grep -m 1 Which | tail -n 1)" -expected="$(echo "$file" | grep -m 2 Which | tail -n 1)" - -echo "$actual" -echo "$expected" - -declare -i actual_char_count=$(echo "$actual" | wc -c) -declare -i expected_char_count=$(echo "$expected" | wc -c) -declare -i exit_code - -if (( actual_char_count != expected_char_count )); then - echo "actual word count: $actual_word_count expected_word_count: $expected_word_count" - exit_code=1 -fi - -for ((i=0; i < actual_char_count; i++)) { - #echo "${actual:$i:1} ${expected:$i:1}" - if [[ ${actual:$i:1} != ${expected:$i:1} ]]; then - echo "mismatch at index $i: actual ${actual:$i:30} | expected ${expected:$i:30}" - exit_code=1 - fi -} - -exit $exit_code diff --git a/envoy-demo.yaml b/envoy-demo.yaml deleted file mode 100644 index 40e12b2206b2..000000000000 --- a/envoy-demo.yaml +++ /dev/null @@ -1,58 +0,0 @@ -admin: - address: - socket_address: { address: 127.0.0.1, port_value: 9000 } - -static_resources: - - listeners: - - name: listener_0 - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_http - access_log: - - name: envoy.access_loggers.stdout - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - route_config: - name: local_route - virtual_hosts: - - name: local_service - domains: ["*"] - routes: - - match: - prefix: "/" - route: - host_rewrite_literal: www.envoyproxy.io - cluster: service_envoyproxy_io - - clusters: - - name: service_envoyproxy_io - type: LOGICAL_DNS - # Comment out the following line to test on v6 networks - dns_lookup_family: V4_ONLY - load_assignment: - cluster_name: service_envoyproxy_io - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: www.envoyproxy.io - port_value: 443 - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - sni: www.envoyproxy.io - diff --git a/file1 b/file1 deleted file mode 100644 index dd4b4fe675d1..000000000000 --- a/file1 +++ /dev/null @@ -1 +0,0 @@ -"{\"cluster_statuses\":[{\"name\":\"test_cluster\",\"observability_name\":\"observability_name\",\"eds_service_name\":\"potato_launcher\",\"circuit_breakers\":{\"thresholds\":[{\"priority\":\"DEFAULT\",\"max_connections\":1024,\"max_pending_requests\":1024,\"max_requests\":1024,\"max_retries\":16},{\"priority\":\"HIGH\",\"max_connections\":4096,\"max_pending_requests\":4096,\"max_requests\":4096,\"max_retries\":16}]},\"success_rate_ejection_threshold\":1.1,\"local_success_rate_ejection_threshold\":1.1,\"added_via_api\":true,\"host_statuses\":[{\"address\":{\"socket_address\":{\"address\":\"1.2.3.4\",\"port\":80}},\"hostname\":\"test_hostname\",\"locality\":{\"region\":\"test_region\",\"zone\":\"test_zone\",\"sub_zone\":\"test_sub_zone\"},\"stats\":[{\"type\":\"COUNTER\",\"name\":\"test_counter\",\"value\":10},{\"type\":\"GAUGE\",\"name\":\"test_gauge\",\"value\":11}],\"health_status\":{\"failed_active_health_check\":true,\"failed_outlier_check\":true,\"eds_health_status\":\"DEGRADED\",\"failed_active_degraded_check\":true,\"eds_health_status\":\"DEGRADED\",\"pending_dynamic_removal\":true,\"pending_active_hc\":false,\"excluded_via_immediate_hc_fail\":false,\"active_hc_timeout\":false,\"eds_health_status\":\"DRAINING\"}}]},{\"name\":\"test_cluster\",\"observability_name\":\"observability_name\",\"eds_service_name\":\"potato_launcher\",\"circuit_breakers\":{\"thresholds\":[{\"priority\":\"DEFAULT\",\"max_connections\":1024,\"max_pending_requests\":1024,\"max_requests\":1024,\"max_retries\":16},{\"priority\":\"HIGH\",\"max_connections\":4096,\"max_pending_requests\":4096,\"max_requests\":4096,\"max_retries\":16}]},\"success_rate_ejection_threshold\":1.1,\"local_success_rate_ejection_threshold\":1.1,\"added_via_api\":true,\"host_statuses\":[{\"address\":{\"socket_address\":{\"address\":\"1.2.3.4\",\"port\":80}},\"hostname\":\"test_hostname\",\"locality\":{\"region\":\"test_region\",\"zone\":\"test_zone\",\"sub_zone\":\"test_sub_zone\"},\"stats\":[{\"type\":\"COUNTER\",\"name\":\"test_counter\",\"value\":10},{\"type\":\"GAUGE\",\"name\":\"test_gauge\",\"value\":11}],\"health_status\":{\"failed_active_health_check\":true,\"failed_outlier_check\":true,\"eds_health_status\":\"DEGRADED\",\"failed_active_degraded_check\":true,\"eds_health_status\":\"DEGRADED\",\"pending_dynamic_removal\":true,\"pending_active_hc\":false,\"excluded_via_immediate_hc_fail\":false,\"active_hc_timeout\":false,\"eds_health_status\":\"DRAINING\"}}]}]}" diff --git a/file2 b/file2 deleted file mode 100644 index 9b39817672a0..000000000000 --- a/file2 +++ /dev/null @@ -1 +0,0 @@ -"{\"cluster_statuses\":[{\"name\":\"test_cluster\",\"observability_name\":\"observability_name\",\"eds_service_name\":\"potato_launcher\",\"circuit_breakers\":{\"thresholds\":[{\"priority\":\"DEFAULT\",\"max_connections\":1024,\"max_pending_requests\":1024,\"max_requests\":1024,\"max_retries\":16},{\"priority\":\"HIGH\",\"max_connections\":4096,\"max_pending_requests\":4096,\"max_requests\":4096,\"max_retries\":16}]},\"success_rate_ejection_threshold\":1.1,\"local_success_rate_ejection_threshold\":1.1,\"added_via_api\":true,\"host_statuses\":[{\"address\":{\"socket_address\":{\"address\":\"1.2.3.4\",\"port\":80}},\"hostname\":\"test_hostname\",\"locality\":{\"region\":\"test_region\",\"zone\":\"test_zone\",\"sub_zone\":\"test_sub_zone\"},\"stats\":[{\"type\":\"COUNTER\",\"name\":\"test_counter\",\"value\":10},{\"type\":\"GAUGE\",\"name\":\"test_gauge\",\"value\":11}],\"health_status\":{\"eds_health_status\":\"DRAINING\",\"failed_active_health_check\":true,\"failed_outlier_check\":true,\"failed_active_degraded_check\":true,\"pending_dynamic_removal\":true}}],},{\"name\":\"test_cluster\",\"observability_name\":\"observability_name\",\"eds_service_name\":\"potato_launcher\",\"circuit_breakers\":{\"thresholds\":[{\"priority\":\"DEFAULT\",\"max_connections\":1024,\"max_pending_requests\":1024,\"max_requests\":1024,\"max_retries\":16},{\"priority\":\"HIGH\",\"max_connections\":4096,\"max_pending_requests\":4096,\"max_requests\":4096,\"max_retries\":16}]},\"success_rate_ejection_threshold\":1.1,\"local_success_rate_ejection_threshold\":1.1,\"added_via_api\":true,\"host_statuses\":[{\"address\":{\"socket_address\":{\"address\":\"1.2.3.4\",\"port\":80}},\"hostname\":\"test_hostname\",\"locality\":{\"region\":\"test_region\",\"zone\":\"test_zone\",\"sub_zone\":\"test_sub_zone\"},\"stats\":[{\"type\":\"COUNTER\",\"name\":\"test_counter\",\"value\":10},{\"type\":\"GAUGE\",\"name\":\"test_gauge\",\"value\":11}],\"health_status\":{\"failed_active_health_check\":false,\"failed_outlier_check\":false,\"eds_health_status\":\"HEALTHY\",\"failed_active_degraded_check\":false,\"pending_dynamic_removal\":false,\"pending_active_hc\":false,\"excluded_via_immediate_hc_fail\":false,\"active_hc_timeout\":false}}]}]}" diff --git a/file3 b/file3 deleted file mode 100644 index b04802db73f4..000000000000 --- a/file3 +++ /dev/null @@ -1 +0,0 @@ -"{\"cluster_statuses\":[{\"circuit_breakers\":{\"thresholds\":[{\"priority\":\"DEFAULT\",\"max_connections\":1024,\"max_pending_requests\":1024,\"max_requests\":1024,\"max_retries\":1024},{\"priority\":\"HIGH\",\"max_connections\":4096,\"max_pending_requests\":4096,\"max_requests\":4096,\"max_retries\":4096}]},\"success_rate_ejection_threshold\":1.1,\"local_success_rate_ejection_threshold\":1.1,\"added_via_api\":true,\"host_statuses\":[{\"address\":{\"socket_address\":{\"address\":\"1.2.3.4\",\"port\":80}},\"locality\":{\"region\":\"test_region\",\"zone\":\"test_zone\",\"sub_zone\":\"test_sub_zone\"},\"stats\":[{\"type\":\"COUNTER\",\"name\":\"test_counter\",\"value\":10},{\"type\":\"GAUGE\",\"name\":\"test_gauge\",\"value\":11}],\"hostname\":\"\\000\\000\\000\\000\\000\\000\\000\\250TI&\\376\\177\",\"success_rate\":1,\"weight\":1,\"priority\":1,\"local_origin_success_rate\":1}]},{\"circuit_breakers\":{\"thresholds\":[{\"priority\":\"DEFAULT\",\"max_connections\":1024,\"max_pending_requests\":1024,\"max_requests\":1024,\"max_retries\":1024},{\"priority\":\"HIGH\",\"max_connections\":4096,\"max_pending_requests\":4096,\"max_requests\":4096,\"max_retries\":4096}]},\"success_rate_ejection_threshold\":1.1,\"local_success_rate_ejection_threshold\":1.1,\"added_via_api\":true,\"host_statuses\":[{\"address\":{\"socket_address\":{\"address\":\"1.2.3.4\",\"port\":80}},\"locality\":{\"region\":\"test_region\",\"zone\":\"test_zone\",\"sub_zone\":\"test_sub_zone\"},\"stats\":[{\"type\":\"COUNTER\",\"name\":\"test_counter\",\"value\":10},{\"type\":\"GAUGE\",\"name\":\"test_gauge\",\"value\":11}],\"hostname\":\"\\000\\000\\000\\000\\000\\000\\000\\250TI&\\376\\177\",\"success_rate\":1,\"weight\":1,\"priority\":1,\"local_origin_success_rate\":1}]}]}" From a23e9ec86884ff5c3202c6bcd2fc2b5e718a1847 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Tue, 30 Apr 2024 23:40:20 +0000 Subject: [PATCH 44/77] Handle omission of empty object Signed-off-by: Demitri Swan --- source/server/admin/admin_filter.cc | 1 - .../server/admin/clusters_chunk_processor.cc | 18 ++++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/source/server/admin/admin_filter.cc b/source/server/admin/admin_filter.cc index 3b885c34cabe..d9dee2576612 100644 --- a/source/server/admin/admin_filter.cc +++ b/source/server/admin/admin_filter.cc @@ -96,7 +96,6 @@ void AdminFilter::onComplete() { bool more_data; do { Buffer::OwnedImpl response; - ENVOY_LOG_MISC(debug, "about to call next chunk"); more_data = handler->nextChunk(response); bool end_stream = end_stream_on_complete_ && !more_data; ENVOY_LOG_MISC(debug, "nextChunk: response.length={} more_data={} end_stream={}", diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 8216e3495a5f..7c8c45dc98ea 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -230,16 +230,18 @@ void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_sta Json::Streamer::MapPtr host_ptr = raw_host_statuses_ptr->addMap(); std::vector host_config; setHostname(host, host_config); - addAddress(host_ptr.get(), host, response); - setLocality(host_ptr.get(), host, response); - buildHostStats(host_ptr.get(), host, response); - setHealthFlags(host_ptr.get(), host, response); + addAddress(host_ptr.get(), host, buffer); + setLocality(host_ptr.get(), host, buffer); + buildHostStats(host_ptr.get(), host, buffer); + setHealthFlags(host_ptr.get(), host, buffer); setSuccessRate(host, host_config); - // TODO(demitriswan) add test to ensure empty object doesn't get written. - // if (buffer.length()) { - // response.move(buffer); - // } + // TODO(demitriswan) add test to ensure empty object doesn't get written if there + // is no relevant data to output due to the omitempty JSON encoding option typically + // used in generated client code to which this implementation is referencing. + if (buffer.length()) { + response.move(buffer); + } if (!host_config.empty()) { addMapEntries(host_ptr.get(), response, host_config); } From fe47bf2022100103cc10508c97890eead53a655a Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Thu, 2 May 2024 18:56:24 +0000 Subject: [PATCH 45/77] Set content type header for JSON output to satisfy integration tests Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 1 + source/server/admin/clusters_request.cc | 2 ++ 2 files changed, 3 insertions(+) diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 8cc55d70f9f2..9e7739e50fb4 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -428,6 +428,7 @@ envoy_cc_library( "//envoy/server:instance_interface", "//source/common/buffer:buffer_lib", "//source/common/common:logger_lib", + "//source/common/http:headers_lib", ], ) diff --git a/source/server/admin/clusters_request.cc b/source/server/admin/clusters_request.cc index b427bcab8180..bf1ed8665245 100644 --- a/source/server/admin/clusters_request.cc +++ b/source/server/admin/clusters_request.cc @@ -7,6 +7,7 @@ #include "envoy/server/instance.h" #include "source/common/common/logger.h" +#include "source/common/http/headers.h" #include "source/server/admin/clusters_chunk_processor.h" #include "clusters_params.h" @@ -27,6 +28,7 @@ Http::Code ClustersRequest::start(Http::ResponseHeaderMap& response_headers) { case ClustersParams::Format::Json: chunk_processor_ = std::make_unique( chunk_limit_, response_headers, server_.clusterManager().clusters().active_clusters_); + response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); break; default: // TODO(demitriswan) handle this case properly. From e2eec3a52f590242f3a420aed4a034570d764b3b Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Thu, 2 May 2024 20:43:08 +0000 Subject: [PATCH 46/77] Fix spelling issue and update comments Signed-off-by: Demitri Swan --- .../server/admin/clusters_chunk_processor.cc | 4 +- .../server/admin/clusters_chunk_processor.h | 41 +++++++++++++------ source/server/admin/clusters_request.h | 6 ++- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 7c8c45dc98ea..85ed589d1372 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -236,9 +236,6 @@ void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_sta setHealthFlags(host_ptr.get(), host, buffer); setSuccessRate(host, host_config); - // TODO(demitriswan) add test to ensure empty object doesn't get written if there - // is no relevant data to output due to the omitempty JSON encoding option typically - // used in generated client code to which this implementation is referencing. if (buffer.length()) { response.move(buffer); } @@ -246,6 +243,7 @@ void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_sta addMapEntries(host_ptr.get(), response, host_config); } } + void JsonClustersChunkProcessor::setHostname( const Upstream::HostSharedPtr& host, std::vector& host_config) { diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index 896aed0f4acc..862f66095600 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -19,24 +19,19 @@ namespace Envoy { namespace Server { -struct ClustersJsonContext { - ClustersJsonContext(std::unique_ptr streamer, - std::reference_wrapper buffer, - Json::Streamer::MapPtr root_map, Json::Streamer::ArrayPtr clusters) - : streamer_(std::move(streamer)), buffer_(buffer.get()), root_map_(std::move(root_map)), - clusters_(std::move(clusters)) {} - std::unique_ptr streamer_; - Buffer::Instance& buffer_; - Json::Streamer::MapPtr root_map_; - Json::Streamer::ArrayPtr clusters_; -}; - +/** + * ClustersChunkProcessor is the interface for streaming Clusters data in a response + * for /clusters. + */ class ClustersChunkProcessor { public: virtual bool nextChunk(Buffer::Instance& response) PURE; virtual ~ClustersChunkProcessor() = default; }; +/** + * TextClustersChunkProcessor streams the response using a newline delimited text format. + */ class TextClustersChunkProcessor : public ClustersChunkProcessor { public: TextClustersChunkProcessor(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, @@ -59,6 +54,28 @@ class TextClustersChunkProcessor : public ClustersChunkProcessor { uint64_t idx_; }; +/** + * ClustersJsonContext holds an Envoy::Json::Streamer and the top-level JSON objects throughout the + * duration of a request. When it is destructed, the buffer will terminate the array and object + * herein. See the Envoy::Json::Streamer implementation for details. + */ +struct ClustersJsonContext { + ClustersJsonContext(std::unique_ptr streamer, + std::reference_wrapper buffer, + Json::Streamer::MapPtr root_map, Json::Streamer::ArrayPtr clusters) + : streamer_(std::move(streamer)), buffer_(buffer.get()), root_map_(std::move(root_map)), + clusters_(std::move(clusters)) {} + std::unique_ptr streamer_; + Buffer::Instance& buffer_; + Json::Streamer::MapPtr root_map_; + Json::Streamer::ArrayPtr clusters_; +}; + +/** + * JsonClustersChunkProcessor streams the response and emulates the JSON encoding behavior of + * the gRPC clients; specifically, "zero-value" values such as false, 0, empty string, and + * empty objects are omitted from the output. + */ class JsonClustersChunkProcessor : public ClustersChunkProcessor { public: JsonClustersChunkProcessor(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, diff --git a/source/server/admin/clusters_request.h b/source/server/admin/clusters_request.h index 65b617328105..7f87d989223b 100644 --- a/source/server/admin/clusters_request.h +++ b/source/server/admin/clusters_request.h @@ -13,7 +13,10 @@ namespace Envoy { namespace Server { -// Captures context for a streaming request, implementing the Admin::Request interface. +/** + * ClustersRequest captures context for a streaming request, implementing the + * Admin::Request interface. + */ class ClustersRequest : public Admin::Request { public: static constexpr uint64_t DefaultChunkLimit = 2 << 20; // 2 MB @@ -22,7 +25,6 @@ class ClustersRequest : public Admin::Request { Http::Code start(Http::ResponseHeaderMap& response_headers) override; bool nextChunk(Buffer::Instance& response) override; - private: uint64_t chunk_limit_{DefaultChunkLimit}; Server::Instance& server_; From fdf459a64116d8bac879f57dd23aba52e659014b Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Thu, 2 May 2024 20:44:49 +0000 Subject: [PATCH 47/77] Fix formatting Signed-off-by: Demitri Swan --- source/server/admin/clusters_request.h | 1 + 1 file changed, 1 insertion(+) diff --git a/source/server/admin/clusters_request.h b/source/server/admin/clusters_request.h index 7f87d989223b..5711e5840b8f 100644 --- a/source/server/admin/clusters_request.h +++ b/source/server/admin/clusters_request.h @@ -25,6 +25,7 @@ class ClustersRequest : public Admin::Request { Http::Code start(Http::ResponseHeaderMap& response_headers) override; bool nextChunk(Buffer::Instance& response) override; + private: uint64_t chunk_limit_{DefaultChunkLimit}; Server::Instance& server_; From ccc5f45d076a669fe13f4ab741267661f3301d05 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Sat, 4 May 2024 05:24:12 +0000 Subject: [PATCH 48/77] Fix odd bug in gcc 11.4.0 build Signed-off-by: Demitri Swan --- .../server/admin/clusters_chunk_processor.cc | 32 +++++++++---------- .../server/admin/clusters_chunk_processor.h | 6 ++-- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 85ed589d1372..589d787ec8ec 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -181,7 +181,7 @@ void JsonClustersChunkProcessor::render(std::reference_wrapperclusters_->addMap(); Upstream::ClusterInfoConstSharedPtr cluster_info = cluster.get().info(); - std::vector top_level_entries; + std::vector top_level_entries; if (const std::string& name = cluster_info->name(); !name.empty()) { top_level_entries.emplace_back("name", name); } @@ -228,7 +228,7 @@ void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_sta Buffer::Instance& response) { Buffer::OwnedImpl buffer; Json::Streamer::MapPtr host_ptr = raw_host_statuses_ptr->addMap(); - std::vector host_config; + std::vector host_config; setHostname(host, host_config); addAddress(host_ptr.get(), host, buffer); setLocality(host_ptr.get(), host, buffer); @@ -245,16 +245,14 @@ void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_sta } void JsonClustersChunkProcessor::setHostname( - const Upstream::HostSharedPtr& host, - std::vector& host_config) { + const Upstream::HostSharedPtr& host, std::vector& host_config) { if (const std::string& hostname = host->hostname(); !hostname.empty()) { host_config.emplace_back("hostname", hostname); } } void JsonClustersChunkProcessor::setSuccessRate( - const Upstream::HostSharedPtr& host, - std::vector& host_config) { + const Upstream::HostSharedPtr& host, std::vector& host_config) { double external_success_rate = host->outlierDetector().successRate( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin); @@ -288,7 +286,7 @@ void JsonClustersChunkProcessor::setLocality(Json::Streamer::Map* raw_host_ptr, } raw_host_ptr->addKey("locality"); Json::Streamer::MapPtr locality_ptr = raw_host_ptr->addMap(); - std::vector locality; + std::vector locality; if (const std::string& region = host->locality().region(); !region.empty()) { locality.emplace_back("region", region); } @@ -311,7 +309,7 @@ void JsonClustersChunkProcessor::addAddress(Json::Streamer::Map* raw_host_ptr, Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); address_ptr->addKey("pipe"); Json::Streamer::MapPtr pipe_ptr = address_ptr->addMap(); - std::vector pipe{ + std::vector pipe{ {"pipe", host->address()->asString()}, }; addMapEntries(pipe_ptr.get(), response, pipe); @@ -324,7 +322,7 @@ void JsonClustersChunkProcessor::addAddress(Json::Streamer::Map* raw_host_ptr, Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); address_ptr->addKey("socket_address"); Json::Streamer::MapPtr socket_address_ptr = address_ptr->addMap(); - std::vector socket_address; + std::vector socket_address; if (const std::string& address = host->address()->ip()->addressAsString(); !address.empty()) { socket_address.emplace_back("address", address); } @@ -342,7 +340,7 @@ void JsonClustersChunkProcessor::addAddress(Json::Streamer::Map* raw_host_ptr, Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); raw_host_ptr->addKey("envoy_internal_address"); Json::Streamer::MapPtr envoy_internal_address_ptr = raw_host_ptr->addMap(); - std::vector envoy_internal_address; + std::vector envoy_internal_address; if (const std::string& server_listener_name = host->address()->envoyInternalAddress()->addressId(); !server_listener_name.empty()) { @@ -371,7 +369,7 @@ void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_pt if (counter_name.empty() || counter.get().value() == 0) { continue; } - std::vector counter_object{ + std::vector counter_object{ {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::COUNTER)}}; if (!counter_name.empty()) { @@ -387,7 +385,7 @@ void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_pt if (gauge_name.empty() || gauge.get().value() == 0) { continue; } - std::vector gauge_object{ + std::vector gauge_object{ {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::GAUGE)}}; if (!gauge_name.empty()) { gauge_object.emplace_back("name", gauge_name); @@ -414,7 +412,7 @@ void JsonClustersChunkProcessor::setHealthFlags(Json::Streamer::Map* raw_host_pt } raw_host_ptr->addKey("health_status"); Json::Streamer::MapPtr health_flags_ptr = raw_host_ptr->addMap(); - std::vector flags; + std::vector flags; for (const auto& [name, flag_value] : flag_map) { if (name == "eds_health_status") { if (absl::holds_alternative(flag_value)) { @@ -499,7 +497,7 @@ void JsonClustersChunkProcessor::addEjectionThresholds(Json::Streamer::Map* raw_ if (outlier_detector != nullptr && outlier_detector->successRateEjectionThreshold( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin) > 0.0) { - std::vector success_rate_ejection_threshold{ + std::vector success_rate_ejection_threshold{ {"success_rate_ejection_threshold", double(outlier_detector->successRateEjectionThreshold( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))}, @@ -509,7 +507,7 @@ void JsonClustersChunkProcessor::addEjectionThresholds(Json::Streamer::Map* raw_ if (outlier_detector != nullptr && outlier_detector->successRateEjectionThreshold( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin) > 0.0) { - std::vector local_success_rate_ejection_threshold{ + std::vector local_success_rate_ejection_threshold{ {"local_success_rate_ejection_threshold", double(outlier_detector->successRateEjectionThreshold( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))}, @@ -538,7 +536,7 @@ void JsonClustersChunkProcessor::addCircuitBreakerForPriority( Json::Streamer::Array* raw_thresholds_ptr, Buffer::Instance& response, Upstream::ResourceManager& resource_manager) { Json::Streamer::MapPtr threshold = raw_thresholds_ptr->addMap(); - std::vector entries{ + std::vector entries{ {"priority", priority == envoy::config::core::v3::RoutingPriority::DEFAULT ? "DEFAULT" : "HIGH"}}; if (uint64_t max_connections = resource_manager.connections().max(); max_connections) { @@ -569,7 +567,7 @@ void JsonClustersChunkProcessor::drainBufferIntoResponse(Buffer::Instance& respo void JsonClustersChunkProcessor::addMapEntries( Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, - std::vector& entries) { + std::vector& entries) { raw_map_ptr->addEntries(entries); drainBufferIntoResponse(response); } diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index 862f66095600..3b6f2d09b857 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -89,7 +89,7 @@ class JsonClustersChunkProcessor : public ClustersChunkProcessor { void addAddress(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response); void addMapEntries(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, - std::vector& entries); + std::vector& entries); void addCircuitBreakers(Json::Streamer::Map* raw_clusters_map_ptr, Upstream::ClusterInfoConstSharedPtr cluster_info, Buffer::Instance& response); @@ -112,9 +112,9 @@ class JsonClustersChunkProcessor : public ClustersChunkProcessor { void setLocality(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response); void setSuccessRate(const Upstream::HostSharedPtr& host, - std::vector& top_level_entries); + std::vector& top_level_entries); void setHostname(const Upstream::HostSharedPtr& host, - std::vector& top_level_entries); + std::vector& top_level_entries); void loadHealthFlagMap( absl::btree_map>& flag_map, Upstream::Host::HealthFlag flag, const Upstream::HostSharedPtr& host); From 97690edb7760b6522116ac07685d4419bd5928c9 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Sat, 4 May 2024 07:12:17 +0000 Subject: [PATCH 49/77] Fix some formatting issues Signed-off-by: Demitri Swan --- .../server/admin/clusters_chunk_processor.cc | 63 +++++++++++-------- .../server/admin/clusters_chunk_processor.h | 4 +- test/server/admin/admin_test.cc | 2 +- test/server/admin/clusters_request_test.cc | 48 +++++++++----- 4 files changed, 73 insertions(+), 44 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 589d787ec8ec..7d6ce40fb54e 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -234,7 +234,14 @@ void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_sta setLocality(host_ptr.get(), host, buffer); buildHostStats(host_ptr.get(), host, buffer); setHealthFlags(host_ptr.get(), host, buffer); - setSuccessRate(host, host_config); + setSuccessRate(host_ptr.get(), host, buffer); + + if (uint64_t weight = host->weight(); weight) { + host_config.emplace_back("weight", weight); + } + if (uint64_t priority = host->priority(); priority) { + host_config.emplace_back("priority", priority); + } if (buffer.length()) { response.move(buffer); @@ -251,8 +258,9 @@ void JsonClustersChunkProcessor::setHostname( } } -void JsonClustersChunkProcessor::setSuccessRate( - const Upstream::HostSharedPtr& host, std::vector& host_config) { +void JsonClustersChunkProcessor::setSuccessRate(Json::Streamer::Map* raw_host_statuses_ptr, + const Upstream::HostSharedPtr& host, + Buffer::Instance& response) { double external_success_rate = host->outlierDetector().successRate( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin); @@ -263,16 +271,17 @@ void JsonClustersChunkProcessor::setSuccessRate( } if (external_success_rate >= 0.0) { - host_config.emplace_back("success_rate", external_success_rate); - } - if (uint64_t weight = host->weight(); weight) { - host_config.emplace_back("weight", weight); - } - if (uint64_t priority = host->priority(); priority) { - host_config.emplace_back("priority", priority); + raw_host_statuses_ptr->addKey("success_rate"); + Json::Streamer::MapPtr success_rate_map = raw_host_statuses_ptr->addMap(); + std::vector success_rate{{"value", external_success_rate}}; + addMapEntries(success_rate_map.get(), response, success_rate); } if (local_success_rate >= 0.0) { - host_config.emplace_back("local_origin_success_rate", local_success_rate); + raw_host_statuses_ptr->addKey("local_origin_success_rate"); + Json::Streamer::MapPtr local_origin_map = raw_host_statuses_ptr->addMap(); + std::vector local_origin_success_rate{ + {"value", local_success_rate}}; + addMapEntries(local_origin_map.get(), response, local_origin_success_rate); } } @@ -327,7 +336,7 @@ void JsonClustersChunkProcessor::addAddress(Json::Streamer::Map* raw_host_ptr, socket_address.emplace_back("address", address); } if (uint64_t port = uint64_t(host->address()->ip()->port()); port) { - socket_address.emplace_back("port", port); + socket_address.emplace_back("port_value", port); } addMapEntries(socket_address_ptr.get(), response, socket_address); } @@ -497,22 +506,26 @@ void JsonClustersChunkProcessor::addEjectionThresholds(Json::Streamer::Map* raw_ if (outlier_detector != nullptr && outlier_detector->successRateEjectionThreshold( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin) > 0.0) { - std::vector success_rate_ejection_threshold{ - {"success_rate_ejection_threshold", - double(outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))}, - }; - addMapEntries(raw_clusters_map_ptr, response, success_rate_ejection_threshold); + raw_clusters_map_ptr->addKey("success_rate_ejection_threshold"); + Json::Streamer::MapPtr success_rate_map_ptr = raw_clusters_map_ptr->addMap(); + std::vector success_rate_ejection_threshold; + success_rate_ejection_threshold.emplace_back( + "value", + double(outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))); + addMapEntries(success_rate_map_ptr.get(), response, success_rate_ejection_threshold); } if (outlier_detector != nullptr && outlier_detector->successRateEjectionThreshold( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin) > 0.0) { - std::vector local_success_rate_ejection_threshold{ - {"local_success_rate_ejection_threshold", - double(outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))}, - }; - addMapEntries(raw_clusters_map_ptr, response, local_success_rate_ejection_threshold); + raw_clusters_map_ptr->addKey("local_origin_success_rate_ejection_threshold"); + Json::Streamer::MapPtr local_success_rate_map_ptr = raw_clusters_map_ptr->addMap(); + std::vector local_origin_success_rate_ejection_threshold; + local_origin_success_rate_ejection_threshold.emplace_back( + "value", double(outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))); + addMapEntries(local_success_rate_map_ptr.get(), response, + local_origin_success_rate_ejection_threshold); } } @@ -549,7 +562,7 @@ void JsonClustersChunkProcessor::addCircuitBreakerForPriority( if (uint64_t max_requests = resource_manager.requests().max(); max_requests) { entries.emplace_back("max_requests", max_requests); } - if (uint64_t max_retries = resource_manager.requests().max(); max_retries) { + if (uint64_t max_retries = resource_manager.retries().max(); max_retries) { entries.emplace_back("max_retries", max_retries); } addMapEntries(threshold.get(), response, entries); diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index 3b6f2d09b857..d96bbd0ca26f 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -111,8 +111,8 @@ class JsonClustersChunkProcessor : public ClustersChunkProcessor { Buffer::Instance& response); void setLocality(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, Buffer::Instance& response); - void setSuccessRate(const Upstream::HostSharedPtr& host, - std::vector& top_level_entries); + void setSuccessRate(Json::Streamer::Map* raw_host_statuses_ptr, + const Upstream::HostSharedPtr& host, Buffer::Instance& response); void setHostname(const Upstream::HostSharedPtr& host, std::vector& top_level_entries); void loadHealthFlagMap( diff --git a/test/server/admin/admin_test.cc b/test/server/admin/admin_test.cc index 3c57a8fcd337..f71544bfef1c 100644 --- a/test/server/admin/admin_test.cc +++ b/test/server/admin/admin_test.cc @@ -143,7 +143,7 @@ TEST_P(AdminInstanceTest, Help) { const std::string expected = R"EOF(admin commands are: /: Admin home page /certs: print certs on machine - /clusters: upstream cluster status + /clusters: upstream clusters status format: The output format; One of (text, json) /config_dump: dump current Envoy configs (experimental) resource: The resource to dump diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index 1723a0b83425..5a751cb7c851 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -204,19 +204,23 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { "max_connections": 1024, "max_pending_requests": 1024, "max_requests": 1024, - "max_retries": 1024 + "max_retries": 16 }, { "priority": "HIGH", "max_connections": 4096, "max_pending_requests": 4096, "max_requests": 4096, - "max_retries": 4096 + "max_retries": 16 } ] }, - "success_rate_ejection_threshold": 1.1, - "local_success_rate_ejection_threshold": 1.1, + "success_rate_ejection_threshold": { + "value": 1.1 + }, + "local_origin_success_rate_ejection_threshold": { + "value": 1.1 + }, "name": "test_cluster", "observability_name": "observability_name", "eds_service_name": "potato_launcher", @@ -226,7 +230,7 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { "address": { "socket_address": { "address": "1.2.3.4", - "port": 80 + "port_value": 80 } }, "locality": { @@ -256,11 +260,15 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { "pending_active_hc": true, "pending_dynamic_removal": true }, + "success_rate": { + "value": 1 + }, + "local_origin_success_rate": { + "value": 1 + }, "hostname": "test_hostname", - "success_rate": 1, "weight": 1, - "priority": 1, - "local_origin_success_rate": 1 + "priority": 1 } ] }, @@ -272,19 +280,23 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { "max_connections": 1024, "max_pending_requests": 1024, "max_requests": 1024, - "max_retries": 1024 + "max_retries": 16 }, { "priority": "HIGH", "max_connections": 4096, "max_pending_requests": 4096, "max_requests": 4096, - "max_retries": 4096 + "max_retries": 16 } ] }, - "success_rate_ejection_threshold": 1.1, - "local_success_rate_ejection_threshold": 1.1, + "success_rate_ejection_threshold": { + "value": 1.1 + }, + "local_origin_success_rate_ejection_threshold": { + "value": 1.1 + }, "name": "test_cluster", "observability_name": "observability_name", "eds_service_name": "potato_launcher", @@ -294,7 +306,7 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { "address": { "socket_address": { "address": "1.2.3.4", - "port": 80 + "port_value": 80 } }, "locality": { @@ -324,11 +336,15 @@ TEST_P(VerifyJsonOutputFixture, VerifyJsonOutput) { "pending_active_hc": true, "pending_dynamic_removal": true }, + "success_rate": { + "value": 1 + }, + "local_origin_success_rate": { + "value": 1 + }, "hostname": "test_hostname", - "success_rate": 1, "weight": 1, - "priority": 1, - "local_origin_success_rate": 1 + "priority": 1 } ] } From bbba34863ddadde2891495fb2b76edc2d053e3e7 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Sat, 4 May 2024 08:02:32 +0000 Subject: [PATCH 50/77] Remove unused import Signed-off-by: Demitri Swan --- source/server/admin/clusters_request.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/server/admin/clusters_request.cc b/source/server/admin/clusters_request.cc index bf1ed8665245..84f3d2317ba4 100644 --- a/source/server/admin/clusters_request.cc +++ b/source/server/admin/clusters_request.cc @@ -1,7 +1,6 @@ #include "source/server/admin/clusters_request.h" #include -#include #include #include "envoy/server/instance.h" From c0dc33f1a878c276309f88635931326174f948fa Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Sat, 4 May 2024 21:19:29 +0000 Subject: [PATCH 51/77] Set text content type and prevent unnecessary fallthrough in switch statement Signed-off-by: Demitri Swan --- source/server/admin/clusters_chunk_processor.cc | 1 + source/server/admin/clusters_request.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 7d6ce40fb54e..8a2e5a7b1145 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -465,6 +465,7 @@ void JsonClustersChunkProcessor::loadHealthFlagMap( flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name( envoy::config::core::v3::HEALTHY)); } + break; case Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC: if (bool value = host.get()->healthFlagGet(flag); value) { flag_map.insert_or_assign("failed_active_degraded_check", value); diff --git a/source/server/admin/clusters_request.cc b/source/server/admin/clusters_request.cc index 84f3d2317ba4..8bbccb26222d 100644 --- a/source/server/admin/clusters_request.cc +++ b/source/server/admin/clusters_request.cc @@ -23,6 +23,7 @@ Http::Code ClustersRequest::start(Http::ResponseHeaderMap& response_headers) { case ClustersParams::Format::Text: chunk_processor_ = std::make_unique( chunk_limit_, response_headers, server_.clusterManager().clusters().active_clusters_); + response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Text); break; case ClustersParams::Format::Json: chunk_processor_ = std::make_unique( From 6a9314644531edf4945953fb932c182280595951 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Mon, 6 May 2024 05:47:18 +0000 Subject: [PATCH 52/77] Allow default content type for text processing Signed-off-by: Demitri Swan --- source/server/admin/clusters_chunk_processor.cc | 1 + source/server/admin/clusters_request.cc | 2 -- test/server/admin/BUILD | 15 +++++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 8a2e5a7b1145..6235a81ceead 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -2,6 +2,7 @@ #include "source/server/admin/clusters_chunk_processor.h" #include +#include #include #include #include diff --git a/source/server/admin/clusters_request.cc b/source/server/admin/clusters_request.cc index 8bbccb26222d..2c67be5c22a1 100644 --- a/source/server/admin/clusters_request.cc +++ b/source/server/admin/clusters_request.cc @@ -23,7 +23,6 @@ Http::Code ClustersRequest::start(Http::ResponseHeaderMap& response_headers) { case ClustersParams::Format::Text: chunk_processor_ = std::make_unique( chunk_limit_, response_headers, server_.clusterManager().clusters().active_clusters_); - response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Text); break; case ClustersParams::Format::Json: chunk_processor_ = std::make_unique( @@ -31,7 +30,6 @@ Http::Code ClustersRequest::start(Http::ResponseHeaderMap& response_headers) { response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); break; default: - // TODO(demitriswan) handle this case properly. return Http::Code::BadRequest; } return Http::Code::OK; diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index 0dd826e09813..9086d3e12a8e 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -290,3 +290,18 @@ envoy_cc_test( "//test/test_common:utility_lib", ], ) + +envoy_cc_test( + name = "clusters_chunk_processor_test", + srcs = envoy_select_admin_functionality(["clusters_chunk_processor_test.cc"], + deps = [ + "//source/common/upstream:reosurce_manager_lib", + "//envoy/upstream:outlier_detection_interface", + "//envoy/upstream:resource_manager_interface", + "//envoy/stats:primitive_stats_interface", + "//test/mocks/upstream:cluster_mocks", + "//test/mocks/upstream:cluster_info_mocks", + "//test/mocks/upstream:host_mocks", + "@com_google_absl//absl/strings", + ], +) \ No newline at end of file From 65574bda5f23a1e1bd66703ce3ff92b06012eceb Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Mon, 6 May 2024 05:54:12 +0000 Subject: [PATCH 53/77] Remove build target that is not yet used Signed-off-by: Demitri Swan --- test/server/admin/BUILD | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index 9086d3e12a8e..0dd826e09813 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -290,18 +290,3 @@ envoy_cc_test( "//test/test_common:utility_lib", ], ) - -envoy_cc_test( - name = "clusters_chunk_processor_test", - srcs = envoy_select_admin_functionality(["clusters_chunk_processor_test.cc"], - deps = [ - "//source/common/upstream:reosurce_manager_lib", - "//envoy/upstream:outlier_detection_interface", - "//envoy/upstream:resource_manager_interface", - "//envoy/stats:primitive_stats_interface", - "//test/mocks/upstream:cluster_mocks", - "//test/mocks/upstream:cluster_info_mocks", - "//test/mocks/upstream:host_mocks", - "@com_google_absl//absl/strings", - ], -) \ No newline at end of file From c1a9934f82a104a40e5e16d1e0b3a97981f28db3 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Mon, 6 May 2024 06:44:57 +0000 Subject: [PATCH 54/77] Remove unused import Signed-off-by: Demitri Swan --- source/server/admin/clusters_chunk_processor.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 6235a81ceead..8a2e5a7b1145 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -2,7 +2,6 @@ #include "source/server/admin/clusters_chunk_processor.h" #include -#include #include #include #include From a7e4a781da418118837d32f18dfd46cd4f0cfafb Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 8 May 2024 16:42:14 +0000 Subject: [PATCH 55/77] Remove declaration from when member functions where static functions Signed-off-by: Demitri Swan --- source/server/admin/clusters_chunk_processor.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 8a2e5a7b1145..ea7ee3a17f3b 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -22,9 +22,6 @@ namespace Envoy { namespace Server { -void setHealthFlag(Json::Streamer::Map* raw_host_ptr, Upstream::Host::HealthFlag flag, - const Upstream::Host& host); - TextClustersChunkProcessor::TextClustersChunkProcessor( uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map) From 3175c3952044babed02dceeea368f9d4f6887c43 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 8 May 2024 16:43:46 +0000 Subject: [PATCH 56/77] Remove redundant ternary expression Signed-off-by: Demitri Swan --- source/server/admin/clusters_chunk_processor.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index ea7ee3a17f3b..3cd81e19fcc1 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -40,7 +40,7 @@ bool TextClustersChunkProcessor::nextChunk(Buffer::Instance& response) { } // TODO(demitriswan) See if these need to be updated here. UNREFERENCED_PARAMETER(response_headers_); - return idx_ < clusters_.size() ? true : false; + return idx_ < clusters_.size(); } void TextClustersChunkProcessor::render(std::reference_wrapper cluster, From bae07818e7b6dd42ba8daae4452aa2d0c6fb07b6 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 8 May 2024 17:26:02 +0000 Subject: [PATCH 57/77] Removing designated initialization since C++20 is not yet supported Signed-off-by: Demitri Swan --- source/server/admin/admin.cc | 20 +------------------- source/server/admin/clusters_handler.cc | 21 ++++++++++++++++++++- source/server/admin/clusters_handler.h | 5 +++-- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index 2181c651055e..04ddc2c2799b 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -128,25 +128,7 @@ AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server, makeHandler("/", "Admin home page", MAKE_ADMIN_HANDLER(handlerAdminHome), false, false), makeHandler("/certs", "print certs on machine", MAKE_ADMIN_HANDLER(server_info_handler_.handlerCerts), false, false), - { - // Using designated initialization to improve readability of request routing - // configuration. See - // https://en.cppreference.com/w/cpp/language/aggregate_initialization#Designated_initializers. - .prefix_ = "/clusters", - .help_text_ = "upstream clusters status", - .handler_ = MAKE_STREAMING_HANDLER(clusters_handler_.makeRequest), - .removable_ = false, - .mutates_server_state_ = false, - .params_ = - { - { - .type_ = Admin::ParamDescriptor::Type::Enum, - .id_ = "format", - .help_ = "The output format", - .enum_choices_ = {"text", "json"}, - }, - }, - }, + clusters_handler_.urlHandler(), makeHandler( "/config_dump", "dump current Envoy configs (experimental)", MAKE_ADMIN_HANDLER(config_dump_handler_.handlerConfigDump), false, false, diff --git a/source/server/admin/clusters_handler.cc b/source/server/admin/clusters_handler.cc index f508ac68c970..47ca88acc104 100644 --- a/source/server/admin/clusters_handler.cc +++ b/source/server/admin/clusters_handler.cc @@ -1,6 +1,7 @@ #include "source/server/admin/clusters_handler.h" #include "envoy/admin/v3/clusters.pb.h" +#include "envoy/server/admin.h" #include "source/common/buffer/buffer_impl.h" #include "source/common/common/macros.h" @@ -44,6 +45,25 @@ void addCircuitBreakerSettingsAsJson(const envoy::config::core::v3::RoutingPrior } // namespace +Admin::UrlHandler ClustersHandler::urlHandler() { + return { + /* prefix =*/"/clusters", + /* help_text =*/"upstream clusters status", + /* handler =*/MAKE_STREAMING_HANDLER(makeRequest), + /* removable =*/false, + /* mutates_server_state =*/false, + /* params =*/ + { + { + /* type =*/Admin::ParamDescriptor::Type::Enum, + /* id =*/"format", + /* help =*/"The output format", + /* enum_choices =*/{"text", "json"}, + }, + }, + }; +} + ClustersHandler::ClustersHandler(Server::Instance& server) : HandlerContextBase(server) {} Http::Code ClustersHandler::handlerClusters(Http::ResponseHeaderMap& response_headers, @@ -60,7 +80,6 @@ Http::Code ClustersHandler::handlerClusters(Http::ResponseHeaderMap& response_he return Http::Code::OK; } -// TODO(demitriswan) Implement this member function. Admin::RequestPtr ClustersHandler::makeRequest(AdminStream& admin_stream) { Buffer::OwnedImpl response; ClustersParams params; diff --git a/source/server/admin/clusters_handler.h b/source/server/admin/clusters_handler.h index c6feacbd5f89..fdffcfdab6a6 100644 --- a/source/server/admin/clusters_handler.h +++ b/source/server/admin/clusters_handler.h @@ -31,9 +31,10 @@ class ClustersHandler : public HandlerContextBase { Http::Code handlerClusters(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream&); - // TODO(demitriswan) Document this member function. + // Returns a RequestPtr implementation suitable for streaminmg its repsonse. Admin::RequestPtr makeRequest(AdminStream& admin_stream); - + // Returns a UrlHandler suitable for AdminImpl. + Admin::UrlHandler urlHandler(); private: void addOutlierInfo(const std::string& cluster_name, const Upstream::Outlier::Detector* outlier_detector, From c7f4ece02a590d8ad7b206ada23b06956d3e0a0e Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 8 May 2024 17:33:27 +0000 Subject: [PATCH 58/77] Using a vector and sorting instead of std::map Signed-off-by: Demitri Swan --- source/server/admin/clusters_chunk_processor.cc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 3cd81e19fcc1..07973805a08c 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -1,6 +1,7 @@ #include "clusters_chunk_processor.h" #include "source/server/admin/clusters_chunk_processor.h" +#include #include #include #include @@ -66,15 +67,19 @@ void TextClustersChunkProcessor::render(std::reference_wrapperhosts()) { const std::string& host_address = host->address()->asString(); - std::map all_stats; + std::vector> all_stats; for (const auto& [counter_name, counter] : host->counters()) { - all_stats[counter_name] = counter.get().value(); + all_stats.emplace_back(counter_name, counter.get().value()); } for (const auto& [gauge_name, gauge] : host->gauges()) { - all_stats[gauge_name] = gauge.get().value(); + all_stats.emplace_back(gauge_name, gauge.get().value()); } + std::sort(all_stats.begin(), all_stats.end(), [](const std::pair& a, const std::pair& b) { + return a.first < b.first; + }); + for (const auto& [stat_name, stat] : all_stats) { response.add(fmt::format("{}::{}::{}::{}\n", cluster_name, host_address, stat_name, stat)); } From 12347d21891bee17844b2eed844309faa7aef6e6 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 8 May 2024 17:35:31 +0000 Subject: [PATCH 59/77] Formatting fix Signed-off-by: Demitri Swan --- source/server/admin/clusters_chunk_processor.cc | 6 +++--- source/server/admin/clusters_handler.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index 07973805a08c..b35f850398ed 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -76,9 +76,9 @@ void TextClustersChunkProcessor::render(std::reference_wrapper& a, const std::pair& b) { - return a.first < b.first; - }); + std::sort(all_stats.begin(), all_stats.end(), + [](const std::pair& a, + const std::pair& b) { return a.first < b.first; }); for (const auto& [stat_name, stat] : all_stats) { response.add(fmt::format("{}::{}::{}::{}\n", cluster_name, host_address, stat_name, stat)); diff --git a/source/server/admin/clusters_handler.h b/source/server/admin/clusters_handler.h index fdffcfdab6a6..e19c345a16c8 100644 --- a/source/server/admin/clusters_handler.h +++ b/source/server/admin/clusters_handler.h @@ -35,6 +35,7 @@ class ClustersHandler : public HandlerContextBase { Admin::RequestPtr makeRequest(AdminStream& admin_stream); // Returns a UrlHandler suitable for AdminImpl. Admin::UrlHandler urlHandler(); + private: void addOutlierInfo(const std::string& cluster_name, const Upstream::Outlier::Detector* outlier_detector, From d5bc39b462360eebdf6083990a776988cf3c60a3 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 8 May 2024 17:42:35 +0000 Subject: [PATCH 60/77] Use structured bindings in loop Signed-off-by: Demitri Swan --- source/server/admin/clusters_chunk_processor.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc index b35f850398ed..5d13886ebf6f 100644 --- a/source/server/admin/clusters_chunk_processor.cc +++ b/source/server/admin/clusters_chunk_processor.cc @@ -27,8 +27,8 @@ TextClustersChunkProcessor::TextClustersChunkProcessor( uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map) : chunk_limit_(chunk_limit), response_headers_(response_headers), idx_(0) { - for (const auto& pair : cluster_info_map) { - clusters_.push_back(pair.second); + for (const auto& [str, reference_wrapped_const_cluster] : cluster_info_map) { + clusters_.push_back(reference_wrapped_const_cluster); } } From 3bcfdf705f755803a827c6c6a5cf7a32e74222cd Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 8 May 2024 17:44:47 +0000 Subject: [PATCH 61/77] Add TODO comment for adding designated initialization in handler config when C++20 is supported Signed-off-by: Demitri Swan --- source/server/admin/admin.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index 04ddc2c2799b..c1dd50087d7b 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -124,6 +124,8 @@ AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server, runtime_handler_(server), listeners_handler_(server), server_cmd_handler_(server), server_info_handler_(server), // TODO(jsedgwick) add /runtime_reset endpoint that removes all admin-set values + // TODO(demitriswan) When C++20 is supported we might want to use designated initialization + // for readability. handlers_{ makeHandler("/", "Admin home page", MAKE_ADMIN_HANDLER(handlerAdminHome), false, false), makeHandler("/certs", "print certs on machine", From 9c95663abfbe778ee90fa5da7976f5f4ac6297a0 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 8 May 2024 18:32:15 +0000 Subject: [PATCH 62/77] Fix spelling Signed-off-by: Demitri Swan --- source/server/admin/clusters_handler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/server/admin/clusters_handler.h b/source/server/admin/clusters_handler.h index e19c345a16c8..7e9412ead200 100644 --- a/source/server/admin/clusters_handler.h +++ b/source/server/admin/clusters_handler.h @@ -31,7 +31,7 @@ class ClustersHandler : public HandlerContextBase { Http::Code handlerClusters(Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream&); - // Returns a RequestPtr implementation suitable for streaminmg its repsonse. + // Returns a RequestPtr implementation suitable for streaming its response. Admin::RequestPtr makeRequest(AdminStream& admin_stream); // Returns a UrlHandler suitable for AdminImpl. Admin::UrlHandler urlHandler(); From c096e71a4982bde3bce9406a18a63774cfa6bc60 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Fri, 10 May 2024 18:58:29 +0000 Subject: [PATCH 63/77] Wrapped reference to reference Signed-off-by: Demitri Swan --- source/server/admin/clusters_chunk_processor.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h index d96bbd0ca26f..d2fd9b53ca7c 100644 --- a/source/server/admin/clusters_chunk_processor.h +++ b/source/server/admin/clusters_chunk_processor.h @@ -60,10 +60,9 @@ class TextClustersChunkProcessor : public ClustersChunkProcessor { * herein. See the Envoy::Json::Streamer implementation for details. */ struct ClustersJsonContext { - ClustersJsonContext(std::unique_ptr streamer, - std::reference_wrapper buffer, + ClustersJsonContext(std::unique_ptr streamer, Buffer::Instance& buffer, Json::Streamer::MapPtr root_map, Json::Streamer::ArrayPtr clusters) - : streamer_(std::move(streamer)), buffer_(buffer.get()), root_map_(std::move(root_map)), + : streamer_(std::move(streamer)), buffer_(buffer), root_map_(std::move(root_map)), clusters_(std::move(clusters)) {} std::unique_ptr streamer_; Buffer::Instance& buffer_; From 188aa47050dfedd3af93a14b9e36979340ca6e59 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Sat, 11 May 2024 00:37:27 +0000 Subject: [PATCH 64/77] Adding anonymous namespace for the unit tests Signed-off-by: Demitri Swan --- test/server/admin/clusters_params_test.cc | 2 ++ test/server/admin/clusters_request_test.cc | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test/server/admin/clusters_params_test.cc b/test/server/admin/clusters_params_test.cc index 9676cf4ff9aa..ccd7d860e5d9 100644 --- a/test/server/admin/clusters_params_test.cc +++ b/test/server/admin/clusters_params_test.cc @@ -5,6 +5,7 @@ namespace Envoy { namespace Server { +namespace { struct ParamsCase { std::string url_; @@ -37,5 +38,6 @@ INSTANTIATE_TEST_SUITE_P( {"localhost:1337/clusters?format=json", ClustersParams::Format::Json, Http::Code::OK}, })); +} // namespace } // namespace Server } // namespace Envoy diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index 5a751cb7c851..09375a65e0e5 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -22,6 +22,7 @@ namespace Envoy { namespace Server { +namespace { using testing::Const; using testing::NiceMock; @@ -488,5 +489,6 @@ TEST(Json, VerifyArrayPtrDestructionTerminatesJsonArray) { EXPECT_EQ(request_buffer.toString(), "[1,2]"); } +} // namespace } // namespace Server } // namespace Envoy From 63edb28a06a9c253ccebf2158616a47180f2412e Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Fri, 24 May 2024 21:34:44 +0000 Subject: [PATCH 65/77] Return Http::Code::BadRequest if format query parameter is invalid Signed-off-by: Demitri Swan --- source/server/admin/clusters_params.cc | 8 +++++--- source/server/admin/clusters_params.h | 1 + test/server/admin/clusters_params_test.cc | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/source/server/admin/clusters_params.cc b/source/server/admin/clusters_params.cc index 866c49645965..2e574afabd20 100644 --- a/source/server/admin/clusters_params.cc +++ b/source/server/admin/clusters_params.cc @@ -10,16 +10,18 @@ namespace Envoy { namespace Server { Http::Code ClustersParams::parse(absl::string_view url, Buffer::Instance& response) { - UNREFERENCED_PARAMETER(response); - Http::Utility::QueryParamsMulti query = Http::Utility::QueryParamsMulti::parseAndDecodeQueryString(url); absl::optional optional_format = query.getFirstValue("format"); if (optional_format.has_value()) { if (*optional_format == "json") { format_ = Format::Json; - } else { + } else if (*optional_format == "text") { format_ = Format::Text; + } else { + response.addFragments({"invalid format ", *optional_format}); + format_ = Format::Unknown; + return Http::Code::BadRequest; } } return Http::Code::OK; diff --git a/source/server/admin/clusters_params.h b/source/server/admin/clusters_params.h index ae299cfeabfd..1e7e638c60c7 100644 --- a/source/server/admin/clusters_params.h +++ b/source/server/admin/clusters_params.h @@ -10,6 +10,7 @@ namespace Server { struct ClustersParams { enum class Format { + Unknown, Text, Json, }; diff --git a/test/server/admin/clusters_params_test.cc b/test/server/admin/clusters_params_test.cc index ccd7d860e5d9..8a010dc2bcfe 100644 --- a/test/server/admin/clusters_params_test.cc +++ b/test/server/admin/clusters_params_test.cc @@ -36,6 +36,7 @@ INSTANTIATE_TEST_SUITE_P( {"localhost:1337/clusters", ClustersParams::Format::Text, Http::Code::OK}, {"localhsot:1337/clusters?format=text", ClustersParams::Format::Text, Http::Code::OK}, {"localhost:1337/clusters?format=json", ClustersParams::Format::Json, Http::Code::OK}, + {"localhost:1337/clusters?format=fail", ClustersParams::Format::Unknown, Http::Code::BadRequest}, })); } // namespace From 1aaabac059a6ae380f205f6cb6f031bd9b5a17de Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Fri, 24 May 2024 21:43:50 +0000 Subject: [PATCH 66/77] Update unit tests for ClustersParams Signed-off-by: Demitri Swan --- test/server/admin/clusters_params_test.cc | 59 ++++++++++++++++++----- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/test/server/admin/clusters_params_test.cc b/test/server/admin/clusters_params_test.cc index 8a010dc2bcfe..ee25a6b36cc0 100644 --- a/test/server/admin/clusters_params_test.cc +++ b/test/server/admin/clusters_params_test.cc @@ -20,24 +20,57 @@ TEST(ClustersParamsTest, ClustersParamsHasExpectedDefaultValue) { EXPECT_EQ(params.format_, ClustersParams::Format::Text); } -TEST_P(ParamsFixture, ClustersParamsHasExpectedFormatAndStatusCode) { +TEST(ClustersParamsTest, FormatDefaultsToTextWhenNotSupplied) { ClustersParams params; Buffer::OwnedImpl buffer; - ParamsCase test_case = GetParam(); - Http::Code code = params.parse(test_case.url_, buffer); + std::string url = "localhost:1337/clusters"; + ClustersParams::Format expected_format = ClustersParams::Format::Text; + Http::Code expected_code = Http::Code::OK; - EXPECT_EQ(params.format_, test_case.expected_format_); - EXPECT_EQ(code, test_case.expected_code_); + Http::Code code = params.parse(url, buffer); + + EXPECT_EQ(params.format_, expected_format); + EXPECT_EQ(code, expected_code); +} + +TEST(ClustersParamsTest, FormatSetToTextWhenTextSupplied) { + ClustersParams params; + Buffer::OwnedImpl buffer; + std::string url = "localhost:1337/clusters?format=text"; + ClustersParams::Format expected_format = ClustersParams::Format::Text; + Http::Code expected_code = Http::Code::OK; + + Http::Code code = params.parse(url, buffer); + + EXPECT_EQ(params.format_, expected_format); + EXPECT_EQ(code, expected_code); +} + +TEST(ClustersParamsTest, FormatSetToJsonWhenJsonSupplied) { + ClustersParams params; + Buffer::OwnedImpl buffer; + std::string url = "localhost:1337/clusters?format=json"; + ClustersParams::Format expected_format = ClustersParams::Format::Json; + Http::Code expected_code = Http::Code::OK; + + Http::Code code = params.parse(url, buffer); + + EXPECT_EQ(params.format_, expected_format); + EXPECT_EQ(code, expected_code); } -INSTANTIATE_TEST_SUITE_P( - AllCases, ParamsFixture, - testing::ValuesIn({ - {"localhost:1337/clusters", ClustersParams::Format::Text, Http::Code::OK}, - {"localhsot:1337/clusters?format=text", ClustersParams::Format::Text, Http::Code::OK}, - {"localhost:1337/clusters?format=json", ClustersParams::Format::Json, Http::Code::OK}, - {"localhost:1337/clusters?format=fail", ClustersParams::Format::Unknown, Http::Code::BadRequest}, - })); +TEST(ClustersParamsTest, FormatSetToUnknownWhenInvalidFormatSupplied) { + ClustersParams params; + Buffer::OwnedImpl buffer; + std::string url = "localhost:1337/clusters?format=fail"; + ClustersParams::Format expected_format = ClustersParams::Format::Unknown; + Http::Code expected_code = Http::Code::BadRequest; + + Http::Code code = params.parse(url, buffer); + + EXPECT_EQ(params.format_, expected_format); + EXPECT_EQ(code, expected_code); +} } // namespace } // namespace Server From db88b19265c5c84aa14c1e3c252178dd334df5e5 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Fri, 24 May 2024 22:56:09 +0000 Subject: [PATCH 67/77] Updating ClustersHandler::makeRequest to return text response when query param parsing fails Signed-off-by: Demitri Swan --- source/server/admin/clusters_handler.cc | 5 ++++- test/server/admin/clusters_params_test.cc | 5 ----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/source/server/admin/clusters_handler.cc b/source/server/admin/clusters_handler.cc index 47ca88acc104..2d6046147f81 100644 --- a/source/server/admin/clusters_handler.cc +++ b/source/server/admin/clusters_handler.cc @@ -83,7 +83,10 @@ Http::Code ClustersHandler::handlerClusters(Http::ResponseHeaderMap& response_he Admin::RequestPtr ClustersHandler::makeRequest(AdminStream& admin_stream) { Buffer::OwnedImpl response; ClustersParams params; - params.parse(admin_stream.getRequestHeaders().getPathValue(), response); + Http::Code code = params.parse(admin_stream.getRequestHeaders().getPathValue(), response); + if (code != Http::Code::OK) { + return Admin::makeStaticTextRequest(response, code); + } return std::make_unique(ClustersRequest::DefaultChunkLimit, server_, params); } diff --git a/test/server/admin/clusters_params_test.cc b/test/server/admin/clusters_params_test.cc index ee25a6b36cc0..326f2f0ec999 100644 --- a/test/server/admin/clusters_params_test.cc +++ b/test/server/admin/clusters_params_test.cc @@ -15,11 +15,6 @@ struct ParamsCase { class ParamsFixture : public testing::TestWithParam {}; -TEST(ClustersParamsTest, ClustersParamsHasExpectedDefaultValue) { - ClustersParams params; - EXPECT_EQ(params.format_, ClustersParams::Format::Text); -} - TEST(ClustersParamsTest, FormatDefaultsToTextWhenNotSupplied) { ClustersParams params; Buffer::OwnedImpl buffer; From 8c5790c1c34050441d6f481852538d05cfd9ab97 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Sat, 25 May 2024 03:59:16 +0000 Subject: [PATCH 68/77] Implement nextChunk within sub classes of Request Signed-off-by: Demitri Swan --- source/server/admin/clusters_handler.cc | 11 +- source/server/admin/clusters_request.cc | 588 ++++++++++++++++++++- source/server/admin/clusters_request.h | 78 ++- test/server/admin/clusters_request_test.cc | 9 +- 4 files changed, 668 insertions(+), 18 deletions(-) diff --git a/source/server/admin/clusters_handler.cc b/source/server/admin/clusters_handler.cc index 2d6046147f81..9c819a48d343 100644 --- a/source/server/admin/clusters_handler.cc +++ b/source/server/admin/clusters_handler.cc @@ -87,7 +87,16 @@ Admin::RequestPtr ClustersHandler::makeRequest(AdminStream& admin_stream) { if (code != Http::Code::OK) { return Admin::makeStaticTextRequest(response, code); } - return std::make_unique(ClustersRequest::DefaultChunkLimit, server_, params); + switch (params.format_) { + case ClustersParams::Format::Text: + return std::make_unique(ClustersRequest::DefaultChunkLimit, server_, + params); + case ClustersParams::Format::Json: + return std::make_unique(ClustersRequest::DefaultChunkLimit, server_, + params); + default: + return Admin::makeStaticTextRequest("unknown format type", Http::Code::BadRequest); + } } // Helper method that ensures that we've setting flags based on all the health flag values on the diff --git a/source/server/admin/clusters_request.cc b/source/server/admin/clusters_request.cc index 2c67be5c22a1..7583080e2cff 100644 --- a/source/server/admin/clusters_request.cc +++ b/source/server/admin/clusters_request.cc @@ -7,6 +7,7 @@ #include "source/common/common/logger.h" #include "source/common/http/headers.h" +#include "source/common/upstream/host_utility.h" #include "source/server/admin/clusters_chunk_processor.h" #include "clusters_params.h" @@ -16,27 +17,586 @@ namespace Server { ClustersRequest::ClustersRequest(uint64_t chunk_limit, Instance& server, const ClustersParams& params) - : chunk_limit_(chunk_limit), server_(server), params_(params) {} + : chunk_limit_(chunk_limit), server_(server), params_(params) { + for (const auto& [str, reference_wrapped_const_cluster] : + server_.clusterManager().clusters().active_clusters_) { + clusters_.push_back(reference_wrapped_const_cluster); + } +} Http::Code ClustersRequest::start(Http::ResponseHeaderMap& response_headers) { - switch (params_.format_) { - case ClustersParams::Format::Text: - chunk_processor_ = std::make_unique( - chunk_limit_, response_headers, server_.clusterManager().clusters().active_clusters_); + UNREFERENCED_PARAMETER(response_headers); + return Http::Code::OK; +} + +bool ClustersRequest::nextChunk(Buffer::Instance& response) { + UNREFERENCED_PARAMETER(response); + return false; +} + +TextClustersRequest::TextClustersRequest(uint64_t chunk_limit, Instance& server, + const ClustersParams& params) + : ClustersRequest(chunk_limit, server, params), idx_{0} {} + +bool TextClustersRequest::nextChunk(Buffer::Instance& response) { + const uint64_t original_request_size = response.length(); + for (; idx_ < clusters_.size() && response.length() - original_request_size < chunk_limit_; + idx_++) { + + render(clusters_[idx_], response); + } + return idx_ < clusters_.size(); +} + +void TextClustersRequest::render(std::reference_wrapper cluster, + Buffer::Instance& response) { + const Upstream::Cluster& unwrapped_cluster = cluster.get(); + const std::string& cluster_name = unwrapped_cluster.info()->name(); + response.add(fmt::format("{}::observability_name::{}\n", cluster_name, + unwrapped_cluster.info()->observabilityName())); + addOutlierInfo(cluster_name, unwrapped_cluster.outlierDetector(), response); + + addCircuitBreakerSettings( + cluster_name, "default", + unwrapped_cluster.info()->resourceManager(Upstream::ResourcePriority::Default), response); + addCircuitBreakerSettings( + cluster_name, "high", + unwrapped_cluster.info()->resourceManager(Upstream::ResourcePriority::High), response); + + response.add(fmt::format("{}::added_via_api::{}\n", cluster_name, + unwrapped_cluster.info()->addedViaApi())); + if (const auto& name = unwrapped_cluster.info()->edsServiceName(); !name.empty()) { + response.add(fmt::format("{}::eds_service_name::{}\n", cluster_name, name)); + } + for (auto& host_set : unwrapped_cluster.prioritySet().hostSetsPerPriority()) { + for (auto& host : host_set->hosts()) { + const std::string& host_address = host->address()->asString(); + std::vector> all_stats; + for (const auto& [counter_name, counter] : host->counters()) { + all_stats.emplace_back(counter_name, counter.get().value()); + } + + for (const auto& [gauge_name, gauge] : host->gauges()) { + all_stats.emplace_back(gauge_name, gauge.get().value()); + } + + std::sort(all_stats.begin(), all_stats.end(), + [](const std::pair& a, + const std::pair& b) { return a.first < b.first; }); + + for (const auto& [stat_name, stat] : all_stats) { + response.add(fmt::format("{}::{}::{}::{}\n", cluster_name, host_address, stat_name, stat)); + } + + response.add( + fmt::format("{}::{}::hostname::{}\n", cluster_name, host_address, host->hostname())); + response.add(fmt::format("{}::{}::health_flags::{}\n", cluster_name, host_address, + Upstream::HostUtility::healthFlagsToString(*host))); + response.add(fmt::format("{}::{}::weight::{}\n", cluster_name, host_address, host->weight())); + response.add(fmt::format("{}::{}::region::{}\n", cluster_name, host_address, + host->locality().region())); + response.add( + fmt::format("{}::{}::zone::{}\n", cluster_name, host_address, host->locality().zone())); + response.add(fmt::format("{}::{}::sub_zone::{}\n", cluster_name, host_address, + host->locality().sub_zone())); + response.add(fmt::format("{}::{}::canary::{}\n", cluster_name, host_address, host->canary())); + response.add( + fmt::format("{}::{}::priority::{}\n", cluster_name, host_address, host->priority())); + response.add(fmt::format( + "{}::{}::success_rate::{}\n", cluster_name, host_address, + host->outlierDetector().successRate( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))); + response.add(fmt::format( + "{}::{}::local_origin_success_rate::{}\n", cluster_name, host_address, + host->outlierDetector().successRate( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))); + } + } +} + +void TextClustersRequest::addOutlierInfo(const std::string& cluster_name, + const Upstream::Outlier::Detector* outlier_detector, + Buffer::Instance& response) { + if (outlier_detector) { + response.add(fmt::format( + "{}::outlier::success_rate_average::{:g}\n", cluster_name, + outlier_detector->successRateAverage( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))); + response.add(fmt::format( + "{}::outlier::success_rate_ejection_threshold::{:g}\n", cluster_name, + outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))); + response.add(fmt::format( + "{}::outlier::local_origin_success_rate_average::{:g}\n", cluster_name, + outlier_detector->successRateAverage( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))); + response.add(fmt::format( + "{}::outlier::local_origin_success_rate_ejection_threshold::{:g}\n", cluster_name, + outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))); + } +} + +void TextClustersRequest::addCircuitBreakerSettings(const std::string& cluster_name, + const std::string& priority_str, + Upstream::ResourceManager& resource_manager, + Buffer::Instance& response) { + response.add(fmt::format("{}::{}_priority::max_connections::{}\n", cluster_name, priority_str, + resource_manager.connections().max())); + response.add(fmt::format("{}::{}_priority::max_pending_requests::{}\n", cluster_name, + priority_str, resource_manager.pendingRequests().max())); + response.add(fmt::format("{}::{}_priority::max_requests::{}\n", cluster_name, priority_str, + resource_manager.requests().max())); + response.add(fmt::format("{}::{}_priority::max_retries::{}\n", cluster_name, priority_str, + resource_manager.retries().max())); +} + +JsonClustersRequest::JsonClustersRequest(uint64_t chunk_limit, Instance& server, + const ClustersParams& params) + : ClustersRequest(chunk_limit, server, params), idx_(0) { + std::unique_ptr streamer = std::make_unique(buffer_); + Json::Streamer::MapPtr root_map = streamer->makeRootMap(); + root_map->addKey("cluster_statuses"); + Json::Streamer::ArrayPtr clusters = root_map->addArray(); + json_context_holder_.push_back(std::make_unique( + std::move(streamer), buffer_, std::move(root_map), std::move(clusters))); +} + +Http::Code JsonClustersRequest::start(Http::ResponseHeaderMap& response_headers) { + response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); + return Http::Code::OK; +} + +bool JsonClustersRequest::nextChunk(Buffer::Instance& response) { + const uint64_t original_request_size = response.length(); + for (; idx_ < clusters_.size() && response.length() - original_request_size < chunk_limit_; + idx_++) { + + render(clusters_[idx_], response); + } + if (idx_ < clusters_.size()) { + return true; + } + finalize(response); + return false; +} + +void JsonClustersRequest::render(std::reference_wrapper cluster, + Buffer::Instance& response) { + Json::Streamer::MapPtr cluster_map = json_context_holder_.back()->clusters_->addMap(); + Upstream::ClusterInfoConstSharedPtr cluster_info = cluster.get().info(); + + std::vector top_level_entries; + if (const std::string& name = cluster_info->name(); !name.empty()) { + top_level_entries.emplace_back("name", name); + } + if (const std::string& observability_name = cluster_info->observabilityName(); + !observability_name.empty()) { + top_level_entries.emplace_back("observability_name", observability_name); + } + + if (const std::string& eds_service_name = cluster_info->edsServiceName(); + !eds_service_name.empty()) { + top_level_entries.emplace_back("eds_service_name", eds_service_name); + } + + addCircuitBreakers(cluster_map.get(), cluster_info, response); + addEjectionThresholds(cluster_map.get(), cluster.get(), response); + if (bool added_via_api = cluster_info->addedViaApi(); added_via_api) { + top_level_entries.emplace_back("added_via_api", added_via_api); + } + addMapEntries(cluster_map.get(), response, top_level_entries); + addHostStatuses(cluster_map.get(), cluster, response); +} + +void JsonClustersRequest::addHostStatuses(Json::Streamer::Map* raw_clusters_map_ptr, + const Upstream::Cluster& unwrapped_cluster, + Buffer::Instance& response) { + raw_clusters_map_ptr->addKey("host_statuses"); + Json::Streamer::ArrayPtr host_statuses_ptr = raw_clusters_map_ptr->addArray(); + for (const Upstream::HostSetPtr& host_set : + unwrapped_cluster.prioritySet().hostSetsPerPriority()) { + processHostSet(host_statuses_ptr.get(), host_set, response); + } +} + +void JsonClustersRequest::processHostSet(Json::Streamer::Array* raw_host_statuses_ptr, + const Upstream::HostSetPtr& host_set, + Buffer::Instance& response) { + for (const Upstream::HostSharedPtr& host : host_set->hosts()) { + processHost(raw_host_statuses_ptr, host, response); + } +} + +void JsonClustersRequest::processHost(Json::Streamer::Array* raw_host_statuses_ptr, + const Upstream::HostSharedPtr& host, + Buffer::Instance& response) { + Buffer::OwnedImpl buffer; + Json::Streamer::MapPtr host_ptr = raw_host_statuses_ptr->addMap(); + std::vector host_config; + setHostname(host, host_config); + addAddress(host_ptr.get(), host, buffer); + setLocality(host_ptr.get(), host, buffer); + buildHostStats(host_ptr.get(), host, buffer); + setHealthFlags(host_ptr.get(), host, buffer); + setSuccessRate(host_ptr.get(), host, buffer); + + if (uint64_t weight = host->weight(); weight) { + host_config.emplace_back("weight", weight); + } + if (uint64_t priority = host->priority(); priority) { + host_config.emplace_back("priority", priority); + } + + if (buffer.length()) { + response.move(buffer); + } + if (!host_config.empty()) { + addMapEntries(host_ptr.get(), response, host_config); + } +} + +void JsonClustersRequest::setHostname(const Upstream::HostSharedPtr& host, + std::vector& host_config) { + if (const std::string& hostname = host->hostname(); !hostname.empty()) { + host_config.emplace_back("hostname", hostname); + } +} + +void JsonClustersRequest::setSuccessRate(Json::Streamer::Map* raw_host_statuses_ptr, + const Upstream::HostSharedPtr& host, + Buffer::Instance& response) { + + double external_success_rate = host->outlierDetector().successRate( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin); + double local_success_rate = host->outlierDetector().successRate( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin); + if (!external_success_rate && !local_success_rate) { + return; + } + + if (external_success_rate >= 0.0) { + raw_host_statuses_ptr->addKey("success_rate"); + Json::Streamer::MapPtr success_rate_map = raw_host_statuses_ptr->addMap(); + std::vector success_rate{{"value", external_success_rate}}; + addMapEntries(success_rate_map.get(), response, success_rate); + } + if (local_success_rate >= 0.0) { + raw_host_statuses_ptr->addKey("local_origin_success_rate"); + Json::Streamer::MapPtr local_origin_map = raw_host_statuses_ptr->addMap(); + std::vector local_origin_success_rate{ + {"value", local_success_rate}}; + addMapEntries(local_origin_map.get(), response, local_origin_success_rate); + } +} + +void JsonClustersRequest::setLocality(Json::Streamer::Map* raw_host_ptr, + const Upstream::HostSharedPtr& host, + Buffer::Instance& response) { + + if (host->locality().region().empty() && host->locality().zone().empty() && + host->locality().sub_zone().empty()) { + return; + } + raw_host_ptr->addKey("locality"); + Json::Streamer::MapPtr locality_ptr = raw_host_ptr->addMap(); + std::vector locality; + if (const std::string& region = host->locality().region(); !region.empty()) { + locality.emplace_back("region", region); + } + if (const std::string& zone = host->locality().zone(); !zone.empty()) { + locality.emplace_back("zone", zone); + } + if (const std::string& sub_zone = host->locality().sub_zone(); !sub_zone.empty()) { + locality.emplace_back("sub_zone", sub_zone); + } + addMapEntries(locality_ptr.get(), response, locality); +} + +void JsonClustersRequest::addAddress(Json::Streamer::Map* raw_host_ptr, + const Upstream::HostSharedPtr& host, + Buffer::Instance& response) { + switch (host->address()->type()) { + case Network::Address::Type::Pipe: { + if (const std::string& path = host->address()->asString(); !path.empty()) { + raw_host_ptr->addKey("address"); + Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); + address_ptr->addKey("pipe"); + Json::Streamer::MapPtr pipe_ptr = address_ptr->addMap(); + std::vector pipe{ + {"pipe", host->address()->asString()}, + }; + addMapEntries(pipe_ptr.get(), response, pipe); + } break; - case ClustersParams::Format::Json: - chunk_processor_ = std::make_unique( - chunk_limit_, response_headers, server_.clusterManager().clusters().active_clusters_); - response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); + } + case Network::Address::Type::Ip: { + if (!host->address()->ip()->addressAsString().empty() || host->address()->ip()->port()) { + raw_host_ptr->addKey("address"); + Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); + address_ptr->addKey("socket_address"); + Json::Streamer::MapPtr socket_address_ptr = address_ptr->addMap(); + std::vector socket_address; + if (const std::string& address = host->address()->ip()->addressAsString(); !address.empty()) { + socket_address.emplace_back("address", address); + } + if (uint64_t port = uint64_t(host->address()->ip()->port()); port) { + socket_address.emplace_back("port_value", port); + } + addMapEntries(socket_address_ptr.get(), response, socket_address); + } break; - default: - return Http::Code::BadRequest; } - return Http::Code::OK; + case Network::Address::Type::EnvoyInternal: { + if (!host->address()->envoyInternalAddress()->addressId().empty() || + !host->address()->envoyInternalAddress()->endpointId().empty()) { + raw_host_ptr->addKey("address"); + Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); + raw_host_ptr->addKey("envoy_internal_address"); + Json::Streamer::MapPtr envoy_internal_address_ptr = raw_host_ptr->addMap(); + std::vector envoy_internal_address; + if (const std::string& server_listener_name = + host->address()->envoyInternalAddress()->addressId(); + !server_listener_name.empty()) { + envoy_internal_address.emplace_back("server_listener_name", server_listener_name); + } + if (const std::string& endpoint_id = host->address()->envoyInternalAddress()->endpointId(); + !endpoint_id.empty()) { + envoy_internal_address.emplace_back("endpoint_id", endpoint_id); + } + addMapEntries(envoy_internal_address_ptr.get(), response, envoy_internal_address); + } + break; + } + } } -bool ClustersRequest::nextChunk(Buffer::Instance& response) { - return chunk_processor_->nextChunk(response); +void JsonClustersRequest::buildHostStats(Json::Streamer::Map* raw_host_ptr, + const Upstream::HostSharedPtr& host, + Buffer::Instance& response) { + if (host->counters().empty() && host->gauges().empty()) { + return; + } + raw_host_ptr->addKey("stats"); + Json::Streamer::ArrayPtr stats_ptr = raw_host_ptr->addArray(); + for (const auto& [counter_name, counter] : host->counters()) { + if (counter_name.empty() || counter.get().value() == 0) { + continue; + } + std::vector counter_object{ + {"type", + envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::COUNTER)}}; + if (!counter_name.empty()) { + counter_object.emplace_back("name", counter_name); + } + if (uint64_t value = counter.get().value(); value) { + counter_object.emplace_back("value", value); + } + Json::Streamer::MapPtr stats_obj_ptr = stats_ptr->addMap(); + addMapEntries(stats_obj_ptr.get(), response, counter_object); + } + for (const auto& [gauge_name, gauge] : host->gauges()) { + if (gauge_name.empty() || gauge.get().value() == 0) { + continue; + } + std::vector gauge_object{ + {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::GAUGE)}}; + if (!gauge_name.empty()) { + gauge_object.emplace_back("name", gauge_name); + } + if (uint64_t value = gauge.get().value(); value) { + gauge_object.emplace_back("value", value); + } + Json::Streamer::MapPtr stats_obj_ptr = stats_ptr->addMap(); + addMapEntries(stats_obj_ptr.get(), response, gauge_object); + } +} + +void JsonClustersRequest::setHealthFlags(Json::Streamer::Map* raw_host_ptr, + const Upstream::HostSharedPtr& host, + Buffer::Instance& response) { + absl::btree_map> flag_map; + // Invokes setHealthFlag for each health flag. +#define SET_HEALTH_FLAG(name, notused) \ + loadHealthFlagMap(flag_map, Upstream::Host::HealthFlag::name, host); + HEALTH_FLAG_ENUM_VALUES(SET_HEALTH_FLAG) +#undef SET_HEALTH_FLAG + if (flag_map.empty()) { + return; + } + raw_host_ptr->addKey("health_status"); + Json::Streamer::MapPtr health_flags_ptr = raw_host_ptr->addMap(); + std::vector flags; + for (const auto& [name, flag_value] : flag_map) { + if (name == "eds_health_status") { + if (absl::holds_alternative(flag_value)) { + flags.emplace_back(name, std::get(flag_value)); + } + } else { + if (absl::holds_alternative(flag_value)) { + flags.emplace_back(name, std::get(flag_value)); + } + } + } + addMapEntries(health_flags_ptr.get(), response, flags); +} + +void JsonClustersRequest::loadHealthFlagMap( + absl::btree_map>& flag_map, + Upstream::Host::HealthFlag flag, const Upstream::HostSharedPtr& host) { + switch (flag) { + case Upstream::Host::HealthFlag::FAILED_ACTIVE_HC: + if (bool value = host.get()->healthFlagGet(flag); value) { + flag_map.insert_or_assign("failed_active_health_check", value); + } + break; + case Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK: + if (bool value = host.get()->healthFlagGet(flag); value) { + flag_map.insert_or_assign("failed_outlier_check", value); + } + break; + case Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH: + case Upstream::Host::HealthFlag::FAILED_EDS_HEALTH: + if (host->healthFlagGet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH) || + host->healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH)) { + if (host->healthFlagGet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH)) { + flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name( + envoy::config::core::v3::UNHEALTHY)); + } else { + flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name( + envoy::config::core::v3::DEGRADED)); + } + } else { + flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name( + envoy::config::core::v3::HEALTHY)); + } + break; + case Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC: + if (bool value = host.get()->healthFlagGet(flag); value) { + flag_map.insert_or_assign("failed_active_degraded_check", value); + } + break; + case Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL: + if (bool value = host.get()->healthFlagGet(flag); value) { + flag_map.insert_or_assign("pending_dynamic_removal", value); + } + break; + case Upstream::Host::HealthFlag::PENDING_ACTIVE_HC: + if (bool value = host.get()->healthFlagGet(flag); value) { + flag_map.insert_or_assign("pending_active_hc", value); + } + break; + case Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL: + if (bool value = host.get()->healthFlagGet(flag); value) { + flag_map.insert_or_assign("excluded_via_immediate_hc_fail", value); + } + break; + case Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT: + if (bool value = host.get()->healthFlagGet(flag); value) { + flag_map.insert_or_assign("active_hc_timeout", value); + } + break; + case Upstream::Host::HealthFlag::EDS_STATUS_DRAINING: + if (bool value = host.get()->healthFlagGet(flag); value) { + flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name( + envoy::config::core::v3::DRAINING)); + } + break; + } +} + +void JsonClustersRequest::addEjectionThresholds(Json::Streamer::Map* raw_clusters_map_ptr, + const Upstream::Cluster& unwrapped_cluster, + Buffer::Instance& response) { + const Upstream::Outlier::Detector* outlier_detector = unwrapped_cluster.outlierDetector(); + if (outlier_detector != nullptr && + outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin) > 0.0) { + raw_clusters_map_ptr->addKey("success_rate_ejection_threshold"); + Json::Streamer::MapPtr success_rate_map_ptr = raw_clusters_map_ptr->addMap(); + std::vector success_rate_ejection_threshold; + success_rate_ejection_threshold.emplace_back( + "value", + double(outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))); + addMapEntries(success_rate_map_ptr.get(), response, success_rate_ejection_threshold); + } + if (outlier_detector != nullptr && + outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin) > 0.0) { + raw_clusters_map_ptr->addKey("local_origin_success_rate_ejection_threshold"); + Json::Streamer::MapPtr local_success_rate_map_ptr = raw_clusters_map_ptr->addMap(); + std::vector local_origin_success_rate_ejection_threshold; + local_origin_success_rate_ejection_threshold.emplace_back( + "value", double(outlier_detector->successRateEjectionThreshold( + Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))); + addMapEntries(local_success_rate_map_ptr.get(), response, + local_origin_success_rate_ejection_threshold); + } +} + +void JsonClustersRequest::addCircuitBreakers(Json::Streamer::Map* raw_clusters_map_ptr, + Upstream::ClusterInfoConstSharedPtr cluster_info, + Buffer::Instance& response) { + raw_clusters_map_ptr->addKey("circuit_breakers"); + Json::Streamer::MapPtr circuit_breakers = raw_clusters_map_ptr->addMap(); + circuit_breakers->addKey("thresholds"); + Json::Streamer::ArrayPtr thresholds = circuit_breakers->addArray(); + addCircuitBreakerForPriority(envoy::config::core::v3::RoutingPriority::DEFAULT, thresholds.get(), + response, + cluster_info->resourceManager(Upstream::ResourcePriority::Default)); + addCircuitBreakerForPriority(envoy::config::core::v3::RoutingPriority::HIGH, thresholds.get(), + response, + cluster_info->resourceManager(Upstream::ResourcePriority::High)); +} + +void JsonClustersRequest::addCircuitBreakerForPriority( + const envoy::config::core::v3::RoutingPriority& priority, + Json::Streamer::Array* raw_thresholds_ptr, Buffer::Instance& response, + Upstream::ResourceManager& resource_manager) { + Json::Streamer::MapPtr threshold = raw_thresholds_ptr->addMap(); + std::vector entries{ + {"priority", + priority == envoy::config::core::v3::RoutingPriority::DEFAULT ? "DEFAULT" : "HIGH"}}; + if (uint64_t max_connections = resource_manager.connections().max(); max_connections) { + entries.emplace_back("max_connections", max_connections); + } + if (uint64_t max_pending_requests = resource_manager.pendingRequests().max(); + max_pending_requests) { + entries.emplace_back("max_pending_requests", max_pending_requests); + } + if (uint64_t max_requests = resource_manager.requests().max(); max_requests) { + entries.emplace_back("max_requests", max_requests); + } + if (uint64_t max_retries = resource_manager.retries().max(); max_retries) { + entries.emplace_back("max_retries", max_retries); + } + addMapEntries(threshold.get(), response, entries); +} + +// Json::Streamer holds a reference to a Buffer::Instance reference but the API for Request +// takes a Buffer::Instance reference on each call to nextChunk. So, at the end of each +// Json::Streamer function invocation, call drainBufferIntoResponse to ensure that the +// contents written to its buffer gets moved and appended to the response. +void JsonClustersRequest::drainBufferIntoResponse(Buffer::Instance& response) { + if (&response != &buffer_) { + response.move(buffer_); + } +} + +void JsonClustersRequest::addMapEntries(Json::Streamer::Map* raw_map_ptr, + Buffer::Instance& response, + std::vector& entries) { + raw_map_ptr->addEntries(entries); + drainBufferIntoResponse(response); +} + +// Start destruction of the ClustersJsonContext to render the closing tokens and push to the +// buffer. Since we've pushed data into the buffer in the Json::Streamer, we'll need to drain +// the contents into the response. +void JsonClustersRequest::finalize(Buffer::Instance& response) { + json_context_holder_.pop_back(); + drainBufferIntoResponse(response); } } // namespace Server diff --git a/source/server/admin/clusters_request.h b/source/server/admin/clusters_request.h index 5711e5840b8f..8a3a51a89510 100644 --- a/source/server/admin/clusters_request.h +++ b/source/server/admin/clusters_request.h @@ -26,11 +26,85 @@ class ClustersRequest : public Admin::Request { Http::Code start(Http::ResponseHeaderMap& response_headers) override; bool nextChunk(Buffer::Instance& response) override; -private: +protected: uint64_t chunk_limit_{DefaultChunkLimit}; Server::Instance& server_; const ClustersParams& params_; - std::unique_ptr chunk_processor_ = nullptr; + std::vector> clusters_; +}; + +/** + * TextClustersRequest streams the response using a newline delimited text format. + */ +class TextClustersRequest : public ClustersRequest { +public: + TextClustersRequest(uint64_t chunk_limit, Instance& server, const ClustersParams& params); + bool nextChunk(Buffer::Instance& response) override; + +private: + void render(std::reference_wrapper cluster, Buffer::Instance& response); + + static void addOutlierInfo(const std::string& cluster_name, + const Upstream::Outlier::Detector* outlier_detector, + Buffer::Instance& response); + static void addCircuitBreakerSettings(const std::string& cluster_name, + const std::string& priority_str, + Upstream::ResourceManager& resource_manager, + Buffer::Instance& response); + uint64_t idx_; +}; + +/** + * JsonClustersChunkProcessor streams the response and emulates the JSON encoding behavior of + * the gRPC clients; specifically, "zero-value" values such as false, 0, empty string, and + * empty objects are omitted from the output. + */ +class JsonClustersRequest : public ClustersRequest { +public: + JsonClustersRequest(uint64_t chunk_limit, Instance& server, const ClustersParams& params); + Http::Code start(Http::ResponseHeaderMap& response_headers) override; + bool nextChunk(Buffer::Instance& response) override; + +private: + void render(std::reference_wrapper cluster, Buffer::Instance& response); + void drainBufferIntoResponse(Buffer::Instance& response); + void finalize(Buffer::Instance& response); + void addAddress(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, + Buffer::Instance& response); + void addMapEntries(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, + std::vector& entries); + void addCircuitBreakers(Json::Streamer::Map* raw_clusters_map_ptr, + Upstream::ClusterInfoConstSharedPtr cluster_info, + Buffer::Instance& response); + void addCircuitBreakerForPriority(const envoy::config::core::v3::RoutingPriority& priority, + Json::Streamer::Array* raw_map_ptr, Buffer::Instance& response, + Upstream::ResourceManager& resource_manager); + void addEjectionThresholds(Json::Streamer::Map* raw_clusters_map_ptr, + const Upstream::Cluster& unwrapped_cluster, + Buffer::Instance& response); + void addHostStatuses(Json::Streamer::Map* raw_clusters_map_ptr, + const Upstream::Cluster& unwrapped_cluster, Buffer::Instance& response); + void processHostSet(Json::Streamer::Array* raw_hosts_statuses_ptr, + const Upstream::HostSetPtr& host_set, Buffer::Instance& response); + void processHost(Json::Streamer::Array* raw_host_statuses_ptr, + const Upstream::HostSharedPtr& host, Buffer::Instance& response); + void buildHostStats(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, + Buffer::Instance& response); + void setHealthFlags(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, + Buffer::Instance& response); + void setLocality(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, + Buffer::Instance& response); + void setSuccessRate(Json::Streamer::Map* raw_host_statuses_ptr, + const Upstream::HostSharedPtr& host, Buffer::Instance& response); + void setHostname(const Upstream::HostSharedPtr& host, + std::vector& top_level_entries); + void loadHealthFlagMap( + absl::btree_map>& flag_map, + Upstream::Host::HealthFlag flag, const Upstream::HostSharedPtr& host); + + Buffer::OwnedImpl buffer_; + std::vector> json_context_holder_; + uint64_t idx_; }; } // namespace Server diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index 09375a65e0e5..36f7012d4a7c 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -55,7 +55,14 @@ class BaseClustersRequestFixture : public testing::Test { using ClustersRequestPtr = std::unique_ptr; ClustersRequestPtr makeRequest(uint64_t chunk_limit, ClustersParams& params) { - return std::make_unique(chunk_limit, mock_server_, params); + switch (params.format_) { + case ClustersParams::Format::Text: + return std::make_unique(chunk_limit, mock_server_, params); + case ClustersParams::Format::Json: + return std::make_unique(chunk_limit, mock_server_, params); + case ClustersParams::Format::Unknown: + return nullptr; + } } struct ResponseResult { From 594b51c8249743f52bce6e18257b26a2e0e5a778 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Sat, 25 May 2024 05:08:56 +0000 Subject: [PATCH 69/77] Remove cluster chunk processor since nextChunk is fully implemented in the request classes Signed-off-by: Demitri Swan --- source/server/admin/BUILD | 23 +- .../server/admin/clusters_chunk_processor.cc | 600 ------------------ .../server/admin/clusters_chunk_processor.h | 130 ---- source/server/admin/clusters_request.cc | 2 +- source/server/admin/clusters_request.h | 19 +- test/server/admin/BUILD | 1 - 6 files changed, 22 insertions(+), 753 deletions(-) delete mode 100644 source/server/admin/clusters_chunk_processor.cc delete mode 100644 source/server/admin/clusters_chunk_processor.h diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 9e7739e50fb4..ac91d178161a 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -419,7 +419,6 @@ envoy_cc_library( srcs = ["clusters_request.cc"], hdrs = ["clusters_request.h"], deps = [ - ":clusters_chunk_processor_lib", ":clusters_params_lib", "//envoy/buffer:buffer_interface", "//envoy/http:codes_interface", @@ -429,6 +428,9 @@ envoy_cc_library( "//source/common/buffer:buffer_lib", "//source/common/common:logger_lib", "//source/common/http:headers_lib", + "//source/common/json:json_streamer_lib", + "//source/common/upstream:host_utility_lib", + "@envoy_api//envoy/admin/v3:pkg_cc_proto", ], ) @@ -446,22 +448,3 @@ envoy_cc_library( "//envoy/http:query_params_interface", ], ) - -envoy_cc_library( - name = "clusters_chunk_processor_lib", - srcs = ["clusters_chunk_processor.cc"], - hdrs = ["clusters_chunk_processor.h"], - deps = [ - "//envoy/buffer:buffer_interface", - "//envoy/common:pure_lib", - "//envoy/upstream:cluster_manager_interface", - "//envoy/upstream:upstream_interface", - "//source/common/common:logger_lib", - "//source/common/json:json_streamer_lib", - "//source/common/network:utility_lib", - "//source/common/upstream:host_utility_lib", - "@com_google_absl//absl/strings:string_view", - "@envoy_api//envoy/admin/v3:pkg_cc_proto", - "@envoy_api//envoy/config/core/v3:pkg_cc_proto", - ], -) diff --git a/source/server/admin/clusters_chunk_processor.cc b/source/server/admin/clusters_chunk_processor.cc deleted file mode 100644 index 5d13886ebf6f..000000000000 --- a/source/server/admin/clusters_chunk_processor.cc +++ /dev/null @@ -1,600 +0,0 @@ -#include "clusters_chunk_processor.h" -#include "source/server/admin/clusters_chunk_processor.h" - -#include -#include -#include -#include -#include - -#include "envoy/buffer/buffer.h" -#include "envoy/config/core/v3/base.pb.h" -#include "envoy/config/core/v3/health_check.pb.h" -#include "envoy/network/address.h" -#include "envoy/upstream/cluster_manager.h" -#include "envoy/upstream/resource_manager.h" -#include "envoy/upstream/upstream.h" - -#include "source/common/buffer/buffer_impl.h" -#include "source/common/upstream/host_utility.h" - -#include "absl/strings/string_view.h" - -namespace Envoy { -namespace Server { - -TextClustersChunkProcessor::TextClustersChunkProcessor( - uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, - const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map) - : chunk_limit_(chunk_limit), response_headers_(response_headers), idx_(0) { - for (const auto& [str, reference_wrapped_const_cluster] : cluster_info_map) { - clusters_.push_back(reference_wrapped_const_cluster); - } -} - -bool TextClustersChunkProcessor::nextChunk(Buffer::Instance& response) { - const uint64_t original_request_size = response.length(); - for (; idx_ < clusters_.size() && response.length() - original_request_size < chunk_limit_; - idx_++) { - - render(clusters_[idx_], response); - } - // TODO(demitriswan) See if these need to be updated here. - UNREFERENCED_PARAMETER(response_headers_); - return idx_ < clusters_.size(); -} - -void TextClustersChunkProcessor::render(std::reference_wrapper cluster, - Buffer::Instance& response) { - const Upstream::Cluster& unwrapped_cluster = cluster.get(); - const std::string& cluster_name = unwrapped_cluster.info()->name(); - response.add(fmt::format("{}::observability_name::{}\n", cluster_name, - unwrapped_cluster.info()->observabilityName())); - addOutlierInfo(cluster_name, unwrapped_cluster.outlierDetector(), response); - - addCircuitBreakerSettings( - cluster_name, "default", - unwrapped_cluster.info()->resourceManager(Upstream::ResourcePriority::Default), response); - addCircuitBreakerSettings( - cluster_name, "high", - unwrapped_cluster.info()->resourceManager(Upstream::ResourcePriority::High), response); - - response.add(fmt::format("{}::added_via_api::{}\n", cluster_name, - unwrapped_cluster.info()->addedViaApi())); - if (const auto& name = unwrapped_cluster.info()->edsServiceName(); !name.empty()) { - response.add(fmt::format("{}::eds_service_name::{}\n", cluster_name, name)); - } - for (auto& host_set : unwrapped_cluster.prioritySet().hostSetsPerPriority()) { - for (auto& host : host_set->hosts()) { - const std::string& host_address = host->address()->asString(); - std::vector> all_stats; - for (const auto& [counter_name, counter] : host->counters()) { - all_stats.emplace_back(counter_name, counter.get().value()); - } - - for (const auto& [gauge_name, gauge] : host->gauges()) { - all_stats.emplace_back(gauge_name, gauge.get().value()); - } - - std::sort(all_stats.begin(), all_stats.end(), - [](const std::pair& a, - const std::pair& b) { return a.first < b.first; }); - - for (const auto& [stat_name, stat] : all_stats) { - response.add(fmt::format("{}::{}::{}::{}\n", cluster_name, host_address, stat_name, stat)); - } - - response.add( - fmt::format("{}::{}::hostname::{}\n", cluster_name, host_address, host->hostname())); - response.add(fmt::format("{}::{}::health_flags::{}\n", cluster_name, host_address, - Upstream::HostUtility::healthFlagsToString(*host))); - response.add(fmt::format("{}::{}::weight::{}\n", cluster_name, host_address, host->weight())); - response.add(fmt::format("{}::{}::region::{}\n", cluster_name, host_address, - host->locality().region())); - response.add( - fmt::format("{}::{}::zone::{}\n", cluster_name, host_address, host->locality().zone())); - response.add(fmt::format("{}::{}::sub_zone::{}\n", cluster_name, host_address, - host->locality().sub_zone())); - response.add(fmt::format("{}::{}::canary::{}\n", cluster_name, host_address, host->canary())); - response.add( - fmt::format("{}::{}::priority::{}\n", cluster_name, host_address, host->priority())); - response.add(fmt::format( - "{}::{}::success_rate::{}\n", cluster_name, host_address, - host->outlierDetector().successRate( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))); - response.add(fmt::format( - "{}::{}::local_origin_success_rate::{}\n", cluster_name, host_address, - host->outlierDetector().successRate( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))); - } - } -} - -void TextClustersChunkProcessor::addOutlierInfo(const std::string& cluster_name, - const Upstream::Outlier::Detector* outlier_detector, - Buffer::Instance& response) { - if (outlier_detector) { - response.add(fmt::format( - "{}::outlier::success_rate_average::{:g}\n", cluster_name, - outlier_detector->successRateAverage( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))); - response.add(fmt::format( - "{}::outlier::success_rate_ejection_threshold::{:g}\n", cluster_name, - outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))); - response.add(fmt::format( - "{}::outlier::local_origin_success_rate_average::{:g}\n", cluster_name, - outlier_detector->successRateAverage( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))); - response.add(fmt::format( - "{}::outlier::local_origin_success_rate_ejection_threshold::{:g}\n", cluster_name, - outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))); - } -} - -void TextClustersChunkProcessor::addCircuitBreakerSettings( - const std::string& cluster_name, const std::string& priority_str, - Upstream::ResourceManager& resource_manager, Buffer::Instance& response) { - response.add(fmt::format("{}::{}_priority::max_connections::{}\n", cluster_name, priority_str, - resource_manager.connections().max())); - response.add(fmt::format("{}::{}_priority::max_pending_requests::{}\n", cluster_name, - priority_str, resource_manager.pendingRequests().max())); - response.add(fmt::format("{}::{}_priority::max_requests::{}\n", cluster_name, priority_str, - resource_manager.requests().max())); - response.add(fmt::format("{}::{}_priority::max_retries::{}\n", cluster_name, priority_str, - resource_manager.retries().max())); -} - -JsonClustersChunkProcessor::JsonClustersChunkProcessor( - uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, - const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map) - : chunk_limit_(chunk_limit), response_headers_(response_headers), idx_(0) { - for (const auto& pair : cluster_info_map) { - clusters_.push_back(pair.second); - } - std::unique_ptr streamer = std::make_unique(buffer_); - Json::Streamer::MapPtr root_map = streamer->makeRootMap(); - root_map->addKey("cluster_statuses"); - Json::Streamer::ArrayPtr clusters = root_map->addArray(); - json_context_holder_.push_back(std::make_unique( - std::move(streamer), buffer_, std::move(root_map), std::move(clusters))); -} - -bool JsonClustersChunkProcessor::nextChunk(Buffer::Instance& response) { - // TODO(demitriswan) See if these need to be updated here. - UNREFERENCED_PARAMETER(response_headers_); - - const uint64_t original_request_size = response.length(); - for (; idx_ < clusters_.size() && response.length() - original_request_size < chunk_limit_; - idx_++) { - - render(clusters_[idx_], response); - } - if (idx_ < clusters_.size()) { - return true; - } - finalize(response); - return false; -} - -void JsonClustersChunkProcessor::render(std::reference_wrapper cluster, - Buffer::Instance& response) { - Json::Streamer::MapPtr cluster_map = json_context_holder_.back()->clusters_->addMap(); - Upstream::ClusterInfoConstSharedPtr cluster_info = cluster.get().info(); - - std::vector top_level_entries; - if (const std::string& name = cluster_info->name(); !name.empty()) { - top_level_entries.emplace_back("name", name); - } - if (const std::string& observability_name = cluster_info->observabilityName(); - !observability_name.empty()) { - top_level_entries.emplace_back("observability_name", observability_name); - } - - if (const std::string& eds_service_name = cluster_info->edsServiceName(); - !eds_service_name.empty()) { - top_level_entries.emplace_back("eds_service_name", eds_service_name); - } - - addCircuitBreakers(cluster_map.get(), cluster_info, response); - addEjectionThresholds(cluster_map.get(), cluster.get(), response); - if (bool added_via_api = cluster_info->addedViaApi(); added_via_api) { - top_level_entries.emplace_back("added_via_api", added_via_api); - } - addMapEntries(cluster_map.get(), response, top_level_entries); - addHostStatuses(cluster_map.get(), cluster, response); -} - -void JsonClustersChunkProcessor::addHostStatuses(Json::Streamer::Map* raw_clusters_map_ptr, - const Upstream::Cluster& unwrapped_cluster, - Buffer::Instance& response) { - raw_clusters_map_ptr->addKey("host_statuses"); - Json::Streamer::ArrayPtr host_statuses_ptr = raw_clusters_map_ptr->addArray(); - for (const Upstream::HostSetPtr& host_set : - unwrapped_cluster.prioritySet().hostSetsPerPriority()) { - processHostSet(host_statuses_ptr.get(), host_set, response); - } -} - -void JsonClustersChunkProcessor::processHostSet(Json::Streamer::Array* raw_host_statuses_ptr, - const Upstream::HostSetPtr& host_set, - Buffer::Instance& response) { - for (const Upstream::HostSharedPtr& host : host_set->hosts()) { - processHost(raw_host_statuses_ptr, host, response); - } -} - -void JsonClustersChunkProcessor::processHost(Json::Streamer::Array* raw_host_statuses_ptr, - const Upstream::HostSharedPtr& host, - Buffer::Instance& response) { - Buffer::OwnedImpl buffer; - Json::Streamer::MapPtr host_ptr = raw_host_statuses_ptr->addMap(); - std::vector host_config; - setHostname(host, host_config); - addAddress(host_ptr.get(), host, buffer); - setLocality(host_ptr.get(), host, buffer); - buildHostStats(host_ptr.get(), host, buffer); - setHealthFlags(host_ptr.get(), host, buffer); - setSuccessRate(host_ptr.get(), host, buffer); - - if (uint64_t weight = host->weight(); weight) { - host_config.emplace_back("weight", weight); - } - if (uint64_t priority = host->priority(); priority) { - host_config.emplace_back("priority", priority); - } - - if (buffer.length()) { - response.move(buffer); - } - if (!host_config.empty()) { - addMapEntries(host_ptr.get(), response, host_config); - } -} - -void JsonClustersChunkProcessor::setHostname( - const Upstream::HostSharedPtr& host, std::vector& host_config) { - if (const std::string& hostname = host->hostname(); !hostname.empty()) { - host_config.emplace_back("hostname", hostname); - } -} - -void JsonClustersChunkProcessor::setSuccessRate(Json::Streamer::Map* raw_host_statuses_ptr, - const Upstream::HostSharedPtr& host, - Buffer::Instance& response) { - - double external_success_rate = host->outlierDetector().successRate( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin); - double local_success_rate = host->outlierDetector().successRate( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin); - if (!external_success_rate && !local_success_rate) { - return; - } - - if (external_success_rate >= 0.0) { - raw_host_statuses_ptr->addKey("success_rate"); - Json::Streamer::MapPtr success_rate_map = raw_host_statuses_ptr->addMap(); - std::vector success_rate{{"value", external_success_rate}}; - addMapEntries(success_rate_map.get(), response, success_rate); - } - if (local_success_rate >= 0.0) { - raw_host_statuses_ptr->addKey("local_origin_success_rate"); - Json::Streamer::MapPtr local_origin_map = raw_host_statuses_ptr->addMap(); - std::vector local_origin_success_rate{ - {"value", local_success_rate}}; - addMapEntries(local_origin_map.get(), response, local_origin_success_rate); - } -} - -void JsonClustersChunkProcessor::setLocality(Json::Streamer::Map* raw_host_ptr, - const Upstream::HostSharedPtr& host, - Buffer::Instance& response) { - - if (host->locality().region().empty() && host->locality().zone().empty() && - host->locality().sub_zone().empty()) { - return; - } - raw_host_ptr->addKey("locality"); - Json::Streamer::MapPtr locality_ptr = raw_host_ptr->addMap(); - std::vector locality; - if (const std::string& region = host->locality().region(); !region.empty()) { - locality.emplace_back("region", region); - } - if (const std::string& zone = host->locality().zone(); !zone.empty()) { - locality.emplace_back("zone", zone); - } - if (const std::string& sub_zone = host->locality().sub_zone(); !sub_zone.empty()) { - locality.emplace_back("sub_zone", sub_zone); - } - addMapEntries(locality_ptr.get(), response, locality); -} - -void JsonClustersChunkProcessor::addAddress(Json::Streamer::Map* raw_host_ptr, - const Upstream::HostSharedPtr& host, - Buffer::Instance& response) { - switch (host->address()->type()) { - case Network::Address::Type::Pipe: { - if (const std::string& path = host->address()->asString(); !path.empty()) { - raw_host_ptr->addKey("address"); - Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); - address_ptr->addKey("pipe"); - Json::Streamer::MapPtr pipe_ptr = address_ptr->addMap(); - std::vector pipe{ - {"pipe", host->address()->asString()}, - }; - addMapEntries(pipe_ptr.get(), response, pipe); - } - break; - } - case Network::Address::Type::Ip: { - if (!host->address()->ip()->addressAsString().empty() || host->address()->ip()->port()) { - raw_host_ptr->addKey("address"); - Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); - address_ptr->addKey("socket_address"); - Json::Streamer::MapPtr socket_address_ptr = address_ptr->addMap(); - std::vector socket_address; - if (const std::string& address = host->address()->ip()->addressAsString(); !address.empty()) { - socket_address.emplace_back("address", address); - } - if (uint64_t port = uint64_t(host->address()->ip()->port()); port) { - socket_address.emplace_back("port_value", port); - } - addMapEntries(socket_address_ptr.get(), response, socket_address); - } - break; - } - case Network::Address::Type::EnvoyInternal: { - if (!host->address()->envoyInternalAddress()->addressId().empty() || - !host->address()->envoyInternalAddress()->endpointId().empty()) { - raw_host_ptr->addKey("address"); - Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); - raw_host_ptr->addKey("envoy_internal_address"); - Json::Streamer::MapPtr envoy_internal_address_ptr = raw_host_ptr->addMap(); - std::vector envoy_internal_address; - if (const std::string& server_listener_name = - host->address()->envoyInternalAddress()->addressId(); - !server_listener_name.empty()) { - envoy_internal_address.emplace_back("server_listener_name", server_listener_name); - } - if (const std::string& endpoint_id = host->address()->envoyInternalAddress()->endpointId(); - !endpoint_id.empty()) { - envoy_internal_address.emplace_back("endpoint_id", endpoint_id); - } - addMapEntries(envoy_internal_address_ptr.get(), response, envoy_internal_address); - } - break; - } - } -} - -void JsonClustersChunkProcessor::buildHostStats(Json::Streamer::Map* raw_host_ptr, - const Upstream::HostSharedPtr& host, - Buffer::Instance& response) { - if (host->counters().empty() && host->gauges().empty()) { - return; - } - raw_host_ptr->addKey("stats"); - Json::Streamer::ArrayPtr stats_ptr = raw_host_ptr->addArray(); - for (const auto& [counter_name, counter] : host->counters()) { - if (counter_name.empty() || counter.get().value() == 0) { - continue; - } - std::vector counter_object{ - {"type", - envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::COUNTER)}}; - if (!counter_name.empty()) { - counter_object.emplace_back("name", counter_name); - } - if (uint64_t value = counter.get().value(); value) { - counter_object.emplace_back("value", value); - } - Json::Streamer::MapPtr stats_obj_ptr = stats_ptr->addMap(); - addMapEntries(stats_obj_ptr.get(), response, counter_object); - } - for (const auto& [gauge_name, gauge] : host->gauges()) { - if (gauge_name.empty() || gauge.get().value() == 0) { - continue; - } - std::vector gauge_object{ - {"type", envoy::admin::v3::SimpleMetric_Type_Name(envoy::admin::v3::SimpleMetric::GAUGE)}}; - if (!gauge_name.empty()) { - gauge_object.emplace_back("name", gauge_name); - } - if (uint64_t value = gauge.get().value(); value) { - gauge_object.emplace_back("value", value); - } - Json::Streamer::MapPtr stats_obj_ptr = stats_ptr->addMap(); - addMapEntries(stats_obj_ptr.get(), response, gauge_object); - } -} - -void JsonClustersChunkProcessor::setHealthFlags(Json::Streamer::Map* raw_host_ptr, - const Upstream::HostSharedPtr& host, - Buffer::Instance& response) { - absl::btree_map> flag_map; - // Invokes setHealthFlag for each health flag. -#define SET_HEALTH_FLAG(name, notused) \ - loadHealthFlagMap(flag_map, Upstream::Host::HealthFlag::name, host); - HEALTH_FLAG_ENUM_VALUES(SET_HEALTH_FLAG) -#undef SET_HEALTH_FLAG - if (flag_map.empty()) { - return; - } - raw_host_ptr->addKey("health_status"); - Json::Streamer::MapPtr health_flags_ptr = raw_host_ptr->addMap(); - std::vector flags; - for (const auto& [name, flag_value] : flag_map) { - if (name == "eds_health_status") { - if (absl::holds_alternative(flag_value)) { - flags.emplace_back(name, std::get(flag_value)); - } - } else { - if (absl::holds_alternative(flag_value)) { - flags.emplace_back(name, std::get(flag_value)); - } - } - } - addMapEntries(health_flags_ptr.get(), response, flags); -} - -void JsonClustersChunkProcessor::loadHealthFlagMap( - absl::btree_map>& flag_map, - Upstream::Host::HealthFlag flag, const Upstream::HostSharedPtr& host) { - switch (flag) { - case Upstream::Host::HealthFlag::FAILED_ACTIVE_HC: - if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.insert_or_assign("failed_active_health_check", value); - } - break; - case Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK: - if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.insert_or_assign("failed_outlier_check", value); - } - break; - case Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH: - case Upstream::Host::HealthFlag::FAILED_EDS_HEALTH: - if (host->healthFlagGet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH) || - host->healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH)) { - if (host->healthFlagGet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH)) { - flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name( - envoy::config::core::v3::UNHEALTHY)); - } else { - flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name( - envoy::config::core::v3::DEGRADED)); - } - } else { - flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name( - envoy::config::core::v3::HEALTHY)); - } - break; - case Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC: - if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.insert_or_assign("failed_active_degraded_check", value); - } - break; - case Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL: - if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.insert_or_assign("pending_dynamic_removal", value); - } - break; - case Upstream::Host::HealthFlag::PENDING_ACTIVE_HC: - if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.insert_or_assign("pending_active_hc", value); - } - break; - case Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL: - if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.insert_or_assign("excluded_via_immediate_hc_fail", value); - } - break; - case Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT: - if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.insert_or_assign("active_hc_timeout", value); - } - break; - case Upstream::Host::HealthFlag::EDS_STATUS_DRAINING: - if (bool value = host.get()->healthFlagGet(flag); value) { - flag_map.insert_or_assign("eds_health_status", envoy::config::core::v3::HealthStatus_Name( - envoy::config::core::v3::DRAINING)); - } - break; - } -} - -void JsonClustersChunkProcessor::addEjectionThresholds(Json::Streamer::Map* raw_clusters_map_ptr, - const Upstream::Cluster& unwrapped_cluster, - Buffer::Instance& response) { - const Upstream::Outlier::Detector* outlier_detector = unwrapped_cluster.outlierDetector(); - if (outlier_detector != nullptr && - outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin) > 0.0) { - raw_clusters_map_ptr->addKey("success_rate_ejection_threshold"); - Json::Streamer::MapPtr success_rate_map_ptr = raw_clusters_map_ptr->addMap(); - std::vector success_rate_ejection_threshold; - success_rate_ejection_threshold.emplace_back( - "value", - double(outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))); - addMapEntries(success_rate_map_ptr.get(), response, success_rate_ejection_threshold); - } - if (outlier_detector != nullptr && - outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin) > 0.0) { - raw_clusters_map_ptr->addKey("local_origin_success_rate_ejection_threshold"); - Json::Streamer::MapPtr local_success_rate_map_ptr = raw_clusters_map_ptr->addMap(); - std::vector local_origin_success_rate_ejection_threshold; - local_origin_success_rate_ejection_threshold.emplace_back( - "value", double(outlier_detector->successRateEjectionThreshold( - Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))); - addMapEntries(local_success_rate_map_ptr.get(), response, - local_origin_success_rate_ejection_threshold); - } -} - -void JsonClustersChunkProcessor::addCircuitBreakers( - Json::Streamer::Map* raw_clusters_map_ptr, Upstream::ClusterInfoConstSharedPtr cluster_info, - Buffer::Instance& response) { - raw_clusters_map_ptr->addKey("circuit_breakers"); - Json::Streamer::MapPtr circuit_breakers = raw_clusters_map_ptr->addMap(); - circuit_breakers->addKey("thresholds"); - Json::Streamer::ArrayPtr thresholds = circuit_breakers->addArray(); - addCircuitBreakerForPriority(envoy::config::core::v3::RoutingPriority::DEFAULT, thresholds.get(), - response, - cluster_info->resourceManager(Upstream::ResourcePriority::Default)); - addCircuitBreakerForPriority(envoy::config::core::v3::RoutingPriority::HIGH, thresholds.get(), - response, - cluster_info->resourceManager(Upstream::ResourcePriority::High)); -} - -void JsonClustersChunkProcessor::addCircuitBreakerForPriority( - const envoy::config::core::v3::RoutingPriority& priority, - Json::Streamer::Array* raw_thresholds_ptr, Buffer::Instance& response, - Upstream::ResourceManager& resource_manager) { - Json::Streamer::MapPtr threshold = raw_thresholds_ptr->addMap(); - std::vector entries{ - {"priority", - priority == envoy::config::core::v3::RoutingPriority::DEFAULT ? "DEFAULT" : "HIGH"}}; - if (uint64_t max_connections = resource_manager.connections().max(); max_connections) { - entries.emplace_back("max_connections", max_connections); - } - if (uint64_t max_pending_requests = resource_manager.pendingRequests().max(); - max_pending_requests) { - entries.emplace_back("max_pending_requests", max_pending_requests); - } - if (uint64_t max_requests = resource_manager.requests().max(); max_requests) { - entries.emplace_back("max_requests", max_requests); - } - if (uint64_t max_retries = resource_manager.retries().max(); max_retries) { - entries.emplace_back("max_retries", max_retries); - } - addMapEntries(threshold.get(), response, entries); -} - -// Json::Streamer holds a reference to a Buffer::Instance reference but the API for Request -// takes a Buffer::Instance reference on each call to nextChunk. So, at the end of each -// Json::Streamer function invocation, call drainBufferIntoResponse to ensure that the -// contents written to its buffer gets moved and appended to the response. -void JsonClustersChunkProcessor::drainBufferIntoResponse(Buffer::Instance& response) { - if (&response != &buffer_) { - response.move(buffer_); - } -} - -void JsonClustersChunkProcessor::addMapEntries( - Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, - std::vector& entries) { - raw_map_ptr->addEntries(entries); - drainBufferIntoResponse(response); -} - -// Start destruction of the ClustersJsonContext to render the closing tokens and push to the -// buffer. Since we've pushed data into the buffer in the Json::Streamer, we'll need to drain -// the contents into the response. -void JsonClustersChunkProcessor::finalize(Buffer::Instance& response) { - json_context_holder_.pop_back(); - drainBufferIntoResponse(response); -} - -} // namespace Server -} // namespace Envoy diff --git a/source/server/admin/clusters_chunk_processor.h b/source/server/admin/clusters_chunk_processor.h deleted file mode 100644 index d2fd9b53ca7c..000000000000 --- a/source/server/admin/clusters_chunk_processor.h +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "envoy/admin/v3/clusters.pb.h" -#include "envoy/buffer/buffer.h" -#include "envoy/common/pure.h" -#include "envoy/upstream/cluster_manager.h" -#include "envoy/upstream/outlier_detection.h" -#include "envoy/upstream/resource_manager.h" - -#include "source/common/buffer/buffer_impl.h" -#include "source/common/json/json_streamer.h" - -#include "absl/strings/string_view.h" - -namespace Envoy { -namespace Server { - -/** - * ClustersChunkProcessor is the interface for streaming Clusters data in a response - * for /clusters. - */ -class ClustersChunkProcessor { -public: - virtual bool nextChunk(Buffer::Instance& response) PURE; - virtual ~ClustersChunkProcessor() = default; -}; - -/** - * TextClustersChunkProcessor streams the response using a newline delimited text format. - */ -class TextClustersChunkProcessor : public ClustersChunkProcessor { -public: - TextClustersChunkProcessor(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, - const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map); - bool nextChunk(Buffer::Instance& response) override; - -private: - void render(std::reference_wrapper cluster, Buffer::Instance& response); - - static void addOutlierInfo(const std::string& cluster_name, - const Upstream::Outlier::Detector* outlier_detector, - Buffer::Instance& response); - static void addCircuitBreakerSettings(const std::string& cluster_name, - const std::string& priority_str, - Upstream::ResourceManager& resource_manager, - Buffer::Instance& response); - const uint64_t chunk_limit_; - Http::ResponseHeaderMap& response_headers_; - std::vector> clusters_; - uint64_t idx_; -}; - -/** - * ClustersJsonContext holds an Envoy::Json::Streamer and the top-level JSON objects throughout the - * duration of a request. When it is destructed, the buffer will terminate the array and object - * herein. See the Envoy::Json::Streamer implementation for details. - */ -struct ClustersJsonContext { - ClustersJsonContext(std::unique_ptr streamer, Buffer::Instance& buffer, - Json::Streamer::MapPtr root_map, Json::Streamer::ArrayPtr clusters) - : streamer_(std::move(streamer)), buffer_(buffer), root_map_(std::move(root_map)), - clusters_(std::move(clusters)) {} - std::unique_ptr streamer_; - Buffer::Instance& buffer_; - Json::Streamer::MapPtr root_map_; - Json::Streamer::ArrayPtr clusters_; -}; - -/** - * JsonClustersChunkProcessor streams the response and emulates the JSON encoding behavior of - * the gRPC clients; specifically, "zero-value" values such as false, 0, empty string, and - * empty objects are omitted from the output. - */ -class JsonClustersChunkProcessor : public ClustersChunkProcessor { -public: - JsonClustersChunkProcessor(uint64_t chunk_limit, Http::ResponseHeaderMap& response_headers, - const Upstream::ClusterManager::ClusterInfoMap& cluster_info_map); - bool nextChunk(Buffer::Instance& response) override; - -private: - void render(std::reference_wrapper cluster, Buffer::Instance& response); - void drainBufferIntoResponse(Buffer::Instance& response); - void finalize(Buffer::Instance& response); - void addAddress(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, - Buffer::Instance& response); - void addMapEntries(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, - std::vector& entries); - void addCircuitBreakers(Json::Streamer::Map* raw_clusters_map_ptr, - Upstream::ClusterInfoConstSharedPtr cluster_info, - Buffer::Instance& response); - void addCircuitBreakerForPriority(const envoy::config::core::v3::RoutingPriority& priority, - Json::Streamer::Array* raw_map_ptr, Buffer::Instance& response, - Upstream::ResourceManager& resource_manager); - void addEjectionThresholds(Json::Streamer::Map* raw_clusters_map_ptr, - const Upstream::Cluster& unwrapped_cluster, - Buffer::Instance& response); - void addHostStatuses(Json::Streamer::Map* raw_clusters_map_ptr, - const Upstream::Cluster& unwrapped_cluster, Buffer::Instance& response); - void processHostSet(Json::Streamer::Array* raw_hosts_statuses_ptr, - const Upstream::HostSetPtr& host_set, Buffer::Instance& response); - void processHost(Json::Streamer::Array* raw_host_statuses_ptr, - const Upstream::HostSharedPtr& host, Buffer::Instance& response); - void buildHostStats(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, - Buffer::Instance& response); - void setHealthFlags(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, - Buffer::Instance& response); - void setLocality(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, - Buffer::Instance& response); - void setSuccessRate(Json::Streamer::Map* raw_host_statuses_ptr, - const Upstream::HostSharedPtr& host, Buffer::Instance& response); - void setHostname(const Upstream::HostSharedPtr& host, - std::vector& top_level_entries); - void loadHealthFlagMap( - absl::btree_map>& flag_map, - Upstream::Host::HealthFlag flag, const Upstream::HostSharedPtr& host); - - const uint64_t chunk_limit_; - Http::ResponseHeaderMap& response_headers_; - std::vector> clusters_; - Buffer::OwnedImpl buffer_; - std::vector> json_context_holder_; - uint64_t idx_; -}; - -} // namespace Server -} // namespace Envoy diff --git a/source/server/admin/clusters_request.cc b/source/server/admin/clusters_request.cc index 7583080e2cff..25bd08b02606 100644 --- a/source/server/admin/clusters_request.cc +++ b/source/server/admin/clusters_request.cc @@ -3,12 +3,12 @@ #include #include +#include "envoy/admin/v3/clusters.pb.h" #include "envoy/server/instance.h" #include "source/common/common/logger.h" #include "source/common/http/headers.h" #include "source/common/upstream/host_utility.h" -#include "source/server/admin/clusters_chunk_processor.h" #include "clusters_params.h" diff --git a/source/server/admin/clusters_request.h b/source/server/admin/clusters_request.h index 8a3a51a89510..106d02872186 100644 --- a/source/server/admin/clusters_request.h +++ b/source/server/admin/clusters_request.h @@ -7,7 +7,8 @@ #include "envoy/server/admin.h" #include "envoy/server/instance.h" -#include "source/server/admin/clusters_chunk_processor.h" +#include "source/common/buffer/buffer_impl.h" +#include "source/common/json/json_streamer.h" #include "source/server/admin/clusters_params.h" namespace Envoy { @@ -54,6 +55,22 @@ class TextClustersRequest : public ClustersRequest { uint64_t idx_; }; +/** + * ClustersJsonContext holds an Envoy::Json::Streamer and the top-level JSON objects throughout the + * duration of a request. When it is destructed, the buffer will terminate the array and object + * herein. See the Envoy::Json::Streamer implementation for details. + */ +struct ClustersJsonContext { + ClustersJsonContext(std::unique_ptr streamer, Buffer::Instance& buffer, + Json::Streamer::MapPtr root_map, Json::Streamer::ArrayPtr clusters) + : streamer_(std::move(streamer)), buffer_(buffer), root_map_(std::move(root_map)), + clusters_(std::move(clusters)) {} + std::unique_ptr streamer_; + Buffer::Instance& buffer_; + Json::Streamer::MapPtr root_map_; + Json::Streamer::ArrayPtr clusters_; +}; + /** * JsonClustersChunkProcessor streams the response and emulates the JSON encoding behavior of * the gRPC clients; specifically, "zero-value" values such as false, 0, empty string, and diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index 0dd826e09813..594f102a8407 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -281,7 +281,6 @@ envoy_cc_test( "//envoy/server:instance_interface", "//envoy/stream_info:stream_info_interface", "//source/common/buffer:buffer_lib", - "//source/server/admin:clusters_chunk_processor_lib", "//source/server/admin:clusters_params_lib", "//source/server/admin:clusters_request_lib", "//test/mocks/server:instance_mocks", From 7379a1e2228cc9eb0ee92ae71c70cab47812d592 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Sat, 25 May 2024 19:45:05 +0000 Subject: [PATCH 70/77] Resolve: control reaches end of non-void function Signed-off-by: Demitri Swan --- test/server/admin/clusters_request_test.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index 36f7012d4a7c..03d1a45e3732 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -63,6 +63,7 @@ class BaseClustersRequestFixture : public testing::Test { case ClustersParams::Format::Unknown: return nullptr; } + return nullptr; } struct ResponseResult { From f910712f8a72c45667ab17ae76d1ff02722cd617 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Sat, 25 May 2024 21:29:51 +0000 Subject: [PATCH 71/77] Remove unused code from the clusters_params_test Signed-off-by: Demitri Swan --- test/server/admin/clusters_params_test.cc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test/server/admin/clusters_params_test.cc b/test/server/admin/clusters_params_test.cc index 326f2f0ec999..bc8e0eb778b6 100644 --- a/test/server/admin/clusters_params_test.cc +++ b/test/server/admin/clusters_params_test.cc @@ -7,14 +7,6 @@ namespace Envoy { namespace Server { namespace { -struct ParamsCase { - std::string url_; - ClustersParams::Format expected_format_; - Http::Code expected_code_; -}; - -class ParamsFixture : public testing::TestWithParam {}; - TEST(ClustersParamsTest, FormatDefaultsToTextWhenNotSupplied) { ClustersParams params; Buffer::OwnedImpl buffer; From 22c596123a5cc08666eced7441d2683071f29200 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 5 Jun 2024 22:07:11 +0000 Subject: [PATCH 72/77] Remove raw pointers from API surface in clusters_request.h and clusters_request.cc Signed-off-by: Demitri Swan --- source/server/admin/clusters_request.cc | 138 ++++++++++++------------ source/server/admin/clusters_request.h | 32 +++--- 2 files changed, 84 insertions(+), 86 deletions(-) diff --git a/source/server/admin/clusters_request.cc b/source/server/admin/clusters_request.cc index 25bd08b02606..0bf6aff4c271 100644 --- a/source/server/admin/clusters_request.cc +++ b/source/server/admin/clusters_request.cc @@ -200,46 +200,46 @@ void JsonClustersRequest::render(std::reference_wrapper top_level_entries.emplace_back("eds_service_name", eds_service_name); } - addCircuitBreakers(cluster_map.get(), cluster_info, response); - addEjectionThresholds(cluster_map.get(), cluster.get(), response); + addCircuitBreakers(*cluster_map.get(), cluster_info, response); + addEjectionThresholds(*cluster_map.get(), cluster.get(), response); if (bool added_via_api = cluster_info->addedViaApi(); added_via_api) { top_level_entries.emplace_back("added_via_api", added_via_api); } - addMapEntries(cluster_map.get(), response, top_level_entries); - addHostStatuses(cluster_map.get(), cluster, response); + addMapEntries(*cluster_map.get(), response, top_level_entries); + addHostStatuses(*cluster_map.get(), cluster, response); } -void JsonClustersRequest::addHostStatuses(Json::Streamer::Map* raw_clusters_map_ptr, +void JsonClustersRequest::addHostStatuses(Json::Streamer::Map& map_json, const Upstream::Cluster& unwrapped_cluster, Buffer::Instance& response) { - raw_clusters_map_ptr->addKey("host_statuses"); - Json::Streamer::ArrayPtr host_statuses_ptr = raw_clusters_map_ptr->addArray(); + map_json.addKey("host_statuses"); + Json::Streamer::ArrayPtr host_statuses_ptr = map_json.addArray(); for (const Upstream::HostSetPtr& host_set : unwrapped_cluster.prioritySet().hostSetsPerPriority()) { - processHostSet(host_statuses_ptr.get(), host_set, response); + processHostSet(*host_statuses_ptr.get(), host_set, response); } } -void JsonClustersRequest::processHostSet(Json::Streamer::Array* raw_host_statuses_ptr, +void JsonClustersRequest::processHostSet(Json::Streamer::Array& array_json, const Upstream::HostSetPtr& host_set, Buffer::Instance& response) { for (const Upstream::HostSharedPtr& host : host_set->hosts()) { - processHost(raw_host_statuses_ptr, host, response); + processHost(array_json, host, response); } } -void JsonClustersRequest::processHost(Json::Streamer::Array* raw_host_statuses_ptr, +void JsonClustersRequest::processHost(Json::Streamer::Array& array_json, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { Buffer::OwnedImpl buffer; - Json::Streamer::MapPtr host_ptr = raw_host_statuses_ptr->addMap(); + Json::Streamer::MapPtr host_ptr = array_json.addMap(); std::vector host_config; setHostname(host, host_config); - addAddress(host_ptr.get(), host, buffer); - setLocality(host_ptr.get(), host, buffer); - buildHostStats(host_ptr.get(), host, buffer); - setHealthFlags(host_ptr.get(), host, buffer); - setSuccessRate(host_ptr.get(), host, buffer); + addAddress(*host_ptr.get(), host, buffer); + setLocality(*host_ptr.get(), host, buffer); + buildHostStats(*host_ptr.get(), host, buffer); + setHealthFlags(*host_ptr.get(), host, buffer); + setSuccessRate(*host_ptr.get(), host, buffer); if (uint64_t weight = host->weight(); weight) { host_config.emplace_back("weight", weight); @@ -252,7 +252,7 @@ void JsonClustersRequest::processHost(Json::Streamer::Array* raw_host_statuses_p response.move(buffer); } if (!host_config.empty()) { - addMapEntries(host_ptr.get(), response, host_config); + addMapEntries(*host_ptr.get(), response, host_config); } } @@ -263,7 +263,7 @@ void JsonClustersRequest::setHostname(const Upstream::HostSharedPtr& host, } } -void JsonClustersRequest::setSuccessRate(Json::Streamer::Map* raw_host_statuses_ptr, +void JsonClustersRequest::setSuccessRate(Json::Streamer::Map& map_json, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { @@ -276,21 +276,21 @@ void JsonClustersRequest::setSuccessRate(Json::Streamer::Map* raw_host_statuses_ } if (external_success_rate >= 0.0) { - raw_host_statuses_ptr->addKey("success_rate"); - Json::Streamer::MapPtr success_rate_map = raw_host_statuses_ptr->addMap(); + map_json.addKey("success_rate"); + Json::Streamer::MapPtr success_rate_map = map_json.addMap(); std::vector success_rate{{"value", external_success_rate}}; - addMapEntries(success_rate_map.get(), response, success_rate); + addMapEntries(*success_rate_map.get(), response, success_rate); } if (local_success_rate >= 0.0) { - raw_host_statuses_ptr->addKey("local_origin_success_rate"); - Json::Streamer::MapPtr local_origin_map = raw_host_statuses_ptr->addMap(); + map_json.addKey("local_origin_success_rate"); + Json::Streamer::MapPtr local_origin_map = map_json.addMap(); std::vector local_origin_success_rate{ {"value", local_success_rate}}; - addMapEntries(local_origin_map.get(), response, local_origin_success_rate); + addMapEntries(*local_origin_map.get(), response, local_origin_success_rate); } } -void JsonClustersRequest::setLocality(Json::Streamer::Map* raw_host_ptr, +void JsonClustersRequest::setLocality(Json::Streamer::Map& map_json, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { @@ -298,8 +298,8 @@ void JsonClustersRequest::setLocality(Json::Streamer::Map* raw_host_ptr, host->locality().sub_zone().empty()) { return; } - raw_host_ptr->addKey("locality"); - Json::Streamer::MapPtr locality_ptr = raw_host_ptr->addMap(); + map_json.addKey("locality"); + Json::Streamer::MapPtr locality_ptr = map_json.addMap(); std::vector locality; if (const std::string& region = host->locality().region(); !region.empty()) { locality.emplace_back("region", region); @@ -310,30 +310,30 @@ void JsonClustersRequest::setLocality(Json::Streamer::Map* raw_host_ptr, if (const std::string& sub_zone = host->locality().sub_zone(); !sub_zone.empty()) { locality.emplace_back("sub_zone", sub_zone); } - addMapEntries(locality_ptr.get(), response, locality); + addMapEntries(*locality_ptr.get(), response, locality); } -void JsonClustersRequest::addAddress(Json::Streamer::Map* raw_host_ptr, +void JsonClustersRequest::addAddress(Json::Streamer::Map& map_json, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { switch (host->address()->type()) { case Network::Address::Type::Pipe: { if (const std::string& path = host->address()->asString(); !path.empty()) { - raw_host_ptr->addKey("address"); - Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); + map_json.addKey("address"); + Json::Streamer::MapPtr address_ptr = map_json.addMap(); address_ptr->addKey("pipe"); Json::Streamer::MapPtr pipe_ptr = address_ptr->addMap(); std::vector pipe{ {"pipe", host->address()->asString()}, }; - addMapEntries(pipe_ptr.get(), response, pipe); + addMapEntries(*pipe_ptr.get(), response, pipe); } break; } case Network::Address::Type::Ip: { if (!host->address()->ip()->addressAsString().empty() || host->address()->ip()->port()) { - raw_host_ptr->addKey("address"); - Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); + map_json.addKey("address"); + Json::Streamer::MapPtr address_ptr = map_json.addMap(); address_ptr->addKey("socket_address"); Json::Streamer::MapPtr socket_address_ptr = address_ptr->addMap(); std::vector socket_address; @@ -343,17 +343,17 @@ void JsonClustersRequest::addAddress(Json::Streamer::Map* raw_host_ptr, if (uint64_t port = uint64_t(host->address()->ip()->port()); port) { socket_address.emplace_back("port_value", port); } - addMapEntries(socket_address_ptr.get(), response, socket_address); + addMapEntries(*socket_address_ptr.get(), response, socket_address); } break; } case Network::Address::Type::EnvoyInternal: { if (!host->address()->envoyInternalAddress()->addressId().empty() || !host->address()->envoyInternalAddress()->endpointId().empty()) { - raw_host_ptr->addKey("address"); - Json::Streamer::MapPtr address_ptr = raw_host_ptr->addMap(); - raw_host_ptr->addKey("envoy_internal_address"); - Json::Streamer::MapPtr envoy_internal_address_ptr = raw_host_ptr->addMap(); + map_json.addKey("address"); + Json::Streamer::MapPtr address_ptr = map_json.addMap(); + map_json.addKey("envoy_internal_address"); + Json::Streamer::MapPtr envoy_internal_address_ptr = map_json.addMap(); std::vector envoy_internal_address; if (const std::string& server_listener_name = host->address()->envoyInternalAddress()->addressId(); @@ -364,21 +364,21 @@ void JsonClustersRequest::addAddress(Json::Streamer::Map* raw_host_ptr, !endpoint_id.empty()) { envoy_internal_address.emplace_back("endpoint_id", endpoint_id); } - addMapEntries(envoy_internal_address_ptr.get(), response, envoy_internal_address); + addMapEntries(*envoy_internal_address_ptr.get(), response, envoy_internal_address); } break; } } } -void JsonClustersRequest::buildHostStats(Json::Streamer::Map* raw_host_ptr, +void JsonClustersRequest::buildHostStats(Json::Streamer::Map& map_json, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { if (host->counters().empty() && host->gauges().empty()) { return; } - raw_host_ptr->addKey("stats"); - Json::Streamer::ArrayPtr stats_ptr = raw_host_ptr->addArray(); + map_json.addKey("stats"); + Json::Streamer::ArrayPtr stats_ptr = map_json.addArray(); for (const auto& [counter_name, counter] : host->counters()) { if (counter_name.empty() || counter.get().value() == 0) { continue; @@ -393,7 +393,7 @@ void JsonClustersRequest::buildHostStats(Json::Streamer::Map* raw_host_ptr, counter_object.emplace_back("value", value); } Json::Streamer::MapPtr stats_obj_ptr = stats_ptr->addMap(); - addMapEntries(stats_obj_ptr.get(), response, counter_object); + addMapEntries(*stats_obj_ptr.get(), response, counter_object); } for (const auto& [gauge_name, gauge] : host->gauges()) { if (gauge_name.empty() || gauge.get().value() == 0) { @@ -408,11 +408,11 @@ void JsonClustersRequest::buildHostStats(Json::Streamer::Map* raw_host_ptr, gauge_object.emplace_back("value", value); } Json::Streamer::MapPtr stats_obj_ptr = stats_ptr->addMap(); - addMapEntries(stats_obj_ptr.get(), response, gauge_object); + addMapEntries(*stats_obj_ptr.get(), response, gauge_object); } } -void JsonClustersRequest::setHealthFlags(Json::Streamer::Map* raw_host_ptr, +void JsonClustersRequest::setHealthFlags(Json::Streamer::Map& map_json, const Upstream::HostSharedPtr& host, Buffer::Instance& response) { absl::btree_map> flag_map; @@ -424,8 +424,8 @@ void JsonClustersRequest::setHealthFlags(Json::Streamer::Map* raw_host_ptr, if (flag_map.empty()) { return; } - raw_host_ptr->addKey("health_status"); - Json::Streamer::MapPtr health_flags_ptr = raw_host_ptr->addMap(); + map_json.addKey("health_status"); + Json::Streamer::MapPtr health_flags_ptr = map_json.addMap(); std::vector flags; for (const auto& [name, flag_value] : flag_map) { if (name == "eds_health_status") { @@ -438,7 +438,7 @@ void JsonClustersRequest::setHealthFlags(Json::Streamer::Map* raw_host_ptr, } } } - addMapEntries(health_flags_ptr.get(), response, flags); + addMapEntries(*health_flags_ptr.get(), response, flags); } void JsonClustersRequest::loadHealthFlagMap( @@ -505,56 +505,55 @@ void JsonClustersRequest::loadHealthFlagMap( } } -void JsonClustersRequest::addEjectionThresholds(Json::Streamer::Map* raw_clusters_map_ptr, +void JsonClustersRequest::addEjectionThresholds(Json::Streamer::Map& map_json, const Upstream::Cluster& unwrapped_cluster, Buffer::Instance& response) { const Upstream::Outlier::Detector* outlier_detector = unwrapped_cluster.outlierDetector(); if (outlier_detector != nullptr && outlier_detector->successRateEjectionThreshold( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin) > 0.0) { - raw_clusters_map_ptr->addKey("success_rate_ejection_threshold"); - Json::Streamer::MapPtr success_rate_map_ptr = raw_clusters_map_ptr->addMap(); + map_json.addKey("success_rate_ejection_threshold"); + Json::Streamer::MapPtr success_rate_map_ptr = map_json.addMap(); std::vector success_rate_ejection_threshold; success_rate_ejection_threshold.emplace_back( "value", double(outlier_detector->successRateEjectionThreshold( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin))); - addMapEntries(success_rate_map_ptr.get(), response, success_rate_ejection_threshold); + addMapEntries(*success_rate_map_ptr.get(), response, success_rate_ejection_threshold); } if (outlier_detector != nullptr && outlier_detector->successRateEjectionThreshold( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin) > 0.0) { - raw_clusters_map_ptr->addKey("local_origin_success_rate_ejection_threshold"); - Json::Streamer::MapPtr local_success_rate_map_ptr = raw_clusters_map_ptr->addMap(); + map_json.addKey("local_origin_success_rate_ejection_threshold"); + Json::Streamer::MapPtr local_success_rate_map_ptr = map_json.addMap(); std::vector local_origin_success_rate_ejection_threshold; local_origin_success_rate_ejection_threshold.emplace_back( "value", double(outlier_detector->successRateEjectionThreshold( Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin))); - addMapEntries(local_success_rate_map_ptr.get(), response, + addMapEntries(*local_success_rate_map_ptr.get(), response, local_origin_success_rate_ejection_threshold); } } -void JsonClustersRequest::addCircuitBreakers(Json::Streamer::Map* raw_clusters_map_ptr, +void JsonClustersRequest::addCircuitBreakers(Json::Streamer::Map& raw_clusters_map_ptr, Upstream::ClusterInfoConstSharedPtr cluster_info, Buffer::Instance& response) { - raw_clusters_map_ptr->addKey("circuit_breakers"); - Json::Streamer::MapPtr circuit_breakers = raw_clusters_map_ptr->addMap(); + raw_clusters_map_ptr.addKey("circuit_breakers"); + Json::Streamer::MapPtr circuit_breakers = raw_clusters_map_ptr.addMap(); circuit_breakers->addKey("thresholds"); Json::Streamer::ArrayPtr thresholds = circuit_breakers->addArray(); - addCircuitBreakerForPriority(envoy::config::core::v3::RoutingPriority::DEFAULT, thresholds.get(), + addCircuitBreakerForPriority(envoy::config::core::v3::RoutingPriority::DEFAULT, *thresholds.get(), response, cluster_info->resourceManager(Upstream::ResourcePriority::Default)); - addCircuitBreakerForPriority(envoy::config::core::v3::RoutingPriority::HIGH, thresholds.get(), + addCircuitBreakerForPriority(envoy::config::core::v3::RoutingPriority::HIGH, *thresholds.get(), response, cluster_info->resourceManager(Upstream::ResourcePriority::High)); } void JsonClustersRequest::addCircuitBreakerForPriority( - const envoy::config::core::v3::RoutingPriority& priority, - Json::Streamer::Array* raw_thresholds_ptr, Buffer::Instance& response, - Upstream::ResourceManager& resource_manager) { - Json::Streamer::MapPtr threshold = raw_thresholds_ptr->addMap(); + const envoy::config::core::v3::RoutingPriority& priority, Json::Streamer::Array& array_json, + Buffer::Instance& response, Upstream::ResourceManager& resource_manager) { + Json::Streamer::MapPtr threshold = array_json.addMap(); std::vector entries{ {"priority", priority == envoy::config::core::v3::RoutingPriority::DEFAULT ? "DEFAULT" : "HIGH"}}; @@ -571,7 +570,7 @@ void JsonClustersRequest::addCircuitBreakerForPriority( if (uint64_t max_retries = resource_manager.retries().max(); max_retries) { entries.emplace_back("max_retries", max_retries); } - addMapEntries(threshold.get(), response, entries); + addMapEntries(*threshold.get(), response, entries); } // Json::Streamer holds a reference to a Buffer::Instance reference but the API for Request @@ -584,10 +583,9 @@ void JsonClustersRequest::drainBufferIntoResponse(Buffer::Instance& response) { } } -void JsonClustersRequest::addMapEntries(Json::Streamer::Map* raw_map_ptr, - Buffer::Instance& response, +void JsonClustersRequest::addMapEntries(Json::Streamer::Map& map_json, Buffer::Instance& response, std::vector& entries) { - raw_map_ptr->addEntries(entries); + map_json.addEntries(entries); drainBufferIntoResponse(response); } diff --git a/source/server/admin/clusters_request.h b/source/server/admin/clusters_request.h index 106d02872186..2e4cffb8fb38 100644 --- a/source/server/admin/clusters_request.h +++ b/source/server/admin/clusters_request.h @@ -86,33 +86,33 @@ class JsonClustersRequest : public ClustersRequest { void render(std::reference_wrapper cluster, Buffer::Instance& response); void drainBufferIntoResponse(Buffer::Instance& response); void finalize(Buffer::Instance& response); - void addAddress(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, + void addAddress(Json::Streamer::Map& map_json, const Upstream::HostSharedPtr& host, Buffer::Instance& response); - void addMapEntries(Json::Streamer::Map* raw_map_ptr, Buffer::Instance& response, + void addMapEntries(Json::Streamer::Map& map_json, Buffer::Instance& response, std::vector& entries); - void addCircuitBreakers(Json::Streamer::Map* raw_clusters_map_ptr, + void addCircuitBreakers(Json::Streamer::Map& map_json, Upstream::ClusterInfoConstSharedPtr cluster_info, Buffer::Instance& response); void addCircuitBreakerForPriority(const envoy::config::core::v3::RoutingPriority& priority, - Json::Streamer::Array* raw_map_ptr, Buffer::Instance& response, + Json::Streamer::Array& array_json, Buffer::Instance& response, Upstream::ResourceManager& resource_manager); - void addEjectionThresholds(Json::Streamer::Map* raw_clusters_map_ptr, + void addEjectionThresholds(Json::Streamer::Map& map_json, const Upstream::Cluster& unwrapped_cluster, Buffer::Instance& response); - void addHostStatuses(Json::Streamer::Map* raw_clusters_map_ptr, - const Upstream::Cluster& unwrapped_cluster, Buffer::Instance& response); - void processHostSet(Json::Streamer::Array* raw_hosts_statuses_ptr, - const Upstream::HostSetPtr& host_set, Buffer::Instance& response); - void processHost(Json::Streamer::Array* raw_host_statuses_ptr, - const Upstream::HostSharedPtr& host, Buffer::Instance& response); - void buildHostStats(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, + void addHostStatuses(Json::Streamer::Map& map_json, const Upstream::Cluster& unwrapped_cluster, + Buffer::Instance& response); + void processHostSet(Json::Streamer::Array& array_json, const Upstream::HostSetPtr& host_set, Buffer::Instance& response); - void setHealthFlags(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, + void processHost(Json::Streamer::Array& map_json, const Upstream::HostSharedPtr& host, + Buffer::Instance& response); + void buildHostStats(Json::Streamer::Map& map_json, const Upstream::HostSharedPtr& host, + Buffer::Instance& response); + void setHealthFlags(Json::Streamer::Map& map_json, const Upstream::HostSharedPtr& host, Buffer::Instance& response); - void setLocality(Json::Streamer::Map* raw_host_ptr, const Upstream::HostSharedPtr& host, + void setLocality(Json::Streamer::Map& map_json, const Upstream::HostSharedPtr& host, Buffer::Instance& response); - void setSuccessRate(Json::Streamer::Map* raw_host_statuses_ptr, - const Upstream::HostSharedPtr& host, Buffer::Instance& response); + void setSuccessRate(Json::Streamer::Map& map_json, const Upstream::HostSharedPtr& host, + Buffer::Instance& response); void setHostname(const Upstream::HostSharedPtr& host, std::vector& top_level_entries); void loadHealthFlagMap( From c9b91312c9cd3806fbe03c152a1fa89e51e99760 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 5 Jun 2024 22:20:10 +0000 Subject: [PATCH 73/77] Remove default case from switch statement in clusters_handler Signed-off-by: Demitri Swan --- source/server/admin/clusters_handler.cc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/source/server/admin/clusters_handler.cc b/source/server/admin/clusters_handler.cc index 9c819a48d343..dd3ddcc8a77d 100644 --- a/source/server/admin/clusters_handler.cc +++ b/source/server/admin/clusters_handler.cc @@ -87,16 +87,19 @@ Admin::RequestPtr ClustersHandler::makeRequest(AdminStream& admin_stream) { if (code != Http::Code::OK) { return Admin::makeStaticTextRequest(response, code); } + Admin::RequestPtr request{nullptr}; switch (params.format_) { + case ClustersParams::Format::Unknown: + request = Admin::makeStaticTextRequest("unknown format type", Http::Code::BadRequest); case ClustersParams::Format::Text: - return std::make_unique(ClustersRequest::DefaultChunkLimit, server_, - params); + request = + std::make_unique(ClustersRequest::DefaultChunkLimit, server_, params); case ClustersParams::Format::Json: - return std::make_unique(ClustersRequest::DefaultChunkLimit, server_, - params); - default: - return Admin::makeStaticTextRequest("unknown format type", Http::Code::BadRequest); + request = + std::make_unique(ClustersRequest::DefaultChunkLimit, server_, params); } + ASSERT(request != nullptr); + return request; } // Helper method that ensures that we've setting flags based on all the health flag values on the From 72710182bad3be1d3ee63621530d72dfe10439cc Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 5 Jun 2024 22:27:33 +0000 Subject: [PATCH 74/77] Remove default constructor for ClustersParams and add necessary break statement to switch in clusters_handler Signed-off-by: Demitri Swan --- source/server/admin/clusters_handler.cc | 3 +++ source/server/admin/clusters_params.h | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/source/server/admin/clusters_handler.cc b/source/server/admin/clusters_handler.cc index dd3ddcc8a77d..019e518c426b 100644 --- a/source/server/admin/clusters_handler.cc +++ b/source/server/admin/clusters_handler.cc @@ -91,12 +91,15 @@ Admin::RequestPtr ClustersHandler::makeRequest(AdminStream& admin_stream) { switch (params.format_) { case ClustersParams::Format::Unknown: request = Admin::makeStaticTextRequest("unknown format type", Http::Code::BadRequest); + break; case ClustersParams::Format::Text: request = std::make_unique(ClustersRequest::DefaultChunkLimit, server_, params); + break; case ClustersParams::Format::Json: request = std::make_unique(ClustersRequest::DefaultChunkLimit, server_, params); + break; } ASSERT(request != nullptr); return request; diff --git a/source/server/admin/clusters_params.h b/source/server/admin/clusters_params.h index 1e7e638c60c7..a801853e7de1 100644 --- a/source/server/admin/clusters_params.h +++ b/source/server/admin/clusters_params.h @@ -15,8 +15,6 @@ struct ClustersParams { Json, }; - ClustersParams() = default; - Http::Code parse(absl::string_view url, Buffer::Instance& response); Format format_{Format::Text}; From c8d379bb190a51639e061d7d1d8b8aefd4b93057 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 5 Jun 2024 22:36:09 +0000 Subject: [PATCH 75/77] Remove UNKNOWN format from ClustersParams Signed-off-by: Demitri Swan --- source/server/admin/clusters_handler.cc | 3 --- source/server/admin/clusters_params.cc | 1 - source/server/admin/clusters_params.h | 1 - test/server/admin/clusters_params_test.cc | 4 +--- test/server/admin/clusters_request_test.cc | 2 -- 5 files changed, 1 insertion(+), 10 deletions(-) diff --git a/source/server/admin/clusters_handler.cc b/source/server/admin/clusters_handler.cc index 019e518c426b..ce09553da76e 100644 --- a/source/server/admin/clusters_handler.cc +++ b/source/server/admin/clusters_handler.cc @@ -89,9 +89,6 @@ Admin::RequestPtr ClustersHandler::makeRequest(AdminStream& admin_stream) { } Admin::RequestPtr request{nullptr}; switch (params.format_) { - case ClustersParams::Format::Unknown: - request = Admin::makeStaticTextRequest("unknown format type", Http::Code::BadRequest); - break; case ClustersParams::Format::Text: request = std::make_unique(ClustersRequest::DefaultChunkLimit, server_, params); diff --git a/source/server/admin/clusters_params.cc b/source/server/admin/clusters_params.cc index 2e574afabd20..51bc82da2d70 100644 --- a/source/server/admin/clusters_params.cc +++ b/source/server/admin/clusters_params.cc @@ -20,7 +20,6 @@ Http::Code ClustersParams::parse(absl::string_view url, Buffer::Instance& respon format_ = Format::Text; } else { response.addFragments({"invalid format ", *optional_format}); - format_ = Format::Unknown; return Http::Code::BadRequest; } } diff --git a/source/server/admin/clusters_params.h b/source/server/admin/clusters_params.h index a801853e7de1..a0a440f4d350 100644 --- a/source/server/admin/clusters_params.h +++ b/source/server/admin/clusters_params.h @@ -10,7 +10,6 @@ namespace Server { struct ClustersParams { enum class Format { - Unknown, Text, Json, }; diff --git a/test/server/admin/clusters_params_test.cc b/test/server/admin/clusters_params_test.cc index bc8e0eb778b6..93b1f88eea51 100644 --- a/test/server/admin/clusters_params_test.cc +++ b/test/server/admin/clusters_params_test.cc @@ -46,16 +46,14 @@ TEST(ClustersParamsTest, FormatSetToJsonWhenJsonSupplied) { EXPECT_EQ(code, expected_code); } -TEST(ClustersParamsTest, FormatSetToUnknownWhenInvalidFormatSupplied) { +TEST(ClustersParamsTest, ReturnsBadRequestWhenInvalidFormatSupplied) { ClustersParams params; Buffer::OwnedImpl buffer; std::string url = "localhost:1337/clusters?format=fail"; - ClustersParams::Format expected_format = ClustersParams::Format::Unknown; Http::Code expected_code = Http::Code::BadRequest; Http::Code code = params.parse(url, buffer); - EXPECT_EQ(params.format_, expected_format); EXPECT_EQ(code, expected_code); } diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index 03d1a45e3732..71ee486de661 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -60,8 +60,6 @@ class BaseClustersRequestFixture : public testing::Test { return std::make_unique(chunk_limit, mock_server_, params); case ClustersParams::Format::Json: return std::make_unique(chunk_limit, mock_server_, params); - case ClustersParams::Format::Unknown: - return nullptr; } return nullptr; } From 51ab1e1eb0289b5ff4e76e59a99a68684eddabf0 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 5 Jun 2024 22:39:30 +0000 Subject: [PATCH 76/77] Update documentation comment for ClustersRequest Signed-off-by: Demitri Swan --- source/server/admin/clusters_request.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/server/admin/clusters_request.h b/source/server/admin/clusters_request.h index 2e4cffb8fb38..582dfbd7d3b3 100644 --- a/source/server/admin/clusters_request.h +++ b/source/server/admin/clusters_request.h @@ -15,8 +15,7 @@ namespace Envoy { namespace Server { /** - * ClustersRequest captures context for a streaming request, implementing the - * Admin::Request interface. + * ClustersRequest captures context for a streaming /clusters request. */ class ClustersRequest : public Admin::Request { public: From 6bb869e68492aa3f1194e33953fae463b8f699f5 Mon Sep 17 00:00:00 2001 From: Demitri Swan Date: Wed, 5 Jun 2024 22:48:04 +0000 Subject: [PATCH 77/77] Cleanup unit tests Signed-off-by: Demitri Swan --- test/server/admin/clusters_params_test.cc | 37 ++++------------------ test/server/admin/clusters_request_test.cc | 4 +-- 2 files changed, 9 insertions(+), 32 deletions(-) diff --git a/test/server/admin/clusters_params_test.cc b/test/server/admin/clusters_params_test.cc index 93b1f88eea51..c9fe2479a669 100644 --- a/test/server/admin/clusters_params_test.cc +++ b/test/server/admin/clusters_params_test.cc @@ -10,51 +10,28 @@ namespace { TEST(ClustersParamsTest, FormatDefaultsToTextWhenNotSupplied) { ClustersParams params; Buffer::OwnedImpl buffer; - std::string url = "localhost:1337/clusters"; - ClustersParams::Format expected_format = ClustersParams::Format::Text; - Http::Code expected_code = Http::Code::OK; - - Http::Code code = params.parse(url, buffer); - - EXPECT_EQ(params.format_, expected_format); - EXPECT_EQ(code, expected_code); + EXPECT_EQ(params.format_, ClustersParams::Format::Text); + EXPECT_EQ(params.parse("localhost:1337/clusters", buffer), Http::Code::OK); } TEST(ClustersParamsTest, FormatSetToTextWhenTextSupplied) { ClustersParams params; Buffer::OwnedImpl buffer; - std::string url = "localhost:1337/clusters?format=text"; - ClustersParams::Format expected_format = ClustersParams::Format::Text; - Http::Code expected_code = Http::Code::OK; - - Http::Code code = params.parse(url, buffer); - - EXPECT_EQ(params.format_, expected_format); - EXPECT_EQ(code, expected_code); + EXPECT_EQ(params.format_, ClustersParams::Format::Text); + EXPECT_EQ(params.parse("localhost:1337/clusters?format=text", buffer), Http::Code::OK); } TEST(ClustersParamsTest, FormatSetToJsonWhenJsonSupplied) { ClustersParams params; Buffer::OwnedImpl buffer; - std::string url = "localhost:1337/clusters?format=json"; - ClustersParams::Format expected_format = ClustersParams::Format::Json; - Http::Code expected_code = Http::Code::OK; - - Http::Code code = params.parse(url, buffer); - - EXPECT_EQ(params.format_, expected_format); - EXPECT_EQ(code, expected_code); + EXPECT_EQ(params.format_, ClustersParams::Format::Text); + EXPECT_EQ(params.parse("localhost:1337/clusters?format=json", buffer), Http::Code::OK); } TEST(ClustersParamsTest, ReturnsBadRequestWhenInvalidFormatSupplied) { ClustersParams params; Buffer::OwnedImpl buffer; - std::string url = "localhost:1337/clusters?format=fail"; - Http::Code expected_code = Http::Code::BadRequest; - - Http::Code code = params.parse(url, buffer); - - EXPECT_EQ(code, expected_code); + EXPECT_EQ(params.parse("localhost:1337/clusters?format=fail", buffer), Http::Code::BadRequest); } } // namespace diff --git a/test/server/admin/clusters_request_test.cc b/test/server/admin/clusters_request_test.cc index 71ee486de661..0c82db279edd 100644 --- a/test/server/admin/clusters_request_test.cc +++ b/test/server/admin/clusters_request_test.cc @@ -108,9 +108,9 @@ class BaseClustersRequestFixture : public testing::Test { ON_CALL(*mock_cluster.info_, addedViaApi()).WillByDefault(Return(true)); cluster_info_maps_.active_clusters_.emplace(name, std::ref(mock_cluster)); - Network::Address::InstanceConstSharedPtr address = + absl::StatusOr address = Network::Utility::resolveUrl("tcp://1.2.3.4:80"); - ON_CALL(*mock_host_, address()).WillByDefault(Return(address)); + ON_CALL(*mock_host_, address()).WillByDefault(Return(address.value())); Upstream::MockHostSet* host_set = mock_cluster.priority_set_.getMockHostSet(0); ON_CALL(*mock_host_, counters()).WillByDefault(Return(counters_));