From d2d6d161bbb7633543c8d421128b1adee954ccb2 Mon Sep 17 00:00:00 2001 From: talis Date: Sun, 4 Mar 2018 12:22:49 +0200 Subject: [PATCH 01/25] support for hystrix event stream output Signed-off-by: talis --- include/envoy/http/header_map.h | 1 + include/envoy/server/BUILD | 2 + include/envoy/server/admin.h | 46 ++++++- source/common/http/headers.h | 12 ++ source/common/http/http1/codec_impl.cc | 11 +- source/common/router/rds_impl.cc | 3 +- source/common/router/rds_impl.h | 2 +- source/common/stats/BUILD | 6 + source/common/stats/hystrix.cc | 178 +++++++++++++++++++++++++ source/common/stats/hystrix.h | 99 ++++++++++++++ source/server/http/admin.cc | 169 +++++++++++++++++++---- source/server/http/admin.h | 77 ++++++++--- test/common/router/rds_impl_test.cc | 23 ++-- test/common/stats/BUILD | 8 ++ test/common/stats/hystrix_test.cc | 91 +++++++++++++ test/server/http/admin_test.cc | 61 +++++---- 16 files changed, 705 insertions(+), 84 deletions(-) create mode 100644 source/common/stats/hystrix.cc create mode 100644 source/common/stats/hystrix.h create mode 100644 test/common/stats/hystrix_test.cc diff --git a/include/envoy/http/header_map.h b/include/envoy/http/header_map.h index e9f3922543e8f..3c04c1148cf9f 100644 --- a/include/envoy/http/header_map.h +++ b/include/envoy/http/header_map.h @@ -281,6 +281,7 @@ class HeaderEntry { HEADER_FUNC(KeepAlive) \ HEADER_FUNC(LastModified) \ HEADER_FUNC(Method) \ + HEADER_FUNC(NoChunks) \ HEADER_FUNC(Origin) \ HEADER_FUNC(OtSpanContext) \ HEADER_FUNC(Path) \ diff --git a/include/envoy/server/BUILD b/include/envoy/server/BUILD index 67b5d12544f08..5539a60ddb7d8 100644 --- a/include/envoy/server/BUILD +++ b/include/envoy/server/BUILD @@ -24,7 +24,9 @@ envoy_cc_library( deps = [ "//include/envoy/buffer:buffer_interface", "//include/envoy/http:codes_interface", + "//include/envoy/http:filter_interface", "//include/envoy/network:listen_socket_interface", + "//source/common/stats:hystrix_lib", ], ) diff --git a/include/envoy/server/admin.h b/include/envoy/server/admin.h index 40d4259dcaaea..64714b21ccb1a 100644 --- a/include/envoy/server/admin.h +++ b/include/envoy/server/admin.h @@ -6,9 +6,12 @@ #include "envoy/buffer/buffer.h" #include "envoy/common/pure.h" #include "envoy/http/codes.h" +#include "envoy/http/filter.h" #include "envoy/http/header_map.h" #include "envoy/network/listen_socket.h" +#include "common/stats/hystrix.h" + namespace Envoy { namespace Server { @@ -19,8 +22,45 @@ namespace Server { * done in the RouteConfigProviderManagerImpl constructor in source/common/router/rds_impl.cc. */ #define MAKE_ADMIN_HANDLER(X) \ - [this](const std::string& url, Http::HeaderMap& response_headers, \ - Buffer::Instance& data) -> Http::Code { return X(url, response_headers, data); } + [this](const std::string& url, Http::HeaderMap& response_headers, Buffer::Instance& data, \ + Server::HandlerInfo& handler_info) -> Http::Code { \ + return X(url, response_headers, data, handler_info); \ + } + +/** + * This class is a base class for data which will be sent from admin filter to a handler + * in admin impl. Each handler which needs to receive data from admin filter can inherit from + * HandlerInfo and build a class which contains the relevant data. + */ +class HandlerInfo { +public: + HandlerInfo(){}; + virtual ~HandlerInfo(){}; + virtual void Destroy(){}; +}; +typedef std::unique_ptr HandlerInfoPtr; + +/** + * This class contains data which will be sent from admin filter to a hystrix_event_stream handler + * and build a class which contains the relevant data. + */ +class HystrixHandlerInfo : public HandlerInfo { +public: + HystrixHandlerInfo(Http::StreamDecoderFilterCallbacks* callbacks) + : stats_(new Stats::Hystrix()), data_timer_(nullptr), ping_timer_(nullptr), + callbacks_(callbacks) {} + virtual ~HystrixHandlerInfo(){}; + void Destroy(); + + /** + * HystrixHandlerInfo includes statistics for hystrix API, timers for build (and send) data and + * keep alive messages and the handler's callback + */ + Stats::HystrixPtr stats_; + Event::TimerPtr data_timer_; + Event::TimerPtr ping_timer_; + Http::StreamDecoderFilterCallbacks* callbacks_{}; +}; /** * Global admin HTTP endpoint for the server. @@ -38,7 +78,7 @@ class Admin { * @return Http::Code the response code. */ typedef std::function + Buffer::Instance& response, Server::HandlerInfo& handler_info)> HandlerCb; /** diff --git a/source/common/http/headers.h b/source/common/http/headers.h index 539e8b89ae4e2..ad985e1e7b865 100644 --- a/source/common/http/headers.h +++ b/source/common/http/headers.h @@ -70,6 +70,7 @@ class HeaderValues { const LowerCaseString LastModified{"last-modified"}; const LowerCaseString Location{"location"}; const LowerCaseString Method{":method"}; + const LowerCaseString NoChunks{":no-chunks"}; const LowerCaseString Origin{"origin"}; const LowerCaseString OtSpanContext{"x-ot-span-context"}; const LowerCaseString Path{":path"}; @@ -103,12 +104,14 @@ class HeaderValues { } UpgradeValues; struct { + const std::string NoCache{"no-cache"}; const std::string NoCacheMaxAge0{"no-cache, max-age=0"}; const std::string NoTransform{"no-transform"}; } CacheControlValues; struct { const std::string Text{"text/plain"}; + const std::string TextEventStream{"text/event-stream"}; const std::string TextUtf8{"text/plain; charset=UTF-8"}; // TODO(jmarantz): fold this into Text const std::string Html{"text/html; charset=UTF-8"}; const std::string Grpc{"application/grpc"}; @@ -207,6 +210,15 @@ class HeaderValues { const std::string AcceptEncoding{"Accept-Encoding"}; const std::string Wildcard{"*"}; } VaryValues; + + struct { + const std::string AccessControlAllowHeadersHystrix{ + "Accept, Cache-Control, X-Requested-With, Last-Event-ID"}; + } AccessControlAllowHeadersValue; + + struct { + const std::string All{"*"}; + } AccessControlAllowOriginValue; }; typedef ConstSingleton Headers; diff --git a/source/common/http/http1/codec_impl.cc b/source/common/http/http1/codec_impl.cc index 8b7c5a35a80cf..5d0a7d1895757 100644 --- a/source/common/http/http1/codec_impl.cc +++ b/source/common/http/http1/codec_impl.cc @@ -44,6 +44,7 @@ void StreamEncoderImpl::encode100ContinueHeaders(const HeaderMap& headers) { void StreamEncoderImpl::encodeHeaders(const HeaderMap& headers, bool end_stream) { bool saw_content_length = false; + bool no_chunks = false; headers.iterate( [](const HeaderEntry& header, void* context) -> HeaderMap::Iterate { const char* key_to_use = header.key().c_str(); @@ -69,12 +70,16 @@ void StreamEncoderImpl::encodeHeaders(const HeaderMap& headers, bool end_stream) saw_content_length = true; } + if (headers.NoChunks()) { + no_chunks = true; + } + ASSERT(!headers.TransferEncoding()); // Assume we are chunk encoding unless we are passed a content length or this is a header only // response. Upper layers generally should strip transfer-encoding since it only applies to // HTTP/1.1. The codec will infer it based on the type of response. - if (saw_content_length) { + if (saw_content_length || no_chunks) { chunk_encoding_ = false; } else { if (processing_100_continue_) { @@ -105,8 +110,8 @@ void StreamEncoderImpl::encodeHeaders(const HeaderMap& headers, bool end_stream) } void StreamEncoderImpl::encodeData(Buffer::Instance& data, bool end_stream) { - // end_stream may be indicated with a zero length data buffer. If that is the case, so not - // atually write the zero length buffer out. + // end_stream may be indicated with a zero length data buffer. If that is the case, do not + // actually write the zero length buffer out. if (data.length() > 0) { if (chunk_encoding_) { connection_.buffer().add(fmt::format("{:x}\r\n", data.length())); diff --git a/source/common/router/rds_impl.cc b/source/common/router/rds_impl.cc index ff8c574ca2e31..0ccb92add0541 100644 --- a/source/common/router/rds_impl.cc +++ b/source/common/router/rds_impl.cc @@ -204,7 +204,8 @@ Router::RouteConfigProviderSharedPtr RouteConfigProviderManagerImpl::getRouteCon }; Http::Code RouteConfigProviderManagerImpl::handlerRoutes(const std::string& url, Http::HeaderMap&, - Buffer::Instance& response) { + Buffer::Instance& response, + Server::HandlerInfo&) { Http::Utility::QueryParams query_params = Http::Utility::parseQueryString(url); // If there are no query params, print out all the configured route tables. if (query_params.size() == 0) { diff --git a/source/common/router/rds_impl.h b/source/common/router/rds_impl.h index 3f9f801036de6..e259436ab738d 100644 --- a/source/common/router/rds_impl.h +++ b/source/common/router/rds_impl.h @@ -177,7 +177,7 @@ class RouteConfigProviderManagerImpl : public ServerRouteConfigProviderManager, * @return Http::Code OK if the endpoint can parse and operate on the url, NotFound otherwise. */ Http::Code handlerRoutes(const std::string& path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); + Buffer::Instance& response, Server::HandlerInfo&); /** * Helper function used by handlerRoutes. The function loops through the providers diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index 5815f86227384..2f868069c4181 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -69,3 +69,9 @@ envoy_cc_library( "//include/envoy/thread_local:thread_local_interface", ], ) + +envoy_cc_library( + name = "hystrix_lib", + srcs = ["hystrix.cc"], + hdrs = ["hystrix.h"], +) diff --git a/source/common/stats/hystrix.cc b/source/common/stats/hystrix.cc new file mode 100644 index 0000000000000..a28ee8a283da0 --- /dev/null +++ b/source/common/stats/hystrix.cc @@ -0,0 +1,178 @@ +#include "common/stats/hystrix.h" + +#include +#include +#include +#include + +namespace Envoy { +namespace Stats { + +const uint64_t Hystrix::DEFAULT_NUM_OF_BUCKETS; +const uint64_t Hystrix::ROLLING_WINDOW_IN_MS; +const uint64_t Hystrix::PING_INTERVAL_IN_MS; + +// add new value to rolling window, in place of oldest one +void Hystrix::pushNewValue(std::string key, int value) { + // create vector if do not exist + if (rolling_stats_map_.find(key) == rolling_stats_map_.end()) { + rolling_stats_map_[key].resize(num_of_buckets_, value); + } else { + rolling_stats_map_[key][current_index_] = value; + } +} + +uint64_t Hystrix::getRollingValue(std::string cluster_name, std::string stats) { + std::string key = "cluster." + cluster_name + "." + stats; + if (rolling_stats_map_.find(key) != rolling_stats_map_.end()) { + // if the counter was reset, the result is negative + // better return 0, will be back to normal once one rolling window passes + // better idea what to return? could change the algorithm to keep last valid delta, + // updated with pushNewValue and kept stable when the update is negative. + if (rolling_stats_map_[key][current_index_] < + rolling_stats_map_[key][(current_index_ + 1) % num_of_buckets_]) { + return 0; + } else { + return rolling_stats_map_[key][current_index_] - + rolling_stats_map_[key][(current_index_ + 1) % num_of_buckets_]; + } + } else { + return 0; + } +} + +void Hystrix::resetRollingWindow() { rolling_stats_map_.clear(); } + +void Hystrix::addStringToStream(std::string key, std::string value, std::stringstream& info) { + addInfoToStream(key, "\"" + value + "\"", info); +} + +void Hystrix::addIntToStream(std::string key, uint64_t value, std::stringstream& info) { + addInfoToStream(key, std::to_string(value), info); +} + +void Hystrix::addInfoToStream(std::string key, std::string value, std::stringstream& info) { + if (!info.str().empty()) { + info << ", "; + } + info << "\"" + key + "\": " + value; +} + +void Hystrix::addHystrixCommand(std::stringstream& ss, std::string cluster_name, + uint64_t max_concurrent_requests, uint64_t reporting_hosts) { + std::stringstream cluster_info; + std::time_t currentTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + addStringToStream("type", "HystrixCommand", cluster_info); + addStringToStream("name", cluster_name, cluster_info); + addStringToStream("group", "NA", cluster_info); + addIntToStream("currentTime", static_cast(currentTime), cluster_info); + addInfoToStream("isCircuitBreakerOpen", "false", cluster_info); + + // combining timeouts+retries - retries are counted as separate requests + // (alternative: each request including the retries counted as 1) + double timeouts = getRollingValue(cluster_name, "upstream_rq_timeout") + + getRollingValue(cluster_name, "upstream_rq_per_try_timeout"); + + // combining errors+retry errors - retries are counted as separate requests + // (alternative: each request including the retries counted as 1) + // since timeouts are 504 (or 408), deduce them from here. + // timeout retries were not counted here anyway. + double errors = getRollingValue(cluster_name, "upstream_rq_5xx") + + getRollingValue(cluster_name, "retry.upstream_rq_5xx") + + getRollingValue(cluster_name, "upstream_rq_4xx") + + getRollingValue(cluster_name, "retry.upstream_rq_4xx") - + getRollingValue(cluster_name, "upstream_rq_timeout"); + + double success = getRollingValue(cluster_name, "upstream_rq_2xx"); + double rejected = getRollingValue(cluster_name, "upstream_rq_pending_overflow"); + + // should not take from upstream_rq_total since it is updated before its components, + // leading to wrong results such as error percentage bigger than 100% + double total = errors + timeouts + success + rejected; + double error_rate = total == 0 ? 0 : ((errors + timeouts + rejected) / total) * 100; + + addIntToStream("errorPercentage", error_rate, cluster_info); + addIntToStream("errorCount", errors, cluster_info); + addIntToStream("requestCount", total, cluster_info); + addIntToStream("rollingCountCollapsedRequests", 0, cluster_info); + addIntToStream("rollingCountExceptionsThrown", 0, cluster_info); + addIntToStream("rollingCountFailure", errors, cluster_info); + addIntToStream("rollingCountFallbackFailure", 0, cluster_info); + addIntToStream("rollingCountFallbackRejection", 0, cluster_info); + addIntToStream("rollingCountFallbackSuccess", 0, cluster_info); + addIntToStream("rollingCountResponsesFromCache", 0, cluster_info); + + // Envoy's "circuit breaker" has similar meaning to hystrix's isolation + // so we count upstream_rq_pending_overflow and present it as rejected + addIntToStream("rollingCountSemaphoreRejected", rejected, cluster_info); + + // Hystrix's short circuit is not similar to Envoy's since it is trrigered by 503 responses + // there is no parallel counter in Envoy since as a result of errors (outlier detection) + // requests are not rejected, but rather the node is removed from load balancer healthy pool + addIntToStream("rollingCountShortCircuited", 0, cluster_info); + addIntToStream("rollingCountSuccess", success, cluster_info); + addIntToStream("rollingCountThreadPoolRejected", 0, cluster_info); + addIntToStream("rollingCountTimeout", timeouts, cluster_info); + addIntToStream("rollingCountBadRequests", 0, cluster_info); + addIntToStream("currentConcurrentExecutionCount", 0, cluster_info); + addIntToStream("latencyExecute_mean", 0, cluster_info); + + // latency information can be taken rom hystogram, which is only available to sinks + // we should consider make this a sink so we can get this information + addInfoToStream( + "latencyExecute", + "{\"0\":0,\"25\":0,\"50\":0,\"75\":0,\"90\":0,\"95\":0,\"99\":0,\"99.5\":0,\"100\":0}", + cluster_info); + addIntToStream("propertyValue_circuitBreakerRequestVolumeThreshold", 0, cluster_info); + addIntToStream("propertyValue_circuitBreakerSleepWindowInMilliseconds", 0, cluster_info); + addIntToStream("propertyValue_circuitBreakerErrorThresholdPercentage", 0, cluster_info); + addInfoToStream("propertyValue_circuitBreakerForceOpen", "false", cluster_info); + addInfoToStream("propertyValue_circuitBreakerForceClosed", "true", cluster_info); + addStringToStream("propertyValue_executionIsolationStrategy", "SEMAPHORE", cluster_info); + addIntToStream("propertyValue_executionIsolationThreadTimeoutInMilliseconds", 0, cluster_info); + addInfoToStream("propertyValue_executionIsolationThreadInterruptOnTimeout", "false", + cluster_info); + addIntToStream("propertyValue_executionIsolationSemaphoreMaxConcurrentRequests", + max_concurrent_requests, cluster_info); + addIntToStream("propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests", 0, cluster_info); + addInfoToStream("propertyValue_requestCacheEnabled", "false", cluster_info); + addInfoToStream("propertyValue_requestLogEnabled", "true", cluster_info); + addIntToStream("reportingHosts", reporting_hosts, cluster_info); + addIntToStream("propertyValue_metricsRollingStatisticalWindowInMilliseconds", + ROLLING_WINDOW_IN_MS, cluster_info); + + ss << "data: {" << cluster_info.str() << "}" << std::endl << std::endl; +} + +void Hystrix::addHystrixThreadPool(std::stringstream& ss, std::string cluster_name, + uint64_t queue_size, uint64_t reporting_hosts) { + std::stringstream cluster_info; + + addIntToStream("currentPoolSize", 0, cluster_info); + addIntToStream("rollingMaxActiveThreads", 0, cluster_info); + addIntToStream("currentActiveCount", 0, cluster_info); + addIntToStream("currentCompletedTaskCount", 0, cluster_info); + addIntToStream("propertyValue_queueSizeRejectionThreshold", queue_size, cluster_info); + addStringToStream("type", "HystrixThreadPool", cluster_info); + addIntToStream("reportingHosts", reporting_hosts, cluster_info); + addIntToStream("propertyValue_metricsRollingStatisticalWindowInMilliseconds", + ROLLING_WINDOW_IN_MS, cluster_info); + addStringToStream("name", cluster_name, cluster_info); + addIntToStream("currentLargestPoolSize", 0, cluster_info); + addIntToStream("currentCorePoolSize", 0, cluster_info); + addIntToStream("currentQueueSize", 0, cluster_info); + addIntToStream("currentTaskCount", 0, cluster_info); + addIntToStream("rollingCountThreadsExecuted", 0, cluster_info); + addIntToStream("currentMaximumPoolSize", 0, cluster_info); + + ss << "data: {" << cluster_info.str() << "}" << std::endl << std::endl; +} + +void Hystrix::getClusterStats(std::stringstream& ss, std::string cluster_name, + uint64_t max_concurrent_requests, uint64_t reporting_hosts) { + addHystrixCommand(ss, cluster_name, max_concurrent_requests, reporting_hosts); + addHystrixThreadPool(ss, cluster_name, max_concurrent_requests, reporting_hosts); +} + +} // namespace Stats +} // namespace Envoy diff --git a/source/common/stats/hystrix.h b/source/common/stats/hystrix.h new file mode 100644 index 0000000000000..af28d70fb3f5c --- /dev/null +++ b/source/common/stats/hystrix.h @@ -0,0 +1,99 @@ +#include +#include +#include + +namespace Envoy { +namespace Stats { + +typedef std::vector RollingStats; +typedef std::map RollingStatsMap; + +// Consider implement the HystrixStats as a sink to have access to histograms data +class Hystrix { + +public: + Hystrix() : current_index_(DEFAULT_NUM_OF_BUCKETS - 1), num_of_buckets_(DEFAULT_NUM_OF_BUCKETS){}; + + Hystrix(int num_of_buckets) + : current_index_(num_of_buckets - 1), num_of_buckets_(num_of_buckets){}; + + /** + * Add new value to top of rolling window, pushing out the oldest value + */ + void pushNewValue(std::string key, int value); + + /** + * increment pointer of next value to add to rolling window + */ + void incCounter() { current_index_ = (current_index_ + 1) % num_of_buckets_; } + + /** + * Generate the streams to be sent to hystrix dashboard + */ + void getClusterStats(std::stringstream& ss, std::string cluster_name, + uint64_t max_concurrent_requests, uint64_t reporting_hosts); + + /** + * Get value of the sampling buckets + */ + static uint64_t GetRollingWindowIntervalInMs() { + return static_cast(ROLLING_WINDOW_IN_MS / DEFAULT_NUM_OF_BUCKETS); + } + + /** + * Get value of the keep alive ping interval + */ + static uint64_t GetPingIntervalInMs() { return PING_INTERVAL_IN_MS; } + + /** + * clear map + */ + void resetRollingWindow(); + +private: + /** + * Get the statistic's value change over the rolling window time frame + */ + uint64_t getRollingValue(std::string cluster_name, std::string stats); + + /** + * Format the given key and std::string value to "key"="value", and adding to the stringstream + */ + void addStringToStream(std::string key, std::string value, std::stringstream& info); + + /** + * Format the given key and uint64_t value to "key"=, and adding to the + * stringstream + */ + void addIntToStream(std::string key, uint64_t value, std::stringstream& info); + + /** + * Format the given key and value to "key"=value, and adding to the stringstream + */ + void addInfoToStream(std::string key, std::string value, std::stringstream& info); + + /** + * generate HystrixCommand event stream + */ + void addHystrixCommand(std::stringstream& ss, std::string cluster_name, + uint64_t max_concurrent_requests, uint64_t reporting_hosts); + + /** + * generate HystrixThreadPool event stream + */ + void addHystrixThreadPool(std::stringstream& ss, std::string cluster_name, uint64_t queue_size, + uint64_t reporting_hosts); + + RollingStatsMap rolling_stats_map_; + int current_index_; + int num_of_buckets_; + // TODO(trabetti): May want to make this configurable via config file + static const uint64_t DEFAULT_NUM_OF_BUCKETS = 10; + static const uint64_t ROLLING_WINDOW_IN_MS = 10000; + static const uint64_t PING_INTERVAL_IN_MS = 3000; +}; + +typedef std::unique_ptr HystrixPtr; + +} // namespace Stats +} // namespace Envoy diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 0a3f407781073..686bcdef64d2e 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -219,7 +219,7 @@ void AdminImpl::addCircuitSettings(const std::string& cluster_name, const std::s } Http::Code AdminImpl::handlerClusters(const std::string&, Http::HeaderMap&, - Buffer::Instance& response) { + Buffer::Instance& response, HandlerInfo&) { response.add(fmt::format("version_info::{}\n", server_.clusterManager().versionInfo())); for (auto& cluster : server_.clusterManager().clusters()) { @@ -276,7 +276,7 @@ Http::Code AdminImpl::handlerClusters(const std::string&, Http::HeaderMap&, } Http::Code AdminImpl::handlerCpuProfiler(const std::string& url, Http::HeaderMap&, - Buffer::Instance& response) { + Buffer::Instance& response, HandlerInfo&) { Http::Utility::QueryParams query_params = Http::Utility::parseQueryString(url); if (query_params.size() != 1 || query_params.begin()->first != "enable" || (query_params.begin()->second != "y" && query_params.begin()->second != "n")) { @@ -300,27 +300,27 @@ Http::Code AdminImpl::handlerCpuProfiler(const std::string& url, Http::HeaderMap } Http::Code AdminImpl::handlerHealthcheckFail(const std::string&, Http::HeaderMap&, - Buffer::Instance& response) { + Buffer::Instance& response, HandlerInfo&) { server_.failHealthcheck(true); response.add("OK\n"); return Http::Code::OK; } Http::Code AdminImpl::handlerHealthcheckOk(const std::string&, Http::HeaderMap&, - Buffer::Instance& response) { + Buffer::Instance& response, HandlerInfo&) { server_.failHealthcheck(false); response.add("OK\n"); return Http::Code::OK; } Http::Code AdminImpl::handlerHotRestartVersion(const std::string&, Http::HeaderMap&, - Buffer::Instance& response) { + Buffer::Instance& response, HandlerInfo&) { response.add(server_.hotRestart().version()); return Http::Code::OK; } Http::Code AdminImpl::handlerLogging(const std::string& url, Http::HeaderMap&, - Buffer::Instance& response) { + Buffer::Instance& response, HandlerInfo&) { Http::Utility::QueryParams query_params = Http::Utility::parseQueryString(url); Http::Code rc = Http::Code::OK; @@ -346,7 +346,7 @@ Http::Code AdminImpl::handlerLogging(const std::string& url, Http::HeaderMap&, } Http::Code AdminImpl::handlerResetCounters(const std::string&, Http::HeaderMap&, - Buffer::Instance& response) { + Buffer::Instance& response, HandlerInfo&) { for (const Stats::CounterSharedPtr& counter : server_.stats().counters()) { counter->reset(); } @@ -356,7 +356,7 @@ Http::Code AdminImpl::handlerResetCounters(const std::string&, Http::HeaderMap&, } Http::Code AdminImpl::handlerServerInfo(const std::string&, Http::HeaderMap&, - Buffer::Instance& response) { + Buffer::Instance& response, HandlerInfo&) { time_t current_time = time(nullptr); response.add(fmt::format("envoy {} {} {} {} {}\n", VersionInfo::version(), server_.healthCheckFailed() ? "draining" : "live", @@ -367,7 +367,7 @@ Http::Code AdminImpl::handlerServerInfo(const std::string&, Http::HeaderMap&, } Http::Code AdminImpl::handlerStats(const std::string& url, Http::HeaderMap& response_headers, - Buffer::Instance& response) { + Buffer::Instance& response, HandlerInfo&) { // We currently don't support timers locally (only via statsd) so just group all the counters // and gauges together, alpha sort them, and spit them out. Http::Code rc = Http::Code::OK; @@ -469,14 +469,14 @@ std::string AdminImpl::statsAsJson(const std::map& all_st } Http::Code AdminImpl::handlerQuitQuitQuit(const std::string&, Http::HeaderMap&, - Buffer::Instance& response) { + Buffer::Instance& response, HandlerInfo&) { server_.shutdown(); response.add("OK\n"); return Http::Code::OK; } Http::Code AdminImpl::handlerListenerInfo(const std::string&, Http::HeaderMap& response_headers, - Buffer::Instance& response) { + Buffer::Instance& response, HandlerInfo&) { response_headers.insertContentType().value().setReference( Http::Headers::get().ContentTypeValues.Json); std::list listeners; @@ -487,8 +487,8 @@ Http::Code AdminImpl::handlerListenerInfo(const std::string&, Http::HeaderMap& r return Http::Code::OK; } -Http::Code AdminImpl::handlerCerts(const std::string&, Http::HeaderMap&, - Buffer::Instance& response) { +Http::Code AdminImpl::handlerCerts(const std::string&, Http::HeaderMap&, Buffer::Instance& response, + HandlerInfo&) { // This set is used to track distinct certificates. We may have multiple listeners, upstreams, etc // using the same cert. std::unordered_set context_info_set; @@ -507,7 +507,7 @@ Http::Code AdminImpl::handlerCerts(const std::string&, Http::HeaderMap&, } Http::Code AdminImpl::handlerRuntime(const std::string& url, Http::HeaderMap& response_headers, - Buffer::Instance& response) { + Buffer::Instance& response, HandlerInfo&) { Http::Code rc = Http::Code::OK; const Http::Utility::QueryParams params = Http::Utility::parseQueryString(url); const auto& entries = server_.runtime().snapshot().getAll(); @@ -528,7 +528,6 @@ Http::Code AdminImpl::handlerRuntime(const std::string& url, Http::HeaderMap& re rc = Http::Code::BadRequest; } } - return rc; } @@ -575,13 +574,66 @@ std::string AdminImpl::runtimeAsJson( return strbuf.GetString(); } +Http::Code AdminImpl::handlerHystrixEventStream(const std::string&, + Http::HeaderMap& response_headers, + Buffer::Instance&, HandlerInfo& handler_info) { + + response_headers.insertContentType().value().setReference( + Http::Headers::get().ContentTypeValues.TextEventStream); + response_headers.insertCacheControl().value().setReference( + Http::Headers::get().CacheControlValues.NoCache); + response_headers.insertConnection().value().setReference( + Http::Headers::get().ConnectionValues.Close); + response_headers.insertAccessControlAllowHeaders().value().setReference( + Http::Headers::get().AccessControlAllowHeadersValue.AccessControlAllowHeadersHystrix); + response_headers.insertAccessControlAllowOrigin().value().setReference( + Http::Headers::get().AccessControlAllowOriginValue.All); + response_headers.insertNoChunks().value().setReference("0"); + + HystrixHandlerInfo& hystrix_handler_info = dynamic_cast(handler_info); + + // start streaming + hystrix_handler_info.data_timer_ = hystrix_handler_info.callbacks_->dispatcher().createTimer( + [this, &hystrix_handler_info]() -> void { + HystrixHandler::prepareAndSendHystrixStream(&hystrix_handler_info, server_); + }); + hystrix_handler_info.data_timer_->enableTimer( + std::chrono::milliseconds(Stats::Hystrix::GetRollingWindowIntervalInMs())); + + // start keep alive ping + hystrix_handler_info.ping_timer_ = hystrix_handler_info.callbacks_->dispatcher().createTimer( + [this, &hystrix_handler_info]() -> void { + HystrixHandler::sendKeepAlivePing(&hystrix_handler_info); + }); + + hystrix_handler_info.ping_timer_->enableTimer( + std::chrono::milliseconds(Stats::Hystrix::GetPingIntervalInMs())); + + ENVOY_LOG(debug, "start sending data to hystrix dashboard on port {}", + hystrix_handler_info.callbacks_->connection()->localAddress()->asString()); + return Http::Code::OK; +} + void AdminFilter::onComplete() { std::string path = request_headers_->Path()->value().c_str(); ENVOY_STREAM_LOG(debug, "request complete: path: {}", *callbacks_, path); Buffer::OwnedImpl response; Http::HeaderMapPtr header_map{new Http::HeaderMapImpl}; - Http::Code code = parent_.runCallback(path, *header_map, response); + Http::Code code; + bool end_stream = true; + + if (path.find("/hystrix_event_stream") == std::string::npos) { + HandlerInfoPtr temp_handler(new HandlerInfo); + handler_info_ = std::move(temp_handler); + code = parent_.runCallback(path, *header_map, response, *handler_info_); + } else { + HandlerInfoPtr temp_handler(new HystrixHandlerInfo(callbacks_)); + handler_info_ = std::move(temp_handler); + code = parent_.runCallback(path, *header_map, response, *handler_info_); + end_stream = false; + } + header_map->insertStatus().value(std::to_string(enumToInt(code))); const auto& headers = Http::Headers::get(); if (header_map->ContentType() == nullptr) { @@ -596,7 +648,7 @@ void AdminFilter::onComplete() { // Under no circumstance should browsers sniff content-type. header_map->addReference(headers.XContentTypeOptions, headers.XContentTypeOptionValues.Nosniff); - callbacks_->encodeHeaders(std::move(header_map), response.length() == 0); + callbacks_->encodeHeaders(std::move(header_map), end_stream && response.length() == 0); if (response.length() > 0) { callbacks_->encodeData(response, true); @@ -641,7 +693,9 @@ AdminImpl::AdminImpl(const std::string& access_log_path, const std::string& prof {"/stats", "print server stats", MAKE_ADMIN_HANDLER(handlerStats), false, false}, {"/listeners", "print listener addresses", MAKE_ADMIN_HANDLER(handlerListenerInfo), false, false}, - {"/runtime", "print runtime values", MAKE_ADMIN_HANDLER(handlerRuntime), false, false}}, + {"/runtime", "print runtime values", MAKE_ADMIN_HANDLER(handlerRuntime), false, false}, + {"/hystrix_event_stream", "send hystrix event stream", + MAKE_ADMIN_HANDLER(handlerHystrixEventStream), false, false}}, listener_(*this, std::move(listener_scope)) { if (!address_out_path.empty()) { @@ -678,7 +732,8 @@ void AdminImpl::createFilterChain(Http::FilterChainFactoryCallbacks& callbacks) } Http::Code AdminImpl::runCallback(const std::string& path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance& response) { + Http::HeaderMap& response_headers, Buffer::Instance& response, + HandlerInfo& handler_info) { Http::Code code = Http::Code::OK; bool found_handler = false; @@ -689,7 +744,7 @@ Http::Code AdminImpl::runCallback(const std::string& path_and_query, for (const UrlHandler& handler : handlers_) { if (path_and_query.compare(0, query_index, handler.prefix_) == 0) { - code = handler.handler_(path_and_query, response_headers, response); + code = handler.handler_(path_and_query, response_headers, response, handler_info); found_handler = true; break; } @@ -699,7 +754,7 @@ Http::Code AdminImpl::runCallback(const std::string& path_and_query, // Extra space is emitted below to have "invalid path." be a separate sentence in the // 404 output from "admin commands are:" in handlerHelp. response.add("invalid path. "); - handlerHelp(path_and_query, response_headers, response); + handlerHelp(path_and_query, response_headers, response, handler_info); code = Http::Code::NotFound; } @@ -717,8 +772,8 @@ std::vector AdminImpl::sortedHandlers() const { return sorted_handlers; } -Http::Code AdminImpl::handlerHelp(const std::string&, Http::HeaderMap&, - Buffer::Instance& response) { +Http::Code AdminImpl::handlerHelp(const std::string&, Http::HeaderMap&, Buffer::Instance& response, + HandlerInfo&) { response.add("admin commands are:\n"); // Prefix order is used during searching, but for printing do them in alpha order. @@ -729,7 +784,7 @@ Http::Code AdminImpl::handlerHelp(const std::string&, Http::HeaderMap&, } Http::Code AdminImpl::handlerAdminHome(const std::string&, Http::HeaderMap& response_headers, - Buffer::Instance& response) { + Buffer::Instance& response, HandlerInfo&) { response_headers.insertContentType().value().setReference( Http::Headers::get().ContentTypeValues.Html); @@ -793,5 +848,71 @@ bool AdminImpl::removeHandler(const std::string& prefix) { return false; } +void HystrixHandlerInfo::Destroy() { + if (data_timer_) { + data_timer_->disableTimer(); + data_timer_.reset(); + } + if (ping_timer_) { + ping_timer_->disableTimer(); + ping_timer_.reset(); + } +} + +void HystrixHandler::updateHystrixRollingWindow(HystrixHandlerInfo* hystrix_handler_info, + Server::Instance& server) { + hystrix_handler_info->stats_->incCounter(); + + for (const Stats::CounterSharedPtr& counter : server.stats().counters()) { + // we save all upstream_rq stats. + if (counter->name().find("upstream_rq_") != std::string::npos) { + hystrix_handler_info->stats_->pushNewValue(counter->name(), counter->value()); + } + } +} + +void HystrixHandler::prepareAndSendHystrixStream(HystrixHandlerInfo* hystrix_handler_info, + Server::Instance& server) { + updateHystrixRollingWindow(hystrix_handler_info, server); + std::stringstream ss; + for (auto& cluster : server.clusterManager().clusters()) { + hystrix_handler_info->stats_->getClusterStats( + ss, cluster.second.get().info()->name(), + cluster.second.get() + .info() + ->resourceManager(Upstream::ResourcePriority::Default) + .pendingRequests() + .max(), + server.stats() + .gauge("cluster." + cluster.second.get().info()->name() + ".membership_total") + .value()); + } + Buffer::OwnedImpl data; + data.add(ss.str()); + + // using write() since we are sending network level + // TODO(trabetti): is there an alternative to the const_cast? + (const_cast((hystrix_handler_info->callbacks_)->connection())) + ->write(data, false); + + // restart timer + hystrix_handler_info->data_timer_->enableTimer( + std::chrono::milliseconds(Stats::Hystrix::GetRollingWindowIntervalInMs())); +} + +void HystrixHandler::sendKeepAlivePing(HystrixHandlerInfo* hystrix_handler_info) { + Buffer::OwnedImpl data; + data.add(":\n\n"); + + // using write() since we are sending network level + // TODO(trabetti): is there an alternative to the const_cast? + (const_cast((hystrix_handler_info->callbacks_)->connection())) + ->write(data, false); + + // restart timer + hystrix_handler_info->ping_timer_->enableTimer( + std::chrono::milliseconds(Stats::Hystrix::GetPingIntervalInMs())); +} + } // namespace Server } // namespace Envoy diff --git a/source/server/http/admin.h b/source/server/http/admin.h index d45715ff7506b..82161fc53f0fb 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -42,7 +42,7 @@ class AdminImpl : public Admin, Server::Instance& server, Stats::ScopePtr&& listener_scope); Http::Code runCallback(const std::string& path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); + Buffer::Instance& response, HandlerInfo& handler_info); const Network::Socket& socket() override { return *socket_; } Network::Socket& mutable_socket() { return *socket_; } Network::ListenerConfig& listener() { return listener_; } @@ -136,37 +136,46 @@ class AdminImpl : public Admin, * URL handlers. */ Http::Code handlerAdminHome(const std::string& path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); + Buffer::Instance& response, HandlerInfo&); Http::Code handlerCerts(const std::string& path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); + Buffer::Instance& response, HandlerInfo&); Http::Code handlerClusters(const std::string& path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); + Buffer::Instance& response, HandlerInfo&); Http::Code handlerCpuProfiler(const std::string& path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance& response); + Http::HeaderMap& response_headers, Buffer::Instance& response, + HandlerInfo&); Http::Code handlerHealthcheckFail(const std::string& path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance& response); + Http::HeaderMap& response_headers, Buffer::Instance& response, + HandlerInfo&); Http::Code handlerHealthcheckOk(const std::string& path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance& response); + Http::HeaderMap& response_headers, Buffer::Instance& response, + HandlerInfo&); Http::Code handlerHelp(const std::string& path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); + Buffer::Instance& response, HandlerInfo&); Http::Code handlerHotRestartVersion(const std::string& path_and_query, - Http::HeaderMap& response_headers, - Buffer::Instance& response); + Http::HeaderMap& response_headers, Buffer::Instance& response, + HandlerInfo&); Http::Code handlerListenerInfo(const std::string& path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance& response); + Http::HeaderMap& response_headers, Buffer::Instance& response, + HandlerInfo&); Http::Code handlerLogging(const std::string& path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); + Buffer::Instance& response, HandlerInfo&); Http::Code handlerMain(const std::string& path, Buffer::Instance& response); Http::Code handlerQuitQuitQuit(const std::string& path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance& response); + Http::HeaderMap& response_headers, Buffer::Instance& response, + HandlerInfo&); Http::Code handlerResetCounters(const std::string& path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance& response); + Http::HeaderMap& response_headers, Buffer::Instance& response, + HandlerInfo&); Http::Code handlerServerInfo(const std::string& path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); + Buffer::Instance& response, HandlerInfo&); Http::Code handlerStats(const std::string& path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); + Buffer::Instance& response, HandlerInfo&); Http::Code handlerRuntime(const std::string& path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); + Buffer::Instance& response, HandlerInfo&); + Http::Code handlerHystrixEventStream(const std::string& path_and_query, + Http::HeaderMap& response_headers, Buffer::Instance&, + HandlerInfo& handler_info); class AdminListener : public Network::ListenerConfig { public: @@ -217,7 +226,7 @@ class AdminFilter : public Http::StreamDecoderFilter, Logger::LoggableDestroy(); } // Http::StreamDecoderFilter Http::FilterHeadersStatus decodeHeaders(Http::HeaderMap& response_headers, @@ -237,6 +246,7 @@ class AdminFilter : public Http::StreamDecoderFilter, Logger::LoggableversionInfo()); Http::HeaderMapImpl header_map; - EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data)); + Server::HandlerInfo handler_info; + EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data, handler_info)); EXPECT_EQ(routes_expected_output_no_routes, TestUtility::bufferToString(data)); data.drain(data.length()); EXPECT_EQ(0UL, store_.gauge("foo.rds.foo_route_config.version").value()); @@ -277,7 +278,7 @@ TEST_F(RdsImplTest, Basic) { )EOF"; EXPECT_EQ("hash_15ed54077da94d8b", rds_->versionInfo()); - EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data)); + EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data, handler_info)); EXPECT_EQ(routes_expected_output_only_name, TestUtility::bufferToString(data)); data.drain(data.length()); EXPECT_EQ(1580011435426663819U, store_.gauge("foo.rds.foo_route_config.version").value()); @@ -295,7 +296,7 @@ TEST_F(RdsImplTest, Basic) { EXPECT_EQ(nullptr, rds_->config()->route(Http::TestHeaderMapImpl{{":authority", "foo"}}, 0)); // Test Admin /routes handler. The route table should not change. - EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data)); + EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data, handler_info)); EXPECT_EQ(routes_expected_output_only_name, TestUtility::bufferToString(data)); data.drain(data.length()); EXPECT_EQ(1580011435426663819U, store_.gauge("foo.rds.foo_route_config.version").value()); @@ -392,20 +393,20 @@ TEST_F(RdsImplTest, Basic) { ] )EOF"; - EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data)); + EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data, handler_info)); EXPECT_EQ(routes_expected_output_full_table, TestUtility::bufferToString(data)); data.drain(data.length()); EXPECT_EQ(8808926191882896258U, store_.gauge("foo.rds.foo_route_config.version").value()); // Test that we get the same dump if we specify the route name. - EXPECT_EQ(Http::Code::OK, - handler_callback_("/routes?route_config_name=foo_route_config", header_map, data)); + EXPECT_EQ(Http::Code::OK, handler_callback_("/routes?route_config_name=foo_route_config", + header_map, data, handler_info)); EXPECT_EQ(routes_expected_output_full_table, TestUtility::bufferToString(data)); data.drain(data.length()); // Test that we get an emtpy response if the name does not match. - EXPECT_EQ(Http::Code::OK, - handler_callback_("/routes?route_config_name=does_not_exist", header_map, data)); + EXPECT_EQ(Http::Code::OK, handler_callback_("/routes?route_config_name=does_not_exist", + header_map, data, handler_info)); EXPECT_EQ("[\n]\n", TestUtility::bufferToString(data)); data.drain(data.length()); @@ -415,7 +416,8 @@ TEST_F(RdsImplTest, Basic) { })EOF"; // Test that we get the help text if we use the command in an invalid ways. - EXPECT_EQ(Http::Code::NotFound, handler_callback_("/routes?bad_param", header_map, data)); + EXPECT_EQ(Http::Code::NotFound, + handler_callback_("/routes?bad_param", header_map, data, handler_info)); EXPECT_EQ(routes_expected_output_usage, TestUtility::bufferToString(data)); data.drain(data.length()); @@ -615,7 +617,8 @@ TEST_F(RouteConfigProviderManagerImplTest, Basic) { // Test Admin /routes handler. Http::HeaderMapImpl header_map; - EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data)); + Server::HandlerInfo handler_info; + EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data, handler_info)); EXPECT_EQ(routes_expected_output, TestUtility::bufferToString(data)); data.drain(data.length()); diff --git a/test/common/stats/BUILD b/test/common/stats/BUILD index 64f69c29f30a3..73d5686c5a54f 100644 --- a/test/common/stats/BUILD +++ b/test/common/stats/BUILD @@ -80,3 +80,11 @@ envoy_cc_test( "//test/test_common:utility_lib", ], ) + +envoy_cc_test( + name = "hystrix_test", + srcs = ["hystrix_test.cc"], + deps = [ + "//source/common/stats:hystrix_lib", + ], +) diff --git a/test/common/stats/hystrix_test.cc b/test/common/stats/hystrix_test.cc new file mode 100644 index 0000000000000..f62e1cd2e8d2e --- /dev/null +++ b/test/common/stats/hystrix_test.cc @@ -0,0 +1,91 @@ +#include + +#include "common/stats/hystrix.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Stats { + +void checkStreamField(std::string dataMessage, std::string key, uint64_t expected) { + std::string actual = dataMessage.substr(dataMessage.find(key)); + actual = actual.substr(actual.find(" ") + 1); + std::size_t length = actual.find(","); + actual = actual.substr(0, length); + EXPECT_EQ(actual, std::to_string(expected)); +} + +TEST(Hystrix, CreateDataMessage) { + Stats::Hystrix hystrix; + std::stringstream ss; + std::string clusterName = "clusterName"; + uint64_t expectedQueueSize = 12; + uint64_t expectedReportingHosts = 16; + + EXPECT_EQ(hystrix.GetRollingWindowIntervalInMs(), 1000); + EXPECT_EQ(hystrix.GetPingIntervalInMs(), 3000); + + // insert data to rolling window + for (uint64_t i = 0; i < 10; i++) { + hystrix.incCounter(); + hystrix.pushNewValue("cluster.clusterName.upstream_rq_timeout", i + 1); + hystrix.pushNewValue("cluster.clusterName.upstream_rq_per_try_timeout", (i + 1) * 2); + hystrix.pushNewValue("cluster.clusterName.upstream_rq_5xx", (i + 1) * 3); + hystrix.pushNewValue("cluster.clusterName.retry.upstream_rq_5xx", (i + 1) * 4); + hystrix.pushNewValue("cluster.clusterName.upstream_rq_4xx", (i + 1) * 5); + hystrix.pushNewValue("cluster.clusterName.retry.upstream_rq_4xx", (i + 1) * 6); + hystrix.pushNewValue("cluster.clusterName.upstream_rq_2xx", (i + 1) * 7); + hystrix.pushNewValue("cluster.clusterName.upstream_rq_pending_overflow", (i + 1) * 8); + } + hystrix.getClusterStats(ss, clusterName, expectedQueueSize, expectedReportingHosts); + std::string dataMessage = ss.str(); + + // check stream format and data + checkStreamField(dataMessage, "errorPercentage", 80); + checkStreamField(dataMessage, "errorCount", 153); + checkStreamField(dataMessage, "requestCount", 315); + checkStreamField(dataMessage, "rollingCountSemaphoreRejected", 72); + checkStreamField(dataMessage, "rollingCountSuccess", 63); + checkStreamField(dataMessage, "rollingCountTimeout", 27); + checkStreamField(dataMessage, "propertyValue_queueSizeRejectionThreshold", expectedQueueSize); + checkStreamField(dataMessage, "reportingHosts", expectedReportingHosts); + + // make sure the rolling window really rolls + ss.str(""); + for (uint64_t i = 10; i < 13; i++) { + hystrix.incCounter(); + hystrix.pushNewValue("cluster.clusterName.upstream_rq_timeout", i + 1); + hystrix.pushNewValue("cluster.clusterName.upstream_rq_per_try_timeout", (i + 1) * 2); + hystrix.pushNewValue("cluster.clusterName.upstream_rq_5xx", (i + 1) * 3); + hystrix.pushNewValue("cluster.clusterName.retry.upstream_rq_5xx", (i + 1) * 4); + hystrix.pushNewValue("cluster.clusterName.upstream_rq_4xx", (i + 1) * 5); + hystrix.pushNewValue("cluster.clusterName.retry.upstream_rq_4xx", (i + 1) * 6); + hystrix.pushNewValue("cluster.clusterName.upstream_rq_2xx", (i + 1) * 7); + hystrix.pushNewValue("cluster.clusterName.upstream_rq_pending_overflow", (i + 1) * 8); + } + hystrix.getClusterStats(ss, clusterName, expectedQueueSize, expectedReportingHosts); + dataMessage = ss.str(); + + checkStreamField(dataMessage, "errorPercentage", 80); + checkStreamField(dataMessage, "errorCount", 153); + checkStreamField(dataMessage, "requestCount", 315); + checkStreamField(dataMessage, "rollingCountSemaphoreRejected", 72); + checkStreamField(dataMessage, "rollingCountSuccess", 63); + checkStreamField(dataMessage, "rollingCountTimeout", 27); + + // check reset of window + ss.str(""); + hystrix.resetRollingWindow(); + hystrix.getClusterStats(ss, clusterName, expectedQueueSize, expectedReportingHosts); + dataMessage = ss.str(); + + checkStreamField(dataMessage, "errorPercentage", 0); + checkStreamField(dataMessage, "errorCount", 0); + checkStreamField(dataMessage, "requestCount", 0); + checkStreamField(dataMessage, "rollingCountSemaphoreRejected", 0); + checkStreamField(dataMessage, "rollingCountSuccess", 0); + checkStreamField(dataMessage, "rollingCountTimeout", 0); +} + +} // namespace Stats +} // namespace Envoy diff --git a/test/server/http/admin_test.cc b/test/server/http/admin_test.cc index f347185bb7b75..5409855218e09 100644 --- a/test/server/http/admin_test.cc +++ b/test/server/http/admin_test.cc @@ -99,9 +99,12 @@ INSTANTIATE_TEST_CASE_P(IpVersions, AdminInstanceTest, TEST_P(AdminInstanceTest, AdminProfiler) { Buffer::OwnedImpl data; Http::HeaderMapImpl header_map; - EXPECT_EQ(Http::Code::OK, admin_.runCallback("/cpuprofiler?enable=y", header_map, data)); + HandlerInfo handler_info; + EXPECT_EQ(Http::Code::OK, + admin_.runCallback("/cpuprofiler?enable=y", header_map, data, handler_info)); EXPECT_TRUE(Profiler::Cpu::profilerEnabled()); - EXPECT_EQ(Http::Code::OK, admin_.runCallback("/cpuprofiler?enable=n", header_map, data)); + EXPECT_EQ(Http::Code::OK, + admin_.runCallback("/cpuprofiler?enable=n", header_map, data, handler_info)); EXPECT_FALSE(Profiler::Cpu::profilerEnabled()); } @@ -114,7 +117,8 @@ TEST_P(AdminInstanceTest, AdminBadProfiler) { "", Network::Test::getCanonicalLoopbackAddress(GetParam()), server_, listener_scope_.createScope("listener.admin.")); Http::HeaderMapImpl header_map; - admin_bad_profile_path.runCallback("/cpuprofiler?enable=y", header_map, data); + HandlerInfo handler_info; + admin_bad_profile_path.runCallback("/cpuprofiler?enable=y", header_map, data, handler_info); EXPECT_FALSE(Profiler::Cpu::profilerEnabled()); } @@ -134,52 +138,56 @@ TEST_P(AdminInstanceTest, AdminBadAddressOutPath) { } TEST_P(AdminInstanceTest, CustomHandler) { - auto callback = [&](const std::string&, Http::HeaderMap&, Buffer::Instance&) -> Http::Code { - return Http::Code::Accepted; - }; + auto callback = [&](const std::string&, Http::HeaderMap&, Buffer::Instance&, + HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; // Test removable handler. EXPECT_TRUE(admin_.addHandler("/foo/bar", "hello", callback, true, false)); Http::HeaderMapImpl header_map; Buffer::OwnedImpl response; - EXPECT_EQ(Http::Code::Accepted, admin_.runCallback("/foo/bar", header_map, response)); + HandlerInfo handler_info; + + EXPECT_EQ(Http::Code::Accepted, + admin_.runCallback("/foo/bar", header_map, response, handler_info)); // Test that removable handler gets removed. EXPECT_TRUE(admin_.removeHandler("/foo/bar")); - EXPECT_EQ(Http::Code::NotFound, admin_.runCallback("/foo/bar", header_map, response)); + EXPECT_EQ(Http::Code::NotFound, + admin_.runCallback("/foo/bar", header_map, response, handler_info)); EXPECT_FALSE(admin_.removeHandler("/foo/bar")); // Add non removable handler. EXPECT_TRUE(admin_.addHandler("/foo/bar", "hello", callback, false, false)); - EXPECT_EQ(Http::Code::Accepted, admin_.runCallback("/foo/bar", header_map, response)); + EXPECT_EQ(Http::Code::Accepted, + admin_.runCallback("/foo/bar", header_map, response, handler_info)); // Add again and make sure it is not there twice. EXPECT_FALSE(admin_.addHandler("/foo/bar", "hello", callback, false, false)); // Try to remove non removable handler, and make sure it is not removed. EXPECT_FALSE(admin_.removeHandler("/foo/bar")); - EXPECT_EQ(Http::Code::Accepted, admin_.runCallback("/foo/bar", header_map, response)); + EXPECT_EQ(Http::Code::Accepted, + admin_.runCallback("/foo/bar", header_map, response, handler_info)); } TEST_P(AdminInstanceTest, RejectHandlerWithXss) { - auto callback = [&](const std::string&, Http::HeaderMap&, Buffer::Instance&) -> Http::Code { - return Http::Code::Accepted; - }; + auto callback = [&](const std::string&, Http::HeaderMap&, Buffer::Instance&, + HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; EXPECT_FALSE( admin_.addHandler("/foo", "hello", callback, true, false)); } TEST_P(AdminInstanceTest, RejectHandlerWithEmbeddedQuery) { - auto callback = [&](const std::string&, Http::HeaderMap&, Buffer::Instance&) -> Http::Code { - return Http::Code::Accepted; - }; + auto callback = [&](const std::string&, Http::HeaderMap&, Buffer::Instance&, + HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; EXPECT_FALSE(admin_.addHandler("/bar?queryShouldNotBeInPrefix", "hello", callback, true, false)); } TEST_P(AdminInstanceTest, EscapeHelpTextWithPunctuation) { - auto callback = [&](const std::string&, Http::HeaderMap&, Buffer::Instance&) -> Http::Code { - return Http::Code::Accepted; - }; + auto callback = [&](const std::string&, Http::HeaderMap&, Buffer::Instance&, + HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; + + HandlerInfo handler_info; // It's OK to have help text with HTML characters in it, but when we render the home // page they need to be escaped. @@ -188,7 +196,7 @@ TEST_P(AdminInstanceTest, EscapeHelpTextWithPunctuation) { Http::HeaderMapImpl header_map; Buffer::OwnedImpl response; - EXPECT_EQ(Http::Code::OK, admin_.runCallback("/", header_map, response)); + EXPECT_EQ(Http::Code::OK, admin_.runCallback("/", header_map, response, handler_info)); Http::HeaderString& content_type = header_map.ContentType()->value(); EXPECT_TRUE(content_type.find("text/html")) << content_type.c_str(); EXPECT_EQ(-1, response.search(planets.data(), planets.size(), 0)); @@ -199,7 +207,8 @@ TEST_P(AdminInstanceTest, EscapeHelpTextWithPunctuation) { TEST_P(AdminInstanceTest, HelpUsesFormForMutations) { Http::HeaderMapImpl header_map; Buffer::OwnedImpl response; - EXPECT_EQ(Http::Code::OK, admin_.runCallback("/", header_map, response)); + HandlerInfo handler_info; + EXPECT_EQ(Http::Code::OK, admin_.runCallback("/", header_map, response, handler_info)); const std::string logging_action = "
entries; Runtime::MockSnapshot snapshot; Runtime::MockLoader loader; + HandlerInfo handler_info; EXPECT_CALL(snapshot, getAll()).WillRepeatedly(testing::ReturnRef(entries)); EXPECT_CALL(loader, snapshot()).WillRepeatedly(testing::ReturnPointee(&snapshot)); EXPECT_CALL(server_, runtime()).WillRepeatedly(testing::ReturnPointee(&loader)); EXPECT_EQ(Http::Code::BadRequest, - admin_.runCallback("/runtime?format=foo", header_map, response)); + admin_.runCallback("/runtime?format=foo", header_map, response, handler_info)); EXPECT_EQ("usage: /runtime?format=json\n", TestUtility::bufferToString(response)); } From 5c865c56bf213968ce3970a666abd730be878efb Mon Sep 17 00:00:00 2001 From: talis Date: Tue, 6 Mar 2018 14:38:28 +0200 Subject: [PATCH 02/25] fix formatting Signed-off-by: talis --- test/common/stats/hystrix_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/common/stats/hystrix_test.cc b/test/common/stats/hystrix_test.cc index f62e1cd2e8d2e..574831496b55a 100644 --- a/test/common/stats/hystrix_test.cc +++ b/test/common/stats/hystrix_test.cc @@ -25,7 +25,7 @@ TEST(Hystrix, CreateDataMessage) { EXPECT_EQ(hystrix.GetRollingWindowIntervalInMs(), 1000); EXPECT_EQ(hystrix.GetPingIntervalInMs(), 3000); - // insert data to rolling window + // insert data to rolling window for (uint64_t i = 0; i < 10; i++) { hystrix.incCounter(); hystrix.pushNewValue("cluster.clusterName.upstream_rq_timeout", i + 1); From 035b38e118ee4bc15153838be8a8365c63deda88 Mon Sep 17 00:00:00 2001 From: trabetti Date: Tue, 6 Mar 2018 15:36:47 +0200 Subject: [PATCH 03/25] fix clang compilation error Signed-off-by: trabetti --- source/server/http/admin.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 686bcdef64d2e..33aed75cad7ae 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -602,7 +602,7 @@ Http::Code AdminImpl::handlerHystrixEventStream(const std::string&, // start keep alive ping hystrix_handler_info.ping_timer_ = hystrix_handler_info.callbacks_->dispatcher().createTimer( - [this, &hystrix_handler_info]() -> void { + [&hystrix_handler_info]() -> void { HystrixHandler::sendKeepAlivePing(&hystrix_handler_info); }); From b9a4301d5b885ec2c244315791768edd68fa8352 Mon Sep 17 00:00:00 2001 From: trabetti Date: Tue, 6 Mar 2018 17:08:04 +0200 Subject: [PATCH 04/25] fix formatting Signed-off-by: trabetti --- source/server/http/admin.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 33aed75cad7ae..6fda22737728c 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -601,8 +601,8 @@ Http::Code AdminImpl::handlerHystrixEventStream(const std::string&, std::chrono::milliseconds(Stats::Hystrix::GetRollingWindowIntervalInMs())); // start keep alive ping - hystrix_handler_info.ping_timer_ = hystrix_handler_info.callbacks_->dispatcher().createTimer( - [&hystrix_handler_info]() -> void { + hystrix_handler_info.ping_timer_ = + hystrix_handler_info.callbacks_->dispatcher().createTimer([&hystrix_handler_info]() -> void { HystrixHandler::sendKeepAlivePing(&hystrix_handler_info); }); From cacf90e2946ab8650bb0186bed2cdb6b9e9bac8d Mon Sep 17 00:00:00 2001 From: trabetti Date: Sun, 11 Mar 2018 20:23:17 +0200 Subject: [PATCH 05/25] fix some PR comments, and change content of rolling window to match only the needed values Signed-off-by: trabetti --- include/envoy/server/admin.h | 2 +- source/common/http/http1/codec_impl.cc | 3 + source/common/stats/BUILD | 4 + source/common/stats/hystrix.cc | 87 +++++++++++---- source/common/stats/hystrix.h | 18 ++- source/server/http/admin.cc | 29 ++--- test/common/stats/BUILD | 4 + test/common/stats/hystrix_test.cc | 145 ++++++++++++++++--------- test/server/http/admin_test.cc | 16 +-- 9 files changed, 197 insertions(+), 111 deletions(-) diff --git a/include/envoy/server/admin.h b/include/envoy/server/admin.h index 64714b21ccb1a..9895b15472206 100644 --- a/include/envoy/server/admin.h +++ b/include/envoy/server/admin.h @@ -47,7 +47,7 @@ typedef std::unique_ptr HandlerInfoPtr; class HystrixHandlerInfo : public HandlerInfo { public: HystrixHandlerInfo(Http::StreamDecoderFilterCallbacks* callbacks) - : stats_(new Stats::Hystrix()), data_timer_(nullptr), ping_timer_(nullptr), + : stats_(std::make_unique()), data_timer_(nullptr), ping_timer_(nullptr), callbacks_(callbacks) {} virtual ~HystrixHandlerInfo(){}; void Destroy(); diff --git a/source/common/http/http1/codec_impl.cc b/source/common/http/http1/codec_impl.cc index 5d0a7d1895757..4842321b839a2 100644 --- a/source/common/http/http1/codec_impl.cc +++ b/source/common/http/http1/codec_impl.cc @@ -70,6 +70,9 @@ void StreamEncoderImpl::encodeHeaders(const HeaderMap& headers, bool end_stream) saw_content_length = true; } + // for streaming (e.g. SSE stream sent to hystrix dashboard), we do not want + // chunk transfer encoding but we don't have a content-length so we pass "envoy only" + // header to avoid adding chunks if (headers.NoChunks()) { no_chunks = true; } diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index fd8f91879ea18..e6f47763f23a1 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -75,4 +75,8 @@ envoy_cc_library( name = "hystrix_lib", srcs = ["hystrix.cc"], hdrs = ["hystrix.h"], + deps = [ + ":stats_lib", + "//source/common/common:logger_lib", + ], ) diff --git a/source/common/stats/hystrix.cc b/source/common/stats/hystrix.cc index a28ee8a283da0..1f9853a636610 100644 --- a/source/common/stats/hystrix.cc +++ b/source/common/stats/hystrix.cc @@ -5,6 +5,8 @@ #include #include +#include "common/common/logger.h" + namespace Envoy { namespace Stats { @@ -13,7 +15,7 @@ const uint64_t Hystrix::ROLLING_WINDOW_IN_MS; const uint64_t Hystrix::PING_INTERVAL_IN_MS; // add new value to rolling window, in place of oldest one -void Hystrix::pushNewValue(std::string key, int value) { +void Hystrix::pushNewValue(std::string key, uint64_t value) { // create vector if do not exist if (rolling_stats_map_.find(key) == rolling_stats_map_.end()) { rolling_stats_map_[key].resize(num_of_buckets_, value); @@ -41,6 +43,43 @@ uint64_t Hystrix::getRollingValue(std::string cluster_name, std::string stats) { } } +void Hystrix::updateRollingWindowMap(Stats::Store& stats, std::string cluster_name) { + std::string prefix = "cluster." + cluster_name + "."; + + // combining timeouts+retries - retries are counted as separate requests + // (alternative: each request including the retries counted as 1) + uint64_t timeouts = stats.counter(prefix + "upstream_rq_timeout").value() + + stats.counter(prefix + "upstream_rq_per_try_timeout").value(); + + pushNewValue(prefix + "timeouts", timeouts); + + // combining errors+retry errors - retries are counted as separate requests + // (alternative: each request including the retries counted as 1) + // since timeouts are 504 (or 408), deduce them from here. + // timeout retries were not counted here anyway. + uint64_t errors = stats.counter(prefix + "upstream_rq_5xx").value() + + stats.counter(prefix + "retry.upstream_rq_5xx").value() + + stats.counter(prefix + "upstream_rq_4xx").value() + + stats.counter(prefix + "retry.upstream_rq_4xx").value() - + stats.counter(prefix + "upstream_rq_timeout").value(); + + pushNewValue(prefix + "errors", errors); + + uint64_t success = stats.counter(prefix + "upstream_rq_2xx").value(); + pushNewValue(prefix + "success", success); + + uint64_t rejected = stats.counter(prefix + "upstream_rq_pending_overflow").value(); + pushNewValue(prefix + "rejected", rejected); + + // should not take from upstream_rq_total since it is updated before its components, + // leading to wrong results such as error percentage higher than 100% + uint64_t total = errors + timeouts + success + rejected; + pushNewValue(prefix + "total", total); + + // TODO (@trabetti) : why does it fail compilation? + // ENVOY_LOG(trace, "{}", printRollingWindow()); +} + void Hystrix::resetRollingWindow() { rolling_stats_map_.clear(); } void Hystrix::addStringToStream(std::string key, std::string value, std::stringstream& info) { @@ -68,28 +107,15 @@ void Hystrix::addHystrixCommand(std::stringstream& ss, std::string cluster_name, addIntToStream("currentTime", static_cast(currentTime), cluster_info); addInfoToStream("isCircuitBreakerOpen", "false", cluster_info); - // combining timeouts+retries - retries are counted as separate requests - // (alternative: each request including the retries counted as 1) - double timeouts = getRollingValue(cluster_name, "upstream_rq_timeout") + - getRollingValue(cluster_name, "upstream_rq_per_try_timeout"); - - // combining errors+retry errors - retries are counted as separate requests - // (alternative: each request including the retries counted as 1) - // since timeouts are 504 (or 408), deduce them from here. - // timeout retries were not counted here anyway. - double errors = getRollingValue(cluster_name, "upstream_rq_5xx") + - getRollingValue(cluster_name, "retry.upstream_rq_5xx") + - getRollingValue(cluster_name, "upstream_rq_4xx") + - getRollingValue(cluster_name, "retry.upstream_rq_4xx") - - getRollingValue(cluster_name, "upstream_rq_timeout"); - - double success = getRollingValue(cluster_name, "upstream_rq_2xx"); - double rejected = getRollingValue(cluster_name, "upstream_rq_pending_overflow"); + uint64_t errors = getRollingValue(cluster_name, "errors"); + uint64_t timeouts = getRollingValue(cluster_name, "timeouts"); + uint64_t rejected = getRollingValue(cluster_name, "rejected"); + uint64_t total = getRollingValue(cluster_name, "total"); - // should not take from upstream_rq_total since it is updated before its components, - // leading to wrong results such as error percentage bigger than 100% - double total = errors + timeouts + success + rejected; - double error_rate = total == 0 ? 0 : ((errors + timeouts + rejected) / total) * 100; + uint64_t error_rate = + total == 0 + ? 0 + : (static_cast(errors + timeouts + rejected) / static_cast(total)) * 100; addIntToStream("errorPercentage", error_rate, cluster_info); addIntToStream("errorCount", errors, cluster_info); @@ -110,7 +136,7 @@ void Hystrix::addHystrixCommand(std::stringstream& ss, std::string cluster_name, // there is no parallel counter in Envoy since as a result of errors (outlier detection) // requests are not rejected, but rather the node is removed from load balancer healthy pool addIntToStream("rollingCountShortCircuited", 0, cluster_info); - addIntToStream("rollingCountSuccess", success, cluster_info); + addIntToStream("rollingCountSuccess", getRollingValue(cluster_name, "success"), cluster_info); addIntToStream("rollingCountThreadPoolRejected", 0, cluster_info); addIntToStream("rollingCountTimeout", timeouts, cluster_info); addIntToStream("rollingCountBadRequests", 0, cluster_info); @@ -174,5 +200,20 @@ void Hystrix::getClusterStats(std::stringstream& ss, std::string cluster_name, addHystrixThreadPool(ss, cluster_name, max_concurrent_requests, reporting_hosts); } +std::string Hystrix::printRollingWindow() { + std::stringstream out_str; + + for (std::map::const_iterator it = rolling_stats_map_.begin(); + it != rolling_stats_map_.end(); ++it) { + out_str << it->first << " | "; + RollingStats rolling_stats = it->second; + for (uint64_t i = 0; i < rolling_stats.size(); i++) { + out_str << rolling_stats[i] << " | "; + } + out_str << std::endl; + } + return out_str.str(); +} + } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/hystrix.h b/source/common/stats/hystrix.h index af28d70fb3f5c..65542515178a3 100644 --- a/source/common/stats/hystrix.h +++ b/source/common/stats/hystrix.h @@ -2,6 +2,8 @@ #include #include +#include "envoy/stats/stats.h" + namespace Envoy { namespace Stats { @@ -12,15 +14,15 @@ typedef std::map RollingStatsMap; class Hystrix { public: - Hystrix() : current_index_(DEFAULT_NUM_OF_BUCKETS - 1), num_of_buckets_(DEFAULT_NUM_OF_BUCKETS){}; + Hystrix() : current_index_(DEFAULT_NUM_OF_BUCKETS), num_of_buckets_(DEFAULT_NUM_OF_BUCKETS + 1){}; - Hystrix(int num_of_buckets) - : current_index_(num_of_buckets - 1), num_of_buckets_(num_of_buckets){}; + Hystrix(uint64_t num_of_buckets) + : current_index_(num_of_buckets), num_of_buckets_(num_of_buckets + 1){}; /** * Add new value to top of rolling window, pushing out the oldest value */ - void pushNewValue(std::string key, int value); + void pushNewValue(std::string key, uint64_t value); /** * increment pointer of next value to add to rolling window @@ -45,11 +47,15 @@ class Hystrix { */ static uint64_t GetPingIntervalInMs() { return PING_INTERVAL_IN_MS; } + void updateRollingWindowMap(Stats::Store& stats, std::string cluster_name); + /** * clear map */ void resetRollingWindow(); + std::string printRollingWindow(); + private: /** * Get the statistic's value change over the rolling window time frame @@ -85,8 +91,8 @@ class Hystrix { uint64_t reporting_hosts); RollingStatsMap rolling_stats_map_; - int current_index_; - int num_of_buckets_; + uint64_t current_index_; + uint64_t num_of_buckets_; // TODO(trabetti): May want to make this configurable via config file static const uint64_t DEFAULT_NUM_OF_BUCKETS = 10; static const uint64_t ROLLING_WINDOW_IN_MS = 10000; diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 6fda22737728c..f95d31577a342 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -624,12 +624,10 @@ void AdminFilter::onComplete() { bool end_stream = true; if (path.find("/hystrix_event_stream") == std::string::npos) { - HandlerInfoPtr temp_handler(new HandlerInfo); - handler_info_ = std::move(temp_handler); + handler_info_ = std::make_unique(); code = parent_.runCallback(path, *header_map, response, *handler_info_); } else { - HandlerInfoPtr temp_handler(new HystrixHandlerInfo(callbacks_)); - handler_info_ = std::move(temp_handler); + handler_info_ = std::make_unique(callbacks_); code = parent_.runCallback(path, *header_map, response, *handler_info_); end_stream = false; } @@ -680,7 +678,7 @@ AdminImpl::AdminImpl(const std::string& access_log_path, const std::string& prof MAKE_ADMIN_HANDLER(handlerHealthcheckOk), false, true}, {"/help", "print out list of admin commands", MAKE_ADMIN_HANDLER(handlerHelp), false, false}, - {"/hot_restart_version", "print the hot restart compatability version", + {"/hot_restart_version", "print the hot restart compatibility version", MAKE_ADMIN_HANDLER(handlerHotRestartVersion), false, false}, {"/logging", "query/change logging levels", MAKE_ADMIN_HANDLER(handlerLogging), false, true}, @@ -862,12 +860,9 @@ void HystrixHandlerInfo::Destroy() { void HystrixHandler::updateHystrixRollingWindow(HystrixHandlerInfo* hystrix_handler_info, Server::Instance& server) { hystrix_handler_info->stats_->incCounter(); - - for (const Stats::CounterSharedPtr& counter : server.stats().counters()) { - // we save all upstream_rq stats. - if (counter->name().find("upstream_rq_") != std::string::npos) { - hystrix_handler_info->stats_->pushNewValue(counter->name(), counter->value()); - } + for (auto& cluster : server.clusterManager().clusters()) { + hystrix_handler_info->stats_->updateRollingWindowMap(server.stats(), + cluster.second.get().info()->name()); } } @@ -889,11 +884,7 @@ void HystrixHandler::prepareAndSendHystrixStream(HystrixHandlerInfo* hystrix_han } Buffer::OwnedImpl data; data.add(ss.str()); - - // using write() since we are sending network level - // TODO(trabetti): is there an alternative to the const_cast? - (const_cast((hystrix_handler_info->callbacks_)->connection())) - ->write(data, false); + hystrix_handler_info->callbacks_->encodeData(data, false); // restart timer hystrix_handler_info->data_timer_->enableTimer( @@ -903,11 +894,7 @@ void HystrixHandler::prepareAndSendHystrixStream(HystrixHandlerInfo* hystrix_han void HystrixHandler::sendKeepAlivePing(HystrixHandlerInfo* hystrix_handler_info) { Buffer::OwnedImpl data; data.add(":\n\n"); - - // using write() since we are sending network level - // TODO(trabetti): is there an alternative to the const_cast? - (const_cast((hystrix_handler_info->callbacks_)->connection())) - ->write(data, false); + hystrix_handler_info->callbacks_->encodeData(data, false); // restart timer hystrix_handler_info->ping_timer_->enableTimer( diff --git a/test/common/stats/BUILD b/test/common/stats/BUILD index 73d5686c5a54f..d281d2c3b925c 100644 --- a/test/common/stats/BUILD +++ b/test/common/stats/BUILD @@ -85,6 +85,10 @@ envoy_cc_test( name = "hystrix_test", srcs = ["hystrix_test.cc"], deps = [ + "//source/common/common:empty_string", + "//source/common/http:codes_lib", "//source/common/stats:hystrix_lib", + "//source/common/stats:stats_lib", + "//test/test_common:utility_lib", ], ) diff --git a/test/common/stats/hystrix_test.cc b/test/common/stats/hystrix_test.cc index 574831496b55a..bebeaafd31813 100644 --- a/test/common/stats/hystrix_test.cc +++ b/test/common/stats/hystrix_test.cc @@ -1,90 +1,131 @@ #include +#include "common/common/empty_string.h" +#include "common/http/codes.h" #include "common/stats/hystrix.h" +#include "common/stats/stats_impl.h" + +#include "test/test_common/utility.h" #include "gtest/gtest.h" namespace Envoy { namespace Stats { -void checkStreamField(std::string dataMessage, std::string key, uint64_t expected) { +// copied from CodeUtilityTest in codes_test.cc +class HystrixUtilityTest : public testing::Test { +public: + void addResponse(uint64_t code, bool canary, bool internal_request, + const std::string& request_vhost_name = EMPTY_STRING, + const std::string& request_vcluster_name = EMPTY_STRING, + const std::string& from_az = EMPTY_STRING, + const std::string& to_az = EMPTY_STRING) { + Http::CodeUtility::ResponseStatInfo info{global_store_, + cluster_scope_, + "cluster.clusterName.", + code, + internal_request, + request_vhost_name, + request_vcluster_name, + from_az, + to_az, + canary}; + + Http::CodeUtility::chargeResponseStat(info); + } + + IsolatedStoreImpl global_store_; + IsolatedStoreImpl cluster_scope_; +}; + +std::string getStreamField(std::string dataMessage, std::string key) { std::string actual = dataMessage.substr(dataMessage.find(key)); actual = actual.substr(actual.find(" ") + 1); std::size_t length = actual.find(","); actual = actual.substr(0, length); - EXPECT_EQ(actual, std::to_string(expected)); + // EXPECT_EQ(actual, std::to_string(expected)); + return actual; +} + +// this part is useful for testing updateRollingWindowMap() +// by using addResponse() to +TEST_F(HystrixUtilityTest, CreateDataMessage) { + Stats::Hystrix hystrix; + std::stringstream ss; + std::string cluster_name = "clusterName"; + uint64_t expected_queue_size = 12; + uint64_t expectedReportingHosts = 16; + + // insert data to rolling window + for (uint64_t i = 0; i < 14; i++) { + hystrix.incCounter(); + addResponse(201, false, false); + addResponse(401, false, false); + addResponse(501, false, true); + + hystrix.updateRollingWindowMap(cluster_scope_, cluster_name); + } + + hystrix.getClusterStats(ss, cluster_name, expected_queue_size, expectedReportingHosts); + std::string dataMessage = ss.str(); + + // check stream format and data + EXPECT_EQ(getStreamField(dataMessage, "errorPercentage"), "66"); + EXPECT_EQ(getStreamField(dataMessage, "errorCount"), "20"); + EXPECT_EQ(getStreamField(dataMessage, "requestCount"), "30"); + EXPECT_EQ(getStreamField(dataMessage, "rollingCountSemaphoreRejected"), "0"); + EXPECT_EQ(getStreamField(dataMessage, "rollingCountSuccess"), "10"); + EXPECT_EQ(getStreamField(dataMessage, "rollingCountTimeout"), "0"); + EXPECT_EQ(getStreamField(dataMessage, "propertyValue_queueSizeRejectionThreshold"), + std::to_string(expected_queue_size)); + EXPECT_EQ(getStreamField(dataMessage, "reportingHosts"), std::to_string(expectedReportingHosts)); } TEST(Hystrix, CreateDataMessage) { Stats::Hystrix hystrix; std::stringstream ss; - std::string clusterName = "clusterName"; - uint64_t expectedQueueSize = 12; + std::string cluster_name = "clusterName"; + uint64_t expected_queue_size = 12; uint64_t expectedReportingHosts = 16; EXPECT_EQ(hystrix.GetRollingWindowIntervalInMs(), 1000); EXPECT_EQ(hystrix.GetPingIntervalInMs(), 3000); // insert data to rolling window - for (uint64_t i = 0; i < 10; i++) { + for (uint64_t i = 0; i < 15; i++) { hystrix.incCounter(); - hystrix.pushNewValue("cluster.clusterName.upstream_rq_timeout", i + 1); - hystrix.pushNewValue("cluster.clusterName.upstream_rq_per_try_timeout", (i + 1) * 2); - hystrix.pushNewValue("cluster.clusterName.upstream_rq_5xx", (i + 1) * 3); - hystrix.pushNewValue("cluster.clusterName.retry.upstream_rq_5xx", (i + 1) * 4); - hystrix.pushNewValue("cluster.clusterName.upstream_rq_4xx", (i + 1) * 5); - hystrix.pushNewValue("cluster.clusterName.retry.upstream_rq_4xx", (i + 1) * 6); - hystrix.pushNewValue("cluster.clusterName.upstream_rq_2xx", (i + 1) * 7); - hystrix.pushNewValue("cluster.clusterName.upstream_rq_pending_overflow", (i + 1) * 8); + hystrix.pushNewValue("cluster.clusterName.timeouts", (i + 1) * 3); + hystrix.pushNewValue("cluster.clusterName.errors", (i + 1) * 17); + hystrix.pushNewValue("cluster.clusterName.success", (i + 1) * 7); + hystrix.pushNewValue("cluster.clusterName.rejected", (i + 1) * 8); + hystrix.pushNewValue("cluster.clusterName.total", (i + 1) * 35); } - hystrix.getClusterStats(ss, clusterName, expectedQueueSize, expectedReportingHosts); + hystrix.getClusterStats(ss, cluster_name, expected_queue_size, expectedReportingHosts); std::string dataMessage = ss.str(); // check stream format and data - checkStreamField(dataMessage, "errorPercentage", 80); - checkStreamField(dataMessage, "errorCount", 153); - checkStreamField(dataMessage, "requestCount", 315); - checkStreamField(dataMessage, "rollingCountSemaphoreRejected", 72); - checkStreamField(dataMessage, "rollingCountSuccess", 63); - checkStreamField(dataMessage, "rollingCountTimeout", 27); - checkStreamField(dataMessage, "propertyValue_queueSizeRejectionThreshold", expectedQueueSize); - checkStreamField(dataMessage, "reportingHosts", expectedReportingHosts); - - // make sure the rolling window really rolls - ss.str(""); - for (uint64_t i = 10; i < 13; i++) { - hystrix.incCounter(); - hystrix.pushNewValue("cluster.clusterName.upstream_rq_timeout", i + 1); - hystrix.pushNewValue("cluster.clusterName.upstream_rq_per_try_timeout", (i + 1) * 2); - hystrix.pushNewValue("cluster.clusterName.upstream_rq_5xx", (i + 1) * 3); - hystrix.pushNewValue("cluster.clusterName.retry.upstream_rq_5xx", (i + 1) * 4); - hystrix.pushNewValue("cluster.clusterName.upstream_rq_4xx", (i + 1) * 5); - hystrix.pushNewValue("cluster.clusterName.retry.upstream_rq_4xx", (i + 1) * 6); - hystrix.pushNewValue("cluster.clusterName.upstream_rq_2xx", (i + 1) * 7); - hystrix.pushNewValue("cluster.clusterName.upstream_rq_pending_overflow", (i + 1) * 8); - } - hystrix.getClusterStats(ss, clusterName, expectedQueueSize, expectedReportingHosts); - dataMessage = ss.str(); - - checkStreamField(dataMessage, "errorPercentage", 80); - checkStreamField(dataMessage, "errorCount", 153); - checkStreamField(dataMessage, "requestCount", 315); - checkStreamField(dataMessage, "rollingCountSemaphoreRejected", 72); - checkStreamField(dataMessage, "rollingCountSuccess", 63); - checkStreamField(dataMessage, "rollingCountTimeout", 27); + EXPECT_EQ(getStreamField(dataMessage, "errorPercentage"), "80"); + EXPECT_EQ(getStreamField(dataMessage, "errorCount"), "170"); + EXPECT_EQ(getStreamField(dataMessage, "requestCount"), "350"); + EXPECT_EQ(getStreamField(dataMessage, "rollingCountSemaphoreRejected"), "80"); + EXPECT_EQ(getStreamField(dataMessage, "rollingCountSuccess"), "70"); + EXPECT_EQ(getStreamField(dataMessage, "rollingCountTimeout"), "30"); + EXPECT_EQ(getStreamField(dataMessage, "propertyValue_queueSizeRejectionThreshold"), + std::to_string(expected_queue_size)); + EXPECT_EQ(getStreamField(dataMessage, "reportingHosts"), std::to_string(expectedReportingHosts)); // check reset of window ss.str(""); hystrix.resetRollingWindow(); - hystrix.getClusterStats(ss, clusterName, expectedQueueSize, expectedReportingHosts); + hystrix.getClusterStats(ss, cluster_name, expected_queue_size, expectedReportingHosts); dataMessage = ss.str(); - checkStreamField(dataMessage, "errorPercentage", 0); - checkStreamField(dataMessage, "errorCount", 0); - checkStreamField(dataMessage, "requestCount", 0); - checkStreamField(dataMessage, "rollingCountSemaphoreRejected", 0); - checkStreamField(dataMessage, "rollingCountSuccess", 0); - checkStreamField(dataMessage, "rollingCountTimeout", 0); + EXPECT_EQ(getStreamField(dataMessage, "errorPercentage"), "0"); + EXPECT_EQ(getStreamField(dataMessage, "errorCount"), "0"); + EXPECT_EQ(getStreamField(dataMessage, "requestCount"), "0"); + EXPECT_EQ(getStreamField(dataMessage, "rollingCountSemaphoreRejected"), "0"); + EXPECT_EQ(getStreamField(dataMessage, "rollingCountSuccess"), "0"); + EXPECT_EQ(getStreamField(dataMessage, "rollingCountTimeout"), "0"); } } // namespace Stats diff --git a/test/server/http/admin_test.cc b/test/server/http/admin_test.cc index 5409855218e09..6ec7cb07a04cb 100644 --- a/test/server/http/admin_test.cc +++ b/test/server/http/admin_test.cc @@ -138,8 +138,8 @@ TEST_P(AdminInstanceTest, AdminBadAddressOutPath) { } TEST_P(AdminInstanceTest, CustomHandler) { - auto callback = [&](const std::string&, Http::HeaderMap&, Buffer::Instance&, - HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; + auto callback = [](const std::string&, Http::HeaderMap&, Buffer::Instance&, + HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; // Test removable handler. EXPECT_TRUE(admin_.addHandler("/foo/bar", "hello", callback, true, false)); @@ -171,21 +171,21 @@ TEST_P(AdminInstanceTest, CustomHandler) { } TEST_P(AdminInstanceTest, RejectHandlerWithXss) { - auto callback = [&](const std::string&, Http::HeaderMap&, Buffer::Instance&, - HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; + auto callback = [](const std::string&, Http::HeaderMap&, Buffer::Instance&, + HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; EXPECT_FALSE( admin_.addHandler("/foo", "hello", callback, true, false)); } TEST_P(AdminInstanceTest, RejectHandlerWithEmbeddedQuery) { - auto callback = [&](const std::string&, Http::HeaderMap&, Buffer::Instance&, - HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; + auto callback = [](const std::string&, Http::HeaderMap&, Buffer::Instance&, + HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; EXPECT_FALSE(admin_.addHandler("/bar?queryShouldNotBeInPrefix", "hello", callback, true, false)); } TEST_P(AdminInstanceTest, EscapeHelpTextWithPunctuation) { - auto callback = [&](const std::string&, Http::HeaderMap&, Buffer::Instance&, - HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; + auto callback = [](const std::string&, Http::HeaderMap&, Buffer::Instance&, + HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; HandlerInfo handler_info; From 61659a39f3beebfd77c81f776386282f85b5c919 Mon Sep 17 00:00:00 2001 From: Eliran Roffe Date: Wed, 7 Mar 2018 03:53:59 -0500 Subject: [PATCH 06/25] Move HandlerInfoClasses from include folder to source folder Signed-off-by: Eliran Roffe --- include/envoy/server/BUILD | 1 - include/envoy/server/admin.h | 30 +++------------------------ source/server/http/BUILD | 1 + source/server/http/admin.cc | 14 ++++++------- source/server/http/admin.h | 40 +++++++++++++++++++++++++++++++++--- 5 files changed, 48 insertions(+), 38 deletions(-) diff --git a/include/envoy/server/BUILD b/include/envoy/server/BUILD index 5539a60ddb7d8..287de527dd1ae 100644 --- a/include/envoy/server/BUILD +++ b/include/envoy/server/BUILD @@ -26,7 +26,6 @@ envoy_cc_library( "//include/envoy/http:codes_interface", "//include/envoy/http:filter_interface", "//include/envoy/network:listen_socket_interface", - "//source/common/stats:hystrix_lib", ], ) diff --git a/include/envoy/server/admin.h b/include/envoy/server/admin.h index 9895b15472206..ed955ea112f34 100644 --- a/include/envoy/server/admin.h +++ b/include/envoy/server/admin.h @@ -10,8 +10,6 @@ #include "envoy/http/header_map.h" #include "envoy/network/listen_socket.h" -#include "common/stats/hystrix.h" - namespace Envoy { namespace Server { @@ -34,33 +32,11 @@ namespace Server { */ class HandlerInfo { public: - HandlerInfo(){}; - virtual ~HandlerInfo(){}; - virtual void Destroy(){}; + virtual ~HandlerInfo() {}; + virtual void Destroy() PURE; }; -typedef std::unique_ptr HandlerInfoPtr; - -/** - * This class contains data which will be sent from admin filter to a hystrix_event_stream handler - * and build a class which contains the relevant data. - */ -class HystrixHandlerInfo : public HandlerInfo { -public: - HystrixHandlerInfo(Http::StreamDecoderFilterCallbacks* callbacks) - : stats_(std::make_unique()), data_timer_(nullptr), ping_timer_(nullptr), - callbacks_(callbacks) {} - virtual ~HystrixHandlerInfo(){}; - void Destroy(); - /** - * HystrixHandlerInfo includes statistics for hystrix API, timers for build (and send) data and - * keep alive messages and the handler's callback - */ - Stats::HystrixPtr stats_; - Event::TimerPtr data_timer_; - Event::TimerPtr ping_timer_; - Http::StreamDecoderFilterCallbacks* callbacks_{}; -}; +typedef std::unique_ptr HandlerInfoPtr; /** * Global admin HTTP endpoint for the server. diff --git a/source/server/http/BUILD b/source/server/http/BUILD index 6d6c532ed6a48..c9af0bdb88d4c 100644 --- a/source/server/http/BUILD +++ b/source/server/http/BUILD @@ -47,6 +47,7 @@ envoy_cc_library( "//source/common/network:raw_buffer_socket_lib", "//source/common/profiler:profiler_lib", "//source/common/router:config_lib", + "//source/common/stats:hystrix_lib", "//source/common/upstream:host_utility_lib", "//source/server/config/network:http_connection_manager_lib", ], diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index f95d31577a342..944b9df3503d9 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -590,7 +590,7 @@ Http::Code AdminImpl::handlerHystrixEventStream(const std::string&, Http::Headers::get().AccessControlAllowOriginValue.All); response_headers.insertNoChunks().value().setReference("0"); - HystrixHandlerInfo& hystrix_handler_info = dynamic_cast(handler_info); + HystrixHandlerInfoImpl& hystrix_handler_info = dynamic_cast(handler_info); // start streaming hystrix_handler_info.data_timer_ = hystrix_handler_info.callbacks_->dispatcher().createTimer( @@ -624,10 +624,10 @@ void AdminFilter::onComplete() { bool end_stream = true; if (path.find("/hystrix_event_stream") == std::string::npos) { - handler_info_ = std::make_unique(); + handler_info_ = std::make_unique(); code = parent_.runCallback(path, *header_map, response, *handler_info_); } else { - handler_info_ = std::make_unique(callbacks_); + handler_info_ = std::make_unique(callbacks_); code = parent_.runCallback(path, *header_map, response, *handler_info_); end_stream = false; } @@ -846,7 +846,7 @@ bool AdminImpl::removeHandler(const std::string& prefix) { return false; } -void HystrixHandlerInfo::Destroy() { +void HystrixHandlerInfoImpl::Destroy() { if (data_timer_) { data_timer_->disableTimer(); data_timer_.reset(); @@ -857,7 +857,7 @@ void HystrixHandlerInfo::Destroy() { } } -void HystrixHandler::updateHystrixRollingWindow(HystrixHandlerInfo* hystrix_handler_info, +void HystrixHandler::updateHystrixRollingWindow(HystrixHandlerInfoImpl* hystrix_handler_info, Server::Instance& server) { hystrix_handler_info->stats_->incCounter(); for (auto& cluster : server.clusterManager().clusters()) { @@ -866,7 +866,7 @@ void HystrixHandler::updateHystrixRollingWindow(HystrixHandlerInfo* hystrix_hand } } -void HystrixHandler::prepareAndSendHystrixStream(HystrixHandlerInfo* hystrix_handler_info, +void HystrixHandler::prepareAndSendHystrixStream(HystrixHandlerInfoImpl* hystrix_handler_info, Server::Instance& server) { updateHystrixRollingWindow(hystrix_handler_info, server); std::stringstream ss; @@ -891,7 +891,7 @@ void HystrixHandler::prepareAndSendHystrixStream(HystrixHandlerInfo* hystrix_han std::chrono::milliseconds(Stats::Hystrix::GetRollingWindowIntervalInMs())); } -void HystrixHandler::sendKeepAlivePing(HystrixHandlerInfo* hystrix_handler_info) { +void HystrixHandler::sendKeepAlivePing(HystrixHandlerInfoImpl* hystrix_handler_info) { Buffer::OwnedImpl data; data.add(":\n\n"); hystrix_handler_info->callbacks_->encodeData(data, false); diff --git a/source/server/http/admin.h b/source/server/http/admin.h index 82161fc53f0fb..bc5d7cc05c1a1 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -22,12 +22,46 @@ #include "common/http/date_provider_impl.h" #include "common/http/utility.h" #include "common/network/raw_buffer_socket.h" +#include "common/stats/hystrix.h" #include "server/config/network/http_connection_manager.h" namespace Envoy { namespace Server { +/** + * This class contains data which will be sent from admin filter to a handler + * and build a class which contains the relevant data. + */ +class HandlerInfoImpl : public HandlerInfo { +public: + HandlerInfoImpl(){}; + virtual ~HandlerInfoImpl(){}; + virtual void Destroy(){}; +}; + +/** + * This class contains data which will be sent from admin filter to a hystrix_event_stream handler + * and build a class which contains the relevant data. + */ +class HystrixHandlerInfoImpl : public HandlerInfo { +public: + HystrixHandlerInfoImpl(Http::StreamDecoderFilterCallbacks* callbacks) + : stats_(new Stats::Hystrix()), data_timer_(nullptr), ping_timer_(nullptr), + callbacks_(callbacks) {} + virtual ~HystrixHandlerInfoImpl(){}; + virtual void Destroy(); + + /** + * HystrixHandlerInfoImpl includes statistics for hystrix API, timers for build (and send) data and + * keep alive messages and the handler's callback + */ + Stats::HystrixPtr stats_; + Event::TimerPtr data_timer_; + Event::TimerPtr ping_timer_; + Http::StreamDecoderFilterCallbacks* callbacks_{}; +}; + /** * Implementation of Server::admin. */ @@ -292,7 +326,7 @@ class HystrixHandler { * filter (callback, timers, statistics) * @param server contains envoy statistics */ - static void updateHystrixRollingWindow(HystrixHandlerInfo* hystrix_handler_info, + static void updateHystrixRollingWindow(HystrixHandlerInfoImpl* hystrix_handler_info, Server::Instance& server); /** * Builds a buffer of envoy statistics which will be sent to hystrix dashboard according to @@ -301,14 +335,14 @@ class HystrixHandler { * filter (callback, timers, statistics) * @param server contains envoy statistics* */ - static void prepareAndSendHystrixStream(HystrixHandlerInfo* hystrix_handler_info, + static void prepareAndSendHystrixStream(HystrixHandlerInfoImpl* hystrix_handler_info, Server::Instance& server); /** * Sends a keep alive (ping) message to hystrix dashboard * @param hystrix_handler_info is the data which is received in the hystrix handler from the admin * filter (callback, timers, statistics) */ - static void sendKeepAlivePing(HystrixHandlerInfo* hystrix_handler_info); + static void sendKeepAlivePing(HystrixHandlerInfoImpl* hystrix_handler_info); }; } // namespace Server From a05aea1a9388df75e87183c402dd323ce1c8e14b Mon Sep 17 00:00:00 2001 From: Eliran Roffe Date: Wed, 7 Mar 2018 11:03:35 -0500 Subject: [PATCH 07/25] Move Hystrix code from Admin to a decdicated file Signed-off-by: Eliran Roffe --- include/envoy/server/admin.h | 2 +- source/common/stats/BUILD | 3 ++ source/common/stats/hystrix.cc | 79 ++++++++++++++++++++++++++++- source/common/stats/hystrix.h | 57 +++++++++++++++++++++ source/server/http/admin.cc | 78 ++-------------------------- source/server/http/admin.h | 53 ------------------- test/common/router/rds_impl_test.cc | 4 +- test/server/http/admin_test.cc | 16 +++--- 8 files changed, 152 insertions(+), 140 deletions(-) diff --git a/include/envoy/server/admin.h b/include/envoy/server/admin.h index ed955ea112f34..9b0466bf31278 100644 --- a/include/envoy/server/admin.h +++ b/include/envoy/server/admin.h @@ -32,7 +32,7 @@ namespace Server { */ class HandlerInfo { public: - virtual ~HandlerInfo() {}; + virtual ~HandlerInfo(){}; virtual void Destroy() PURE; }; diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index e6f47763f23a1..2c3034e1567b3 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -77,6 +77,9 @@ envoy_cc_library( hdrs = ["hystrix.h"], deps = [ ":stats_lib", + "//include/envoy/server:admin_interface", + "//include/envoy/server:instance_interface", + "//source/common/buffer:buffer_lib", "//source/common/common:logger_lib", ], ) diff --git a/source/common/stats/hystrix.cc b/source/common/stats/hystrix.cc index 1f9853a636610..ad5f049183d91 100644 --- a/source/common/stats/hystrix.cc +++ b/source/common/stats/hystrix.cc @@ -5,6 +5,7 @@ #include #include +#include "common/buffer/buffer_impl.h" #include "common/common/logger.h" namespace Envoy { @@ -29,8 +30,6 @@ uint64_t Hystrix::getRollingValue(std::string cluster_name, std::string stats) { if (rolling_stats_map_.find(key) != rolling_stats_map_.end()) { // if the counter was reset, the result is negative // better return 0, will be back to normal once one rolling window passes - // better idea what to return? could change the algorithm to keep last valid delta, - // updated with pushNewValue and kept stable when the update is negative. if (rolling_stats_map_[key][current_index_] < rolling_stats_map_[key][(current_index_ + 1) % num_of_buckets_]) { return 0; @@ -215,5 +214,81 @@ std::string Hystrix::printRollingWindow() { return out_str.str(); } +void HystrixHandlerInfoImpl::Destroy() { + if (data_timer_) { + data_timer_->disableTimer(); + data_timer_.reset(); + } + if (ping_timer_) { + ping_timer_->disableTimer(); + ping_timer_.reset(); + } +} + +void HystrixHandler::HandleEventStream(HystrixHandlerInfoImpl* hystrix_handler_info, + Server::Instance& server) { + Server::Instance* serverPtr = &server; + // start streaming + hystrix_handler_info->data_timer_ = hystrix_handler_info->callbacks_->dispatcher().createTimer( + [hystrix_handler_info, serverPtr]() -> void { + HystrixHandler::prepareAndSendHystrixStream(hystrix_handler_info, serverPtr); + }); + hystrix_handler_info->data_timer_->enableTimer( + std::chrono::milliseconds(Stats::Hystrix::GetRollingWindowIntervalInMs())); + + // start keep alive ping + hystrix_handler_info->ping_timer_ = + hystrix_handler_info->callbacks_->dispatcher().createTimer([hystrix_handler_info]() -> void { + HystrixHandler::sendKeepAlivePing(hystrix_handler_info); + }); + + hystrix_handler_info->ping_timer_->enableTimer( + std::chrono::milliseconds(Stats::Hystrix::GetPingIntervalInMs())); +} + +void HystrixHandler::updateHystrixRollingWindow(HystrixHandlerInfoImpl* hystrix_handler_info, + Server::Instance* server) { + hystrix_handler_info->stats_->incCounter(); + for (auto& cluster : server->clusterManager().clusters()) { + hystrix_handler_info->stats_->updateRollingWindowMap(server->stats(), + cluster.second.get().info()->name()); + } +} + +void HystrixHandler::prepareAndSendHystrixStream(HystrixHandlerInfoImpl* hystrix_handler_info, + Server::Instance* server) { + updateHystrixRollingWindow(hystrix_handler_info, server); + std::stringstream ss; + for (auto& cluster : server->clusterManager().clusters()) { + hystrix_handler_info->stats_->getClusterStats( + ss, cluster.second.get().info()->name(), + cluster.second.get() + .info() + ->resourceManager(Upstream::ResourcePriority::Default) + .pendingRequests() + .max(), + server->stats() + .gauge("cluster." + cluster.second.get().info()->name() + ".membership_total") + .value()); + } + Buffer::OwnedImpl data; + data.add(ss.str()); + hystrix_handler_info->callbacks_->encodeData(data, false); + + // restart timer + hystrix_handler_info->data_timer_->enableTimer( + std::chrono::milliseconds(Stats::Hystrix::GetRollingWindowIntervalInMs())); +} + +void HystrixHandler::sendKeepAlivePing(HystrixHandlerInfoImpl* hystrix_handler_info) { + Buffer::OwnedImpl data; + data.add(":\n\n"); + hystrix_handler_info->callbacks_->encodeData(data, false); + + // restart timer + hystrix_handler_info->ping_timer_->enableTimer( + std::chrono::milliseconds(Stats::Hystrix::GetPingIntervalInMs())); +} + } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/hystrix.h b/source/common/stats/hystrix.h index 65542515178a3..5b09a5bb1f404 100644 --- a/source/common/stats/hystrix.h +++ b/source/common/stats/hystrix.h @@ -2,6 +2,8 @@ #include #include +#include "envoy/server/admin.h" +#include "envoy/server/instance.h" #include "envoy/stats/stats.h" namespace Envoy { @@ -101,5 +103,60 @@ class Hystrix { typedef std::unique_ptr HystrixPtr; +/** + * This class contains data which will be sent from admin filter to a hystrix_event_stream handler + * and build a class which contains the relevant data. + */ +class HystrixHandlerInfoImpl : public Server::HandlerInfo { +public: + HystrixHandlerInfoImpl(Http::StreamDecoderFilterCallbacks* callbacks) + : stats_(new Stats::Hystrix()), data_timer_(nullptr), ping_timer_(nullptr), + callbacks_(callbacks) {} + virtual ~HystrixHandlerInfoImpl(){}; + virtual void Destroy(); + + /** + * HystrixHandlerInfoImpl includes statistics for hystrix API, timers for build (and send) data + * and keep alive messages and the handler's callback + */ + Stats::HystrixPtr stats_; + Event::TimerPtr data_timer_; + Event::TimerPtr ping_timer_; + Http::StreamDecoderFilterCallbacks* callbacks_{}; +}; + +/** + * Convert statistics from envoy format to hystrix format and prepare them and writes them to the + * appropriate socket + */ +class HystrixHandler { +public: + static void HandleEventStream(HystrixHandlerInfoImpl* hystrix_handler_info, + Server::Instance& server); + /** + * Update counter and set values of upstream_rq statistics + * @param hystrix_handler_info is the data which is received in the hystrix handler from the admin + * filter (callback, timers, statistics) + * @param server contains envoy statistics + */ + static void updateHystrixRollingWindow(HystrixHandlerInfoImpl* hystrix_handler_info, + Server::Instance* server); + /** + * Builds a buffer of envoy statistics which will be sent to hystrix dashboard according to + * hystrix API + * @param hystrix_handler_info is the data which is received in the hystrix handler from the admin + * filter (callback, timers, statistics) + * @param server contains envoy statistics* + */ + static void prepareAndSendHystrixStream(HystrixHandlerInfoImpl* hystrix_handler_info, + Server::Instance* server); + /** + * Sends a keep alive (ping) message to hystrix dashboard + * @param hystrix_handler_info is the data which is received in the hystrix handler from the admin + * filter (callback, timers, statistics) + */ + static void sendKeepAlivePing(HystrixHandlerInfoImpl* hystrix_handler_info); +}; + } // namespace Stats } // namespace Envoy diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 944b9df3503d9..363082cf12c5d 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -590,24 +590,9 @@ Http::Code AdminImpl::handlerHystrixEventStream(const std::string&, Http::Headers::get().AccessControlAllowOriginValue.All); response_headers.insertNoChunks().value().setReference("0"); - HystrixHandlerInfoImpl& hystrix_handler_info = dynamic_cast(handler_info); - - // start streaming - hystrix_handler_info.data_timer_ = hystrix_handler_info.callbacks_->dispatcher().createTimer( - [this, &hystrix_handler_info]() -> void { - HystrixHandler::prepareAndSendHystrixStream(&hystrix_handler_info, server_); - }); - hystrix_handler_info.data_timer_->enableTimer( - std::chrono::milliseconds(Stats::Hystrix::GetRollingWindowIntervalInMs())); - - // start keep alive ping - hystrix_handler_info.ping_timer_ = - hystrix_handler_info.callbacks_->dispatcher().createTimer([&hystrix_handler_info]() -> void { - HystrixHandler::sendKeepAlivePing(&hystrix_handler_info); - }); - - hystrix_handler_info.ping_timer_->enableTimer( - std::chrono::milliseconds(Stats::Hystrix::GetPingIntervalInMs())); + Stats::HystrixHandlerInfoImpl& hystrix_handler_info = + dynamic_cast(handler_info); + Stats::HystrixHandler::HandleEventStream(&hystrix_handler_info, server_); ENVOY_LOG(debug, "start sending data to hystrix dashboard on port {}", hystrix_handler_info.callbacks_->connection()->localAddress()->asString()); @@ -627,7 +612,7 @@ void AdminFilter::onComplete() { handler_info_ = std::make_unique(); code = parent_.runCallback(path, *header_map, response, *handler_info_); } else { - handler_info_ = std::make_unique(callbacks_); + handler_info_ = std::make_unique(callbacks_); code = parent_.runCallback(path, *header_map, response, *handler_info_); end_stream = false; } @@ -846,60 +831,5 @@ bool AdminImpl::removeHandler(const std::string& prefix) { return false; } -void HystrixHandlerInfoImpl::Destroy() { - if (data_timer_) { - data_timer_->disableTimer(); - data_timer_.reset(); - } - if (ping_timer_) { - ping_timer_->disableTimer(); - ping_timer_.reset(); - } -} - -void HystrixHandler::updateHystrixRollingWindow(HystrixHandlerInfoImpl* hystrix_handler_info, - Server::Instance& server) { - hystrix_handler_info->stats_->incCounter(); - for (auto& cluster : server.clusterManager().clusters()) { - hystrix_handler_info->stats_->updateRollingWindowMap(server.stats(), - cluster.second.get().info()->name()); - } -} - -void HystrixHandler::prepareAndSendHystrixStream(HystrixHandlerInfoImpl* hystrix_handler_info, - Server::Instance& server) { - updateHystrixRollingWindow(hystrix_handler_info, server); - std::stringstream ss; - for (auto& cluster : server.clusterManager().clusters()) { - hystrix_handler_info->stats_->getClusterStats( - ss, cluster.second.get().info()->name(), - cluster.second.get() - .info() - ->resourceManager(Upstream::ResourcePriority::Default) - .pendingRequests() - .max(), - server.stats() - .gauge("cluster." + cluster.second.get().info()->name() + ".membership_total") - .value()); - } - Buffer::OwnedImpl data; - data.add(ss.str()); - hystrix_handler_info->callbacks_->encodeData(data, false); - - // restart timer - hystrix_handler_info->data_timer_->enableTimer( - std::chrono::milliseconds(Stats::Hystrix::GetRollingWindowIntervalInMs())); -} - -void HystrixHandler::sendKeepAlivePing(HystrixHandlerInfoImpl* hystrix_handler_info) { - Buffer::OwnedImpl data; - data.add(":\n\n"); - hystrix_handler_info->callbacks_->encodeData(data, false); - - // restart timer - hystrix_handler_info->ping_timer_->enableTimer( - std::chrono::milliseconds(Stats::Hystrix::GetPingIntervalInMs())); -} - } // namespace Server } // namespace Envoy diff --git a/source/server/http/admin.h b/source/server/http/admin.h index bc5d7cc05c1a1..adaf1b9a5715c 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -40,28 +40,6 @@ class HandlerInfoImpl : public HandlerInfo { virtual void Destroy(){}; }; -/** - * This class contains data which will be sent from admin filter to a hystrix_event_stream handler - * and build a class which contains the relevant data. - */ -class HystrixHandlerInfoImpl : public HandlerInfo { -public: - HystrixHandlerInfoImpl(Http::StreamDecoderFilterCallbacks* callbacks) - : stats_(new Stats::Hystrix()), data_timer_(nullptr), ping_timer_(nullptr), - callbacks_(callbacks) {} - virtual ~HystrixHandlerInfoImpl(){}; - virtual void Destroy(); - - /** - * HystrixHandlerInfoImpl includes statistics for hystrix API, timers for build (and send) data and - * keep alive messages and the handler's callback - */ - Stats::HystrixPtr stats_; - Event::TimerPtr data_timer_; - Event::TimerPtr ping_timer_; - Http::StreamDecoderFilterCallbacks* callbacks_{}; -}; - /** * Implementation of Server::admin. */ @@ -314,36 +292,5 @@ class PrometheusStatsFormatter { static std::string sanitizeName(const std::string& name); }; -/** - * Convert statistics from envoy format to hystrix format and prepare them and writes them to the - * appropriate socket - */ -class HystrixHandler { -public: - /** - * Update counter and set values of upstream_rq statistics - * @param hystrix_handler_info is the data which is received in the hystrix handler from the admin - * filter (callback, timers, statistics) - * @param server contains envoy statistics - */ - static void updateHystrixRollingWindow(HystrixHandlerInfoImpl* hystrix_handler_info, - Server::Instance& server); - /** - * Builds a buffer of envoy statistics which will be sent to hystrix dashboard according to - * hystrix API - * @param hystrix_handler_info is the data which is received in the hystrix handler from the admin - * filter (callback, timers, statistics) - * @param server contains envoy statistics* - */ - static void prepareAndSendHystrixStream(HystrixHandlerInfoImpl* hystrix_handler_info, - Server::Instance& server); - /** - * Sends a keep alive (ping) message to hystrix dashboard - * @param hystrix_handler_info is the data which is received in the hystrix handler from the admin - * filter (callback, timers, statistics) - */ - static void sendKeepAlivePing(HystrixHandlerInfoImpl* hystrix_handler_info); -}; - } // namespace Server } // namespace Envoy diff --git a/test/common/router/rds_impl_test.cc b/test/common/router/rds_impl_test.cc index 5268862d9e6f8..c9650b23fac90 100644 --- a/test/common/router/rds_impl_test.cc +++ b/test/common/router/rds_impl_test.cc @@ -233,7 +233,7 @@ TEST_F(RdsImplTest, Basic) { )EOF"; EXPECT_EQ("", rds_->versionInfo()); Http::HeaderMapImpl header_map; - Server::HandlerInfo handler_info; + Server::HandlerInfoImpl handler_info; EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data, handler_info)); EXPECT_EQ(routes_expected_output_no_routes, TestUtility::bufferToString(data)); data.drain(data.length()); @@ -617,7 +617,7 @@ TEST_F(RouteConfigProviderManagerImplTest, Basic) { // Test Admin /routes handler. Http::HeaderMapImpl header_map; - Server::HandlerInfo handler_info; + Server::HandlerInfoImpl handler_info; EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data, handler_info)); EXPECT_EQ(routes_expected_output, TestUtility::bufferToString(data)); data.drain(data.length()); diff --git a/test/server/http/admin_test.cc b/test/server/http/admin_test.cc index 6ec7cb07a04cb..26c07b7100d4c 100644 --- a/test/server/http/admin_test.cc +++ b/test/server/http/admin_test.cc @@ -99,7 +99,7 @@ INSTANTIATE_TEST_CASE_P(IpVersions, AdminInstanceTest, TEST_P(AdminInstanceTest, AdminProfiler) { Buffer::OwnedImpl data; Http::HeaderMapImpl header_map; - HandlerInfo handler_info; + HandlerInfoImpl handler_info; EXPECT_EQ(Http::Code::OK, admin_.runCallback("/cpuprofiler?enable=y", header_map, data, handler_info)); EXPECT_TRUE(Profiler::Cpu::profilerEnabled()); @@ -117,7 +117,7 @@ TEST_P(AdminInstanceTest, AdminBadProfiler) { "", Network::Test::getCanonicalLoopbackAddress(GetParam()), server_, listener_scope_.createScope("listener.admin.")); Http::HeaderMapImpl header_map; - HandlerInfo handler_info; + HandlerInfoImpl handler_info; admin_bad_profile_path.runCallback("/cpuprofiler?enable=y", header_map, data, handler_info); EXPECT_FALSE(Profiler::Cpu::profilerEnabled()); } @@ -145,7 +145,7 @@ TEST_P(AdminInstanceTest, CustomHandler) { EXPECT_TRUE(admin_.addHandler("/foo/bar", "hello", callback, true, false)); Http::HeaderMapImpl header_map; Buffer::OwnedImpl response; - HandlerInfo handler_info; + HandlerInfoImpl handler_info; EXPECT_EQ(Http::Code::Accepted, admin_.runCallback("/foo/bar", header_map, response, handler_info)); @@ -187,7 +187,7 @@ TEST_P(AdminInstanceTest, EscapeHelpTextWithPunctuation) { auto callback = [](const std::string&, Http::HeaderMap&, Buffer::Instance&, HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; - HandlerInfo handler_info; + HandlerInfoImpl handler_info; // It's OK to have help text with HTML characters in it, but when we render the home // page they need to be escaped. @@ -207,7 +207,7 @@ TEST_P(AdminInstanceTest, EscapeHelpTextWithPunctuation) { TEST_P(AdminInstanceTest, HelpUsesFormForMutations) { Http::HeaderMapImpl header_map; Buffer::OwnedImpl response; - HandlerInfo handler_info; + HandlerInfoImpl handler_info; EXPECT_EQ(Http::Code::OK, admin_.runCallback("/", header_map, response, handler_info)); const std::string logging_action = " entries; Runtime::MockSnapshot snapshot; Runtime::MockLoader loader; - HandlerInfo handler_info; + HandlerInfoImpl handler_info; EXPECT_CALL(snapshot, getAll()).WillRepeatedly(testing::ReturnRef(entries)); EXPECT_CALL(loader, snapshot()).WillRepeatedly(testing::ReturnPointee(&snapshot)); From 9eac8cf044111fae17ab737e614cbc5e83fc9d7a Mon Sep 17 00:00:00 2001 From: Eliran Roffe Date: Tue, 20 Mar 2018 11:22:57 -0400 Subject: [PATCH 08/25] Change std::string to absl:string_view in hystrix files --- source/common/stats/hystrix.cc | 81 +++++++++++++++++++------------ source/common/stats/hystrix.h | 20 ++++---- test/common/stats/hystrix_test.cc | 15 ++++-- 3 files changed, 71 insertions(+), 45 deletions(-) diff --git a/source/common/stats/hystrix.cc b/source/common/stats/hystrix.cc index ad5f049183d91..141d3e71cd0fc 100644 --- a/source/common/stats/hystrix.cc +++ b/source/common/stats/hystrix.cc @@ -5,6 +5,8 @@ #include #include +#include "absl/strings/str_cat.h" + #include "common/buffer/buffer_impl.h" #include "common/common/logger.h" @@ -25,8 +27,9 @@ void Hystrix::pushNewValue(std::string key, uint64_t value) { } } -uint64_t Hystrix::getRollingValue(std::string cluster_name, std::string stats) { - std::string key = "cluster." + cluster_name + "." + stats; +uint64_t Hystrix::getRollingValue(absl::string_view cluster_name, absl::string_view stats) { + std::string key; + key = absl::StrCat("cluster.",cluster_name,".",stats); if (rolling_stats_map_.find(key) != rolling_stats_map_.end()) { // if the counter was reset, the result is negative // better return 0, will be back to normal once one rolling window passes @@ -42,38 +45,52 @@ uint64_t Hystrix::getRollingValue(std::string cluster_name, std::string stats) { } } -void Hystrix::updateRollingWindowMap(Stats::Store& stats, std::string cluster_name) { - std::string prefix = "cluster." + cluster_name + "."; +void Hystrix::updateRollingWindowMap(Stats::Store& stats, absl::string_view cluster_name) { + std::string prefix; + prefix = absl::StrCat("cluster.",cluster_name,"."); // combining timeouts+retries - retries are counted as separate requests // (alternative: each request including the retries counted as 1) - uint64_t timeouts = stats.counter(prefix + "upstream_rq_timeout").value() + - stats.counter(prefix + "upstream_rq_per_try_timeout").value(); + std::string upstream_rq_timeout_key = absl::StrCat(prefix,"upstream_rq_timeout"); + std::string upstream_rq_per_try_timeout_key = absl::StrCat(prefix,"upstream_rq_per_try_timeout"); + uint64_t timeouts = stats.counter(upstream_rq_timeout_key).value() + + stats.counter(upstream_rq_per_try_timeout_key).value(); - pushNewValue(prefix + "timeouts", timeouts); + std::string timeouts_key = absl::StrCat(prefix,"timeouts"); + pushNewValue(timeouts_key,timeouts); // combining errors+retry errors - retries are counted as separate requests // (alternative: each request including the retries counted as 1) // since timeouts are 504 (or 408), deduce them from here. // timeout retries were not counted here anyway. - uint64_t errors = stats.counter(prefix + "upstream_rq_5xx").value() + - stats.counter(prefix + "retry.upstream_rq_5xx").value() + - stats.counter(prefix + "upstream_rq_4xx").value() + - stats.counter(prefix + "retry.upstream_rq_4xx").value() - - stats.counter(prefix + "upstream_rq_timeout").value(); - - pushNewValue(prefix + "errors", errors); - - uint64_t success = stats.counter(prefix + "upstream_rq_2xx").value(); - pushNewValue(prefix + "success", success); - - uint64_t rejected = stats.counter(prefix + "upstream_rq_pending_overflow").value(); - pushNewValue(prefix + "rejected", rejected); + std::string upstream_rq_5xx_key = absl::StrCat(prefix,"upstream_rq_5xx"); + std::string retry_upstream_rq_5xx_key = absl::StrCat(prefix,"retry.upstream_rq_5xx"); + std::string upstream_rq_4xx_key = absl::StrCat(prefix,"upstream_rq_4xx"); + std::string retry_upstream_rq_4xx_key = absl::StrCat(prefix,"retry.upstream_rq_4xx"); + uint64_t errors = stats.counter(upstream_rq_5xx_key).value() + + stats.counter(retry_upstream_rq_5xx_key).value() + + stats.counter(upstream_rq_4xx_key).value() + + stats.counter(retry_upstream_rq_4xx_key).value() - + stats.counter(upstream_rq_timeout_key).value(); + + std::string errors_key = absl::StrCat(prefix,"errors"); + pushNewValue(errors_key,errors); + + std::string upstream_rq_2xx_key = absl::StrCat(prefix,"upstream_rq_2xx"); + uint64_t success = stats.counter(upstream_rq_2xx_key).value(); + std::string success_key = absl::StrCat(prefix,"success"); + pushNewValue(success_key,success); + + std::string upstream_rq_pending_overflow_key = absl::StrCat(prefix,"upstream_rq_pending_overflow"); + uint64_t rejected = stats.counter(upstream_rq_pending_overflow_key).value(); + std::string rejected_key = absl::StrCat(prefix,"rejected"); + pushNewValue(rejected_key, rejected); // should not take from upstream_rq_total since it is updated before its components, // leading to wrong results such as error percentage higher than 100% uint64_t total = errors + timeouts + success + rejected; - pushNewValue(prefix + "total", total); + std::string total_key = absl::StrCat(prefix,"total"); + pushNewValue(total_key, total); // TODO (@trabetti) : why does it fail compilation? // ENVOY_LOG(trace, "{}", printRollingWindow()); @@ -81,22 +98,26 @@ void Hystrix::updateRollingWindowMap(Stats::Store& stats, std::string cluster_na void Hystrix::resetRollingWindow() { rolling_stats_map_.clear(); } -void Hystrix::addStringToStream(std::string key, std::string value, std::stringstream& info) { - addInfoToStream(key, "\"" + value + "\"", info); +void Hystrix::addStringToStream(absl::string_view key, absl::string_view value, std::stringstream& info) { + std::string quoted_value; + quoted_value = absl::StrCat("\"", value, "\""); + addInfoToStream(key, quoted_value, info); } -void Hystrix::addIntToStream(std::string key, uint64_t value, std::stringstream& info) { +void Hystrix::addIntToStream(absl::string_view key, uint64_t value, std::stringstream& info) { addInfoToStream(key, std::to_string(value), info); } -void Hystrix::addInfoToStream(std::string key, std::string value, std::stringstream& info) { +void Hystrix::addInfoToStream(absl::string_view key, absl::string_view value, std::stringstream& info) { if (!info.str().empty()) { info << ", "; } - info << "\"" + key + "\": " + value; + std::string added_info; + added_info = absl::StrCat("\"",key,"\": ",value); + info << added_info; } -void Hystrix::addHystrixCommand(std::stringstream& ss, std::string cluster_name, +void Hystrix::addHystrixCommand(std::stringstream& ss, absl::string_view cluster_name, uint64_t max_concurrent_requests, uint64_t reporting_hosts) { std::stringstream cluster_info; std::time_t currentTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); @@ -169,7 +190,7 @@ void Hystrix::addHystrixCommand(std::stringstream& ss, std::string cluster_name, ss << "data: {" << cluster_info.str() << "}" << std::endl << std::endl; } -void Hystrix::addHystrixThreadPool(std::stringstream& ss, std::string cluster_name, +void Hystrix::addHystrixThreadPool(std::stringstream& ss, absl::string_view cluster_name, uint64_t queue_size, uint64_t reporting_hosts) { std::stringstream cluster_info; @@ -193,13 +214,13 @@ void Hystrix::addHystrixThreadPool(std::stringstream& ss, std::string cluster_na ss << "data: {" << cluster_info.str() << "}" << std::endl << std::endl; } -void Hystrix::getClusterStats(std::stringstream& ss, std::string cluster_name, +void Hystrix::getClusterStats(std::stringstream& ss, absl::string_view cluster_name, uint64_t max_concurrent_requests, uint64_t reporting_hosts) { addHystrixCommand(ss, cluster_name, max_concurrent_requests, reporting_hosts); addHystrixThreadPool(ss, cluster_name, max_concurrent_requests, reporting_hosts); } -std::string Hystrix::printRollingWindow() { +absl::string_view Hystrix::printRollingWindow() { std::stringstream out_str; for (std::map::const_iterator it = rolling_stats_map_.begin(); diff --git a/source/common/stats/hystrix.h b/source/common/stats/hystrix.h index 5b09a5bb1f404..8ba5a8d73fba9 100644 --- a/source/common/stats/hystrix.h +++ b/source/common/stats/hystrix.h @@ -34,7 +34,7 @@ class Hystrix { /** * Generate the streams to be sent to hystrix dashboard */ - void getClusterStats(std::stringstream& ss, std::string cluster_name, + void getClusterStats(std::stringstream& ss, absl::string_view cluster_name, uint64_t max_concurrent_requests, uint64_t reporting_hosts); /** @@ -49,47 +49,47 @@ class Hystrix { */ static uint64_t GetPingIntervalInMs() { return PING_INTERVAL_IN_MS; } - void updateRollingWindowMap(Stats::Store& stats, std::string cluster_name); + void updateRollingWindowMap(Stats::Store& stats, absl::string_view cluster_name); /** * clear map */ void resetRollingWindow(); - std::string printRollingWindow(); + absl::string_view printRollingWindow(); private: /** * Get the statistic's value change over the rolling window time frame */ - uint64_t getRollingValue(std::string cluster_name, std::string stats); + uint64_t getRollingValue(absl::string_view cluster_name, absl::string_view stats); /** - * Format the given key and std::string value to "key"="value", and adding to the stringstream + * Format the given key and absl::string_view value to "key"="value", and adding to the stringstream */ - void addStringToStream(std::string key, std::string value, std::stringstream& info); + void addStringToStream(absl::string_view key, absl::string_view value, std::stringstream& info); /** * Format the given key and uint64_t value to "key"=, and adding to the * stringstream */ - void addIntToStream(std::string key, uint64_t value, std::stringstream& info); + void addIntToStream(absl::string_view key, uint64_t value, std::stringstream& info); /** * Format the given key and value to "key"=value, and adding to the stringstream */ - void addInfoToStream(std::string key, std::string value, std::stringstream& info); + void addInfoToStream(absl::string_view key, absl::string_view value, std::stringstream& info); /** * generate HystrixCommand event stream */ - void addHystrixCommand(std::stringstream& ss, std::string cluster_name, + void addHystrixCommand(std::stringstream& ss, absl::string_view cluster_name, uint64_t max_concurrent_requests, uint64_t reporting_hosts); /** * generate HystrixThreadPool event stream */ - void addHystrixThreadPool(std::stringstream& ss, std::string cluster_name, uint64_t queue_size, + void addHystrixThreadPool(std::stringstream& ss, absl::string_view cluster_name, uint64_t queue_size, uint64_t reporting_hosts); RollingStatsMap rolling_stats_map_; diff --git a/test/common/stats/hystrix_test.cc b/test/common/stats/hystrix_test.cc index bebeaafd31813..519c417d343ed 100644 --- a/test/common/stats/hystrix_test.cc +++ b/test/common/stats/hystrix_test.cc @@ -38,11 +38,16 @@ class HystrixUtilityTest : public testing::Test { IsolatedStoreImpl cluster_scope_; }; -std::string getStreamField(std::string dataMessage, std::string key) { - std::string actual = dataMessage.substr(dataMessage.find(key)); - actual = actual.substr(actual.find(" ") + 1); - std::size_t length = actual.find(","); - actual = actual.substr(0, length); +absl::string_view getStreamField(absl::string_view dataMessage, absl::string_view key) { + absl::string_view::size_type key_pos = dataMessage.find(key); + EXPECT_NE(absl::string_view::npos, key_pos); + absl::string_view trimDataBeforeKey = dataMessage.substr(key_pos); + key_pos = trimDataBeforeKey.find(" "); + EXPECT_NE(absl::string_view::npos, key_pos); + absl::string_view trimDataAfterValue = trimDataBeforeKey.substr(key_pos + 1); + key_pos = trimDataAfterValue.find(","); + EXPECT_NE(absl::string_view::npos, key_pos); + absl::string_view actual = trimDataAfterValue.substr(0, key_pos); // EXPECT_EQ(actual, std::to_string(expected)); return actual; } From 5f6b6ee874254a19ee0fe9f11893157ec0ba9deb Mon Sep 17 00:00:00 2001 From: Eliran Roffe Date: Sun, 25 Mar 2018 03:07:47 -0400 Subject: [PATCH 09/25] Refactoring in Hystrix and HystrixTest classes and fixing comments style Signed-off-by: Eliran Roffe --- source/common/stats/hystrix.cc | 90 +++++++++++++++---------------- source/common/stats/hystrix.h | 42 +++++++-------- test/common/stats/hystrix_test.cc | 16 +++--- 3 files changed, 73 insertions(+), 75 deletions(-) diff --git a/source/common/stats/hystrix.cc b/source/common/stats/hystrix.cc index 141d3e71cd0fc..78ad057bff077 100644 --- a/source/common/stats/hystrix.cc +++ b/source/common/stats/hystrix.cc @@ -17,22 +17,19 @@ const uint64_t Hystrix::DEFAULT_NUM_OF_BUCKETS; const uint64_t Hystrix::ROLLING_WINDOW_IN_MS; const uint64_t Hystrix::PING_INTERVAL_IN_MS; -// add new value to rolling window, in place of oldest one -void Hystrix::pushNewValue(std::string key, uint64_t value) { - // create vector if do not exist - if (rolling_stats_map_.find(key) == rolling_stats_map_.end()) { - rolling_stats_map_[key].resize(num_of_buckets_, value); - } else { - rolling_stats_map_[key][current_index_] = value; - } +// Add new value to rolling window, in place of oldest one. +void Hystrix::pushNewValue(const std::string& key, uint64_t value) { + // Create vector if do not exist. + rolling_stats_map_[key].resize(num_of_buckets_); + rolling_stats_map_[key][current_index_] = value; } uint64_t Hystrix::getRollingValue(absl::string_view cluster_name, absl::string_view stats) { std::string key; - key = absl::StrCat("cluster.",cluster_name,".",stats); + key = absl::StrCat("cluster.", cluster_name, ".", stats); if (rolling_stats_map_.find(key) != rolling_stats_map_.end()) { - // if the counter was reset, the result is negative - // better return 0, will be back to normal once one rolling window passes + // If the counter was reset, the result is negative + // better return 0, will be back to normal once one rolling window passes. if (rolling_stats_map_[key][current_index_] < rolling_stats_map_[key][(current_index_ + 1) % num_of_buckets_]) { return 0; @@ -47,49 +44,49 @@ uint64_t Hystrix::getRollingValue(absl::string_view cluster_name, absl::string_v void Hystrix::updateRollingWindowMap(Stats::Store& stats, absl::string_view cluster_name) { std::string prefix; - prefix = absl::StrCat("cluster.",cluster_name,"."); + prefix = absl::StrCat("cluster.", cluster_name, "."); - // combining timeouts+retries - retries are counted as separate requests - // (alternative: each request including the retries counted as 1) - std::string upstream_rq_timeout_key = absl::StrCat(prefix,"upstream_rq_timeout"); - std::string upstream_rq_per_try_timeout_key = absl::StrCat(prefix,"upstream_rq_per_try_timeout"); + // Combining timeouts+retries - retries are counted as separate requests + // (alternative: each request including the retries counted as 1). + std::string upstream_rq_timeout_key = absl::StrCat(prefix, "upstream_rq_timeout"); + std::string upstream_rq_per_try_timeout_key = absl::StrCat(prefix, "upstream_rq_per_try_timeout"); uint64_t timeouts = stats.counter(upstream_rq_timeout_key).value() + stats.counter(upstream_rq_per_try_timeout_key).value(); - std::string timeouts_key = absl::StrCat(prefix,"timeouts"); + std::string timeouts_key = absl::StrCat(prefix, "timeouts"); pushNewValue(timeouts_key,timeouts); - // combining errors+retry errors - retries are counted as separate requests + // Combining errors+retry errors - retries are counted as separate requests // (alternative: each request including the retries counted as 1) // since timeouts are 504 (or 408), deduce them from here. - // timeout retries were not counted here anyway. - std::string upstream_rq_5xx_key = absl::StrCat(prefix,"upstream_rq_5xx"); - std::string retry_upstream_rq_5xx_key = absl::StrCat(prefix,"retry.upstream_rq_5xx"); - std::string upstream_rq_4xx_key = absl::StrCat(prefix,"upstream_rq_4xx"); - std::string retry_upstream_rq_4xx_key = absl::StrCat(prefix,"retry.upstream_rq_4xx"); + // Timeout retries were not counted here anyway. + std::string upstream_rq_5xx_key = absl::StrCat(prefix, "upstream_rq_5xx"); + std::string retry_upstream_rq_5xx_key = absl::StrCat(prefix, "retry.upstream_rq_5xx"); + std::string upstream_rq_4xx_key = absl::StrCat(prefix, "upstream_rq_4xx"); + std::string retry_upstream_rq_4xx_key = absl::StrCat(prefix, "retry.upstream_rq_4xx"); uint64_t errors = stats.counter(upstream_rq_5xx_key).value() + stats.counter(retry_upstream_rq_5xx_key).value() + stats.counter(upstream_rq_4xx_key).value() + stats.counter(retry_upstream_rq_4xx_key).value() - stats.counter(upstream_rq_timeout_key).value(); - std::string errors_key = absl::StrCat(prefix,"errors"); + std::string errors_key = absl::StrCat(prefix, "errors"); pushNewValue(errors_key,errors); - std::string upstream_rq_2xx_key = absl::StrCat(prefix,"upstream_rq_2xx"); + std::string upstream_rq_2xx_key = absl::StrCat(prefix, "upstream_rq_2xx"); uint64_t success = stats.counter(upstream_rq_2xx_key).value(); std::string success_key = absl::StrCat(prefix,"success"); pushNewValue(success_key,success); - std::string upstream_rq_pending_overflow_key = absl::StrCat(prefix,"upstream_rq_pending_overflow"); + std::string upstream_rq_pending_overflow_key = absl::StrCat(prefix, "upstream_rq_pending_overflow"); uint64_t rejected = stats.counter(upstream_rq_pending_overflow_key).value(); - std::string rejected_key = absl::StrCat(prefix,"rejected"); + std::string rejected_key = absl::StrCat(prefix, "rejected"); pushNewValue(rejected_key, rejected); - // should not take from upstream_rq_total since it is updated before its components, - // leading to wrong results such as error percentage higher than 100% + // Should not take from upstream_rq_total since it is updated before its components, + // leading to wrong results such as error percentage higher than 100%. uint64_t total = errors + timeouts + success + rejected; - std::string total_key = absl::StrCat(prefix,"total"); + std::string total_key = absl::StrCat(prefix, "total"); pushNewValue(total_key, total); // TODO (@trabetti) : why does it fail compilation? @@ -113,7 +110,7 @@ void Hystrix::addInfoToStream(absl::string_view key, absl::string_view value, st info << ", "; } std::string added_info; - added_info = absl::StrCat("\"",key,"\": ",value); + added_info = absl::StrCat("\"", key,"\": ", value); info << added_info; } @@ -152,9 +149,9 @@ void Hystrix::addHystrixCommand(std::stringstream& ss, absl::string_view cluster // so we count upstream_rq_pending_overflow and present it as rejected addIntToStream("rollingCountSemaphoreRejected", rejected, cluster_info); - // Hystrix's short circuit is not similar to Envoy's since it is trrigered by 503 responses + // Hystrix's short circuit is not similar to Envoy's since it is triggered by 503 responses // there is no parallel counter in Envoy since as a result of errors (outlier detection) - // requests are not rejected, but rather the node is removed from load balancer healthy pool + // requests are not rejected, but rather the node is removed from load balancer healthy pool. addIntToStream("rollingCountShortCircuited", 0, cluster_info); addIntToStream("rollingCountSuccess", getRollingValue(cluster_name, "success"), cluster_info); addIntToStream("rollingCountThreadPoolRejected", 0, cluster_info); @@ -163,8 +160,8 @@ void Hystrix::addHystrixCommand(std::stringstream& ss, absl::string_view cluster addIntToStream("currentConcurrentExecutionCount", 0, cluster_info); addIntToStream("latencyExecute_mean", 0, cluster_info); - // latency information can be taken rom hystogram, which is only available to sinks - // we should consider make this a sink so we can get this information + // Latency information can be taken from histogram, which is only available to sinks + // we should consider make this a sink so we can get this information. addInfoToStream( "latencyExecute", "{\"0\":0,\"25\":0,\"50\":0,\"75\":0,\"90\":0,\"95\":0,\"99\":0,\"99.5\":0,\"100\":0}", @@ -220,15 +217,16 @@ void Hystrix::getClusterStats(std::stringstream& ss, absl::string_view cluster_n addHystrixThreadPool(ss, cluster_name, max_concurrent_requests, reporting_hosts); } -absl::string_view Hystrix::printRollingWindow() { +absl::string_view Hystrix::printRollingWindow() const { std::stringstream out_str; - for (std::map::const_iterator it = rolling_stats_map_.begin(); - it != rolling_stats_map_.end(); ++it) { - out_str << it->first << " | "; - RollingStats rolling_stats = it->second; - for (uint64_t i = 0; i < rolling_stats.size(); i++) { - out_str << rolling_stats[i] << " | "; + for (auto stats_map_itr = rolling_stats_map_.begin(); + stats_map_itr != rolling_stats_map_.end(); ++stats_map_itr) { + out_str << stats_map_itr->first << " | "; + RollingStats rolling_stats = stats_map_itr->second; + for (auto specific_stat_vec_itr = rolling_stats.begin(); + specific_stat_vec_itr != rolling_stats.end(); ++specific_stat_vec_itr) { + out_str << *specific_stat_vec_itr << " | "; } out_str << std::endl; } @@ -249,7 +247,7 @@ void HystrixHandlerInfoImpl::Destroy() { void HystrixHandler::HandleEventStream(HystrixHandlerInfoImpl* hystrix_handler_info, Server::Instance& server) { Server::Instance* serverPtr = &server; - // start streaming + // Start streaming. hystrix_handler_info->data_timer_ = hystrix_handler_info->callbacks_->dispatcher().createTimer( [hystrix_handler_info, serverPtr]() -> void { HystrixHandler::prepareAndSendHystrixStream(hystrix_handler_info, serverPtr); @@ -257,7 +255,7 @@ void HystrixHandler::HandleEventStream(HystrixHandlerInfoImpl* hystrix_handler_i hystrix_handler_info->data_timer_->enableTimer( std::chrono::milliseconds(Stats::Hystrix::GetRollingWindowIntervalInMs())); - // start keep alive ping + // Start keep alive ping. hystrix_handler_info->ping_timer_ = hystrix_handler_info->callbacks_->dispatcher().createTimer([hystrix_handler_info]() -> void { HystrixHandler::sendKeepAlivePing(hystrix_handler_info); @@ -296,7 +294,7 @@ void HystrixHandler::prepareAndSendHystrixStream(HystrixHandlerInfoImpl* hystrix data.add(ss.str()); hystrix_handler_info->callbacks_->encodeData(data, false); - // restart timer + // Restart timer. hystrix_handler_info->data_timer_->enableTimer( std::chrono::milliseconds(Stats::Hystrix::GetRollingWindowIntervalInMs())); } @@ -306,7 +304,7 @@ void HystrixHandler::sendKeepAlivePing(HystrixHandlerInfoImpl* hystrix_handler_i data.add(":\n\n"); hystrix_handler_info->callbacks_->encodeData(data, false); - // restart timer + // Restart timer. hystrix_handler_info->ping_timer_->enableTimer( std::chrono::milliseconds(Stats::Hystrix::GetPingIntervalInMs())); } diff --git a/source/common/stats/hystrix.h b/source/common/stats/hystrix.h index 8ba5a8d73fba9..dc0bf9d5e9645 100644 --- a/source/common/stats/hystrix.h +++ b/source/common/stats/hystrix.h @@ -10,9 +10,9 @@ namespace Envoy { namespace Stats { typedef std::vector RollingStats; -typedef std::map RollingStatsMap; +typedef std::map RollingStatsMap; -// Consider implement the HystrixStats as a sink to have access to histograms data +// Consider implement the HystrixStats as a sink to have access to histograms data. class Hystrix { public: @@ -22,61 +22,61 @@ class Hystrix { : current_index_(num_of_buckets), num_of_buckets_(num_of_buckets + 1){}; /** - * Add new value to top of rolling window, pushing out the oldest value + * Add new value to top of rolling window, pushing out the oldest value. */ - void pushNewValue(std::string key, uint64_t value); + void pushNewValue(const std::string& key, uint64_t value); /** - * increment pointer of next value to add to rolling window + * increment pointer of next value to add to rolling window. */ void incCounter() { current_index_ = (current_index_ + 1) % num_of_buckets_; } /** - * Generate the streams to be sent to hystrix dashboard + * Generate the streams to be sent to hystrix dashboard. */ void getClusterStats(std::stringstream& ss, absl::string_view cluster_name, uint64_t max_concurrent_requests, uint64_t reporting_hosts); /** - * Get value of the sampling buckets + * Get value of the sampling buckets. */ static uint64_t GetRollingWindowIntervalInMs() { - return static_cast(ROLLING_WINDOW_IN_MS / DEFAULT_NUM_OF_BUCKETS); + return static_cast(ROLLING_WINDOW_IN_MS / DEFAULT_NUM_OF_BUCKETS); } /** - * Get value of the keep alive ping interval + * Get value of the keep alive ping interval. */ static uint64_t GetPingIntervalInMs() { return PING_INTERVAL_IN_MS; } void updateRollingWindowMap(Stats::Store& stats, absl::string_view cluster_name); /** - * clear map + * Clear map. */ void resetRollingWindow(); - absl::string_view printRollingWindow(); + absl::string_view printRollingWindow() const; private: /** - * Get the statistic's value change over the rolling window time frame + * Get the statistic's value change over the rolling window time frame. */ uint64_t getRollingValue(absl::string_view cluster_name, absl::string_view stats); /** - * Format the given key and absl::string_view value to "key"="value", and adding to the stringstream + * Format the given key and absl::string_view value to "key"="value", and adding to the stringstream. */ void addStringToStream(absl::string_view key, absl::string_view value, std::stringstream& info); /** * Format the given key and uint64_t value to "key"=, and adding to the - * stringstream + * stringstream. */ void addIntToStream(absl::string_view key, uint64_t value, std::stringstream& info); /** - * Format the given key and value to "key"=value, and adding to the stringstream + * Format the given key and value to "key"=value, and adding to the stringstream. */ void addInfoToStream(absl::string_view key, absl::string_view value, std::stringstream& info); @@ -87,7 +87,7 @@ class Hystrix { uint64_t max_concurrent_requests, uint64_t reporting_hosts); /** - * generate HystrixThreadPool event stream + * Generate HystrixThreadPool event stream. */ void addHystrixThreadPool(std::stringstream& ss, absl::string_view cluster_name, uint64_t queue_size, uint64_t reporting_hosts); @@ -117,7 +117,7 @@ class HystrixHandlerInfoImpl : public Server::HandlerInfo { /** * HystrixHandlerInfoImpl includes statistics for hystrix API, timers for build (and send) data - * and keep alive messages and the handler's callback + * and keep alive messages and the handler's callback. */ Stats::HystrixPtr stats_; Event::TimerPtr data_timer_; @@ -127,14 +127,14 @@ class HystrixHandlerInfoImpl : public Server::HandlerInfo { /** * Convert statistics from envoy format to hystrix format and prepare them and writes them to the - * appropriate socket + * appropriate socket. */ class HystrixHandler { public: static void HandleEventStream(HystrixHandlerInfoImpl* hystrix_handler_info, Server::Instance& server); /** - * Update counter and set values of upstream_rq statistics + * Update counter and set values of upstream_rq statistics. * @param hystrix_handler_info is the data which is received in the hystrix handler from the admin * filter (callback, timers, statistics) * @param server contains envoy statistics @@ -143,7 +143,7 @@ class HystrixHandler { Server::Instance* server); /** * Builds a buffer of envoy statistics which will be sent to hystrix dashboard according to - * hystrix API + * hystrix API. * @param hystrix_handler_info is the data which is received in the hystrix handler from the admin * filter (callback, timers, statistics) * @param server contains envoy statistics* @@ -151,7 +151,7 @@ class HystrixHandler { static void prepareAndSendHystrixStream(HystrixHandlerInfoImpl* hystrix_handler_info, Server::Instance* server); /** - * Sends a keep alive (ping) message to hystrix dashboard + * Sends a keep alive (ping) message to hystrix dashboard. * @param hystrix_handler_info is the data which is received in the hystrix handler from the admin * filter (callback, timers, statistics) */ diff --git a/test/common/stats/hystrix_test.cc b/test/common/stats/hystrix_test.cc index 519c417d343ed..fa997e8b8169d 100644 --- a/test/common/stats/hystrix_test.cc +++ b/test/common/stats/hystrix_test.cc @@ -12,7 +12,7 @@ namespace Envoy { namespace Stats { -// copied from CodeUtilityTest in codes_test.cc +// Copied from CodeUtilityTest in codes_test.cc. class HystrixUtilityTest : public testing::Test { public: void addResponse(uint64_t code, bool canary, bool internal_request, @@ -52,8 +52,8 @@ absl::string_view getStreamField(absl::string_view dataMessage, absl::string_vie return actual; } -// this part is useful for testing updateRollingWindowMap() -// by using addResponse() to +// This part is useful for testing updateRollingWindowMap() +// by using addResponse(). TEST_F(HystrixUtilityTest, CreateDataMessage) { Stats::Hystrix hystrix; std::stringstream ss; @@ -61,7 +61,7 @@ TEST_F(HystrixUtilityTest, CreateDataMessage) { uint64_t expected_queue_size = 12; uint64_t expectedReportingHosts = 16; - // insert data to rolling window + // Insert data to rolling window. for (uint64_t i = 0; i < 14; i++) { hystrix.incCounter(); addResponse(201, false, false); @@ -74,7 +74,7 @@ TEST_F(HystrixUtilityTest, CreateDataMessage) { hystrix.getClusterStats(ss, cluster_name, expected_queue_size, expectedReportingHosts); std::string dataMessage = ss.str(); - // check stream format and data + // Check stream format and data. EXPECT_EQ(getStreamField(dataMessage, "errorPercentage"), "66"); EXPECT_EQ(getStreamField(dataMessage, "errorCount"), "20"); EXPECT_EQ(getStreamField(dataMessage, "requestCount"), "30"); @@ -96,7 +96,7 @@ TEST(Hystrix, CreateDataMessage) { EXPECT_EQ(hystrix.GetRollingWindowIntervalInMs(), 1000); EXPECT_EQ(hystrix.GetPingIntervalInMs(), 3000); - // insert data to rolling window + // Insert data to rolling window. for (uint64_t i = 0; i < 15; i++) { hystrix.incCounter(); hystrix.pushNewValue("cluster.clusterName.timeouts", (i + 1) * 3); @@ -108,7 +108,7 @@ TEST(Hystrix, CreateDataMessage) { hystrix.getClusterStats(ss, cluster_name, expected_queue_size, expectedReportingHosts); std::string dataMessage = ss.str(); - // check stream format and data + // Check stream format and data. EXPECT_EQ(getStreamField(dataMessage, "errorPercentage"), "80"); EXPECT_EQ(getStreamField(dataMessage, "errorCount"), "170"); EXPECT_EQ(getStreamField(dataMessage, "requestCount"), "350"); @@ -119,7 +119,7 @@ TEST(Hystrix, CreateDataMessage) { std::to_string(expected_queue_size)); EXPECT_EQ(getStreamField(dataMessage, "reportingHosts"), std::to_string(expectedReportingHosts)); - // check reset of window + // Check reset of window. ss.str(""); hystrix.resetRollingWindow(); hystrix.getClusterStats(ss, cluster_name, expected_queue_size, expectedReportingHosts); From 0e92bd5bc027af4dbd5246a838ba527bcdac4097 Mon Sep 17 00:00:00 2001 From: Eliran Roffe Date: Sun, 25 Mar 2018 07:18:03 -0400 Subject: [PATCH 10/25] Name lookup for counters in Hystrix class Signed-off-by: Eliran Roffe --- source/common/stats/hystrix.cc | 115 ++++++++++++++++++++------------- source/common/stats/hystrix.h | 17 +++-- 2 files changed, 81 insertions(+), 51 deletions(-) diff --git a/source/common/stats/hystrix.cc b/source/common/stats/hystrix.cc index 78ad057bff077..c0333d38afe7b 100644 --- a/source/common/stats/hystrix.cc +++ b/source/common/stats/hystrix.cc @@ -5,11 +5,11 @@ #include #include -#include "absl/strings/str_cat.h" - #include "common/buffer/buffer_impl.h" #include "common/common/logger.h" +#include "absl/strings/str_cat.h" + namespace Envoy { namespace Stats { @@ -20,8 +20,11 @@ const uint64_t Hystrix::PING_INTERVAL_IN_MS; // Add new value to rolling window, in place of oldest one. void Hystrix::pushNewValue(const std::string& key, uint64_t value) { // Create vector if do not exist. - rolling_stats_map_[key].resize(num_of_buckets_); - rolling_stats_map_[key][current_index_] = value; + if (rolling_stats_map_.find(key) == rolling_stats_map_.end()) { + rolling_stats_map_[key].resize(num_of_buckets_, value); + } else { + rolling_stats_map_[key][current_index_] = value; + } } uint64_t Hystrix::getRollingValue(absl::string_view cluster_name, absl::string_view stats) { @@ -42,52 +45,70 @@ uint64_t Hystrix::getRollingValue(absl::string_view cluster_name, absl::string_v } } -void Hystrix::updateRollingWindowMap(Stats::Store& stats, absl::string_view cluster_name) { - std::string prefix; - prefix = absl::StrCat("cluster.", cluster_name, "."); +void Hystrix::CreateCounterNameLookupForCluster(const std::string& cluster_name) { + // Building lookup name map for all specific cluster values. + // Every call to the updateRollingWindowMap function should get the appropriate name from the map. + std::string cluster_name_with_prefix = absl::StrCat("cluster.", cluster_name, "."); + counter_name_lookup[cluster_name]["upstream_rq_timeout"] = + absl::StrCat(cluster_name_with_prefix, "upstream_rq_timeout"); + counter_name_lookup[cluster_name]["upstream_rq_per_try_timeout"] = + absl::StrCat(cluster_name_with_prefix, "upstream_rq_per_try_timeout"); + counter_name_lookup[cluster_name]["timeouts"] = + absl::StrCat(cluster_name_with_prefix, "timeouts"); + counter_name_lookup[cluster_name]["upstream_rq_5xx"] = + absl::StrCat(cluster_name_with_prefix, "upstream_rq_5xx"); + counter_name_lookup[cluster_name]["retry.upstream_rq_5xx"] = + absl::StrCat(cluster_name_with_prefix, "retry.upstream_rq_5xx"); + counter_name_lookup[cluster_name]["upstream_rq_4xx"] = + absl::StrCat(cluster_name_with_prefix, "upstream_rq_4xx"); + counter_name_lookup[cluster_name]["retry.upstream_rq_4xx"] = + absl::StrCat(cluster_name_with_prefix, "retry.upstream_rq_4xx"); + counter_name_lookup[cluster_name]["errors"] = absl::StrCat(cluster_name_with_prefix, "errors"); + counter_name_lookup[cluster_name]["upstream_rq_2xx"] = + absl::StrCat(cluster_name_with_prefix, "upstream_rq_2xx"); + counter_name_lookup[cluster_name]["success"] = absl::StrCat(cluster_name_with_prefix, "success"); + counter_name_lookup[cluster_name]["upstream_rq_pending_overflow"] = + absl::StrCat(cluster_name_with_prefix, "upstream_rq_pending_overflow"); + counter_name_lookup[cluster_name]["rejected"] = + absl::StrCat(cluster_name_with_prefix, "rejected"); + counter_name_lookup[cluster_name]["total"] = absl::StrCat(cluster_name_with_prefix, "total"); +} + +void Hystrix::updateRollingWindowMap(Stats::Store& stats, const std::string& cluster_name) { + if (counter_name_lookup.find(cluster_name) == counter_name_lookup.end()) { + CreateCounterNameLookupForCluster(cluster_name); + } // Combining timeouts+retries - retries are counted as separate requests // (alternative: each request including the retries counted as 1). - std::string upstream_rq_timeout_key = absl::StrCat(prefix, "upstream_rq_timeout"); - std::string upstream_rq_per_try_timeout_key = absl::StrCat(prefix, "upstream_rq_per_try_timeout"); - uint64_t timeouts = stats.counter(upstream_rq_timeout_key).value() + - stats.counter(upstream_rq_per_try_timeout_key).value(); - - std::string timeouts_key = absl::StrCat(prefix, "timeouts"); - pushNewValue(timeouts_key,timeouts); - + uint64_t timeouts = + stats.counter(counter_name_lookup[cluster_name]["upstream_rq_timeout"]).value() + + stats.counter(counter_name_lookup[cluster_name]["upstream_rq_per_try_timeout"]).value(); + pushNewValue(counter_name_lookup[cluster_name]["timeouts"], timeouts); // Combining errors+retry errors - retries are counted as separate requests // (alternative: each request including the retries counted as 1) - // since timeouts are 504 (or 408), deduce them from here. - // Timeout retries were not counted here anyway. - std::string upstream_rq_5xx_key = absl::StrCat(prefix, "upstream_rq_5xx"); - std::string retry_upstream_rq_5xx_key = absl::StrCat(prefix, "retry.upstream_rq_5xx"); - std::string upstream_rq_4xx_key = absl::StrCat(prefix, "upstream_rq_4xx"); - std::string retry_upstream_rq_4xx_key = absl::StrCat(prefix, "retry.upstream_rq_4xx"); - uint64_t errors = stats.counter(upstream_rq_5xx_key).value() + - stats.counter(retry_upstream_rq_5xx_key).value() + - stats.counter(upstream_rq_4xx_key).value() + - stats.counter(retry_upstream_rq_4xx_key).value() - - stats.counter(upstream_rq_timeout_key).value(); - - std::string errors_key = absl::StrCat(prefix, "errors"); - pushNewValue(errors_key,errors); - - std::string upstream_rq_2xx_key = absl::StrCat(prefix, "upstream_rq_2xx"); - uint64_t success = stats.counter(upstream_rq_2xx_key).value(); - std::string success_key = absl::StrCat(prefix,"success"); - pushNewValue(success_key,success); - - std::string upstream_rq_pending_overflow_key = absl::StrCat(prefix, "upstream_rq_pending_overflow"); - uint64_t rejected = stats.counter(upstream_rq_pending_overflow_key).value(); - std::string rejected_key = absl::StrCat(prefix, "rejected"); - pushNewValue(rejected_key, rejected); + // since timeouts error code are 504 (or 408), envoy reduce them from here ("-" sign). + // Timeout retries were not counted here anyway and therefore they are not reduced. + uint64_t errors = + stats.counter(counter_name_lookup[cluster_name]["upstream_rq_5xx"]).value() + + stats.counter(counter_name_lookup[cluster_name]["retry.upstream_rq_5xx"]).value() + + stats.counter(counter_name_lookup[cluster_name]["upstream_rq_4xx"]).value() + + stats.counter(counter_name_lookup[cluster_name]["retry.upstream_rq_4xx"]).value() - + stats.counter(counter_name_lookup[cluster_name]["upstream_rq_timeout"]).value(); + + pushNewValue(counter_name_lookup[cluster_name]["errors"], errors); + + uint64_t success = stats.counter(counter_name_lookup[cluster_name]["upstream_rq_2xx"]).value(); + pushNewValue(counter_name_lookup[cluster_name]["success"], success); + + uint64_t rejected = + stats.counter(counter_name_lookup[cluster_name]["upstream_rq_pending_overflow"]).value(); + pushNewValue(counter_name_lookup[cluster_name]["rejected"], rejected); // Should not take from upstream_rq_total since it is updated before its components, // leading to wrong results such as error percentage higher than 100%. uint64_t total = errors + timeouts + success + rejected; - std::string total_key = absl::StrCat(prefix, "total"); - pushNewValue(total_key, total); + pushNewValue(counter_name_lookup[cluster_name]["total"], total); // TODO (@trabetti) : why does it fail compilation? // ENVOY_LOG(trace, "{}", printRollingWindow()); @@ -95,7 +116,8 @@ void Hystrix::updateRollingWindowMap(Stats::Store& stats, absl::string_view clus void Hystrix::resetRollingWindow() { rolling_stats_map_.clear(); } -void Hystrix::addStringToStream(absl::string_view key, absl::string_view value, std::stringstream& info) { +void Hystrix::addStringToStream(absl::string_view key, absl::string_view value, + std::stringstream& info) { std::string quoted_value; quoted_value = absl::StrCat("\"", value, "\""); addInfoToStream(key, quoted_value, info); @@ -105,12 +127,13 @@ void Hystrix::addIntToStream(absl::string_view key, uint64_t value, std::strings addInfoToStream(key, std::to_string(value), info); } -void Hystrix::addInfoToStream(absl::string_view key, absl::string_view value, std::stringstream& info) { +void Hystrix::addInfoToStream(absl::string_view key, absl::string_view value, + std::stringstream& info) { if (!info.str().empty()) { info << ", "; } std::string added_info; - added_info = absl::StrCat("\"", key,"\": ", value); + added_info = absl::StrCat("\"", key, "\": ", value); info << added_info; } @@ -220,8 +243,8 @@ void Hystrix::getClusterStats(std::stringstream& ss, absl::string_view cluster_n absl::string_view Hystrix::printRollingWindow() const { std::stringstream out_str; - for (auto stats_map_itr = rolling_stats_map_.begin(); - stats_map_itr != rolling_stats_map_.end(); ++stats_map_itr) { + for (auto stats_map_itr = rolling_stats_map_.begin(); stats_map_itr != rolling_stats_map_.end(); + ++stats_map_itr) { out_str << stats_map_itr->first << " | "; RollingStats rolling_stats = stats_map_itr->second; for (auto specific_stat_vec_itr = rolling_stats.begin(); diff --git a/source/common/stats/hystrix.h b/source/common/stats/hystrix.h index dc0bf9d5e9645..e4e5cb37597a9 100644 --- a/source/common/stats/hystrix.h +++ b/source/common/stats/hystrix.h @@ -49,7 +49,7 @@ class Hystrix { */ static uint64_t GetPingIntervalInMs() { return PING_INTERVAL_IN_MS; } - void updateRollingWindowMap(Stats::Store& stats, absl::string_view cluster_name); + void updateRollingWindowMap(Stats::Store& stats, const std::string& cluster_name); /** * Clear map. @@ -65,7 +65,8 @@ class Hystrix { uint64_t getRollingValue(absl::string_view cluster_name, absl::string_view stats); /** - * Format the given key and absl::string_view value to "key"="value", and adding to the stringstream. + * Format the given key and absl::string_view value to "key"="value", and adding to the + * stringstream. */ void addStringToStream(absl::string_view key, absl::string_view value, std::stringstream& info); @@ -81,7 +82,7 @@ class Hystrix { void addInfoToStream(absl::string_view key, absl::string_view value, std::stringstream& info); /** - * generate HystrixCommand event stream + * generate HystrixCommand event stream. */ void addHystrixCommand(std::stringstream& ss, absl::string_view cluster_name, uint64_t max_concurrent_requests, uint64_t reporting_hosts); @@ -89,8 +90,13 @@ class Hystrix { /** * Generate HystrixThreadPool event stream. */ - void addHystrixThreadPool(std::stringstream& ss, absl::string_view cluster_name, uint64_t queue_size, - uint64_t reporting_hosts); + void addHystrixThreadPool(std::stringstream& ss, absl::string_view cluster_name, + uint64_t queue_size, uint64_t reporting_hosts); + + /** + * Building lookup name map for all specific cluster values. + */ + void CreateCounterNameLookupForCluster(const std::string& cluster_name); RollingStatsMap rolling_stats_map_; uint64_t current_index_; @@ -99,6 +105,7 @@ class Hystrix { static const uint64_t DEFAULT_NUM_OF_BUCKETS = 10; static const uint64_t ROLLING_WINDOW_IN_MS = 10000; static const uint64_t PING_INTERVAL_IN_MS = 3000; + std::map> counter_name_lookup; }; typedef std::unique_ptr HystrixPtr; From 6e9e8070650bebaea8d8e49a6c7d8618f5d215f4 Mon Sep 17 00:00:00 2001 From: trabetti Date: Tue, 27 Mar 2018 21:35:33 +0300 Subject: [PATCH 11/25] move hystrix support to base on a sink Signed-off-by: trabetti --- bazel/repository_locations.bzl | 4 +- ci/run_envoy_docker.sh | 3 +- include/envoy/server/admin.h | 1 - include/envoy/server/instance.h | 4 + source/common/config/well_known_names.h | 2 + source/common/stats/hystrix.cc | 116 ++++++++++------------- source/common/stats/hystrix.h | 71 +++++++------- source/exe/BUILD | 1 + source/server/config/stats/BUILD | 13 +++ source/server/config/stats/hystrix.cc | 43 +++++++++ source/server/config/stats/hystrix.h | 28 ++++++ source/server/config_validation/server.h | 2 + source/server/configuration_impl.cc | 6 ++ source/server/http/admin.cc | 3 +- source/server/http/admin.h | 5 +- source/server/server.cc | 24 +++++ source/server/server.h | 3 + 17 files changed, 219 insertions(+), 110 deletions(-) create mode 100644 source/server/config/stats/hystrix.cc create mode 100644 source/server/config/stats/hystrix.h diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index c1e8c0e5c2335..947e34d891b1a 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -76,8 +76,8 @@ REPOSITORY_LOCATIONS = dict( urls = ["https://github.com/google/protobuf/archive/v3.5.0.tar.gz"], ), envoy_api = dict( - commit = "60dc08ddbcc4d10ec48cb55d34a1fdc1bdc72496", - remote = "https://github.com/envoyproxy/data-plane-api", + commit = "c1a8d3fa4066ff362a2e8c62feb6544c724bdf0a", + remote = "https://github.com/trabetti/data-plane-api", ), grpc_httpjson_transcoding = dict( commit = "e4f58aa07b9002befa493a0a82e10f2e98b51fc6", diff --git a/ci/run_envoy_docker.sh b/ci/run_envoy_docker.sh index d52172671f73b..c2da28e3abdfa 100755 --- a/ci/run_envoy_docker.sh +++ b/ci/run_envoy_docker.sh @@ -19,7 +19,8 @@ USER_GROUP=root mkdir -p "${ENVOY_DOCKER_BUILD_DIR}" # Since we specify an explicit hash, docker-run will pull from the remote repo if missing. docker run --rm -t -i -e http_proxy=${http_proxy} -e https_proxy=${https_proxy} \ - -u "${USER}":"${USER_GROUP}" -v "${ENVOY_DOCKER_BUILD_DIR}":/build \ + -u "${USER}":"${USER_GROUP}" -v "${ENVOY_DOCKER_BUILD_DIR}":/build -v "/home/talis/envoy_fork/data-plane-api":/opt/data-plan-api \ -v "$PWD":/source -e NUM_CPUS --cap-add SYS_PTRACE "${IMAGE_NAME}":"${IMAGE_ID}" \ /bin/bash -lc "groupadd --gid $(id -g) -f envoygroup && useradd -o --uid $(id -u) --gid $(id -g) \ --no-create-home --home-dir /source envoybuild && su envoybuild -c \"cd source && $*\"" + diff --git a/include/envoy/server/admin.h b/include/envoy/server/admin.h index 9b0466bf31278..c126913a86c83 100644 --- a/include/envoy/server/admin.h +++ b/include/envoy/server/admin.h @@ -33,7 +33,6 @@ namespace Server { class HandlerInfo { public: virtual ~HandlerInfo(){}; - virtual void Destroy() PURE; }; typedef std::unique_ptr HandlerInfoPtr; diff --git a/include/envoy/server/instance.h b/include/envoy/server/instance.h index b6b1299da420a..679d08a4e9e71 100644 --- a/include/envoy/server/instance.h +++ b/include/envoy/server/instance.h @@ -179,6 +179,10 @@ class Instance { * @return information about the local environment the server is running in. */ virtual const LocalInfo::LocalInfo& localInfo() PURE; + + virtual void registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) PURE; + + virtual void UnregisterHystrixSink() PURE; }; } // namespace Server diff --git a/source/common/config/well_known_names.h b/source/common/config/well_known_names.h index 99dd2ee1c2d27..4c7cf96e25262 100644 --- a/source/common/config/well_known_names.h +++ b/source/common/config/well_known_names.h @@ -175,6 +175,8 @@ class StatsSinkNameValues { const std::string DOG_STATSD = "envoy.dog_statsd"; // MetricsService sink const std::string METRICS_SERVICE = "envoy.metrics_service"; + // Hystrix sink + const std::string HYSTRIX = "envoy.hystrix"; }; typedef ConstSingleton StatsSinkNames; diff --git a/source/common/stats/hystrix.cc b/source/common/stats/hystrix.cc index ad5f049183d91..a03082cb35cd1 100644 --- a/source/common/stats/hystrix.cc +++ b/source/common/stats/hystrix.cc @@ -42,13 +42,14 @@ uint64_t Hystrix::getRollingValue(std::string cluster_name, std::string stats) { } } -void Hystrix::updateRollingWindowMap(Stats::Store& stats, std::string cluster_name) { +void Hystrix::updateRollingWindowMap(std::map current_stat_values, + std::string cluster_name) { std::string prefix = "cluster." + cluster_name + "."; // combining timeouts+retries - retries are counted as separate requests // (alternative: each request including the retries counted as 1) - uint64_t timeouts = stats.counter(prefix + "upstream_rq_timeout").value() + - stats.counter(prefix + "upstream_rq_per_try_timeout").value(); + uint64_t timeouts = current_stat_values[prefix + "upstream_rq_timeout"] + + current_stat_values[prefix + "upstream_rq_per_try_timeout"]; pushNewValue(prefix + "timeouts", timeouts); @@ -56,18 +57,18 @@ void Hystrix::updateRollingWindowMap(Stats::Store& stats, std::string cluster_na // (alternative: each request including the retries counted as 1) // since timeouts are 504 (or 408), deduce them from here. // timeout retries were not counted here anyway. - uint64_t errors = stats.counter(prefix + "upstream_rq_5xx").value() + - stats.counter(prefix + "retry.upstream_rq_5xx").value() + - stats.counter(prefix + "upstream_rq_4xx").value() + - stats.counter(prefix + "retry.upstream_rq_4xx").value() - - stats.counter(prefix + "upstream_rq_timeout").value(); + uint64_t errors = current_stat_values[prefix + "upstream_rq_5xx"] + + current_stat_values[prefix + "retry.upstream_rq_5xx"] + + current_stat_values[prefix + "upstream_rq_4xx"] + + current_stat_values[prefix + "retry.upstream_rq_4xx"] - + current_stat_values[prefix + "upstream_rq_timeout"]; pushNewValue(prefix + "errors", errors); - uint64_t success = stats.counter(prefix + "upstream_rq_2xx").value(); + uint64_t success = current_stat_values[prefix + "upstream_rq_2xx"]; pushNewValue(prefix + "success", success); - uint64_t rejected = stats.counter(prefix + "upstream_rq_pending_overflow").value(); + uint64_t rejected = current_stat_values[prefix + "upstream_rq_pending_overflow"]; pushNewValue(prefix + "rejected", rejected); // should not take from upstream_rq_total since it is updated before its components, @@ -75,6 +76,8 @@ void Hystrix::updateRollingWindowMap(Stats::Store& stats, std::string cluster_na uint64_t total = errors + timeouts + success + rejected; pushNewValue(prefix + "total", total); + std::cout << printRollingWindow() << std::endl; + // TODO (@trabetti) : why does it fail compilation? // ENVOY_LOG(trace, "{}", printRollingWindow()); } @@ -214,81 +217,64 @@ std::string Hystrix::printRollingWindow() { return out_str.str(); } -void HystrixHandlerInfoImpl::Destroy() { - if (data_timer_) { - data_timer_->disableTimer(); - data_timer_.reset(); - } - if (ping_timer_) { - ping_timer_->disableTimer(); - ping_timer_.reset(); - } -} +namespace HystrixNameSpace { +HystrixSink::HystrixSink(Server::Instance& server) + : stats_(new Stats::Hystrix()), server_(&server){}; -void HystrixHandler::HandleEventStream(HystrixHandlerInfoImpl* hystrix_handler_info, - Server::Instance& server) { - Server::Instance* serverPtr = &server; - // start streaming - hystrix_handler_info->data_timer_ = hystrix_handler_info->callbacks_->dispatcher().createTimer( - [hystrix_handler_info, serverPtr]() -> void { - HystrixHandler::prepareAndSendHystrixStream(hystrix_handler_info, serverPtr); - }); - hystrix_handler_info->data_timer_->enableTimer( - std::chrono::milliseconds(Stats::Hystrix::GetRollingWindowIntervalInMs())); - - // start keep alive ping - hystrix_handler_info->ping_timer_ = - hystrix_handler_info->callbacks_->dispatcher().createTimer([hystrix_handler_info]() -> void { - HystrixHandler::sendKeepAlivePing(hystrix_handler_info); - }); - - hystrix_handler_info->ping_timer_->enableTimer( - std::chrono::milliseconds(Stats::Hystrix::GetPingIntervalInMs())); -} +void HystrixSink::beginFlush() { current_stat_values_.clear(); } -void HystrixHandler::updateHystrixRollingWindow(HystrixHandlerInfoImpl* hystrix_handler_info, - Server::Instance* server) { - hystrix_handler_info->stats_->incCounter(); - for (auto& cluster : server->clusterManager().clusters()) { - hystrix_handler_info->stats_->updateRollingWindowMap(server->stats(), - cluster.second.get().info()->name()); +void HystrixSink::flushCounter(const Counter& counter, uint64_t delta) { + if (callbacks_ == nullptr) { + std::cout << "callback is null" << std::endl; + return; + } + std::cout << "callback is not null. flushing counter: " << counter.name() + << ", delta: " << std::to_string(delta) << std::endl; + if (counter.name().find("upstream_rq_") != std::string::npos) { + current_stat_values_[counter.name()] = counter.value(); } } - -void HystrixHandler::prepareAndSendHystrixStream(HystrixHandlerInfoImpl* hystrix_handler_info, - Server::Instance* server) { - updateHystrixRollingWindow(hystrix_handler_info, server); +// void HystrixSink::flushGauge(const Gauge& gauge, uint64_t value); +void HystrixSink::endFlush() { + if (callbacks_ == nullptr) + return; + stats_->incCounter(); + for (auto& cluster : server_->clusterManager().clusters()) { + stats_->updateRollingWindowMap(current_stat_values_, cluster.second.get().info()->name()); + } std::stringstream ss; - for (auto& cluster : server->clusterManager().clusters()) { - hystrix_handler_info->stats_->getClusterStats( + for (auto& cluster : server_->clusterManager().clusters()) { + stats_->getClusterStats( ss, cluster.second.get().info()->name(), cluster.second.get() .info() ->resourceManager(Upstream::ResourcePriority::Default) .pendingRequests() .max(), - server->stats() + server_->stats() .gauge("cluster." + cluster.second.get().info()->name() + ".membership_total") .value()); } Buffer::OwnedImpl data; data.add(ss.str()); - hystrix_handler_info->callbacks_->encodeData(data, false); + // std::cout << "endflush : " << ss.str() << std::endl; + callbacks_->encodeData(data, false); - // restart timer - hystrix_handler_info->data_timer_->enableTimer( - std::chrono::milliseconds(Stats::Hystrix::GetRollingWindowIntervalInMs())); + // send keep alive ping + Buffer::OwnedImpl ping_data; + ping_data.add(":\n\n"); + callbacks_->encodeData(ping_data, false); } -void HystrixHandler::sendKeepAlivePing(HystrixHandlerInfoImpl* hystrix_handler_info) { - Buffer::OwnedImpl data; - data.add(":\n\n"); - hystrix_handler_info->callbacks_->encodeData(data, false); - - // restart timer - hystrix_handler_info->ping_timer_->enableTimer( - std::chrono::milliseconds(Stats::Hystrix::GetPingIntervalInMs())); +// void HystrixSink::onHistogramComplete(const Histogram& histogram, uint64_t value); +void HystrixSink::registerConnection(Http::StreamDecoderFilterCallbacks* callbacks) { + callbacks_ = callbacks; } +// TODO (@trabetti) is this correct way? +void HystrixSink::unregisterConnection() { callbacks_ = nullptr; } + +} // namespace HystrixNameSpace + } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/hystrix.h b/source/common/stats/hystrix.h index 5b09a5bb1f404..eff63419a29a6 100644 --- a/source/common/stats/hystrix.h +++ b/source/common/stats/hystrix.h @@ -49,13 +49,17 @@ class Hystrix { */ static uint64_t GetPingIntervalInMs() { return PING_INTERVAL_IN_MS; } - void updateRollingWindowMap(Stats::Store& stats, std::string cluster_name); + void updateRollingWindowMap(std::map current_stat_values, + std::string cluster_name); /** * clear map */ void resetRollingWindow(); + /** + * return string represnting current state of the map. for DEBUG. + */ std::string printRollingWindow(); private: @@ -109,54 +113,45 @@ typedef std::unique_ptr HystrixPtr; */ class HystrixHandlerInfoImpl : public Server::HandlerInfo { public: - HystrixHandlerInfoImpl(Http::StreamDecoderFilterCallbacks* callbacks) - : stats_(new Stats::Hystrix()), data_timer_(nullptr), ping_timer_(nullptr), - callbacks_(callbacks) {} + HystrixHandlerInfoImpl(Http::StreamDecoderFilterCallbacks* callbacks) : callbacks_(callbacks) {} virtual ~HystrixHandlerInfoImpl(){}; - virtual void Destroy(); - /** - * HystrixHandlerInfoImpl includes statistics for hystrix API, timers for build (and send) data - * and keep alive messages and the handler's callback - */ - Stats::HystrixPtr stats_; - Event::TimerPtr data_timer_; - Event::TimerPtr ping_timer_; Http::StreamDecoderFilterCallbacks* callbacks_{}; }; -/** - * Convert statistics from envoy format to hystrix format and prepare them and writes them to the - * appropriate socket - */ -class HystrixHandler { +namespace HystrixNameSpace { + +class HystrixSink : public Sink { public: - static void HandleEventStream(HystrixHandlerInfoImpl* hystrix_handler_info, - Server::Instance& server); - /** - * Update counter and set values of upstream_rq statistics - * @param hystrix_handler_info is the data which is received in the hystrix handler from the admin - * filter (callback, timers, statistics) - * @param server contains envoy statistics - */ - static void updateHystrixRollingWindow(HystrixHandlerInfoImpl* hystrix_handler_info, - Server::Instance* server); + HystrixSink(Server::Instance& server); + void beginFlush(); + void flushCounter(const Counter& counter, uint64_t delta); + void flushGauge(const Gauge&, uint64_t){}; + void endFlush(); + void onHistogramComplete(const Histogram& histogram, uint64_t value) { + std::cout << "histogram complete: " << histogram.name() << ", value: " << std::to_string(value) + << std::endl; + }; + /** - * Builds a buffer of envoy statistics which will be sent to hystrix dashboard according to - * hystrix API - * @param hystrix_handler_info is the data which is received in the hystrix handler from the admin - * filter (callback, timers, statistics) - * @param server contains envoy statistics* + * register a new connection */ - static void prepareAndSendHystrixStream(HystrixHandlerInfoImpl* hystrix_handler_info, - Server::Instance* server); + void registerConnection(Http::StreamDecoderFilterCallbacks* callbacks_); /** - * Sends a keep alive (ping) message to hystrix dashboard - * @param hystrix_handler_info is the data which is received in the hystrix handler from the admin - * filter (callback, timers, statistics) + * remove registered connection */ - static void sendKeepAlivePing(HystrixHandlerInfoImpl* hystrix_handler_info); + void unregisterConnection(); + +private: + Stats::HystrixPtr stats_; + Http::StreamDecoderFilterCallbacks* callbacks_{}; + Server::Instance* server_; + std::map current_stat_values_; }; +typedef std::unique_ptr HystrixSinkPtr; + +} // namespace HystrixNameSpace + } // namespace Stats } // namespace Envoy diff --git a/source/exe/BUILD b/source/exe/BUILD index f779d4dc47d00..638ae36e60b42 100644 --- a/source/exe/BUILD +++ b/source/exe/BUILD @@ -59,6 +59,7 @@ envoy_cc_library( "//source/server/config/network:ssl_socket_lib", "//source/server/config/network:tcp_proxy_lib", "//source/server/config/stats:dog_statsd_lib", + "//source/server/config/stats:hystrix_lib", "//source/server/config/stats:metrics_service_lib", "//source/server/config/stats:statsd_lib", "//source/server/http:health_check_lib", diff --git a/source/server/config/stats/BUILD b/source/server/config/stats/BUILD index 5d32031f9b88e..15eca77212550 100644 --- a/source/server/config/stats/BUILD +++ b/source/server/config/stats/BUILD @@ -51,3 +51,16 @@ envoy_cc_library( "@envoy_api//envoy/service/metrics/v2:metrics_service_cc", ], ) + +envoy_cc_library( + name = "hystrix_lib", + srcs = ["hystrix.cc"], + hdrs = ["hystrix.h"], + deps = [ + "//include/envoy/registry", + "//source/common/config:well_known_names", + "//source/common/stats:hystrix_lib", + "//source/server:configuration_lib", + "@envoy_api//envoy/config/metrics/v2:stats_cc", + ], +) diff --git a/source/server/config/stats/hystrix.cc b/source/server/config/stats/hystrix.cc new file mode 100644 index 0000000000000..1586d94e9f78e --- /dev/null +++ b/source/server/config/stats/hystrix.cc @@ -0,0 +1,43 @@ +#include "server/config/stats/hystrix.h" + +#include + +#include "envoy/config/metrics/v2/stats.pb.h" +#include "envoy/config/metrics/v2/stats.pb.validate.h" +#include "envoy/registry/registry.h" + +#include "common/config/well_known_names.h" +#include "common/network/resolver_impl.h" +#include "common/stats/hystrix.h" + +namespace Envoy { +namespace Server { +namespace Configuration { + +Stats::SinkPtr HystrixSinkFactory::createStatsSink(const Protobuf::Message&, + Server::Instance& server) { + + // const auto& hystrix_sink = + // MessageUtil::downcastAndValidate(config); + std::cout << "HystrixSinkFactory::createStatsSink" << std::endl; + return Stats::SinkPtr(new Stats::HystrixNameSpace::HystrixSink(server)); +} + +ProtobufTypes::MessagePtr HystrixSinkFactory::createEmptyConfigProto() { + // TDOD (@trabetti): until I add hystrix to data_plane_api + return std::unique_ptr( + new envoy::config::metrics::v2::HystrixSink()); + // return std::unique_ptr( + // ica new envoy::config::metrics::v2::StatsdSink()); +} + +std::string HystrixSinkFactory::name() { return Config::StatsSinkNames::get().HYSTRIX; } + +/** + * Static registration for the statsd sink factory. @see RegisterFactory. + */ +static Registry::RegisterFactory register_; + +} // namespace Configuration +} // namespace Server +} // namespace Envoy diff --git a/source/server/config/stats/hystrix.h b/source/server/config/stats/hystrix.h new file mode 100644 index 0000000000000..4956e9ddb05be --- /dev/null +++ b/source/server/config/stats/hystrix.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "envoy/server/instance.h" + +#include "server/configuration_impl.h" + +namespace Envoy { +namespace Server { +namespace Configuration { + +/** + * Config registration for the hystrix sink. @see StatsSinkFactory. + */ +class HystrixSinkFactory : Logger::Loggable, public StatsSinkFactory { +public: + // StatsSinkFactory + Stats::SinkPtr createStatsSink(const Protobuf::Message& config, Instance& server) override; + + ProtobufTypes::MessagePtr createEmptyConfigProto() override; + + std::string name() override; +}; + +} // namespace Configuration +} // namespace Server +} // namespace Envoy diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index 9ba652cd8ded4..3f0f6ec777544 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -87,6 +87,8 @@ class ValidationInstance : Logger::Loggable, Tracing::HttpTracer& httpTracer() override { return config_->httpTracer(); } ThreadLocal::Instance& threadLocal() override { return thread_local_; } const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } + void registerToHystrixSink(Http::StreamDecoderFilterCallbacks*) override { NOT_IMPLEMENTED; } + void UnregisterHystrixSink() override { NOT_IMPLEMENTED; } // Server::ListenerComponentFactory std::vector createNetworkFilterFactoryList( diff --git a/source/server/configuration_impl.cc b/source/server/configuration_impl.cc index 38c530a388378..554642a6b77a5 100644 --- a/source/server/configuration_impl.cc +++ b/source/server/configuration_impl.cc @@ -124,6 +124,12 @@ void MainImpl::initializeStatsSinks(const envoy::config::bootstrap::v2::Bootstra ProtobufTypes::MessagePtr message = Config::Utility::translateToFactoryConfig(sink_object, factory); + // auto& factory = Config::Utility::getAndCheckFactory("envoy.hystrix"); + // ProtobufTypes::MessagePtr message = + // Config::Utility::translateToFactoryConfig(sink_object, factory); + + // std::cout << "sink_object.name() = " << sink_object.name() << std::endl; + stats_sinks_.emplace_back(factory.createStatsSink(*message, server)); } } diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 363082cf12c5d..50180faec6fd7 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -592,7 +592,8 @@ Http::Code AdminImpl::handlerHystrixEventStream(const std::string&, Stats::HystrixHandlerInfoImpl& hystrix_handler_info = dynamic_cast(handler_info); - Stats::HystrixHandler::HandleEventStream(&hystrix_handler_info, server_); + + server_.registerToHystrixSink(hystrix_handler_info.callbacks_); ENVOY_LOG(debug, "start sending data to hystrix dashboard on port {}", hystrix_handler_info.callbacks_->connection()->localAddress()->asString()); diff --git a/source/server/http/admin.h b/source/server/http/admin.h index adaf1b9a5715c..3e9046697c3b9 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -37,7 +37,6 @@ class HandlerInfoImpl : public HandlerInfo { public: HandlerInfoImpl(){}; virtual ~HandlerInfoImpl(){}; - virtual void Destroy(){}; }; /** @@ -101,6 +100,8 @@ class AdminImpl : public Admin, Http::ConnectionManagerListenerStats& listenerStats() override { return listener_.stats_; } bool proxy100Continue() const override { return false; } + void UregisterHystrixConnection() { server_.UnregisterHystrixSink(); } + private: /** * Individual admin handler including prefix, help text, and callback. @@ -238,7 +239,7 @@ class AdminFilter : public Http::StreamDecoderFilter, Logger::LoggableDestroy(); } + void onDestroy() override { parent_.UregisterHystrixConnection(); } // Http::StreamDecoderFilter Http::FilterHeadersStatus decodeHeaders(Http::HeaderMap& response_headers, diff --git a/source/server/server.cc b/source/server/server.cc index 7c5faaa7ff32e..0e71cdade28e7 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -143,6 +143,30 @@ void InstanceImpl::flushStats() { stat_flush_timer_->enableTimer(config_->statsFlushInterval()); } +void InstanceImpl::registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) { + for (const auto& sink : config_->statsSinks()) { + // TODO: is there a better way to find the hystrix sink? + Stats::HystrixNameSpace::HystrixSink* hystrix_sink = + dynamic_cast(sink.get()); + if (hystrix_sink != nullptr) { + hystrix_sink->registerConnection(callbacks); + } + } +} + +void InstanceImpl::UnregisterHystrixSink() { + for (const auto& sink : config_->statsSinks()) { + // TODO: is there a better way to find the hystrix sink? + Stats::HystrixNameSpace::HystrixSink* hystrix_sink = + dynamic_cast(sink.get()); + if (hystrix_sink != nullptr) { + // TODO (@trabetti) : will want to move to a vector of connections, + // need a parameter (callback, hope it will work) to identify which connection to remove + hystrix_sink->unregisterConnection(); + } + } +} + void InstanceImpl::getParentStats(HotRestart::GetParentStatsInfo& info) { info.memory_allocated_ = Memory::Stats::totalCurrentlyAllocated(); info.num_connections_ = numConnections(); diff --git a/source/server/server.h b/source/server/server.h index fa73bd9f6fb5e..2e39313d5f869 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -164,6 +164,9 @@ class InstanceImpl : Logger::Loggable, public Instance { ThreadLocal::Instance& threadLocal() override { return thread_local_; } const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } + void registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) override; + void UnregisterHystrixSink() override; + private: void flushStats(); void initialize(Options& options, Network::Address::InstanceConstSharedPtr local_address, From 847b241eaa733553d49e9787b2611c190420d020 Mon Sep 17 00:00:00 2001 From: trabetti Date: Tue, 27 Mar 2018 21:59:13 +0300 Subject: [PATCH 12/25] fixed typo in function name Signed-off-by: trabetti --- include/envoy/server/instance.h | 2 +- source/server/config_validation/server.h | 2 +- source/server/http/admin.h | 4 ++-- source/server/server.cc | 2 +- source/server/server.h | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/envoy/server/instance.h b/include/envoy/server/instance.h index 679d08a4e9e71..62be6c6c06bcc 100644 --- a/include/envoy/server/instance.h +++ b/include/envoy/server/instance.h @@ -182,7 +182,7 @@ class Instance { virtual void registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) PURE; - virtual void UnregisterHystrixSink() PURE; + virtual void unregisterHystrixSink() PURE; }; } // namespace Server diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index 3f0f6ec777544..48314789c6882 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -88,7 +88,7 @@ class ValidationInstance : Logger::Loggable, ThreadLocal::Instance& threadLocal() override { return thread_local_; } const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } void registerToHystrixSink(Http::StreamDecoderFilterCallbacks*) override { NOT_IMPLEMENTED; } - void UnregisterHystrixSink() override { NOT_IMPLEMENTED; } + void unregisterHystrixSink() override { NOT_IMPLEMENTED; } // Server::ListenerComponentFactory std::vector createNetworkFilterFactoryList( diff --git a/source/server/http/admin.h b/source/server/http/admin.h index 3e9046697c3b9..3d6f1e5a3fe38 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -100,7 +100,7 @@ class AdminImpl : public Admin, Http::ConnectionManagerListenerStats& listenerStats() override { return listener_.stats_; } bool proxy100Continue() const override { return false; } - void UregisterHystrixConnection() { server_.UnregisterHystrixSink(); } + void unregisterHystrixConnection() { server_.unregisterHystrixSink(); } private: /** @@ -239,7 +239,7 @@ class AdminFilter : public Http::StreamDecoderFilter, Logger::LoggablestatsSinks()) { // TODO: is there a better way to find the hystrix sink? Stats::HystrixNameSpace::HystrixSink* hystrix_sink = diff --git a/source/server/server.h b/source/server/server.h index 2e39313d5f869..4bdad277a4d58 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -165,7 +165,7 @@ class InstanceImpl : Logger::Loggable, public Instance { const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } void registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) override; - void UnregisterHystrixSink() override; + void unregisterHystrixSink() override; private: void flushStats(); From f91126c0cf30f2024812c451a9beca0ec07b315f Mon Sep 17 00:00:00 2001 From: trabetti Date: Wed, 28 Mar 2018 12:15:06 +0300 Subject: [PATCH 13/25] small fixes Signed-off-by: trabetti --- source/common/stats/hystrix.cc | 1 - source/server/configuration_impl.cc | 6 ------ source/server/server.cc | 3 +++ 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/source/common/stats/hystrix.cc b/source/common/stats/hystrix.cc index a03082cb35cd1..be9ffeba41535 100644 --- a/source/common/stats/hystrix.cc +++ b/source/common/stats/hystrix.cc @@ -257,7 +257,6 @@ void HystrixSink::endFlush() { } Buffer::OwnedImpl data; data.add(ss.str()); - // std::cout << "endflush : " << ss.str() << std::endl; callbacks_->encodeData(data, false); // send keep alive ping diff --git a/source/server/configuration_impl.cc b/source/server/configuration_impl.cc index 554642a6b77a5..38c530a388378 100644 --- a/source/server/configuration_impl.cc +++ b/source/server/configuration_impl.cc @@ -124,12 +124,6 @@ void MainImpl::initializeStatsSinks(const envoy::config::bootstrap::v2::Bootstra ProtobufTypes::MessagePtr message = Config::Utility::translateToFactoryConfig(sink_object, factory); - // auto& factory = Config::Utility::getAndCheckFactory("envoy.hystrix"); - // ProtobufTypes::MessagePtr message = - // Config::Utility::translateToFactoryConfig(sink_object, factory); - - // std::cout << "sink_object.name() = " << sink_object.name() << std::endl; - stats_sinks_.emplace_back(factory.createStatsSink(*message, server)); } } diff --git a/source/server/server.cc b/source/server/server.cc index ca05f49c99add..86ff831c394ff 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -150,6 +150,7 @@ void InstanceImpl::registerToHystrixSink(Http::StreamDecoderFilterCallbacks* cal dynamic_cast(sink.get()); if (hystrix_sink != nullptr) { hystrix_sink->registerConnection(callbacks); + return; } } } @@ -162,7 +163,9 @@ void InstanceImpl::unregisterHystrixSink() { if (hystrix_sink != nullptr) { // TODO (@trabetti) : will want to move to a vector of connections, // need a parameter (callback, hope it will work) to identify which connection to remove + // also better to return success value. hystrix_sink->unregisterConnection(); + return; } } } From 40851776e612964b90bcd677b2e4c6f417810a65 Mon Sep 17 00:00:00 2001 From: trabetti Date: Thu, 5 Apr 2018 09:04:17 +0300 Subject: [PATCH 14/25] merged absl changes Signed-off-by: trabetti --- include/envoy/server/instance.h | 12 ++ source/common/stats/hystrix.cc | 161 +++++++++++++++-------- source/common/stats/hystrix.h | 76 ++++++----- source/server/config_validation/server.h | 2 + source/server/http/admin.h | 1 + source/server/server.h | 1 + test/mocks/server/BUILD | 1 + test/mocks/server/mocks.h | 4 + 8 files changed, 161 insertions(+), 97 deletions(-) diff --git a/include/envoy/server/instance.h b/include/envoy/server/instance.h index 62be6c6c06bcc..b2bb43d157ba8 100644 --- a/include/envoy/server/instance.h +++ b/include/envoy/server/instance.h @@ -180,8 +180,20 @@ class Instance { */ virtual const LocalInfo::LocalInfo& localInfo() PURE; + /** + * @return the flush interval of stats sinks. + */ + virtual std::chrono::milliseconds statsFlushInterval() PURE; + + // TODO (@trabetti) : too specific? + /** + * @ Registers a callbacks connection to Hystrix sink. + */ virtual void registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) PURE; + /** + * @ Unregisters a callbacks connection to Hystrix sink. + */ virtual void unregisterHystrixSink() PURE; }; diff --git a/source/common/stats/hystrix.cc b/source/common/stats/hystrix.cc index be9ffeba41535..ee31d0629332e 100644 --- a/source/common/stats/hystrix.cc +++ b/source/common/stats/hystrix.cc @@ -8,16 +8,17 @@ #include "common/buffer/buffer_impl.h" #include "common/common/logger.h" +#include "absl/strings/str_cat.h" + namespace Envoy { namespace Stats { const uint64_t Hystrix::DEFAULT_NUM_OF_BUCKETS; -const uint64_t Hystrix::ROLLING_WINDOW_IN_MS; -const uint64_t Hystrix::PING_INTERVAL_IN_MS; -// add new value to rolling window, in place of oldest one -void Hystrix::pushNewValue(std::string key, uint64_t value) { - // create vector if do not exist +// Add new value to rolling window, in place of oldest one. +void Hystrix::pushNewValue(const std::string& key, uint64_t value) { + // Create vector if do not exist. + // TODO trabetti: why resize + value param didn't work without the if? if (rolling_stats_map_.find(key) == rolling_stats_map_.end()) { rolling_stats_map_[key].resize(num_of_buckets_, value); } else { @@ -25,11 +26,12 @@ void Hystrix::pushNewValue(std::string key, uint64_t value) { } } -uint64_t Hystrix::getRollingValue(std::string cluster_name, std::string stats) { - std::string key = "cluster." + cluster_name + "." + stats; +uint64_t Hystrix::getRollingValue(absl::string_view cluster_name, absl::string_view stats) { + std::string key; + key = absl::StrCat("cluster.", cluster_name, ".", stats); if (rolling_stats_map_.find(key) != rolling_stats_map_.end()) { - // if the counter was reset, the result is negative - // better return 0, will be back to normal once one rolling window passes + // If the counter was reset, the result is negative + // better return 0, will be back to normal once one rolling window passes. if (rolling_stats_map_[key][current_index_] < rolling_stats_map_[key][(current_index_ + 1) % num_of_buckets_]) { return 0; @@ -42,39 +44,74 @@ uint64_t Hystrix::getRollingValue(std::string cluster_name, std::string stats) { } } +void Hystrix::CreateCounterNameLookupForCluster(const std::string& cluster_name) { + // Building lookup name map for all specific cluster values. + // Every call to the updateRollingWindowMap function should get the appropriate name from the map. + std::string cluster_name_with_prefix = absl::StrCat("cluster.", cluster_name, "."); + counter_name_lookup[cluster_name]["upstream_rq_timeout"] = + absl::StrCat(cluster_name_with_prefix, "upstream_rq_timeout"); + counter_name_lookup[cluster_name]["upstream_rq_per_try_timeout"] = + absl::StrCat(cluster_name_with_prefix, "upstream_rq_per_try_timeout"); + counter_name_lookup[cluster_name]["timeouts"] = + absl::StrCat(cluster_name_with_prefix, "timeouts"); + counter_name_lookup[cluster_name]["upstream_rq_5xx"] = + absl::StrCat(cluster_name_with_prefix, "upstream_rq_5xx"); + counter_name_lookup[cluster_name]["retry.upstream_rq_5xx"] = + absl::StrCat(cluster_name_with_prefix, "retry.upstream_rq_5xx"); + counter_name_lookup[cluster_name]["upstream_rq_4xx"] = + absl::StrCat(cluster_name_with_prefix, "upstream_rq_4xx"); + counter_name_lookup[cluster_name]["retry.upstream_rq_4xx"] = + absl::StrCat(cluster_name_with_prefix, "retry.upstream_rq_4xx"); + counter_name_lookup[cluster_name]["errors"] = absl::StrCat(cluster_name_with_prefix, "errors"); + counter_name_lookup[cluster_name]["upstream_rq_2xx"] = + absl::StrCat(cluster_name_with_prefix, "upstream_rq_2xx"); + counter_name_lookup[cluster_name]["success"] = absl::StrCat(cluster_name_with_prefix, "success"); + counter_name_lookup[cluster_name]["upstream_rq_pending_overflow"] = + absl::StrCat(cluster_name_with_prefix, "upstream_rq_pending_overflow"); + counter_name_lookup[cluster_name]["rejected"] = + absl::StrCat(cluster_name_with_prefix, "rejected"); + counter_name_lookup[cluster_name]["total"] = absl::StrCat(cluster_name_with_prefix, "total"); +} + void Hystrix::updateRollingWindowMap(std::map current_stat_values, std::string cluster_name) { - std::string prefix = "cluster." + cluster_name + "."; - // combining timeouts+retries - retries are counted as separate requests - // (alternative: each request including the retries counted as 1) - uint64_t timeouts = current_stat_values[prefix + "upstream_rq_timeout"] + - current_stat_values[prefix + "upstream_rq_per_try_timeout"]; + if (counter_name_lookup.find(cluster_name) == counter_name_lookup.end()) { + CreateCounterNameLookupForCluster(cluster_name); + } + + // Combining timeouts+retries - retries are counted as separate requests + // (alternative: each request including the retries counted as 1). + uint64_t timeouts = + current_stat_values[counter_name_lookup[cluster_name]["upstream_rq_timeout"]] + + current_stat_values[counter_name_lookup[cluster_name]["upstream_rq_per_try_timeout"]]; - pushNewValue(prefix + "timeouts", timeouts); + pushNewValue(counter_name_lookup[cluster_name]["timeouts"], timeouts); - // combining errors+retry errors - retries are counted as separate requests + // Combining errors+retry errors - retries are counted as separate requests // (alternative: each request including the retries counted as 1) - // since timeouts are 504 (or 408), deduce them from here. - // timeout retries were not counted here anyway. - uint64_t errors = current_stat_values[prefix + "upstream_rq_5xx"] + - current_stat_values[prefix + "retry.upstream_rq_5xx"] + - current_stat_values[prefix + "upstream_rq_4xx"] + - current_stat_values[prefix + "retry.upstream_rq_4xx"] - - current_stat_values[prefix + "upstream_rq_timeout"]; + // since timeouts are 504 (or 408), deduce them from here ("-" sign). + // Timeout retries were not counted here anyway. + uint64_t errors = + current_stat_values[counter_name_lookup[cluster_name]["upstream_rq_5xx"]] + + current_stat_values[counter_name_lookup[cluster_name]["retry.upstream_rq_5xx"]] + + current_stat_values[counter_name_lookup[cluster_name]["upstream_rq_4xx"]] + + current_stat_values[counter_name_lookup[cluster_name]["retry.upstream_rq_4xx"]] - + current_stat_values[counter_name_lookup[cluster_name]["upstream_rq_timeout"]]; - pushNewValue(prefix + "errors", errors); + pushNewValue(counter_name_lookup[cluster_name]["errors"], errors); - uint64_t success = current_stat_values[prefix + "upstream_rq_2xx"]; - pushNewValue(prefix + "success", success); + uint64_t success = current_stat_values[counter_name_lookup[cluster_name]["upstream_rq_2xx"]]; + pushNewValue(counter_name_lookup[cluster_name]["success"], success); - uint64_t rejected = current_stat_values[prefix + "upstream_rq_pending_overflow"]; - pushNewValue(prefix + "rejected", rejected); + uint64_t rejected = + current_stat_values[counter_name_lookup[cluster_name]["upstream_rq_pending_overflow"]]; + pushNewValue(counter_name_lookup[cluster_name]["rejected"], rejected); // should not take from upstream_rq_total since it is updated before its components, // leading to wrong results such as error percentage higher than 100% uint64_t total = errors + timeouts + success + rejected; - pushNewValue(prefix + "total", total); + pushNewValue(counter_name_lookup[cluster_name]["total"], total); std::cout << printRollingWindow() << std::endl; @@ -84,23 +121,28 @@ void Hystrix::updateRollingWindowMap(std::map current_sta void Hystrix::resetRollingWindow() { rolling_stats_map_.clear(); } -void Hystrix::addStringToStream(std::string key, std::string value, std::stringstream& info) { - addInfoToStream(key, "\"" + value + "\"", info); +void Hystrix::addStringToStream(absl::string_view key, absl::string_view value, + std::stringstream& info) { + std::string quoted_value = absl::StrCat("\"", value, "\""); + addInfoToStream(key, quoted_value, info); } -void Hystrix::addIntToStream(std::string key, uint64_t value, std::stringstream& info) { +void Hystrix::addIntToStream(absl::string_view key, uint64_t value, std::stringstream& info) { addInfoToStream(key, std::to_string(value), info); } -void Hystrix::addInfoToStream(std::string key, std::string value, std::stringstream& info) { +void Hystrix::addInfoToStream(absl::string_view key, absl::string_view value, + std::stringstream& info) { if (!info.str().empty()) { info << ", "; } - info << "\"" + key + "\": " + value; + std::string added_info = absl::StrCat("\"", key, "\": ", value); + info << added_info; } -void Hystrix::addHystrixCommand(std::stringstream& ss, std::string cluster_name, - uint64_t max_concurrent_requests, uint64_t reporting_hosts) { +void Hystrix::addHystrixCommand(std::stringstream& ss, absl::string_view cluster_name, + uint64_t max_concurrent_requests, uint64_t reporting_hosts, + uint64_t rolling_window) { std::stringstream cluster_info; std::time_t currentTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); addStringToStream("type", "HystrixCommand", cluster_info); @@ -134,9 +176,9 @@ void Hystrix::addHystrixCommand(std::stringstream& ss, std::string cluster_name, // so we count upstream_rq_pending_overflow and present it as rejected addIntToStream("rollingCountSemaphoreRejected", rejected, cluster_info); - // Hystrix's short circuit is not similar to Envoy's since it is trrigered by 503 responses + // Hystrix's short circuit is not similar to Envoy's since it is triggered by 503 responses // there is no parallel counter in Envoy since as a result of errors (outlier detection) - // requests are not rejected, but rather the node is removed from load balancer healthy pool + // requests are not rejected, but rather the node is removed from load balancer healthy pool. addIntToStream("rollingCountShortCircuited", 0, cluster_info); addIntToStream("rollingCountSuccess", getRollingValue(cluster_name, "success"), cluster_info); addIntToStream("rollingCountThreadPoolRejected", 0, cluster_info); @@ -145,8 +187,7 @@ void Hystrix::addHystrixCommand(std::stringstream& ss, std::string cluster_name, addIntToStream("currentConcurrentExecutionCount", 0, cluster_info); addIntToStream("latencyExecute_mean", 0, cluster_info); - // latency information can be taken rom hystogram, which is only available to sinks - // we should consider make this a sink so we can get this information + // TODO trabetti : add histogram information once available by PR #2932 addInfoToStream( "latencyExecute", "{\"0\":0,\"25\":0,\"50\":0,\"75\":0,\"90\":0,\"95\":0,\"99\":0,\"99.5\":0,\"100\":0}", @@ -166,14 +207,15 @@ void Hystrix::addHystrixCommand(std::stringstream& ss, std::string cluster_name, addInfoToStream("propertyValue_requestCacheEnabled", "false", cluster_info); addInfoToStream("propertyValue_requestLogEnabled", "true", cluster_info); addIntToStream("reportingHosts", reporting_hosts, cluster_info); - addIntToStream("propertyValue_metricsRollingStatisticalWindowInMilliseconds", - ROLLING_WINDOW_IN_MS, cluster_info); + addIntToStream("propertyValue_metricsRollingStatisticalWindowInMilliseconds", rolling_window, + cluster_info); ss << "data: {" << cluster_info.str() << "}" << std::endl << std::endl; } -void Hystrix::addHystrixThreadPool(std::stringstream& ss, std::string cluster_name, - uint64_t queue_size, uint64_t reporting_hosts) { +void Hystrix::addHystrixThreadPool(std::stringstream& ss, absl::string_view cluster_name, + uint64_t queue_size, uint64_t reporting_hosts, + uint64_t rolling_window) { std::stringstream cluster_info; addIntToStream("currentPoolSize", 0, cluster_info); @@ -183,8 +225,8 @@ void Hystrix::addHystrixThreadPool(std::stringstream& ss, std::string cluster_na addIntToStream("propertyValue_queueSizeRejectionThreshold", queue_size, cluster_info); addStringToStream("type", "HystrixThreadPool", cluster_info); addIntToStream("reportingHosts", reporting_hosts, cluster_info); - addIntToStream("propertyValue_metricsRollingStatisticalWindowInMilliseconds", - ROLLING_WINDOW_IN_MS, cluster_info); + addIntToStream("propertyValue_metricsRollingStatisticalWindowInMilliseconds", rolling_window, + cluster_info); addStringToStream("name", cluster_name, cluster_info); addIntToStream("currentLargestPoolSize", 0, cluster_info); addIntToStream("currentCorePoolSize", 0, cluster_info); @@ -196,21 +238,23 @@ void Hystrix::addHystrixThreadPool(std::stringstream& ss, std::string cluster_na ss << "data: {" << cluster_info.str() << "}" << std::endl << std::endl; } -void Hystrix::getClusterStats(std::stringstream& ss, std::string cluster_name, - uint64_t max_concurrent_requests, uint64_t reporting_hosts) { - addHystrixCommand(ss, cluster_name, max_concurrent_requests, reporting_hosts); - addHystrixThreadPool(ss, cluster_name, max_concurrent_requests, reporting_hosts); +void Hystrix::getClusterStats(std::stringstream& ss, absl::string_view cluster_name, + uint64_t max_concurrent_requests, uint64_t reporting_hosts, + uint64_t rolling_window) { + addHystrixCommand(ss, cluster_name, max_concurrent_requests, reporting_hosts, rolling_window); + addHystrixThreadPool(ss, cluster_name, max_concurrent_requests, reporting_hosts, rolling_window); } -std::string Hystrix::printRollingWindow() { +absl::string_view Hystrix::printRollingWindow() const { std::stringstream out_str; - for (std::map::const_iterator it = rolling_stats_map_.begin(); - it != rolling_stats_map_.end(); ++it) { - out_str << it->first << " | "; - RollingStats rolling_stats = it->second; - for (uint64_t i = 0; i < rolling_stats.size(); i++) { - out_str << rolling_stats[i] << " | "; + for (auto stats_map_itr = rolling_stats_map_.begin(); stats_map_itr != rolling_stats_map_.end(); + ++stats_map_itr) { + out_str << stats_map_itr->first << " | "; + RollingStats rolling_stats = stats_map_itr->second; + for (auto specific_stat_vec_itr = rolling_stats.begin(); + specific_stat_vec_itr != rolling_stats.end(); ++specific_stat_vec_itr) { + out_str << *specific_stat_vec_itr << " | "; } out_str << std::endl; } @@ -253,7 +297,8 @@ void HystrixSink::endFlush() { .max(), server_->stats() .gauge("cluster." + cluster.second.get().info()->name() + ".membership_total") - .value()); + .value(), + server_->statsFlushInterval().count()); } Buffer::OwnedImpl data; data.add(ss.str()); diff --git a/source/common/stats/hystrix.h b/source/common/stats/hystrix.h index eff63419a29a6..20b6ec306d6ba 100644 --- a/source/common/stats/hystrix.h +++ b/source/common/stats/hystrix.h @@ -10,9 +10,8 @@ namespace Envoy { namespace Stats { typedef std::vector RollingStats; -typedef std::map RollingStatsMap; +typedef std::map RollingStatsMap; -// Consider implement the HystrixStats as a sink to have access to histograms data class Hystrix { public: @@ -22,87 +21,85 @@ class Hystrix { : current_index_(num_of_buckets), num_of_buckets_(num_of_buckets + 1){}; /** - * Add new value to top of rolling window, pushing out the oldest value + * Add new value to top of rolling window, pushing out the oldest value. */ - void pushNewValue(std::string key, uint64_t value); + void pushNewValue(const std::string& key, uint64_t value); /** - * increment pointer of next value to add to rolling window + * Increment pointer of next value to add to rolling window. */ void incCounter() { current_index_ = (current_index_ + 1) % num_of_buckets_; } /** - * Generate the streams to be sent to hystrix dashboard + * Generate the streams to be sent to hystrix dashboard. */ - void getClusterStats(std::stringstream& ss, std::string cluster_name, - uint64_t max_concurrent_requests, uint64_t reporting_hosts); + void getClusterStats(std::stringstream& ss, absl::string_view cluster_name, + uint64_t max_concurrent_requests, uint64_t reporting_hosts, + uint64_t rolling_window); /** - * Get value of the sampling buckets + * Calculate values needed to create the stream and write into the map. */ - static uint64_t GetRollingWindowIntervalInMs() { - return static_cast(ROLLING_WINDOW_IN_MS / DEFAULT_NUM_OF_BUCKETS); - } + void updateRollingWindowMap(std::map current_stat_values, + const std::string cluster_name); /** - * Get value of the keep alive ping interval + * Clear map. */ - static uint64_t GetPingIntervalInMs() { return PING_INTERVAL_IN_MS; } - - void updateRollingWindowMap(std::map current_stat_values, - std::string cluster_name); + void resetRollingWindow(); /** - * clear map + * Return string represnting current state of the map. for DEBUG. */ - void resetRollingWindow(); + absl::string_view printRollingWindow() const; /** - * return string represnting current state of the map. for DEBUG. + * Get the statistic's value change over the rolling window time frame. */ - std::string printRollingWindow(); + uint64_t getRollingValue(absl::string_view cluster_name, absl::string_view stats); private: /** - * Get the statistic's value change over the rolling window time frame + * Format the given key and absl::string_view value to "key"="value", and adding to the + * stringstream. */ - uint64_t getRollingValue(std::string cluster_name, std::string stats); + void addStringToStream(absl::string_view key, absl::string_view value, std::stringstream& info); /** - * Format the given key and std::string value to "key"="value", and adding to the stringstream + * Format the given key and uint64_t value to "key"=, and adding to the + * stringstream. */ - void addStringToStream(std::string key, std::string value, std::stringstream& info); + void addIntToStream(absl::string_view key, uint64_t value, std::stringstream& info); /** - * Format the given key and uint64_t value to "key"=, and adding to the - * stringstream + * Format the given key and value to "key"=value, and adding to the stringstream. */ - void addIntToStream(std::string key, uint64_t value, std::stringstream& info); + void addInfoToStream(absl::string_view key, absl::string_view value, std::stringstream& info); /** - * Format the given key and value to "key"=value, and adding to the stringstream + * Generate HystrixCommand event stream. */ - void addInfoToStream(std::string key, std::string value, std::stringstream& info); + void addHystrixCommand(std::stringstream& ss, absl::string_view cluster_name, + uint64_t max_concurrent_requests, uint64_t reporting_hosts, + uint64_t rolling_window); /** - * generate HystrixCommand event stream + * Generate HystrixThreadPool event stream. */ - void addHystrixCommand(std::stringstream& ss, std::string cluster_name, - uint64_t max_concurrent_requests, uint64_t reporting_hosts); + void addHystrixThreadPool(std::stringstream& ss, absl::string_view cluster_name, + uint64_t queue_size, uint64_t reporting_hosts, uint64_t rolling_window); /** - * generate HystrixThreadPool event stream + * Building lookup name map for all specific cluster values. */ - void addHystrixThreadPool(std::stringstream& ss, std::string cluster_name, uint64_t queue_size, - uint64_t reporting_hosts); + void CreateCounterNameLookupForCluster(const std::string& cluster_name); RollingStatsMap rolling_stats_map_; uint64_t current_index_; uint64_t num_of_buckets_; - // TODO(trabetti): May want to make this configurable via config file + // TODO(trabetti): do we want this to be configurable through the HystrixSink in config file? static const uint64_t DEFAULT_NUM_OF_BUCKETS = 10; - static const uint64_t ROLLING_WINDOW_IN_MS = 10000; - static const uint64_t PING_INTERVAL_IN_MS = 3000; + std::map> counter_name_lookup; }; typedef std::unique_ptr HystrixPtr; @@ -141,6 +138,7 @@ class HystrixSink : public Sink { * remove registered connection */ void unregisterConnection(); + Hystrix& getStats() { return *stats_; } private: Stats::HystrixPtr stats_; diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index 48314789c6882..55cc43c8486f2 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -87,6 +87,8 @@ class ValidationInstance : Logger::Loggable, Tracing::HttpTracer& httpTracer() override { return config_->httpTracer(); } ThreadLocal::Instance& threadLocal() override { return thread_local_; } const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } + + std::chrono::milliseconds statsFlushInterval() override { return config_->statsFlushInterval(); } void registerToHystrixSink(Http::StreamDecoderFilterCallbacks*) override { NOT_IMPLEMENTED; } void unregisterHystrixSink() override { NOT_IMPLEMENTED; } diff --git a/source/server/http/admin.h b/source/server/http/admin.h index 3d6f1e5a3fe38..e2ee2cb52aa63 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -239,6 +239,7 @@ class AdminFilter : public Http::StreamDecoderFilter, Logger::Loggable, public Instance { ThreadLocal::Instance& threadLocal() override { return thread_local_; } const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } + std::chrono::milliseconds statsFlushInterval() override { return config_->statsFlushInterval(); } void registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) override; void unregisterHystrixSink() override; diff --git a/test/mocks/server/BUILD b/test/mocks/server/BUILD index 8d97ee9460729..efa4cfa7ee221 100644 --- a/test/mocks/server/BUILD +++ b/test/mocks/server/BUILD @@ -13,6 +13,7 @@ envoy_cc_mock( srcs = ["mocks.cc"], hdrs = ["mocks.h"], deps = [ + "//include/envoy/http:filter_interface", "//include/envoy/server:admin_interface", "//include/envoy/server:configuration_interface", "//include/envoy/server:drain_manager_interface", diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index 7146b00314c7e..d96ab8d842ba3 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -5,6 +5,7 @@ #include #include +#include "envoy/http/filter.h" #include "envoy/server/admin.h" #include "envoy/server/configuration.h" #include "envoy/server/drain_manager.h" @@ -269,6 +270,9 @@ class MockInstance : public Instance { MOCK_METHOD0(httpTracer, Tracing::HttpTracer&()); MOCK_METHOD0(threadLocal, ThreadLocal::Instance&()); MOCK_METHOD0(localInfo, const LocalInfo::LocalInfo&()); + MOCK_METHOD0(statsFlushInterval, std::chrono::milliseconds()); + MOCK_METHOD1(registerToHystrixSink, void(Http::StreamDecoderFilterCallbacks* callbacks)); + MOCK_METHOD0(unregisterHystrixSink, void()); testing::NiceMock thread_local_; Stats::IsolatedStoreImpl stats_store_; From f5cddfba09157a604f59223f8e74e8a68537ea3b Mon Sep 17 00:00:00 2001 From: trabetti Date: Sun, 8 Apr 2018 17:36:29 +0300 Subject: [PATCH 15/25] testing hystrix sink Signed-off-by: trabetti --- include/envoy/server/instance.h | 2 +- source/common/stats/hystrix.cc | 7 +- source/server/config/stats/hystrix.cc | 3 - test/common/stats/BUILD | 5 +- test/common/stats/hystrix_test.cc | 256 ++++++++++++++---------- test/server/config/stats/BUILD | 2 + test/server/config/stats/config_test.cc | 19 ++ 7 files changed, 179 insertions(+), 115 deletions(-) diff --git a/include/envoy/server/instance.h b/include/envoy/server/instance.h index b2bb43d157ba8..219d78dcdbd52 100644 --- a/include/envoy/server/instance.h +++ b/include/envoy/server/instance.h @@ -185,7 +185,7 @@ class Instance { */ virtual std::chrono::milliseconds statsFlushInterval() PURE; - // TODO (@trabetti) : too specific? + // TODO (@trabetti) : for the two methods below - too specific? what is a better way? /** * @ Registers a callbacks connection to Hystrix sink. */ diff --git a/source/common/stats/hystrix.cc b/source/common/stats/hystrix.cc index ee31d0629332e..9a292f8c458d2 100644 --- a/source/common/stats/hystrix.cc +++ b/source/common/stats/hystrix.cc @@ -113,8 +113,6 @@ void Hystrix::updateRollingWindowMap(std::map current_sta uint64_t total = errors + timeouts + success + rejected; pushNewValue(counter_name_lookup[cluster_name]["total"], total); - std::cout << printRollingWindow() << std::endl; - // TODO (@trabetti) : why does it fail compilation? // ENVOY_LOG(trace, "{}", printRollingWindow()); } @@ -267,13 +265,10 @@ HystrixSink::HystrixSink(Server::Instance& server) void HystrixSink::beginFlush() { current_stat_values_.clear(); } -void HystrixSink::flushCounter(const Counter& counter, uint64_t delta) { +void HystrixSink::flushCounter(const Counter& counter, uint64_t) { if (callbacks_ == nullptr) { - std::cout << "callback is null" << std::endl; return; } - std::cout << "callback is not null. flushing counter: " << counter.name() - << ", delta: " << std::to_string(delta) << std::endl; if (counter.name().find("upstream_rq_") != std::string::npos) { current_stat_values_[counter.name()] = counter.value(); } diff --git a/source/server/config/stats/hystrix.cc b/source/server/config/stats/hystrix.cc index 1586d94e9f78e..7e516184bc8e3 100644 --- a/source/server/config/stats/hystrix.cc +++ b/source/server/config/stats/hystrix.cc @@ -17,9 +17,6 @@ namespace Configuration { Stats::SinkPtr HystrixSinkFactory::createStatsSink(const Protobuf::Message&, Server::Instance& server) { - // const auto& hystrix_sink = - // MessageUtil::downcastAndValidate(config); - std::cout << "HystrixSinkFactory::createStatsSink" << std::endl; return Stats::SinkPtr(new Stats::HystrixNameSpace::HystrixSink(server)); } diff --git a/test/common/stats/BUILD b/test/common/stats/BUILD index d281d2c3b925c..e6bec17feb33b 100644 --- a/test/common/stats/BUILD +++ b/test/common/stats/BUILD @@ -85,10 +85,11 @@ envoy_cc_test( name = "hystrix_test", srcs = ["hystrix_test.cc"], deps = [ - "//source/common/common:empty_string", - "//source/common/http:codes_lib", "//source/common/stats:hystrix_lib", "//source/common/stats:stats_lib", + "//test/mocks/server:server_mocks", + "//test/mocks/stats:stats_mocks", + "//test/mocks/upstream:upstream_mocks", "//test/test_common:utility_lib", ], ) diff --git a/test/common/stats/hystrix_test.cc b/test/common/stats/hystrix_test.cc index bebeaafd31813..acd705f836355 100644 --- a/test/common/stats/hystrix_test.cc +++ b/test/common/stats/hystrix_test.cc @@ -1,132 +1,182 @@ +#include +#include #include -#include "common/common/empty_string.h" -#include "common/http/codes.h" #include "common/stats/hystrix.h" #include "common/stats/stats_impl.h" +#include "test/mocks/server/mocks.h" +#include "test/mocks/stats/mocks.h" +#include "test/mocks/upstream/mocks.h" #include "test/test_common/utility.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" +using testing::InSequence; +using testing::Invoke; +using testing::NiceMock; +using testing::Return; +using testing::_; + namespace Envoy { namespace Stats { -// copied from CodeUtilityTest in codes_test.cc -class HystrixUtilityTest : public testing::Test { +namespace HystrixNameSpace { + +class HystrixSinkTest : public testing::Test { public: - void addResponse(uint64_t code, bool canary, bool internal_request, - const std::string& request_vhost_name = EMPTY_STRING, - const std::string& request_vcluster_name = EMPTY_STRING, - const std::string& from_az = EMPTY_STRING, - const std::string& to_az = EMPTY_STRING) { - Http::CodeUtility::ResponseStatInfo info{global_store_, - cluster_scope_, - "cluster.clusterName.", - code, - internal_request, - request_vhost_name, - request_vcluster_name, - from_az, - to_az, - canary}; - - Http::CodeUtility::chargeResponseStat(info); + HystrixSinkTest() { sink_.reset(new HystrixSink(server_)); } + + std::string getStreamField(std::string dataMessage, std::string key) { + std::string actual = dataMessage.substr(dataMessage.find(key)); + actual = actual.substr(actual.find(" ") + 1); + std::size_t length = actual.find(","); + actual = actual.substr(0, length); + return actual; } - IsolatedStoreImpl global_store_; - IsolatedStoreImpl cluster_scope_; + Buffer::OwnedImpl createClusterAndCallbacks() { + + // set cluster + cluster_.info_->name_ = "test_cluster"; + cluster_map_.emplace("test_cluster", cluster_); + ON_CALL(server_, clusterManager()).WillByDefault(ReturnRef(cluster_manager_)); + ON_CALL(cluster_manager_, clusters()).WillByDefault(Return(cluster_map_)); + + // set callbacks to send data to buffer + Buffer::OwnedImpl buffer; + auto encode_callback = [&buffer](Buffer::Instance& data, bool) { + buffer.add( + data); // This will append to the end of the buffer, so multiple calls will all be dumped + // one after another into this buffer. See Buffer::Instance for other buffer + // buffer modification options. + }; + ON_CALL(callbacks_, encodeData(_, _)).WillByDefault(Invoke(encode_callback)); + + return buffer; + } + + NiceMock callbacks_; + NiceMock server_; + NiceMock cluster_; + Upstream::ClusterManager::ClusterInfoMap cluster_map_; + + NiceMock cluster_manager_; + std::unique_ptr sink_; }; -std::string getStreamField(std::string dataMessage, std::string key) { - std::string actual = dataMessage.substr(dataMessage.find(key)); - actual = actual.substr(actual.find(" ") + 1); - std::size_t length = actual.find(","); - actual = actual.substr(0, length); - // EXPECT_EQ(actual, std::to_string(expected)); - return actual; +TEST_F(HystrixSinkTest, EmptyFlush) { + InSequence s; + Buffer::OwnedImpl buffer = createClusterAndCallbacks(); + // register callback to sink + sink_->registerConnection(&callbacks_); + + sink_->beginFlush(); + sink_->endFlush(); + std::string data_message = TestUtility::bufferToString(buffer); + EXPECT_EQ(getStreamField(data_message, "errorPercentage"), "0"); + EXPECT_EQ(getStreamField(data_message, "errorCount"), "0"); + EXPECT_EQ(getStreamField(data_message, "requestCount"), "0"); + EXPECT_EQ(getStreamField(data_message, "rollingCountSemaphoreRejected"), "0"); + EXPECT_EQ(getStreamField(data_message, "rollingCountSuccess"), "0"); + EXPECT_EQ(getStreamField(data_message, "rollingCountTimeout"), "0"); } -// this part is useful for testing updateRollingWindowMap() -// by using addResponse() to -TEST_F(HystrixUtilityTest, CreateDataMessage) { - Stats::Hystrix hystrix; - std::stringstream ss; - std::string cluster_name = "clusterName"; - uint64_t expected_queue_size = 12; - uint64_t expectedReportingHosts = 16; - - // insert data to rolling window - for (uint64_t i = 0; i < 14; i++) { - hystrix.incCounter(); - addResponse(201, false, false); - addResponse(401, false, false); - addResponse(501, false, true); - - hystrix.updateRollingWindowMap(cluster_scope_, cluster_name); +TEST_F(HystrixSinkTest, BasicFlow) { + InSequence s; + + Buffer::OwnedImpl buffer = createClusterAndCallbacks(); + // register callback to sink + sink_->registerConnection(&callbacks_); + + NiceMock success_counter; + success_counter.name_ = "cluster.test_cluster.upstream_rq_2xx"; + NiceMock error_counter; + error_counter.name_ = "cluster.test_cluster.upstream_rq_5xx"; + NiceMock timeout_counter; + timeout_counter.name_ = "cluster.test_cluster.upstream_rq_timeout"; + NiceMock rejected_counter; + rejected_counter.name_ = "cluster.test_cluster.upstream_rq_pending_overflow"; + + for (int i = 0; i < 12; i++) { + buffer.drain(buffer.length()); + ON_CALL(timeout_counter, value()).WillByDefault(Return((i + 1) * 3)); + ON_CALL(error_counter, value()).WillByDefault(Return((i + 1) * 17)); + ON_CALL(success_counter, value()).WillByDefault(Return((i + 1) * 7)); + ON_CALL(rejected_counter, value()).WillByDefault(Return((i + 1) * 8)); + sink_->beginFlush(); + sink_->flushCounter(timeout_counter, 1); + sink_->flushCounter(error_counter, 1); + sink_->flushCounter(success_counter, 1); + sink_->flushCounter(rejected_counter, 1); + sink_->endFlush(); + // std::cout << "BasicFlow: buffer = " << TestUtility::bufferToString(buffer) << std::endl; } + // std::string window = sink_->getStats().printRollingWindow(); // just to cover it + // TODO (@trabetti) : add something to check the data? - hystrix.getClusterStats(ss, cluster_name, expected_queue_size, expectedReportingHosts); - std::string dataMessage = ss.str(); + std::string data_message = TestUtility::bufferToString(buffer); // check stream format and data - EXPECT_EQ(getStreamField(dataMessage, "errorPercentage"), "66"); - EXPECT_EQ(getStreamField(dataMessage, "errorCount"), "20"); - EXPECT_EQ(getStreamField(dataMessage, "requestCount"), "30"); - EXPECT_EQ(getStreamField(dataMessage, "rollingCountSemaphoreRejected"), "0"); - EXPECT_EQ(getStreamField(dataMessage, "rollingCountSuccess"), "10"); - EXPECT_EQ(getStreamField(dataMessage, "rollingCountTimeout"), "0"); - EXPECT_EQ(getStreamField(dataMessage, "propertyValue_queueSizeRejectionThreshold"), - std::to_string(expected_queue_size)); - EXPECT_EQ(getStreamField(dataMessage, "reportingHosts"), std::to_string(expectedReportingHosts)); + EXPECT_EQ(getStreamField(data_message, "errorCount"), "140"); // note that on regular operation, + // 5xx and timeout are raised + // together, so timeouts are reduced + // from 5xx count + EXPECT_EQ(getStreamField(data_message, "requestCount"), "320"); + EXPECT_EQ(getStreamField(data_message, "rollingCountSemaphoreRejected"), "80"); + EXPECT_EQ(getStreamField(data_message, "rollingCountSuccess"), "70"); + EXPECT_EQ(getStreamField(data_message, "rollingCountTimeout"), "30"); + EXPECT_EQ(getStreamField(data_message, "errorPercentage"), "78"); + + // check the values are reset + buffer.drain(buffer.length()); + sink_->getStats().resetRollingWindow(); + sink_->beginFlush(); + sink_->endFlush(); + data_message = TestUtility::bufferToString(buffer); + EXPECT_EQ(getStreamField(data_message, "errorPercentage"), "0"); + EXPECT_EQ(getStreamField(data_message, "errorCount"), "0"); + EXPECT_EQ(getStreamField(data_message, "requestCount"), "0"); + EXPECT_EQ(getStreamField(data_message, "rollingCountSemaphoreRejected"), "0"); + EXPECT_EQ(getStreamField(data_message, "rollingCountSuccess"), "0"); + EXPECT_EQ(getStreamField(data_message, "rollingCountTimeout"), "0"); } -TEST(Hystrix, CreateDataMessage) { - Stats::Hystrix hystrix; - std::stringstream ss; - std::string cluster_name = "clusterName"; - uint64_t expected_queue_size = 12; - uint64_t expectedReportingHosts = 16; - - EXPECT_EQ(hystrix.GetRollingWindowIntervalInMs(), 1000); - EXPECT_EQ(hystrix.GetPingIntervalInMs(), 3000); - - // insert data to rolling window - for (uint64_t i = 0; i < 15; i++) { - hystrix.incCounter(); - hystrix.pushNewValue("cluster.clusterName.timeouts", (i + 1) * 3); - hystrix.pushNewValue("cluster.clusterName.errors", (i + 1) * 17); - hystrix.pushNewValue("cluster.clusterName.success", (i + 1) * 7); - hystrix.pushNewValue("cluster.clusterName.rejected", (i + 1) * 8); - hystrix.pushNewValue("cluster.clusterName.total", (i + 1) * 35); - } - hystrix.getClusterStats(ss, cluster_name, expected_queue_size, expectedReportingHosts); - std::string dataMessage = ss.str(); - - // check stream format and data - EXPECT_EQ(getStreamField(dataMessage, "errorPercentage"), "80"); - EXPECT_EQ(getStreamField(dataMessage, "errorCount"), "170"); - EXPECT_EQ(getStreamField(dataMessage, "requestCount"), "350"); - EXPECT_EQ(getStreamField(dataMessage, "rollingCountSemaphoreRejected"), "80"); - EXPECT_EQ(getStreamField(dataMessage, "rollingCountSuccess"), "70"); - EXPECT_EQ(getStreamField(dataMessage, "rollingCountTimeout"), "30"); - EXPECT_EQ(getStreamField(dataMessage, "propertyValue_queueSizeRejectionThreshold"), - std::to_string(expected_queue_size)); - EXPECT_EQ(getStreamField(dataMessage, "reportingHosts"), std::to_string(expectedReportingHosts)); - - // check reset of window - ss.str(""); - hystrix.resetRollingWindow(); - hystrix.getClusterStats(ss, cluster_name, expected_queue_size, expectedReportingHosts); - dataMessage = ss.str(); - - EXPECT_EQ(getStreamField(dataMessage, "errorPercentage"), "0"); - EXPECT_EQ(getStreamField(dataMessage, "errorCount"), "0"); - EXPECT_EQ(getStreamField(dataMessage, "requestCount"), "0"); - EXPECT_EQ(getStreamField(dataMessage, "rollingCountSemaphoreRejected"), "0"); - EXPECT_EQ(getStreamField(dataMessage, "rollingCountSuccess"), "0"); - EXPECT_EQ(getStreamField(dataMessage, "rollingCountTimeout"), "0"); +TEST_F(HystrixSinkTest, Disconnect) { + InSequence s; + + Buffer::OwnedImpl buffer = createClusterAndCallbacks(); + + // flush with no connection + NiceMock success_counter; + success_counter.name_ = "cluster.test_cluster.upstream_rq_2xx"; + ON_CALL(success_counter, value()).WillByDefault(Return(1234)); + + sink_->beginFlush(); + sink_->flushCounter(success_counter, 1); + sink_->endFlush(); + EXPECT_EQ(buffer.length(), 0); + + // register callback to sink + sink_->registerConnection(&callbacks_); + sink_->beginFlush(); + sink_->flushCounter(success_counter, 1); + sink_->endFlush(); + std::string data_message = TestUtility::bufferToString(buffer); + EXPECT_EQ(getStreamField(data_message, "rollingCountSuccess"), "0"); + EXPECT_NE(buffer.length(), 0); + + // connection disconnect + buffer.drain(buffer.length()); + sink_->unregisterConnection(); + sink_->beginFlush(); + sink_->flushCounter(success_counter, 1); + sink_->endFlush(); + EXPECT_EQ(buffer.length(), 0); } +} // namespace HystrixNameSpace + } // namespace Stats } // namespace Envoy diff --git a/test/server/config/stats/BUILD b/test/server/config/stats/BUILD index 2ae08d70a799b..a82e5c94f293d 100644 --- a/test/server/config/stats/BUILD +++ b/test/server/config/stats/BUILD @@ -15,8 +15,10 @@ envoy_cc_test( "//include/envoy/registry", "//source/common/config:well_known_names", "//source/common/protobuf:utility_lib", + "//source/common/stats:hystrix_lib", "//source/common/stats:statsd_lib", "//source/server/config/stats:dog_statsd_lib", + "//source/server/config/stats:hystrix_lib", "//source/server/config/stats:statsd_lib", "//test/mocks/server:server_mocks", "//test/test_common:environment_lib", diff --git a/test/server/config/stats/config_test.cc b/test/server/config/stats/config_test.cc index 42dc0170a9141..69922140ef8f2 100644 --- a/test/server/config/stats/config_test.cc +++ b/test/server/config/stats/config_test.cc @@ -5,9 +5,11 @@ #include "common/config/well_known_names.h" #include "common/protobuf/utility.h" +#include "common/stats/hystrix.h" #include "common/stats/statsd.h" #include "server/config/stats/dog_statsd.h" +#include "server/config/stats/hystrix.h" #include "server/config/stats/statsd.h" #include "test/mocks/server/mocks.h" @@ -117,6 +119,23 @@ TEST(DogStatsdConfigTest, ValidateFail) { ProtoValidationException); } +TEST(StatsConfigTest, ValidHystrixSink) { + const std::string name = Config::StatsSinkNames::get().HYSTRIX; + + envoy::config::metrics::v2::HystrixSink sink_config; + + StatsSinkFactory* factory = Registry::FactoryRegistry::getFactory(name); + ASSERT_NE(factory, nullptr); + + ProtobufTypes::MessagePtr message = factory->createEmptyConfigProto(); + MessageUtil::jsonConvert(sink_config, *message); + + NiceMock server; + Stats::SinkPtr sink = factory->createStatsSink(*message, server); + EXPECT_NE(sink, nullptr); + EXPECT_NE(dynamic_cast(sink.get()), nullptr); +} + } // namespace Configuration } // namespace Server } // namespace Envoy From d3de2de4486522b94b9a47066d09e78db810c2ef Mon Sep 17 00:00:00 2001 From: trabetti Date: Mon, 9 Apr 2018 15:16:37 +0300 Subject: [PATCH 16/25] second round of tests Signed-off-by: trabetti --- include/envoy/server/instance.h | 2 +- source/server/config_validation/server.h | 2 +- source/server/http/admin.cc | 20 +++++++++++++++----- source/server/server.cc | 5 +++-- source/server/server.h | 2 +- test/common/stats/hystrix_test.cc | 21 +++++++++++++++------ test/integration/integration_admin_test.cc | 7 +++++++ test/mocks/server/mocks.h | 2 +- test/server/server_test.cc | 8 ++++++++ 9 files changed, 52 insertions(+), 17 deletions(-) diff --git a/include/envoy/server/instance.h b/include/envoy/server/instance.h index 219d78dcdbd52..497d9da5a549a 100644 --- a/include/envoy/server/instance.h +++ b/include/envoy/server/instance.h @@ -189,7 +189,7 @@ class Instance { /** * @ Registers a callbacks connection to Hystrix sink. */ - virtual void registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) PURE; + virtual bool registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) PURE; /** * @ Unregisters a callbacks connection to Hystrix sink. diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index 55cc43c8486f2..7cb19280d9c3d 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -89,7 +89,7 @@ class ValidationInstance : Logger::Loggable, const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } std::chrono::milliseconds statsFlushInterval() override { return config_->statsFlushInterval(); } - void registerToHystrixSink(Http::StreamDecoderFilterCallbacks*) override { NOT_IMPLEMENTED; } + bool registerToHystrixSink(Http::StreamDecoderFilterCallbacks*) override { NOT_IMPLEMENTED; } void unregisterHystrixSink() override { NOT_IMPLEMENTED; } // Server::ListenerComponentFactory diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 50180faec6fd7..9d913189c419d 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -593,11 +593,21 @@ Http::Code AdminImpl::handlerHystrixEventStream(const std::string&, Stats::HystrixHandlerInfoImpl& hystrix_handler_info = dynamic_cast(handler_info); - server_.registerToHystrixSink(hystrix_handler_info.callbacks_); - - ENVOY_LOG(debug, "start sending data to hystrix dashboard on port {}", - hystrix_handler_info.callbacks_->connection()->localAddress()->asString()); - return Http::Code::OK; + // TODO (@trabetti) : what should we do when a sink not present? + // is this error message to user enough? + // does it make sense to response with 503? + if (server_.registerToHystrixSink(hystrix_handler_info.callbacks_)) { + + ENVOY_LOG(debug, "start sending data to hystrix dashboard on port {}", + hystrix_handler_info.callbacks_->connection()->localAddress()->asString()); + return Http::Code::OK; + } else { + ENVOY_LOG( + warn, + "Hystrix sink is not set up in config file, could not establish connection on port {}", + hystrix_handler_info.callbacks_->connection()->localAddress()->asString()); + return Http::Code::ServiceUnavailable; + } } void AdminFilter::onComplete() { diff --git a/source/server/server.cc b/source/server/server.cc index 86ff831c394ff..eb440078ff5bd 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -143,16 +143,17 @@ void InstanceImpl::flushStats() { stat_flush_timer_->enableTimer(config_->statsFlushInterval()); } -void InstanceImpl::registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) { +bool InstanceImpl::registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) { for (const auto& sink : config_->statsSinks()) { // TODO: is there a better way to find the hystrix sink? Stats::HystrixNameSpace::HystrixSink* hystrix_sink = dynamic_cast(sink.get()); if (hystrix_sink != nullptr) { hystrix_sink->registerConnection(callbacks); - return; + return true; } } + return false; } void InstanceImpl::unregisterHystrixSink() { diff --git a/source/server/server.h b/source/server/server.h index 3c1ea95507b32..86b22ccfd6f44 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -165,7 +165,7 @@ class InstanceImpl : Logger::Loggable, public Instance { const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } std::chrono::milliseconds statsFlushInterval() override { return config_->statsFlushInterval(); } - void registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) override; + bool registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) override; void unregisterHystrixSink() override; private: diff --git a/test/common/stats/hystrix_test.cc b/test/common/stats/hystrix_test.cc index acd705f836355..d12feb622e9f8 100644 --- a/test/common/stats/hystrix_test.cc +++ b/test/common/stats/hystrix_test.cc @@ -28,11 +28,16 @@ class HystrixSinkTest : public testing::Test { public: HystrixSinkTest() { sink_.reset(new HystrixSink(server_)); } - std::string getStreamField(std::string dataMessage, std::string key) { - std::string actual = dataMessage.substr(dataMessage.find(key)); - actual = actual.substr(actual.find(" ") + 1); - std::size_t length = actual.find(","); - actual = actual.substr(0, length); + absl::string_view getStreamField(absl::string_view dataMessage, absl::string_view key) { + absl::string_view::size_type key_pos = dataMessage.find(key); + EXPECT_NE(absl::string_view::npos, key_pos); + absl::string_view trimDataBeforeKey = dataMessage.substr(key_pos); + key_pos = trimDataBeforeKey.find(" "); + EXPECT_NE(absl::string_view::npos, key_pos); + absl::string_view trimDataAfterValue = trimDataBeforeKey.substr(key_pos + 1); + key_pos = trimDataAfterValue.find(","); + EXPECT_NE(absl::string_view::npos, key_pos); + absl::string_view actual = trimDataAfterValue.substr(0, key_pos); return actual; } @@ -114,7 +119,11 @@ TEST_F(HystrixSinkTest, BasicFlow) { // std::cout << "BasicFlow: buffer = " << TestUtility::bufferToString(buffer) << std::endl; } // std::string window = sink_->getStats().printRollingWindow(); // just to cover it + // std::cout << "printRollingWindow: " << sink_->getStats().printRollingWindow() << std::endl; // TODO (@trabetti) : add something to check the data? + absl::string_view::size_type pos = + sink_->getStats().printRollingWindow().find("cluster.test_cluster.total"); + EXPECT_NE(absl::string_view::npos, pos); std::string data_message = TestUtility::bufferToString(buffer); @@ -167,7 +176,7 @@ TEST_F(HystrixSinkTest, Disconnect) { EXPECT_EQ(getStreamField(data_message, "rollingCountSuccess"), "0"); EXPECT_NE(buffer.length(), 0); - // connection disconnect + // disconnect buffer.drain(buffer.length()); sink_->unregisterConnection(); sink_->beginFlush(); diff --git a/test/integration/integration_admin_test.cc b/test/integration/integration_admin_test.cc index e343eb579f41c..196b7aebb978a 100644 --- a/test/integration/integration_admin_test.cc +++ b/test/integration/integration_admin_test.cc @@ -242,6 +242,13 @@ TEST_P(IntegrationAdminTest, Admin) { EXPECT_EQ(listener_it->get().socket().localAddress()->asString(), (*listener_info_it)->asString()); } + + // TODO (@trabetti) : how to test the endpoint? we don't have response->complete(). + // response = IntegrationUtil::makeSingleRequest(lookupPort("http"), "GET", + // "/hystrix_event_stream", "", + // downstreamProtocol(), version_); + // //EXPECT_TRUE(response->complete()); + // EXPECT_STREQ("503", response->headers().Status()->value().c_str()); } // Successful call to startProfiler requires tcmalloc. diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index d96ab8d842ba3..7035edb3685b1 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -271,7 +271,7 @@ class MockInstance : public Instance { MOCK_METHOD0(threadLocal, ThreadLocal::Instance&()); MOCK_METHOD0(localInfo, const LocalInfo::LocalInfo&()); MOCK_METHOD0(statsFlushInterval, std::chrono::milliseconds()); - MOCK_METHOD1(registerToHystrixSink, void(Http::StreamDecoderFilterCallbacks* callbacks)); + MOCK_METHOD1(registerToHystrixSink, bool(Http::StreamDecoderFilterCallbacks* callbacks)); MOCK_METHOD0(unregisterHystrixSink, void()); testing::NiceMock thread_local_; diff --git a/test/server/server_test.cc b/test/server/server_test.cc index eb66c382d837f..9f09377385c29 100644 --- a/test/server/server_test.cc +++ b/test/server/server_test.cc @@ -133,6 +133,14 @@ TEST_P(ServerInstanceImplTest, V2ConfigOnly) { } } +// TODO (@trabetti) : test registerToHystrixSink/unregisterHystrixSink +// TEST_P(ServerInstanceImplTest, registerSink) { +// InSequence s; +// +// server_->registerToHystrixSink(&callbacks); +// server_->unregisterHystrixSink(); +//} + TEST_P(ServerInstanceImplTest, V1ConfigFallback) { options_.service_cluster_name_ = "some_cluster_name"; options_.service_node_name_ = "some_node_name"; From 6932c6d018147bf29e6a5ec55638c29fb742fc38 Mon Sep 17 00:00:00 2001 From: trabetti Date: Wed, 11 Apr 2018 14:09:00 +0300 Subject: [PATCH 17/25] merge with upstream - sink restructure Signed-off-by: trabetti --- bazel/repository_locations.bzl | 2 +- ci/run_envoy_docker.sh | 3 +- source/common/stats/BUILD | 13 -- source/extensions/extensions_build_config.bzl | 1 + .../stat_sinks/common/hystrix/BUILD | 22 ++ .../stat_sinks/common/hystrix}/hystrix.cc | 16 +- .../stat_sinks/common/hystrix}/hystrix.h | 19 +- source/extensions/stat_sinks/hystrix/BUILD | 25 ++ .../stat_sinks/hystrix/config.cc} | 24 +- .../stat_sinks/hystrix/config.h} | 19 +- source/server/config/stats/BUILD | 66 ------ source/server/http/BUILD | 2 +- source/server/http/admin.cc | 221 +++++------------- source/server/http/admin.h | 101 +++----- source/server/server.cc | 8 +- test/common/router/rds_impl_test.cc | 145 ------------ test/common/stats/BUILD | 13 -- .../stats_sinks/common/hystrix/BUILD | 22 ++ .../common/hystrix}/hystrix_test.cc | 36 +-- test/extensions/stats_sinks/dog_statsd/BUILD | 8 - test/extensions/stats_sinks/hystrix/BUILD | 28 +++ .../stats_sinks/hystrix/config_test.cc | 49 ++++ test/server/config/stats/config_test.cc | 141 ----------- test/server/http/admin_test.cc | 155 ++---------- 24 files changed, 321 insertions(+), 818 deletions(-) create mode 100644 source/extensions/stat_sinks/common/hystrix/BUILD rename source/{common/stats => extensions/stat_sinks/common/hystrix}/hystrix.cc (97%) rename source/{common/stats => extensions/stat_sinks/common/hystrix}/hystrix.h (91%) create mode 100644 source/extensions/stat_sinks/hystrix/BUILD rename source/{server/config/stats/hystrix.cc => extensions/stat_sinks/hystrix/config.cc} (61%) rename source/{server/config/stats/hystrix.h => extensions/stat_sinks/hystrix/config.h} (57%) delete mode 100644 source/server/config/stats/BUILD create mode 100644 test/extensions/stats_sinks/common/hystrix/BUILD rename test/{common/stats => extensions/stats_sinks/common/hystrix}/hystrix_test.cc (89%) create mode 100644 test/extensions/stats_sinks/hystrix/BUILD create mode 100644 test/extensions/stats_sinks/hystrix/config_test.cc delete mode 100644 test/server/config/stats/config_test.cc diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 1cd6f9df363c8..aff9711767c28 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -76,7 +76,7 @@ REPOSITORY_LOCATIONS = dict( urls = ["https://github.com/google/protobuf/archive/v3.5.0.tar.gz"], ), envoy_api = dict( - commit = "c1a8d3fa4066ff362a2e8c62feb6544c724bdf0a", + commit = "00b4a77290f4790febc20811d246ca6083740ef1", remote = "https://github.com/trabetti/data-plane-api", ), grpc_httpjson_transcoding = dict( diff --git a/ci/run_envoy_docker.sh b/ci/run_envoy_docker.sh index c2da28e3abdfa..d52172671f73b 100755 --- a/ci/run_envoy_docker.sh +++ b/ci/run_envoy_docker.sh @@ -19,8 +19,7 @@ USER_GROUP=root mkdir -p "${ENVOY_DOCKER_BUILD_DIR}" # Since we specify an explicit hash, docker-run will pull from the remote repo if missing. docker run --rm -t -i -e http_proxy=${http_proxy} -e https_proxy=${https_proxy} \ - -u "${USER}":"${USER_GROUP}" -v "${ENVOY_DOCKER_BUILD_DIR}":/build -v "/home/talis/envoy_fork/data-plane-api":/opt/data-plan-api \ + -u "${USER}":"${USER_GROUP}" -v "${ENVOY_DOCKER_BUILD_DIR}":/build \ -v "$PWD":/source -e NUM_CPUS --cap-add SYS_PTRACE "${IMAGE_NAME}":"${IMAGE_ID}" \ /bin/bash -lc "groupadd --gid $(id -g) -f envoygroup && useradd -o --uid $(id -u) --gid $(id -g) \ --no-create-home --home-dir /source envoybuild && su envoybuild -c \"cd source && $*\"" - diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index 7d2e17ab91eee..1d6698e72c958 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -36,16 +36,3 @@ envoy_cc_library( "//include/envoy/thread_local:thread_local_interface", ], ) - -envoy_cc_library( - name = "hystrix_lib", - srcs = ["hystrix.cc"], - hdrs = ["hystrix.h"], - deps = [ - ":stats_lib", - "//include/envoy/server:admin_interface", - "//include/envoy/server:instance_interface", - "//source/common/buffer:buffer_lib", - "//source/common/common:logger_lib", - ], -) diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index ae9832a3030fc..e068f648a233a 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -48,6 +48,7 @@ EXTENSIONS = { "envoy.stat_sinks.dog_statsd": "//source/extensions/stat_sinks/dog_statsd:config", "envoy.stat_sinks.metrics_service": "//source/extensions/stat_sinks/metrics_service:config", "envoy.stat_sinks.statsd": "//source/extensions/stat_sinks/statsd:config", + "envoy.stat_sinks.hystrix": "//source/extensions/stat_sinks/hystrix:config", # # Tracers diff --git a/source/extensions/stat_sinks/common/hystrix/BUILD b/source/extensions/stat_sinks/common/hystrix/BUILD new file mode 100644 index 0000000000000..2a9fd7b87f1ac --- /dev/null +++ b/source/extensions/stat_sinks/common/hystrix/BUILD @@ -0,0 +1,22 @@ +licenses(["notice"]) # Apache 2 + +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +envoy_package() + +envoy_cc_library( + name = "hystrix_lib", + srcs = ["hystrix.cc"], + hdrs = ["hystrix.h"], + deps = [ + "//include/envoy/server:admin_interface", + "//include/envoy/server:instance_interface", + "//include/envoy/stats:stats_interface", + "//source/common/buffer:buffer_lib", + "//source/common/common:logger_lib", + ], +) diff --git a/source/common/stats/hystrix.cc b/source/extensions/stat_sinks/common/hystrix/hystrix.cc similarity index 97% rename from source/common/stats/hystrix.cc rename to source/extensions/stat_sinks/common/hystrix/hystrix.cc index 9a292f8c458d2..8900e0e3fb70d 100644 --- a/source/common/stats/hystrix.cc +++ b/source/extensions/stat_sinks/common/hystrix/hystrix.cc @@ -1,4 +1,4 @@ -#include "common/stats/hystrix.h" +#include "extensions/stat_sinks/common/hystrix/hystrix.h" #include #include @@ -11,7 +11,9 @@ #include "absl/strings/str_cat.h" namespace Envoy { -namespace Stats { +namespace Extensions { +namespace StatSinks { +namespace Common { const uint64_t Hystrix::DEFAULT_NUM_OF_BUCKETS; @@ -260,12 +262,11 @@ absl::string_view Hystrix::printRollingWindow() const { } namespace HystrixNameSpace { -HystrixSink::HystrixSink(Server::Instance& server) - : stats_(new Stats::Hystrix()), server_(&server){}; +HystrixSink::HystrixSink(Server::Instance& server) : stats_(new Hystrix()), server_(&server){}; void HystrixSink::beginFlush() { current_stat_values_.clear(); } -void HystrixSink::flushCounter(const Counter& counter, uint64_t) { +void HystrixSink::flushCounter(const Stats::Counter& counter, uint64_t) { if (callbacks_ == nullptr) { return; } @@ -314,6 +315,7 @@ void HystrixSink::registerConnection(Http::StreamDecoderFilterCallbacks* callbac void HystrixSink::unregisterConnection() { callbacks_ = nullptr; } } // namespace HystrixNameSpace - -} // namespace Stats +} // namespace Common +} // namespace StatSinks +} // namespace Extensions } // namespace Envoy diff --git a/source/common/stats/hystrix.h b/source/extensions/stat_sinks/common/hystrix/hystrix.h similarity index 91% rename from source/common/stats/hystrix.h rename to source/extensions/stat_sinks/common/hystrix/hystrix.h index 20b6ec306d6ba..8483c12414f59 100644 --- a/source/common/stats/hystrix.h +++ b/source/extensions/stat_sinks/common/hystrix/hystrix.h @@ -7,7 +7,9 @@ #include "envoy/stats/stats.h" namespace Envoy { -namespace Stats { +namespace Extensions { +namespace StatSinks { +namespace Common { typedef std::vector RollingStats; typedef std::map RollingStatsMap; @@ -118,14 +120,14 @@ class HystrixHandlerInfoImpl : public Server::HandlerInfo { namespace HystrixNameSpace { -class HystrixSink : public Sink { +class HystrixSink : public Stats::Sink { public: HystrixSink(Server::Instance& server); void beginFlush(); - void flushCounter(const Counter& counter, uint64_t delta); - void flushGauge(const Gauge&, uint64_t){}; + void flushCounter(const Stats::Counter& counter, uint64_t delta); + void flushGauge(const Stats::Gauge&, uint64_t){}; void endFlush(); - void onHistogramComplete(const Histogram& histogram, uint64_t value) { + void onHistogramComplete(const Stats::Histogram& histogram, uint64_t value) { std::cout << "histogram complete: " << histogram.name() << ", value: " << std::to_string(value) << std::endl; }; @@ -141,7 +143,7 @@ class HystrixSink : public Sink { Hystrix& getStats() { return *stats_; } private: - Stats::HystrixPtr stats_; + HystrixPtr stats_; Http::StreamDecoderFilterCallbacks* callbacks_{}; Server::Instance* server_; std::map current_stat_values_; @@ -150,6 +152,7 @@ class HystrixSink : public Sink { typedef std::unique_ptr HystrixSinkPtr; } // namespace HystrixNameSpace - -} // namespace Stats +} // namespace Common +} // namespace StatSinks +} // namespace Extensions } // namespace Envoy diff --git a/source/extensions/stat_sinks/hystrix/BUILD b/source/extensions/stat_sinks/hystrix/BUILD new file mode 100644 index 0000000000000..7e2a561f167c8 --- /dev/null +++ b/source/extensions/stat_sinks/hystrix/BUILD @@ -0,0 +1,25 @@ +licenses(["notice"]) # Apache 2 +# Stats sink for the basic version of the hystrix protocol (https://github.com/b/hystrix_spec). + +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +envoy_package() + +envoy_cc_library( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + deps = [ + "//include/envoy/registry", + "//source/common/config:well_known_names", + "//source/common/network:address_lib", + "//source/common/network:resolver_lib", + "//source/extensions/stat_sinks/common/hystrix:hystrix_lib", + "//source/server:configuration_lib", + "@envoy_api//envoy/config/metrics/v2:stats_cc", + ], +) diff --git a/source/server/config/stats/hystrix.cc b/source/extensions/stat_sinks/hystrix/config.cc similarity index 61% rename from source/server/config/stats/hystrix.cc rename to source/extensions/stat_sinks/hystrix/config.cc index 7e516184bc8e3..8fadb0c3a5321 100644 --- a/source/server/config/stats/hystrix.cc +++ b/source/extensions/stat_sinks/hystrix/config.cc @@ -1,4 +1,4 @@ -#include "server/config/stats/hystrix.h" +#include "extensions/stat_sinks/hystrix/config.h" #include @@ -8,24 +8,22 @@ #include "common/config/well_known_names.h" #include "common/network/resolver_impl.h" -#include "common/stats/hystrix.h" + +#include "extensions/stat_sinks/common/hystrix/hystrix.h" namespace Envoy { -namespace Server { -namespace Configuration { +namespace Extensions { +namespace StatSinks { +namespace HystrixNameSpace { Stats::SinkPtr HystrixSinkFactory::createStatsSink(const Protobuf::Message&, Server::Instance& server) { - - return Stats::SinkPtr(new Stats::HystrixNameSpace::HystrixSink(server)); + return std::make_unique(server); } ProtobufTypes::MessagePtr HystrixSinkFactory::createEmptyConfigProto() { - // TDOD (@trabetti): until I add hystrix to data_plane_api return std::unique_ptr( new envoy::config::metrics::v2::HystrixSink()); - // return std::unique_ptr( - // ica new envoy::config::metrics::v2::StatsdSink()); } std::string HystrixSinkFactory::name() { return Config::StatsSinkNames::get().HYSTRIX; } @@ -33,8 +31,10 @@ std::string HystrixSinkFactory::name() { return Config::StatsSinkNames::get().HY /** * Static registration for the statsd sink factory. @see RegisterFactory. */ -static Registry::RegisterFactory register_; +static Registry::RegisterFactory + register_; -} // namespace Configuration -} // namespace Server +} // namespace HystrixNameSpace +} // namespace StatSinks +} // namespace Extensions } // namespace Envoy diff --git a/source/server/config/stats/hystrix.h b/source/extensions/stat_sinks/hystrix/config.h similarity index 57% rename from source/server/config/stats/hystrix.h rename to source/extensions/stat_sinks/hystrix/config.h index 4956e9ddb05be..0df98e6c45234 100644 --- a/source/server/config/stats/hystrix.h +++ b/source/extensions/stat_sinks/hystrix/config.h @@ -7,22 +7,23 @@ #include "server/configuration_impl.h" namespace Envoy { -namespace Server { -namespace Configuration { +namespace Extensions { +namespace StatSinks { +namespace HystrixNameSpace { -/** - * Config registration for the hystrix sink. @see StatsSinkFactory. - */ -class HystrixSinkFactory : Logger::Loggable, public StatsSinkFactory { +class HystrixSinkFactory : Logger::Loggable, + public Server::Configuration::StatsSinkFactory { public: // StatsSinkFactory - Stats::SinkPtr createStatsSink(const Protobuf::Message& config, Instance& server) override; + Stats::SinkPtr createStatsSink(const Protobuf::Message& config, + Server::Instance& server) override; ProtobufTypes::MessagePtr createEmptyConfigProto() override; std::string name() override; }; -} // namespace Configuration -} // namespace Server +} // namespace HystrixNameSpace +} // namespace StatSinks +} // namespace Extensions } // namespace Envoy diff --git a/source/server/config/stats/BUILD b/source/server/config/stats/BUILD deleted file mode 100644 index 15eca77212550..0000000000000 --- a/source/server/config/stats/BUILD +++ /dev/null @@ -1,66 +0,0 @@ -licenses(["notice"]) # Apache 2 - -load( - "//bazel:envoy_build_system.bzl", - "envoy_cc_library", - "envoy_package", -) - -envoy_package() - -envoy_cc_library( - name = "statsd_lib", - srcs = ["statsd.cc"], - hdrs = ["statsd.h"], - deps = [ - "//include/envoy/registry", - "//source/common/config:well_known_names", - "//source/common/network:address_lib", - "//source/common/network:resolver_lib", - "//source/common/stats:statsd_lib", - "//source/server:configuration_lib", - "@envoy_api//envoy/config/metrics/v2:stats_cc", - ], -) - -envoy_cc_library( - name = "dog_statsd_lib", - srcs = ["dog_statsd.cc"], - hdrs = ["dog_statsd.h"], - deps = [ - "//include/envoy/registry", - "//source/common/config:well_known_names", - "//source/common/network:address_lib", - "//source/common/network:resolver_lib", - "//source/common/stats:statsd_lib", - "//source/server:configuration_lib", - "@envoy_api//envoy/config/metrics/v2:stats_cc", - ], -) - -envoy_cc_library( - name = "metrics_service_lib", - srcs = ["metrics_service.cc"], - hdrs = ["metrics_service.h"], - deps = [ - "//include/envoy/registry", - "//source/common/config:well_known_names", - "//source/common/stats:metrics_service_grpc_lib", - "//source/server:configuration_lib", - "@envoy_api//envoy/config/metrics/v2:stats_cc", - "@envoy_api//envoy/service/metrics/v2:metrics_service_cc", - ], -) - -envoy_cc_library( - name = "hystrix_lib", - srcs = ["hystrix.cc"], - hdrs = ["hystrix.h"], - deps = [ - "//include/envoy/registry", - "//source/common/config:well_known_names", - "//source/common/stats:hystrix_lib", - "//source/server:configuration_lib", - "@envoy_api//envoy/config/metrics/v2:stats_cc", - ], -) diff --git a/source/server/http/BUILD b/source/server/http/BUILD index dbc30ebd57ae5..5da20a05768a6 100644 --- a/source/server/http/BUILD +++ b/source/server/http/BUILD @@ -49,9 +49,9 @@ envoy_cc_library( "//source/common/network:raw_buffer_socket_lib", "//source/common/profiler:profiler_lib", "//source/common/router:config_lib", - "//source/common/stats:hystrix_lib", "//source/common/upstream:host_utility_lib", "//source/extensions/access_loggers/file:file_access_log_lib", + "//source/extensions/stat_sinks/common/hystrix:hystrix_lib", "@envoy_api//envoy/admin/v2:config_dump_cc", ], ) diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 681955e618b06..2f3e79e5e56be 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -221,13 +221,8 @@ void AdminImpl::addCircuitSettings(const std::string& cluster_name, const std::s resource_manager.retries().max())); } -<<<<<<< HEAD -Http::Code AdminImpl::handlerClusters(const std::string&, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { -======= Http::Code AdminImpl::handlerClusters(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response) { ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + Buffer::Instance& response, HandlerInfo&) { response.add(fmt::format("version_info::{}\n", server_.clusterManager().versionInfo())); for (auto& cluster : server_.clusterManager().clusters()) { @@ -283,13 +278,9 @@ Http::Code AdminImpl::handlerClusters(absl::string_view, Http::HeaderMap&, return Http::Code::OK; } -<<<<<<< HEAD -Http::Code AdminImpl::handlerCpuProfiler(const std::string& url, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { -======= // TODO(jsedgwick) Use query params to list available dumps, selectively dump, etc Http::Code AdminImpl::handlerConfigDump(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response) const { + Buffer::Instance& response, HandlerInfo&) const { envoy::admin::v2::ConfigDump dump; auto& config_dump_map = *(dump.mutable_configs()); for (const auto& key_callback_pair : config_tracker_.getCallbacksMap()) { @@ -305,8 +296,7 @@ Http::Code AdminImpl::handlerConfigDump(absl::string_view, Http::HeaderMap&, } Http::Code AdminImpl::handlerCpuProfiler(absl::string_view url, Http::HeaderMap&, - Buffer::Instance& response) { ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + Buffer::Instance& response, HandlerInfo&) { Http::Utility::QueryParams query_params = Http::Utility::parseQueryString(url); if (query_params.size() != 1 || query_params.begin()->first != "enable" || (query_params.begin()->second != "y" && query_params.begin()->second != "n")) { @@ -329,48 +319,28 @@ Http::Code AdminImpl::handlerCpuProfiler(absl::string_view url, Http::HeaderMap& return Http::Code::OK; } -<<<<<<< HEAD -Http::Code AdminImpl::handlerHealthcheckFail(const std::string&, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { -======= Http::Code AdminImpl::handlerHealthcheckFail(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response) { ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + Buffer::Instance& response, HandlerInfo&) { server_.failHealthcheck(true); response.add("OK\n"); return Http::Code::OK; } -<<<<<<< HEAD -Http::Code AdminImpl::handlerHealthcheckOk(const std::string&, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { -======= Http::Code AdminImpl::handlerHealthcheckOk(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response) { ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + Buffer::Instance& response, HandlerInfo&) { server_.failHealthcheck(false); response.add("OK\n"); return Http::Code::OK; } -<<<<<<< HEAD -Http::Code AdminImpl::handlerHotRestartVersion(const std::string&, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { -======= Http::Code AdminImpl::handlerHotRestartVersion(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response) { ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + Buffer::Instance& response, HandlerInfo&) { response.add(server_.hotRestart().version()); return Http::Code::OK; } -<<<<<<< HEAD -Http::Code AdminImpl::handlerLogging(const std::string& url, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { -======= Http::Code AdminImpl::handlerLogging(absl::string_view url, Http::HeaderMap&, - Buffer::Instance& response) { ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + Buffer::Instance& response, HandlerInfo&) { Http::Utility::QueryParams query_params = Http::Utility::parseQueryString(url); Http::Code rc = Http::Code::OK; @@ -395,13 +365,8 @@ Http::Code AdminImpl::handlerLogging(absl::string_view url, Http::HeaderMap&, return rc; } -<<<<<<< HEAD -Http::Code AdminImpl::handlerResetCounters(const std::string&, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { -======= Http::Code AdminImpl::handlerResetCounters(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response) { ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + Buffer::Instance& response, HandlerInfo&) { for (const Stats::CounterSharedPtr& counter : server_.stats().counters()) { counter->reset(); } @@ -410,13 +375,8 @@ Http::Code AdminImpl::handlerResetCounters(absl::string_view, Http::HeaderMap&, return Http::Code::OK; } -<<<<<<< HEAD -Http::Code AdminImpl::handlerServerInfo(const std::string&, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { -======= Http::Code AdminImpl::handlerServerInfo(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response) { ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + Buffer::Instance& response, HandlerInfo&) { time_t current_time = time(nullptr); response.add(fmt::format("envoy {} {} {} {} {}\n", VersionInfo::version(), server_.healthCheckFailed() ? "draining" : "live", @@ -426,13 +386,8 @@ Http::Code AdminImpl::handlerServerInfo(absl::string_view, Http::HeaderMap&, return Http::Code::OK; } -<<<<<<< HEAD -Http::Code AdminImpl::handlerStats(const std::string& url, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&) { -======= Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& response_headers, - Buffer::Instance& response) { ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + Buffer::Instance& response, HandlerInfo& handler_info) { // We currently don't support timers locally (only via statsd) so just group all the counters // and gauges together, alpha sort them, and spit them out. Http::Code rc = Http::Code::OK; @@ -459,7 +414,7 @@ Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& respo Http::Headers::get().ContentTypeValues.Json); response.add(AdminImpl::statsAsJson(all_stats)); } else if (format_key == "format" && format_value == "prometheus") { - return handlerPrometheusStats(url, response_headers, response); + return handlerPrometheusStats(url, response_headers, response, handler_info); } else { response.add("usage: /stats?format=json \n"); response.add("\n"); @@ -470,7 +425,7 @@ Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& respo } Http::Code AdminImpl::handlerPrometheusStats(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response) { + Buffer::Instance& response, HandlerInfo&) { PrometheusStatsFormatter::statsAsPrometheus(server_.stats().counters(), server_.stats().gauges(), response); return Http::Code::OK; @@ -548,25 +503,15 @@ std::string AdminImpl::statsAsJson(const std::map& all_st return strbuf.GetString(); } -<<<<<<< HEAD -Http::Code AdminImpl::handlerQuitQuitQuit(const std::string&, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { -======= Http::Code AdminImpl::handlerQuitQuitQuit(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response) { ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + Buffer::Instance& response, HandlerInfo&) { server_.shutdown(); response.add("OK\n"); return Http::Code::OK; } -<<<<<<< HEAD -Http::Code AdminImpl::handlerListenerInfo(const std::string&, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&) { -======= Http::Code AdminImpl::handlerListenerInfo(absl::string_view, Http::HeaderMap& response_headers, - Buffer::Instance& response) { ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + Buffer::Instance& response, HandlerInfo&) { response_headers.insertContentType().value().setReference( Http::Headers::get().ContentTypeValues.Json); std::list listeners; @@ -577,38 +522,27 @@ Http::Code AdminImpl::handlerListenerInfo(absl::string_view, Http::HeaderMap& re return Http::Code::OK; } -<<<<<<< HEAD -Http::Code AdminImpl::handlerCerts(const std::string&, Http::HeaderMap&, Buffer::Instance& response, - HandlerInfo&){ -======= -Http::Code AdminImpl::handlerCerts(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response) { ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 - // This set is used to track distinct certificates. We may have multiple listeners, upstreams, - // etc using the same cert. - std::unordered_set context_info_set; -std::string context_format = "{{\n\t\"ca_cert\": \"{}\",\n\t\"cert_chain\": \"{}\"\n}}\n"; -server_.sslContextManager().iterateContexts([&](const Ssl::Context& context) -> void { - context_info_set.insert(fmt::format(context_format, context.getCaCertInformation(), - context.getCertChainInformation())); -}); - -std::string cert_result_string; -for (const std::string& context_info : context_info_set) { - cert_result_string += context_info; +Http::Code AdminImpl::handlerCerts(absl::string_view, Http::HeaderMap&, Buffer::Instance& response, + HandlerInfo&) { + // This set is used to track distinct certificates. We may have multiple listeners, upstreams, etc + // using the same cert. + std::unordered_set context_info_set; + std::string context_format = "{{\n\t\"ca_cert\": \"{}\",\n\t\"cert_chain\": \"{}\"\n}}\n"; + server_.sslContextManager().iterateContexts([&](const Ssl::Context& context) -> void { + context_info_set.insert(fmt::format(context_format, context.getCaCertInformation(), + context.getCertChainInformation())); + }); + + std::string cert_result_string; + for (const std::string& context_info : context_info_set) { + cert_result_string += context_info; + } + response.add(cert_result_string); + return Http::Code::OK; } -response.add(cert_result_string); -return Http::Code::OK; -} // namespace Server -<<<<<<< HEAD -Http::Code AdminImpl::handlerRuntime(const std::string& url, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&) { - Http::Code rc = Http::Code::OK; -======= Http::Code AdminImpl::handlerRuntime(absl::string_view url, Http::HeaderMap& response_headers, - Buffer::Instance& response) { ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + Buffer::Instance& response, HandlerInfo&) { const Http::Utility::QueryParams params = Http::Utility::parseQueryString(url); response_headers.insertContentType().value().setReference( Http::Headers::get().ContentTypeValues.Json); @@ -636,10 +570,6 @@ Http::Code AdminImpl::handlerRuntime(absl::string_view url, Http::HeaderMap& res } } } -<<<<<<< HEAD - return rc; -} -======= document.AddMember("layers", std::move(layer_names), allocator); for (const auto& layer : layers) { @@ -654,22 +584,21 @@ Http::Code AdminImpl::handlerRuntime(absl::string_view url, Http::HeaderMap& res kv.second["layer_values"].PushBack(entry_value_object, allocator); } } ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 -rapidjson::Value value_arrays_obj{rapidjson::kObjectType}; -for (auto& kv : entry_objects) { - value_arrays_obj.AddMember(rapidjson::StringRef(kv.first.c_str()), std::move(kv.second), - allocator); -} + rapidjson::Value value_arrays_obj{rapidjson::kObjectType}; + for (auto& kv : entry_objects) { + value_arrays_obj.AddMember(rapidjson::StringRef(kv.first.c_str()), std::move(kv.second), + allocator); + } -document.AddMember("entries", std::move(value_arrays_obj), allocator); + document.AddMember("entries", std::move(value_arrays_obj), allocator); -rapidjson::StringBuffer strbuf; -rapidjson::PrettyWriter writer(strbuf); -document.Accept(writer); -response.add(strbuf.GetString()); -return Http::Code::OK; -} // namespace Envoy + rapidjson::StringBuffer strbuf; + rapidjson::PrettyWriter writer(strbuf); + document.Accept(writer); + response.add(strbuf.GetString()); + return Http::Code::OK; +} std::string AdminImpl::runtimeAsJson( const std::vector>& entries) { @@ -700,8 +629,7 @@ std::string AdminImpl::runtimeAsJson( return strbuf.GetString(); } -<<<<<<< HEAD -Http::Code AdminImpl::handlerHystrixEventStream(const std::string&, +Http::Code AdminImpl::handlerHystrixEventStream(absl::string_view, Http::HeaderMap& response_headers, Buffer::Instance&, HandlerInfo& handler_info) { @@ -717,14 +645,13 @@ Http::Code AdminImpl::handlerHystrixEventStream(const std::string&, Http::Headers::get().AccessControlAllowOriginValue.All); response_headers.insertNoChunks().value().setReference("0"); - Stats::HystrixHandlerInfoImpl& hystrix_handler_info = - dynamic_cast(handler_info); + Extensions::StatSinks::Common::HystrixHandlerInfoImpl& hystrix_handler_info = + dynamic_cast(handler_info); // TODO (@trabetti) : what should we do when a sink not present? // is this error message to user enough? // does it make sense to response with 503? if (server_.registerToHystrixSink(hystrix_handler_info.callbacks_)) { - ENVOY_LOG(debug, "start sending data to hystrix dashboard on port {}", hystrix_handler_info.callbacks_->connection()->localAddress()->asString()); return Http::Code::OK; @@ -737,9 +664,8 @@ Http::Code AdminImpl::handlerHystrixEventStream(const std::string&, } } -======= Http::Code AdminImpl::handlerRuntimeModify(absl::string_view url, Http::HeaderMap&, - Buffer::Instance& response) { + Buffer::Instance& response, HandlerInfo&) { const Http::Utility::QueryParams params = Http::Utility::parseQueryString(url); if (params.empty()) { response.add("usage: /runtime_modify?key1=value1&key2=value2&keyN=valueN\n"); @@ -755,30 +681,25 @@ Http::Code AdminImpl::handlerRuntimeModify(absl::string_view url, Http::HeaderMa ConfigTracker& AdminImpl::getConfigTracker() { return config_tracker_; } ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 void AdminFilter::onComplete() { absl::string_view path = request_headers_->Path()->value().getStringView(); ENVOY_STREAM_LOG(debug, "request complete: path: {}", *callbacks_, path); Buffer::OwnedImpl response; Http::HeaderMapPtr header_map{new Http::HeaderMapImpl}; -<<<<<<< HEAD + RELEASE_ASSERT(request_headers_); Http::Code code; bool end_stream = true; if (path.find("/hystrix_event_stream") == std::string::npos) { handler_info_ = std::make_unique(); - code = parent_.runCallback(path, *header_map, response, *handler_info_); + code = parent_.runCallback(path, *request_headers_, *header_map, response, *handler_info_); } else { - handler_info_ = std::make_unique(callbacks_); - code = parent_.runCallback(path, *header_map, response, *handler_info_); + handler_info_ = + std::make_unique(callbacks_); + code = parent_.runCallback(path, *request_headers_, *header_map, response, *handler_info_); end_stream = false; } - -======= - RELEASE_ASSERT(request_headers_); - Http::Code code = parent_.runCallback(path, *request_headers_, *header_map, response); ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 header_map->insertStatus().value(std::to_string(enumToInt(code))); const auto& headers = Http::Headers::get(); if (header_map->ContentType() == nullptr) { @@ -843,14 +764,12 @@ AdminImpl::AdminImpl(const std::string& access_log_path, const std::string& prof {"/listeners", "print listener addresses", MAKE_ADMIN_HANDLER(handlerListenerInfo), false, false}, {"/runtime", "print runtime values", MAKE_ADMIN_HANDLER(handlerRuntime), false, false}, -<<<<<<< HEAD + {"/runtime_modify", "modify runtime values", MAKE_ADMIN_HANDLER(handlerRuntimeModify), + false, true}, {"/hystrix_event_stream", "send hystrix event stream", MAKE_ADMIN_HANDLER(handlerHystrixEventStream), false, false}}, -======= - {"/runtime_modify", "modify runtime values", MAKE_ADMIN_HANDLER(handlerRuntimeModify), - false, true}}, + // TODO(jsedgwick) add /runtime_reset endpoint that removes all admin-set values ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 listener_(*this, std::move(listener_scope)) { if (!address_out_path.empty()) { @@ -888,15 +807,10 @@ void AdminImpl::createFilterChain(Http::FilterChainFactoryCallbacks& callbacks) callbacks.addStreamDecoderFilter(Http::StreamDecoderFilterSharedPtr{new AdminFilter(*this)}); } -<<<<<<< HEAD -Http::Code AdminImpl::runCallback(const std::string& path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance& response, - HandlerInfo& handler_info) { -======= Http::Code AdminImpl::runCallback(absl::string_view path_and_query, const Http::HeaderMap& request_headers, - Http::HeaderMap& response_headers, Buffer::Instance& response) { ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + Http::HeaderMap& response_headers, Buffer::Instance& response, + HandlerInfo& handler_info) { Http::Code code = Http::Code::OK; bool found_handler = false; @@ -907,9 +821,6 @@ Http::Code AdminImpl::runCallback(absl::string_view path_and_query, for (const UrlHandler& handler : handlers_) { if (path_and_query.compare(0, query_index, handler.prefix_) == 0) { -<<<<<<< HEAD - code = handler.handler_(path_and_query, response_headers, response, handler_info); -======= if (handler.mutates_server_state_) { const absl::string_view method = request_headers.Method()->value().getStringView(); if (method != Http::Headers::get().MethodValues.Post) { @@ -917,8 +828,7 @@ Http::Code AdminImpl::runCallback(absl::string_view path_and_query, handler.prefix_, method); } } - code = handler.handler_(path_and_query, response_headers, response); ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + code = handler.handler_(path_and_query, response_headers, response, handler_info); found_handler = true; break; } @@ -946,12 +856,8 @@ std::vector AdminImpl::sortedHandlers() const { return sorted_handlers; } -<<<<<<< HEAD -Http::Code AdminImpl::handlerHelp(const std::string&, Http::HeaderMap&, Buffer::Instance& response, +Http::Code AdminImpl::handlerHelp(absl::string_view, Http::HeaderMap&, Buffer::Instance& response, HandlerInfo&) { -======= -Http::Code AdminImpl::handlerHelp(absl::string_view, Http::HeaderMap&, Buffer::Instance& response) { ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 response.add("admin commands are:\n"); // Prefix order is used during searching, but for printing do them in alpha order. @@ -961,13 +867,8 @@ Http::Code AdminImpl::handlerHelp(absl::string_view, Http::HeaderMap&, Buffer::I return Http::Code::OK; } -<<<<<<< HEAD -Http::Code AdminImpl::handlerAdminHome(const std::string&, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&) { -======= Http::Code AdminImpl::handlerAdminHome(absl::string_view, Http::HeaderMap& response_headers, - Buffer::Instance& response) { ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + Buffer::Instance& response, HandlerInfo&) { response_headers.insertContentType().value().setReference( Http::Headers::get().ContentTypeValues.Html); diff --git a/source/server/http/admin.h b/source/server/http/admin.h index 56533b4842fc4..1664ec00812b6 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -23,17 +23,17 @@ #include "common/http/default_server_string.h" #include "common/http/utility.h" #include "common/network/raw_buffer_socket.h" -#include "common/stats/hystrix.h" #include "server/http/config_tracker_impl.h" +#include "extensions/stat_sinks/common/hystrix/hystrix.h" + #include "absl/strings/string_view.h" namespace Envoy { namespace Server { /** -<<<<<<< HEAD * This class contains data which will be sent from admin filter to a handler * and build a class which contains the relevant data. */ @@ -44,10 +44,7 @@ class HandlerInfoImpl : public HandlerInfo { }; /** - * Implementation of Server::admin. -======= * Implementation of Server::Admin. ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 */ class AdminImpl : public Admin, public Network::FilterChainFactory, @@ -59,13 +56,9 @@ class AdminImpl : public Admin, const std::string& address_out_path, Network::Address::InstanceConstSharedPtr address, Server::Instance& server, Stats::ScopePtr&& listener_scope); -<<<<<<< HEAD - Http::Code runCallback(const std::string& path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo& handler_info); -======= Http::Code runCallback(absl::string_view path_and_query, const Http::HeaderMap& request_headers, - Http::HeaderMap& response_headers, Buffer::Instance& response); ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + Http::HeaderMap& response_headers, Buffer::Instance& response, + HandlerInfo& handler_info); const Network::Socket& socket() override { return *socket_; } Network::Socket& mutable_socket() { return *socket_; } Network::ListenerConfig& listener() { return listener_; } @@ -165,88 +158,54 @@ class AdminImpl : public Admin, /** * URL handlers. */ -<<<<<<< HEAD - Http::Code handlerAdminHome(const std::string& path_and_query, Http::HeaderMap& response_headers, + Http::Code handlerAdminHome(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, HandlerInfo&); - Http::Code handlerCerts(const std::string& path_and_query, Http::HeaderMap& response_headers, + Http::Code handlerCerts(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, HandlerInfo&); - Http::Code handlerClusters(const std::string& path_and_query, Http::HeaderMap& response_headers, + Http::Code handlerClusters(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, HandlerInfo&); - Http::Code handlerCpuProfiler(const std::string& path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance& response, - HandlerInfo&); - Http::Code handlerHealthcheckFail(const std::string& path_and_query, + Http::Code handlerConfigDump(absl::string_view path_and_query, Http::HeaderMap& response_headers, + Buffer::Instance& response, HandlerInfo&) const; + Http::Code handlerCpuProfiler(absl::string_view path_and_query, Http::HeaderMap& response_headers, + Buffer::Instance& response, HandlerInfo&); + Http::Code handlerHealthcheckFail(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, HandlerInfo&); - Http::Code handlerHealthcheckOk(const std::string& path_and_query, + Http::Code handlerHealthcheckOk(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, HandlerInfo&); - Http::Code handlerHelp(const std::string& path_and_query, Http::HeaderMap& response_headers, + Http::Code handlerHelp(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, HandlerInfo&); - Http::Code handlerHotRestartVersion(const std::string& path_and_query, + Http::Code handlerHotRestartVersion(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, HandlerInfo&); - Http::Code handlerListenerInfo(const std::string& path_and_query, + Http::Code handlerListenerInfo(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, HandlerInfo&); - Http::Code handlerLogging(const std::string& path_and_query, Http::HeaderMap& response_headers, + Http::Code handlerLogging(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, HandlerInfo&); - Http::Code handlerMain(const std::string& path, Buffer::Instance& response); - Http::Code handlerQuitQuitQuit(const std::string& path_and_query, + Http::Code handlerMain(const std::string& path, Buffer::Instance& response, HandlerInfo&); + Http::Code handlerQuitQuitQuit(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, HandlerInfo&); - Http::Code handlerResetCounters(const std::string& path_and_query, + Http::Code handlerResetCounters(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, HandlerInfo&); - Http::Code handlerServerInfo(const std::string& path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&); - Http::Code handlerStats(const std::string& path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&); - Http::Code handlerRuntime(const std::string& path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&); - Http::Code handlerHystrixEventStream(const std::string& path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance&, - HandlerInfo& handler_info); -======= - Http::Code handlerAdminHome(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); - Http::Code handlerCerts(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); - Http::Code handlerClusters(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); - Http::Code handlerConfigDump(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response) const; - Http::Code handlerCpuProfiler(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); - Http::Code handlerHealthcheckFail(absl::string_view path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance& response); - Http::Code handlerHealthcheckOk(absl::string_view path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance& response); - Http::Code handlerHelp(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); - Http::Code handlerHotRestartVersion(absl::string_view path_and_query, - Http::HeaderMap& response_headers, - Buffer::Instance& response); - Http::Code handlerListenerInfo(absl::string_view path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance& response); - Http::Code handlerLogging(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); - Http::Code handlerMain(const std::string& path, Buffer::Instance& response); - Http::Code handlerQuitQuitQuit(absl::string_view path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance& response); - Http::Code handlerResetCounters(absl::string_view path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance& response); Http::Code handlerServerInfo(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); + Buffer::Instance& response, HandlerInfo&); Http::Code handlerStats(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); + Buffer::Instance& response, HandlerInfo&); Http::Code handlerPrometheusStats(absl::string_view path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance& response); + Http::HeaderMap& response_headers, Buffer::Instance& response, + HandlerInfo&); Http::Code handlerRuntime(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response); + Buffer::Instance& response, HandlerInfo&); Http::Code handlerRuntimeModify(absl::string_view path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance& response); ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + Http::HeaderMap& response_headers, Buffer::Instance& response, + HandlerInfo&); + Http::Code handlerHystrixEventStream(absl::string_view path_and_query, + Http::HeaderMap& response_headers, Buffer::Instance&, + HandlerInfo& handler_info); class AdminListener : public Network::ListenerConfig { public: diff --git a/source/server/server.cc b/source/server/server.cc index 90575bac790b9..00752d655826f 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -147,8 +147,8 @@ void InstanceImpl::flushStats() { bool InstanceImpl::registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) { for (const auto& sink : config_->statsSinks()) { // TODO: is there a better way to find the hystrix sink? - Stats::HystrixNameSpace::HystrixSink* hystrix_sink = - dynamic_cast(sink.get()); + Extensions::StatSinks::Common::HystrixNameSpace::HystrixSink* hystrix_sink = + dynamic_cast(sink.get()); if (hystrix_sink != nullptr) { hystrix_sink->registerConnection(callbacks); return true; @@ -160,8 +160,8 @@ bool InstanceImpl::registerToHystrixSink(Http::StreamDecoderFilterCallbacks* cal void InstanceImpl::unregisterHystrixSink() { for (const auto& sink : config_->statsSinks()) { // TODO: is there a better way to find the hystrix sink? - Stats::HystrixNameSpace::HystrixSink* hystrix_sink = - dynamic_cast(sink.get()); + Extensions::StatSinks::Common::HystrixNameSpace::HystrixSink* hystrix_sink = + dynamic_cast(sink.get()); if (hystrix_sink != nullptr) { // TODO (@trabetti) : will want to move to a vector of connections, // need a parameter (callback, hope it will work) to identify which connection to remove diff --git a/test/common/router/rds_impl_test.cc b/test/common/router/rds_impl_test.cc index 611cc2afe1603..708aa7b71d8c0 100644 --- a/test/common/router/rds_impl_test.cc +++ b/test/common/router/rds_impl_test.cc @@ -209,14 +209,6 @@ TEST_F(RdsImplTest, Basic) { // Make sure the initial empty route table works. EXPECT_EQ(nullptr, rds_->config()->route(Http::TestHeaderMapImpl{{":authority", "foo"}}, 0)); EXPECT_EQ("", rds_->versionInfo()); -<<<<<<< HEAD - Http::HeaderMapImpl header_map; - Server::HandlerInfoImpl handler_info; - EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data, handler_info)); - EXPECT_EQ(routes_expected_output_no_routes, TestUtility::bufferToString(data)); - data.drain(data.length()); -======= ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 EXPECT_EQ(0UL, store_.gauge("foo.rds.foo_route_config.version").value()); // Initial request. @@ -235,12 +227,6 @@ TEST_F(RdsImplTest, Basic) { callbacks_->onSuccess(std::move(message)); EXPECT_EQ(nullptr, rds_->config()->route(Http::TestHeaderMapImpl{{":authority", "foo"}}, 0)); EXPECT_EQ("hash_15ed54077da94d8b", rds_->versionInfo()); -<<<<<<< HEAD - EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data, handler_info)); - EXPECT_EQ(routes_expected_output_only_name, TestUtility::bufferToString(data)); - data.drain(data.length()); -======= ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 EXPECT_EQ(1580011435426663819U, store_.gauge("foo.rds.foo_route_config.version").value()); expectRequest(); @@ -255,13 +241,6 @@ TEST_F(RdsImplTest, Basic) { callbacks_->onSuccess(std::move(message)); EXPECT_EQ(nullptr, rds_->config()->route(Http::TestHeaderMapImpl{{":authority", "foo"}}, 0)); -<<<<<<< HEAD - // Test Admin /routes handler. The route table should not change. - EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data, handler_info)); - EXPECT_EQ(routes_expected_output_only_name, TestUtility::bufferToString(data)); - data.drain(data.length()); -======= ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 EXPECT_EQ(1580011435426663819U, store_.gauge("foo.rds.foo_route_config.version").value()); expectRequest(); @@ -307,88 +286,8 @@ TEST_F(RdsImplTest, Basic) { ->routeEntry() ->clusterName()); -<<<<<<< HEAD - // Test Admin /routes handler. The route table should now have the information given in - // response2_json. - const std::string routes_expected_output_full_table = R"EOF([ -{ -"version_info": "hash_7a3f97b327d08382", -"route_config_name": "foo_route_config", -"config_source": { - "api_config_source": { - "cluster_names": [ - "foo_cluster" - ], - "refresh_delay": "1s" - } -} -, -"route_table_dump": { - "name": "foo_route_config", - "virtual_hosts": [ - { - "name": "local_service", - "domains": [ - "*" - ], - "routes": [ - { - "match": { - "prefix": "/foo" - }, - "route": { - "cluster_header": ":authority" - } - }, - { - "match": { - "prefix": "/bar" - }, - "route": { - "cluster": "bar" - } - } - ] - } - ] -} - -} -] -)EOF"; - - EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data, handler_info)); - EXPECT_EQ(routes_expected_output_full_table, TestUtility::bufferToString(data)); - data.drain(data.length()); - EXPECT_EQ(8808926191882896258U, store_.gauge("foo.rds.foo_route_config.version").value()); - - // Test that we get the same dump if we specify the route name. - EXPECT_EQ(Http::Code::OK, handler_callback_("/routes?route_config_name=foo_route_config", - header_map, data, handler_info)); - EXPECT_EQ(routes_expected_output_full_table, TestUtility::bufferToString(data)); - data.drain(data.length()); - - // Test that we get an emtpy response if the name does not match. - EXPECT_EQ(Http::Code::OK, handler_callback_("/routes?route_config_name=does_not_exist", - header_map, data, handler_info)); - EXPECT_EQ("[\n]\n", TestUtility::bufferToString(data)); - data.drain(data.length()); - - const std::string routes_expected_output_usage = R"EOF({ - "general_usage": "/routes (dump all dynamic HTTP route tables).", - "specify_name_usage": "/routes?route_config_name= (dump all dynamic HTTP route tables with the if any)." -})EOF"; - - // Test that we get the help text if we use the command in an invalid ways. - EXPECT_EQ(Http::Code::NotFound, - handler_callback_("/routes?bad_param", header_map, data, handler_info)); - EXPECT_EQ(routes_expected_output_usage, TestUtility::bufferToString(data)); - data.drain(data.length()); - -======= EXPECT_EQ(8808926191882896258U, store_.gauge("foo.rds.foo_route_config.version").value()); ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 // Old config use count should be 1 now. EXPECT_EQ(1, config.use_count()); @@ -562,50 +461,6 @@ TEST_F(RouteConfigProviderManagerImplTest, Basic) { EXPECT_EQ(3UL, provider_.use_count()); EXPECT_EQ(2UL, provider3.use_count()); -<<<<<<< HEAD - const std::string routes_expected_output = R"EOF([ -{ -"version_info": "", -"route_config_name": "foo_route_config", -"config_source": { - "api_config_source": { - "cluster_names": [ - "bar_cluster" - ], - "refresh_delay": "1s" - } -} -, -"route_table_dump": {} - -} -,{ -"version_info": "", -"route_config_name": "foo_route_config", -"config_source": { - "api_config_source": { - "cluster_names": [ - "foo_cluster" - ], - "refresh_delay": "1s" - } -} -, -"route_table_dump": {} - -} -] -)EOF"; - - // Test Admin /routes handler. - Http::HeaderMapImpl header_map; - Server::HandlerInfoImpl handler_info; - EXPECT_EQ(Http::Code::OK, handler_callback_("/routes", header_map, data, handler_info)); - EXPECT_EQ(routes_expected_output, TestUtility::bufferToString(data)); - data.drain(data.length()); - -======= ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 provider_.reset(); provider2.reset(); configured_providers.clear(); diff --git a/test/common/stats/BUILD b/test/common/stats/BUILD index c781e8de30b03..ce7cdf1052a18 100644 --- a/test/common/stats/BUILD +++ b/test/common/stats/BUILD @@ -29,16 +29,3 @@ envoy_cc_test( "//test/test_common:utility_lib", ], ) - -envoy_cc_test( - name = "hystrix_test", - srcs = ["hystrix_test.cc"], - deps = [ - "//source/common/stats:hystrix_lib", - "//source/common/stats:stats_lib", - "//test/mocks/server:server_mocks", - "//test/mocks/stats:stats_mocks", - "//test/mocks/upstream:upstream_mocks", - "//test/test_common:utility_lib", - ], -) diff --git a/test/extensions/stats_sinks/common/hystrix/BUILD b/test/extensions/stats_sinks/common/hystrix/BUILD new file mode 100644 index 0000000000000..1d99ec4e13fb0 --- /dev/null +++ b/test/extensions/stats_sinks/common/hystrix/BUILD @@ -0,0 +1,22 @@ +licenses(["notice"]) # Apache 2 + +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_test", + "envoy_package", +) + +envoy_package() + +envoy_cc_test( + name = "hystrix_test", + srcs = ["hystrix_test.cc"], + deps = [ + "//source/common/stats:stats_lib", + "//source/extensions/stat_sinks/common/hystrix:hystrix_lib", + "//test/mocks/server:server_mocks", + "//test/mocks/stats:stats_mocks", + "//test/mocks/upstream:upstream_mocks", + "//test/test_common:utility_lib", + ], +) diff --git a/test/common/stats/hystrix_test.cc b/test/extensions/stats_sinks/common/hystrix/hystrix_test.cc similarity index 89% rename from test/common/stats/hystrix_test.cc rename to test/extensions/stats_sinks/common/hystrix/hystrix_test.cc index d12feb622e9f8..b4f52a643811e 100644 --- a/test/common/stats/hystrix_test.cc +++ b/test/extensions/stats_sinks/common/hystrix/hystrix_test.cc @@ -2,9 +2,10 @@ #include #include -#include "common/stats/hystrix.h" #include "common/stats/stats_impl.h" +#include "extensions/stat_sinks/common/hystrix/hystrix.h" + #include "test/mocks/server/mocks.h" #include "test/mocks/stats/mocks.h" #include "test/mocks/upstream/mocks.h" @@ -20,8 +21,9 @@ using testing::Return; using testing::_; namespace Envoy { -namespace Stats { - +namespace Extensions { +namespace StatSinks { +namespace Common { namespace HystrixNameSpace { class HystrixSinkTest : public testing::Test { @@ -95,13 +97,13 @@ TEST_F(HystrixSinkTest, BasicFlow) { // register callback to sink sink_->registerConnection(&callbacks_); - NiceMock success_counter; + NiceMock success_counter; success_counter.name_ = "cluster.test_cluster.upstream_rq_2xx"; - NiceMock error_counter; + NiceMock error_counter; error_counter.name_ = "cluster.test_cluster.upstream_rq_5xx"; - NiceMock timeout_counter; + NiceMock timeout_counter; timeout_counter.name_ = "cluster.test_cluster.upstream_rq_timeout"; - NiceMock rejected_counter; + NiceMock rejected_counter; rejected_counter.name_ = "cluster.test_cluster.upstream_rq_pending_overflow"; for (int i = 0; i < 12; i++) { @@ -116,14 +118,13 @@ TEST_F(HystrixSinkTest, BasicFlow) { sink_->flushCounter(success_counter, 1); sink_->flushCounter(rejected_counter, 1); sink_->endFlush(); - // std::cout << "BasicFlow: buffer = " << TestUtility::bufferToString(buffer) << std::endl; } - // std::string window = sink_->getStats().printRollingWindow(); // just to cover it - // std::cout << "printRollingWindow: " << sink_->getStats().printRollingWindow() << std::endl; - // TODO (@trabetti) : add something to check the data? - absl::string_view::size_type pos = - sink_->getStats().printRollingWindow().find("cluster.test_cluster.total"); - EXPECT_NE(absl::string_view::npos, pos); + + // //std::string rolling_map = sink_->getStats().printRollingWindow(); + // absl::string_view rolling_map = sink_->getStats().printRollingWindow(); + // absl::string_view::size_type pos = rolling_map.find("cluster.test_cluster.total"); + // EXPECT_NE(absl::string_view::npos, pos); + // //EXPECT_NE(absl::string_view::npos, map.find("cluster.test_cluster.total")); std::string data_message = TestUtility::bufferToString(buffer); @@ -158,7 +159,7 @@ TEST_F(HystrixSinkTest, Disconnect) { Buffer::OwnedImpl buffer = createClusterAndCallbacks(); // flush with no connection - NiceMock success_counter; + NiceMock success_counter; success_counter.name_ = "cluster.test_cluster.upstream_rq_2xx"; ON_CALL(success_counter, value()).WillByDefault(Return(1234)); @@ -186,6 +187,7 @@ TEST_F(HystrixSinkTest, Disconnect) { } } // namespace HystrixNameSpace - -} // namespace Stats +} // namespace Common +} // namespace StatSinks +} // namespace Extensions } // namespace Envoy diff --git a/test/extensions/stats_sinks/dog_statsd/BUILD b/test/extensions/stats_sinks/dog_statsd/BUILD index 30527c68a8ada..c52854bb8c7d3 100644 --- a/test/extensions/stats_sinks/dog_statsd/BUILD +++ b/test/extensions/stats_sinks/dog_statsd/BUILD @@ -19,15 +19,7 @@ envoy_extension_cc_test( "//include/envoy/registry", "//source/common/config:well_known_names", "//source/common/protobuf:utility_lib", -<<<<<<< HEAD:test/server/config/stats/BUILD - "//source/common/stats:hystrix_lib", - "//source/common/stats:statsd_lib", - "//source/server/config/stats:dog_statsd_lib", - "//source/server/config/stats:hystrix_lib", - "//source/server/config/stats:statsd_lib", -======= "//source/extensions/stat_sinks/dog_statsd:config", ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51:test/extensions/stats_sinks/dog_statsd/BUILD "//test/mocks/server:server_mocks", "//test/test_common:environment_lib", "//test/test_common:network_utility_lib", diff --git a/test/extensions/stats_sinks/hystrix/BUILD b/test/extensions/stats_sinks/hystrix/BUILD new file mode 100644 index 0000000000000..69c4521caca3c --- /dev/null +++ b/test/extensions/stats_sinks/hystrix/BUILD @@ -0,0 +1,28 @@ +licenses(["notice"]) # Apache 2 + +load( + "//bazel:envoy_build_system.bzl", + "envoy_package", +) +load( + "//test/extensions:extensions_build_system.bzl", + "envoy_extension_cc_test", +) + +envoy_package() + +envoy_extension_cc_test( + name = "config_test", + srcs = ["config_test.cc"], + extension_name = "envoy.stat_sinks.hystrix", + deps = [ + "//include/envoy/registry", + "//source/common/config:well_known_names", + "//source/common/protobuf:utility_lib", + "//source/extensions/stat_sinks/hystrix:config", + "//test/mocks/server:server_mocks", + "//test/test_common:environment_lib", + "//test/test_common:network_utility_lib", + "//test/test_common:utility_lib", + ], +) diff --git a/test/extensions/stats_sinks/hystrix/config_test.cc b/test/extensions/stats_sinks/hystrix/config_test.cc new file mode 100644 index 0000000000000..65fbc0108cd5e --- /dev/null +++ b/test/extensions/stats_sinks/hystrix/config_test.cc @@ -0,0 +1,49 @@ +#include "envoy/config/bootstrap/v2/bootstrap.pb.h" +#include "envoy/registry/registry.h" + +#include "common/config/well_known_names.h" +#include "common/protobuf/utility.h" + +#include "extensions/stat_sinks/common/hystrix/hystrix.h" +#include "extensions/stat_sinks/hystrix/config.h" + +#include "test/mocks/server/mocks.h" +#include "test/test_common/environment.h" +#include "test/test_common/network_utility.h" +#include "test/test_common/utility.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using testing::NiceMock; +using testing::Return; +using testing::ReturnRef; +using testing::_; + +namespace Envoy { +namespace Extensions { +namespace StatSinks { +namespace HystrixNameSpace { + +TEST(StatsConfigTest, ValidHystrixSink) { + const std::string name = Config::StatsSinkNames::get().HYSTRIX; + + envoy::config::metrics::v2::HystrixSink sink_config; + + Server::Configuration::StatsSinkFactory* factory = + Registry::FactoryRegistry::getFactory(name); + ASSERT_NE(factory, nullptr); + + ProtobufTypes::MessagePtr message = factory->createEmptyConfigProto(); + MessageUtil::jsonConvert(sink_config, *message); + + NiceMock server; + Stats::SinkPtr sink = factory->createStatsSink(*message, server); + EXPECT_NE(sink, nullptr); + EXPECT_NE(dynamic_cast(sink.get()), nullptr); +} + +} // namespace HystrixNameSpace +} // namespace StatSinks +} // namespace Extensions +} // namespace Envoy diff --git a/test/server/config/stats/config_test.cc b/test/server/config/stats/config_test.cc deleted file mode 100644 index 69922140ef8f2..0000000000000 --- a/test/server/config/stats/config_test.cc +++ /dev/null @@ -1,141 +0,0 @@ -#include - -#include "envoy/config/bootstrap/v2/bootstrap.pb.h" -#include "envoy/registry/registry.h" - -#include "common/config/well_known_names.h" -#include "common/protobuf/utility.h" -#include "common/stats/hystrix.h" -#include "common/stats/statsd.h" - -#include "server/config/stats/dog_statsd.h" -#include "server/config/stats/hystrix.h" -#include "server/config/stats/statsd.h" - -#include "test/mocks/server/mocks.h" -#include "test/test_common/environment.h" -#include "test/test_common/network_utility.h" -#include "test/test_common/utility.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -using testing::NiceMock; -using testing::Return; -using testing::ReturnRef; -using testing::_; - -namespace Envoy { -namespace Server { -namespace Configuration { - -TEST(StatsConfigTest, ValidTcpStatsd) { - const std::string name = Config::StatsSinkNames::get().STATSD; - - envoy::config::metrics::v2::StatsdSink sink_config; - sink_config.set_tcp_cluster_name("fake_cluster"); - - StatsSinkFactory* factory = Registry::FactoryRegistry::getFactory(name); - ASSERT_NE(factory, nullptr); - - ProtobufTypes::MessagePtr message = factory->createEmptyConfigProto(); - MessageUtil::jsonConvert(sink_config, *message); - - NiceMock server; - Stats::SinkPtr sink = factory->createStatsSink(*message, server); - EXPECT_NE(sink, nullptr); - EXPECT_NE(dynamic_cast(sink.get()), nullptr); -} - -class StatsConfigLoopbackTest : public testing::TestWithParam {}; -INSTANTIATE_TEST_CASE_P(IpVersions, StatsConfigLoopbackTest, - testing::ValuesIn(TestEnvironment::getIpVersionsForTest())); - -TEST_P(StatsConfigLoopbackTest, ValidUdpIpStatsd) { - const std::string name = Config::StatsSinkNames::get().STATSD; - - envoy::config::metrics::v2::StatsdSink sink_config; - envoy::api::v2::core::Address& address = *sink_config.mutable_address(); - envoy::api::v2::core::SocketAddress& socket_address = *address.mutable_socket_address(); - socket_address.set_protocol(envoy::api::v2::core::SocketAddress::UDP); - auto loopback_flavor = Network::Test::getCanonicalLoopbackAddress(GetParam()); - socket_address.set_address(loopback_flavor->ip()->addressAsString()); - socket_address.set_port_value(8125); - - StatsSinkFactory* factory = Registry::FactoryRegistry::getFactory(name); - ASSERT_NE(factory, nullptr); - - ProtobufTypes::MessagePtr message = factory->createEmptyConfigProto(); - MessageUtil::jsonConvert(sink_config, *message); - - NiceMock server; - Stats::SinkPtr sink = factory->createStatsSink(*message, server); - EXPECT_NE(sink, nullptr); - EXPECT_NE(dynamic_cast(sink.get()), nullptr); - EXPECT_EQ(dynamic_cast(sink.get())->getUseTagForTest(), false); -} - -// Negative test for protoc-gen-validate constraints for statsd. -TEST(StatsdConfigTest, ValidateFail) { - NiceMock server; - EXPECT_THROW( - StatsdSinkFactory().createStatsSink(envoy::config::metrics::v2::StatsdSink(), server), - ProtoValidationException); -} - -class DogStatsdConfigLoopbackTest : public testing::TestWithParam {}; -INSTANTIATE_TEST_CASE_P(IpVersions, DogStatsdConfigLoopbackTest, - testing::ValuesIn(TestEnvironment::getIpVersionsForTest())); - -TEST_P(DogStatsdConfigLoopbackTest, ValidUdpIp) { - const std::string name = Config::StatsSinkNames::get().DOG_STATSD; - - envoy::config::metrics::v2::DogStatsdSink sink_config; - envoy::api::v2::core::Address& address = *sink_config.mutable_address(); - envoy::api::v2::core::SocketAddress& socket_address = *address.mutable_socket_address(); - socket_address.set_protocol(envoy::api::v2::core::SocketAddress::UDP); - auto loopback_flavor = Network::Test::getCanonicalLoopbackAddress(GetParam()); - socket_address.set_address(loopback_flavor->ip()->addressAsString()); - socket_address.set_port_value(8125); - - StatsSinkFactory* factory = Registry::FactoryRegistry::getFactory(name); - ASSERT_NE(factory, nullptr); - - ProtobufTypes::MessagePtr message = factory->createEmptyConfigProto(); - MessageUtil::jsonConvert(sink_config, *message); - - NiceMock server; - Stats::SinkPtr sink = factory->createStatsSink(*message, server); - EXPECT_NE(sink, nullptr); - EXPECT_NE(dynamic_cast(sink.get()), nullptr); - EXPECT_EQ(dynamic_cast(sink.get())->getUseTagForTest(), true); -} - -// Negative test for protoc-gen-validate constraints for dog_statsd. -TEST(DogStatsdConfigTest, ValidateFail) { - NiceMock server; - EXPECT_THROW( - DogStatsdSinkFactory().createStatsSink(envoy::config::metrics::v2::DogStatsdSink(), server), - ProtoValidationException); -} - -TEST(StatsConfigTest, ValidHystrixSink) { - const std::string name = Config::StatsSinkNames::get().HYSTRIX; - - envoy::config::metrics::v2::HystrixSink sink_config; - - StatsSinkFactory* factory = Registry::FactoryRegistry::getFactory(name); - ASSERT_NE(factory, nullptr); - - ProtobufTypes::MessagePtr message = factory->createEmptyConfigProto(); - MessageUtil::jsonConvert(sink_config, *message); - - NiceMock server; - Stats::SinkPtr sink = factory->createStatsSink(*message, server); - EXPECT_NE(sink, nullptr); - EXPECT_NE(dynamic_cast(sink.get()), nullptr); -} - -} // namespace Configuration -} // namespace Server -} // namespace Envoy diff --git a/test/server/http/admin_test.cc b/test/server/http/admin_test.cc index 4f24593e9e15a..95866c4deaa13 100644 --- a/test/server/http/admin_test.cc +++ b/test/server/http/admin_test.cc @@ -90,8 +90,10 @@ class AdminInstanceTest : public testing::TestWithParam>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 EXPECT_FALSE(Profiler::Cpu::profilerEnabled()); } @@ -160,16 +153,13 @@ TEST_P(AdminInstanceTest, AdminBadProfiler) { "", Network::Test::getCanonicalLoopbackAddress(GetParam()), server_, listener_scope_.createScope("listener.admin.")); Http::HeaderMapImpl header_map; -<<<<<<< HEAD - HandlerInfoImpl handler_info; - admin_bad_profile_path.runCallback("/cpuprofiler?enable=y", header_map, data, handler_info); -======= const absl::string_view post = Http::Headers::get().MethodValues.Post; request_headers_.insertMethod().value(post.data(), post.size()); - EXPECT_NO_LOGS(EXPECT_EQ(Http::Code::InternalServerError, - admin_bad_profile_path.runCallback("/cpuprofiler?enable=y", - request_headers_, header_map, data))); ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + HandlerInfoImpl handler_info; + EXPECT_NO_LOGS( + EXPECT_EQ(Http::Code::InternalServerError, + admin_bad_profile_path.runCallback("/cpuprofiler?enable=y", request_headers_, + header_map, data, handler_info))); EXPECT_FALSE(Profiler::Cpu::profilerEnabled()); } @@ -193,83 +183,35 @@ TEST_P(AdminInstanceTest, AdminBadAddressOutPath) { } TEST_P(AdminInstanceTest, CustomHandler) { -<<<<<<< HEAD - auto callback = [](const std::string&, Http::HeaderMap&, Buffer::Instance&, + auto callback = [](absl::string_view, Http::HeaderMap&, Buffer::Instance&, HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; -======= - auto callback = [](absl::string_view, Http::HeaderMap&, Buffer::Instance&) -> Http::Code { - return Http::Code::Accepted; - }; ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 // Test removable handler. EXPECT_NO_LOGS(EXPECT_TRUE(admin_.addHandler("/foo/bar", "hello", callback, true, false))); Http::HeaderMapImpl header_map; Buffer::OwnedImpl response; -<<<<<<< HEAD - HandlerInfoImpl handler_info; - - EXPECT_EQ(Http::Code::Accepted, - admin_.runCallback("/foo/bar", header_map, response, handler_info)); - - // Test that removable handler gets removed. - EXPECT_TRUE(admin_.removeHandler("/foo/bar")); - EXPECT_EQ(Http::Code::NotFound, - admin_.runCallback("/foo/bar", header_map, response, handler_info)); -======= EXPECT_EQ(Http::Code::Accepted, getCallback("/foo/bar", header_map, response)); // Test that removable handler gets removed. EXPECT_TRUE(admin_.removeHandler("/foo/bar")); EXPECT_EQ(Http::Code::NotFound, getCallback("/foo/bar", header_map, response)); ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 EXPECT_FALSE(admin_.removeHandler("/foo/bar")); // Add non removable handler. EXPECT_TRUE(admin_.addHandler("/foo/bar", "hello", callback, false, false)); -<<<<<<< HEAD - EXPECT_EQ(Http::Code::Accepted, - admin_.runCallback("/foo/bar", header_map, response, handler_info)); -======= EXPECT_EQ(Http::Code::Accepted, getCallback("/foo/bar", header_map, response)); ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 // Add again and make sure it is not there twice. EXPECT_FALSE(admin_.addHandler("/foo/bar", "hello", callback, false, false)); // Try to remove non removable handler, and make sure it is not removed. EXPECT_FALSE(admin_.removeHandler("/foo/bar")); -<<<<<<< HEAD - EXPECT_EQ(Http::Code::Accepted, - admin_.runCallback("/foo/bar", header_map, response, handler_info)); -} - -TEST_P(AdminInstanceTest, RejectHandlerWithXss) { - auto callback = [](const std::string&, Http::HeaderMap&, Buffer::Instance&, - HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; - EXPECT_FALSE( - admin_.addHandler("/foo", "hello", callback, true, false)); -} - -TEST_P(AdminInstanceTest, RejectHandlerWithEmbeddedQuery) { - auto callback = [](const std::string&, Http::HeaderMap&, Buffer::Instance&, - HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; - EXPECT_FALSE(admin_.addHandler("/bar?queryShouldNotBeInPrefix", "hello", callback, true, false)); -} - -TEST_P(AdminInstanceTest, EscapeHelpTextWithPunctuation) { - auto callback = [](const std::string&, Http::HeaderMap&, Buffer::Instance&, - HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; - - HandlerInfoImpl handler_info; -======= EXPECT_EQ(Http::Code::Accepted, getCallback("/foo/bar", header_map, response)); } TEST_P(AdminInstanceTest, RejectHandlerWithXss) { - auto callback = [](absl::string_view, Http::HeaderMap&, Buffer::Instance&) -> Http::Code { - return Http::Code::Accepted; - }; + auto callback = [](absl::string_view, Http::HeaderMap&, Buffer::Instance&, + HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; EXPECT_LOG_CONTAINS("error", "filter \"/foo\" contains invalid character '<'", EXPECT_FALSE(admin_.addHandler("/foo", "hello", @@ -277,9 +219,8 @@ TEST_P(AdminInstanceTest, RejectHandlerWithXss) { } TEST_P(AdminInstanceTest, RejectHandlerWithEmbeddedQuery) { - auto callback = [](absl::string_view, Http::HeaderMap&, Buffer::Instance&) -> Http::Code { - return Http::Code::Accepted; - }; + auto callback = [](absl::string_view, Http::HeaderMap&, Buffer::Instance&, + HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; EXPECT_LOG_CONTAINS("error", "filter \"/bar?queryShouldNotBeInPrefix\" contains invalid character '?'", EXPECT_FALSE(admin_.addHandler("/bar?queryShouldNotBeInPrefix", "hello", @@ -287,10 +228,8 @@ TEST_P(AdminInstanceTest, RejectHandlerWithEmbeddedQuery) { } TEST_P(AdminInstanceTest, EscapeHelpTextWithPunctuation) { - auto callback = [](absl::string_view, Http::HeaderMap&, Buffer::Instance&) -> Http::Code { - return Http::Code::Accepted; - }; ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 + auto callback = [](absl::string_view, Http::HeaderMap&, Buffer::Instance&, + HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; // It's OK to have help text with HTML characters in it, but when we render the home // page they need to be escaped. @@ -299,11 +238,7 @@ TEST_P(AdminInstanceTest, EscapeHelpTextWithPunctuation) { Http::HeaderMapImpl header_map; Buffer::OwnedImpl response; -<<<<<<< HEAD - EXPECT_EQ(Http::Code::OK, admin_.runCallback("/", header_map, response, handler_info)); -======= EXPECT_EQ(Http::Code::OK, getCallback("/", header_map, response)); ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 Http::HeaderString& content_type = header_map.ContentType()->value(); EXPECT_TRUE(content_type.find("text/html")) << content_type.c_str(); EXPECT_EQ(-1, response.search(planets.data(), planets.size(), 0)); @@ -314,12 +249,7 @@ TEST_P(AdminInstanceTest, EscapeHelpTextWithPunctuation) { TEST_P(AdminInstanceTest, HelpUsesFormForMutations) { Http::HeaderMapImpl header_map; Buffer::OwnedImpl response; -<<<<<<< HEAD - HandlerInfoImpl handler_info; - EXPECT_EQ(Http::Code::OK, admin_.runCallback("/", header_map, response, handler_info)); -======= EXPECT_EQ(Http::Code::OK, getCallback("/", header_map, response)); ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 const std::string logging_action = ">(); auto layer2 = std::make_unique>(); std::unordered_map entries1{ @@ -410,18 +337,11 @@ TEST_P(AdminInstanceTest, Runtime) { } } })EOF"; ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 EXPECT_CALL(loader, snapshot()).WillRepeatedly(testing::ReturnPointee(&snapshot)); EXPECT_CALL(server_, runtime()).WillRepeatedly(testing::ReturnPointee(&loader)); -<<<<<<< HEAD - - EXPECT_EQ(Http::Code::OK, admin_.runCallback("/runtime", header_map, response, handler_info)); - EXPECT_EQ("int_key: 1\nother_key: bar\nstring_key: foo\n", TestUtility::bufferToString(response)); -======= EXPECT_EQ(Http::Code::OK, getCallback("/runtime", header_map, response)); EXPECT_EQ(expected_json, TestUtility::bufferToString(response)); ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 } TEST_P(AdminInstanceTest, RuntimeModify) { @@ -429,35 +349,6 @@ TEST_P(AdminInstanceTest, RuntimeModify) { Buffer::OwnedImpl response; Runtime::MockLoader loader; -<<<<<<< HEAD - HandlerInfoImpl handler_info; - - EXPECT_CALL(snapshot, getAll()).WillRepeatedly(testing::ReturnRef(entries)); - EXPECT_CALL(loader, snapshot()).WillRepeatedly(testing::ReturnPointee(&snapshot)); - EXPECT_CALL(server_, runtime()).WillRepeatedly(testing::ReturnPointee(&loader)); - - EXPECT_EQ(Http::Code::OK, - admin_.runCallback("/runtime?format=json", header_map, response, handler_info)); - - std::string output = TestUtility::bufferToString(response); - Json::ObjectSharedPtr json = Json::Factory::loadFromString(output); - - EXPECT_TRUE(json->hasObject("runtime")); - std::vector pairs = json->getObjectArray("runtime"); - EXPECT_EQ(3, pairs.size()); - - Json::ObjectSharedPtr pair = pairs[0]; - EXPECT_EQ("int_key", pair->getString("name", "")); - EXPECT_EQ(1, pair->getInteger("value", -1)); - - pair = pairs[1]; - EXPECT_EQ("other_key", pair->getString("name", "")); - EXPECT_EQ("bar", pair->getString("value", "")); - - pair = pairs[2]; - EXPECT_EQ("string_key", pair->getString("name", "")); - EXPECT_EQ("foo", pair->getString("value", "")); -======= EXPECT_CALL(server_, runtime()).WillRepeatedly(testing::ReturnPointee(&loader)); std::unordered_map overrides; @@ -468,30 +359,14 @@ TEST_P(AdminInstanceTest, RuntimeModify) { EXPECT_EQ(Http::Code::OK, getCallback("/runtime_modify?foo=bar&x=42¬hing=", header_map, response)); EXPECT_EQ("OK\n", TestUtility::bufferToString(response)); ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 } TEST_P(AdminInstanceTest, RuntimeModifyNoArguments) { Http::HeaderMapImpl header_map; Buffer::OwnedImpl response; -<<<<<<< HEAD - std::unordered_map entries; - Runtime::MockSnapshot snapshot; - Runtime::MockLoader loader; - HandlerInfoImpl handler_info; - - EXPECT_CALL(snapshot, getAll()).WillRepeatedly(testing::ReturnRef(entries)); - EXPECT_CALL(loader, snapshot()).WillRepeatedly(testing::ReturnPointee(&snapshot)); - EXPECT_CALL(server_, runtime()).WillRepeatedly(testing::ReturnPointee(&loader)); - - EXPECT_EQ(Http::Code::BadRequest, - admin_.runCallback("/runtime?format=foo", header_map, response, handler_info)); - EXPECT_EQ("usage: /runtime?format=json\n", TestUtility::bufferToString(response)); -======= EXPECT_EQ(Http::Code::BadRequest, getCallback("/runtime_modify", header_map, response)); EXPECT_TRUE(absl::StartsWith(TestUtility::bufferToString(response), "usage:")); ->>>>>>> aa61c6c34f7bd4c1448649686b5bd7511aaa8d51 } TEST(PrometheusStatsFormatter, MetricName) { From d0cb0b63055b02b5f5797a21ae7f2f1c5612f623 Mon Sep 17 00:00:00 2001 From: trabetti Date: Wed, 11 Apr 2018 16:47:07 +0300 Subject: [PATCH 18/25] added log message Signed-off-by: trabetti --- source/common/common/logger.h | 1 + source/extensions/stat_sinks/common/hystrix/hystrix.cc | 3 +-- source/extensions/stat_sinks/common/hystrix/hystrix.h | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/source/common/common/logger.h b/source/common/common/logger.h index 9d1b0b3661680..b6ce4d361d01b 100644 --- a/source/common/common/logger.h +++ b/source/common/common/logger.h @@ -32,6 +32,7 @@ namespace Logger { FUNCTION(hc) \ FUNCTION(http) \ FUNCTION(http2) \ + FUNCTION(hystrix) \ FUNCTION(lua) \ FUNCTION(main) \ FUNCTION(mongo) \ diff --git a/source/extensions/stat_sinks/common/hystrix/hystrix.cc b/source/extensions/stat_sinks/common/hystrix/hystrix.cc index 8900e0e3fb70d..1a037b4cd934c 100644 --- a/source/extensions/stat_sinks/common/hystrix/hystrix.cc +++ b/source/extensions/stat_sinks/common/hystrix/hystrix.cc @@ -115,8 +115,7 @@ void Hystrix::updateRollingWindowMap(std::map current_sta uint64_t total = errors + timeouts + success + rejected; pushNewValue(counter_name_lookup[cluster_name]["total"], total); - // TODO (@trabetti) : why does it fail compilation? - // ENVOY_LOG(trace, "{}", printRollingWindow()); + ENVOY_LOG(trace, "{}", printRollingWindow()); } void Hystrix::resetRollingWindow() { rolling_stats_map_.clear(); } diff --git a/source/extensions/stat_sinks/common/hystrix/hystrix.h b/source/extensions/stat_sinks/common/hystrix/hystrix.h index 8483c12414f59..1da91601f4f2d 100644 --- a/source/extensions/stat_sinks/common/hystrix/hystrix.h +++ b/source/extensions/stat_sinks/common/hystrix/hystrix.h @@ -14,8 +14,7 @@ namespace Common { typedef std::vector RollingStats; typedef std::map RollingStatsMap; -class Hystrix { - +class Hystrix : public Logger::Loggable { public: Hystrix() : current_index_(DEFAULT_NUM_OF_BUCKETS), num_of_buckets_(DEFAULT_NUM_OF_BUCKETS + 1){}; From d33c1b7dcbdf8a9ac72c2ffaa61dcaf2d026237e Mon Sep 17 00:00:00 2001 From: trabetti Date: Mon, 16 Apr 2018 16:18:35 +0300 Subject: [PATCH 19/25] moved addHandler and hystrix handler to sink Signed-off-by: trabetti --- include/envoy/server/admin.h | 18 +-- include/envoy/server/instance.h | 8 +- .../stat_sinks/common/hystrix/BUILD | 2 +- .../stat_sinks/common/hystrix/hystrix.cc | 38 +++++- .../stat_sinks/common/hystrix/hystrix.h | 20 +--- source/server/BUILD | 2 + source/server/config_validation/server.h | 2 +- source/server/http/BUILD | 1 - source/server/http/admin.cc | 110 +++++++----------- source/server/http/admin.h | 58 ++++----- source/server/server.cc | 15 +-- source/server/server.h | 2 +- .../common/hystrix/hystrix_test.cc | 6 +- test/mocks/server/mocks.h | 2 +- test/server/http/admin_test.cc | 24 ++-- 15 files changed, 137 insertions(+), 171 deletions(-) diff --git a/include/envoy/server/admin.h b/include/envoy/server/admin.h index 5950302186649..ffc62d856cc13 100644 --- a/include/envoy/server/admin.h +++ b/include/envoy/server/admin.h @@ -24,22 +24,10 @@ namespace Server { */ #define MAKE_ADMIN_HANDLER(X) \ [this](absl::string_view path_and_query, Http::HeaderMap& response_headers, \ - Buffer::Instance& data, Server::HandlerInfo& handler_info) -> Http::Code { \ - return X(path_and_query, response_headers, data, handler_info); \ + Buffer::Instance& data, Http::StreamDecoderFilterCallbacks* callbacks) -> Http::Code { \ + return X(path_and_query, response_headers, data, callbacks); \ } -/** - * This class is a base class for data which will be sent from admin filter to a handler - * in admin impl. Each handler which needs to receive data from admin filter can inherit from - * HandlerInfo and build a class which contains the relevant data. - */ -class HandlerInfo { -public: - virtual ~HandlerInfo(){}; -}; - -typedef std::unique_ptr HandlerInfoPtr; - /** * Global admin HTTP endpoint for the server. */ @@ -57,7 +45,7 @@ class Admin { */ typedef std::function + Http::StreamDecoderFilterCallbacks* callbacks)> HandlerCb; diff --git a/include/envoy/server/instance.h b/include/envoy/server/instance.h index 8b6e35ec8a78a..f2e82535720f9 100644 --- a/include/envoy/server/instance.h +++ b/include/envoy/server/instance.h @@ -185,12 +185,8 @@ class Instance { */ virtual std::chrono::milliseconds statsFlushInterval() PURE; - // TODO (@trabetti) : for the two methods below - too specific? what is a better way? - /** - * @ Registers a callbacks connection to Hystrix sink. - */ - virtual bool registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) PURE; - + // TODO (@trabetti) : for the method below - too specific? what is a better way? it is called on + // callback destroy. /** * @ Unregisters a callbacks connection to Hystrix sink. */ diff --git a/source/extensions/stat_sinks/common/hystrix/BUILD b/source/extensions/stat_sinks/common/hystrix/BUILD index 2a9fd7b87f1ac..06dfdea8dcdc9 100644 --- a/source/extensions/stat_sinks/common/hystrix/BUILD +++ b/source/extensions/stat_sinks/common/hystrix/BUILD @@ -13,10 +13,10 @@ envoy_cc_library( srcs = ["hystrix.cc"], hdrs = ["hystrix.h"], deps = [ - "//include/envoy/server:admin_interface", "//include/envoy/server:instance_interface", "//include/envoy/stats:stats_interface", "//source/common/buffer:buffer_lib", "//source/common/common:logger_lib", + "//source/server/http:admin_lib", ], ) diff --git a/source/extensions/stat_sinks/common/hystrix/hystrix.cc b/source/extensions/stat_sinks/common/hystrix/hystrix.cc index 1a037b4cd934c..8907ef8cb99c9 100644 --- a/source/extensions/stat_sinks/common/hystrix/hystrix.cc +++ b/source/extensions/stat_sinks/common/hystrix/hystrix.cc @@ -8,6 +8,8 @@ #include "common/buffer/buffer_impl.h" #include "common/common/logger.h" +#include "server/http/admin.h" + #include "absl/strings/str_cat.h" namespace Envoy { @@ -244,7 +246,7 @@ void Hystrix::getClusterStats(std::stringstream& ss, absl::string_view cluster_n addHystrixThreadPool(ss, cluster_name, max_concurrent_requests, reporting_hosts, rolling_window); } -absl::string_view Hystrix::printRollingWindow() const { +const std::string Hystrix::printRollingWindow() const { std::stringstream out_str; for (auto stats_map_itr = rolling_stats_map_.begin(); stats_map_itr != rolling_stats_map_.end(); @@ -261,7 +263,36 @@ absl::string_view Hystrix::printRollingWindow() const { } namespace HystrixNameSpace { -HystrixSink::HystrixSink(Server::Instance& server) : stats_(new Hystrix()), server_(&server){}; +HystrixSink::HystrixSink(Server::Instance& server) : stats_(new Hystrix()), server_(&server) { + Server::Admin& admin = server_->admin(); + ENVOY_LOG(debug, + "adding hystrix_event_stream endpoint to enable connection to hystrix dashboard"); + admin.addHandler("/hystrix_event_stream", "send hystrix event stream", + MAKE_ADMIN_HANDLER(handlerHystrixEventStream), false, false); +}; + +Http::Code HystrixSink::handlerHystrixEventStream(absl::string_view, + Http::HeaderMap& response_headers, + Buffer::Instance&, + Http::StreamDecoderFilterCallbacks* callbacks) { + + response_headers.insertContentType().value().setReference( + Http::Headers::get().ContentTypeValues.TextEventStream); + response_headers.insertCacheControl().value().setReference( + Http::Headers::get().CacheControlValues.NoCache); + response_headers.insertConnection().value().setReference( + Http::Headers::get().ConnectionValues.Close); + response_headers.insertAccessControlAllowHeaders().value().setReference( + Http::Headers::get().AccessControlAllowHeadersValue.AccessControlAllowHeadersHystrix); + response_headers.insertAccessControlAllowOrigin().value().setReference( + Http::Headers::get().AccessControlAllowOriginValue.All); + response_headers.insertNoChunks().value().setReference("0"); + + registerConnection(callbacks); + ENVOY_LOG(debug, "start sending data to hystrix dashboard on port {}", + callbacks->connection()->localAddress()->asString()); + return Http::Code::OK; +} void HystrixSink::beginFlush() { current_stat_values_.clear(); } @@ -305,12 +336,11 @@ void HystrixSink::endFlush() { callbacks_->encodeData(ping_data, false); } -// void HystrixSink::onHistogramComplete(const Histogram& histogram, uint64_t value); void HystrixSink::registerConnection(Http::StreamDecoderFilterCallbacks* callbacks) { callbacks_ = callbacks; } -// TODO (@trabetti) is this correct way? +// TODO (@trabetti) is this correct way - to set nullptr? void HystrixSink::unregisterConnection() { callbacks_ = nullptr; } } // namespace HystrixNameSpace diff --git a/source/extensions/stat_sinks/common/hystrix/hystrix.h b/source/extensions/stat_sinks/common/hystrix/hystrix.h index 1da91601f4f2d..92a58219da887 100644 --- a/source/extensions/stat_sinks/common/hystrix/hystrix.h +++ b/source/extensions/stat_sinks/common/hystrix/hystrix.h @@ -52,7 +52,7 @@ class Hystrix : public Logger::Loggable { /** * Return string represnting current state of the map. for DEBUG. */ - absl::string_view printRollingWindow() const; + const std::string printRollingWindow() const; /** * Get the statistic's value change over the rolling window time frame. @@ -105,23 +105,14 @@ class Hystrix : public Logger::Loggable { typedef std::unique_ptr HystrixPtr; -/** - * This class contains data which will be sent from admin filter to a hystrix_event_stream handler - * and build a class which contains the relevant data. - */ -class HystrixHandlerInfoImpl : public Server::HandlerInfo { -public: - HystrixHandlerInfoImpl(Http::StreamDecoderFilterCallbacks* callbacks) : callbacks_(callbacks) {} - virtual ~HystrixHandlerInfoImpl(){}; - - Http::StreamDecoderFilterCallbacks* callbacks_{}; -}; - namespace HystrixNameSpace { -class HystrixSink : public Stats::Sink { +class HystrixSink : public Stats::Sink, public Logger::Loggable { public: HystrixSink(Server::Instance& server); + Http::Code handlerHystrixEventStream(absl::string_view, Http::HeaderMap& response_headers, + Buffer::Instance&, + Http::StreamDecoderFilterCallbacks* callbacks); void beginFlush(); void flushCounter(const Stats::Counter& counter, uint64_t delta); void flushGauge(const Stats::Gauge&, uint64_t){}; @@ -131,6 +122,7 @@ class HystrixSink : public Stats::Sink { << std::endl; }; + // TODO (@trabetti) : support multiple connections /** * register a new connection */ diff --git a/source/server/BUILD b/source/server/BUILD index 293f396f4166e..750e69d566184 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -270,6 +270,8 @@ envoy_cc_library( "//source/common/singleton:manager_impl_lib", "//source/common/stats:thread_local_store_lib", "//source/common/upstream:cluster_manager_lib", + "//source/extensions/stat_sinks/common/hystrix:hystrix_lib", + "//source/extensions/stat_sinks/common/statsd:statsd_lib", "//source/server/http:admin_lib", "@envoy_api//envoy/config/bootstrap/v2:bootstrap_cc", ], diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index 981163a89109b..102f9ccb51d0d 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -90,7 +90,7 @@ class ValidationInstance : Logger::Loggable, const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } std::chrono::milliseconds statsFlushInterval() override { return config_->statsFlushInterval(); } - bool registerToHystrixSink(Http::StreamDecoderFilterCallbacks*) override { NOT_IMPLEMENTED; } + // bool registerToHystrixSink(Http::StreamDecoderFilterCallbacks*) override { NOT_IMPLEMENTED; } void unregisterHystrixSink() override { NOT_IMPLEMENTED; } // Server::ListenerComponentFactory diff --git a/source/server/http/BUILD b/source/server/http/BUILD index 5da20a05768a6..abe26aedb479c 100644 --- a/source/server/http/BUILD +++ b/source/server/http/BUILD @@ -51,7 +51,6 @@ envoy_cc_library( "//source/common/router:config_lib", "//source/common/upstream:host_utility_lib", "//source/extensions/access_loggers/file:file_access_log_lib", - "//source/extensions/stat_sinks/common/hystrix:hystrix_lib", "@envoy_api//envoy/admin/v2:config_dump_cc", ], ) diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 2f3e79e5e56be..e57d15483b367 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -222,7 +222,8 @@ void AdminImpl::addCircuitSettings(const std::string& cluster_name, const std::s } Http::Code AdminImpl::handlerClusters(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*) { response.add(fmt::format("version_info::{}\n", server_.clusterManager().versionInfo())); for (auto& cluster : server_.clusterManager().clusters()) { @@ -280,7 +281,8 @@ Http::Code AdminImpl::handlerClusters(absl::string_view, Http::HeaderMap&, // TODO(jsedgwick) Use query params to list available dumps, selectively dump, etc Http::Code AdminImpl::handlerConfigDump(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) const { + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*) const { envoy::admin::v2::ConfigDump dump; auto& config_dump_map = *(dump.mutable_configs()); for (const auto& key_callback_pair : config_tracker_.getCallbacksMap()) { @@ -296,7 +298,8 @@ Http::Code AdminImpl::handlerConfigDump(absl::string_view, Http::HeaderMap&, } Http::Code AdminImpl::handlerCpuProfiler(absl::string_view url, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*) { Http::Utility::QueryParams query_params = Http::Utility::parseQueryString(url); if (query_params.size() != 1 || query_params.begin()->first != "enable" || (query_params.begin()->second != "y" && query_params.begin()->second != "n")) { @@ -320,27 +323,31 @@ Http::Code AdminImpl::handlerCpuProfiler(absl::string_view url, Http::HeaderMap& } Http::Code AdminImpl::handlerHealthcheckFail(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*) { server_.failHealthcheck(true); response.add("OK\n"); return Http::Code::OK; } Http::Code AdminImpl::handlerHealthcheckOk(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*) { server_.failHealthcheck(false); response.add("OK\n"); return Http::Code::OK; } Http::Code AdminImpl::handlerHotRestartVersion(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*) { response.add(server_.hotRestart().version()); return Http::Code::OK; } Http::Code AdminImpl::handlerLogging(absl::string_view url, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*) { Http::Utility::QueryParams query_params = Http::Utility::parseQueryString(url); Http::Code rc = Http::Code::OK; @@ -366,7 +373,8 @@ Http::Code AdminImpl::handlerLogging(absl::string_view url, Http::HeaderMap&, } Http::Code AdminImpl::handlerResetCounters(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*) { for (const Stats::CounterSharedPtr& counter : server_.stats().counters()) { counter->reset(); } @@ -376,7 +384,8 @@ Http::Code AdminImpl::handlerResetCounters(absl::string_view, Http::HeaderMap&, } Http::Code AdminImpl::handlerServerInfo(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*) { time_t current_time = time(nullptr); response.add(fmt::format("envoy {} {} {} {} {}\n", VersionInfo::version(), server_.healthCheckFailed() ? "draining" : "live", @@ -387,7 +396,8 @@ Http::Code AdminImpl::handlerServerInfo(absl::string_view, Http::HeaderMap&, } Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo& handler_info) { + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks* callbacks) { // We currently don't support timers locally (only via statsd) so just group all the counters // and gauges together, alpha sort them, and spit them out. Http::Code rc = Http::Code::OK; @@ -414,7 +424,7 @@ Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& respo Http::Headers::get().ContentTypeValues.Json); response.add(AdminImpl::statsAsJson(all_stats)); } else if (format_key == "format" && format_value == "prometheus") { - return handlerPrometheusStats(url, response_headers, response, handler_info); + return handlerPrometheusStats(url, response_headers, response, callbacks); } else { response.add("usage: /stats?format=json \n"); response.add("\n"); @@ -425,7 +435,8 @@ Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& respo } Http::Code AdminImpl::handlerPrometheusStats(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*) { PrometheusStatsFormatter::statsAsPrometheus(server_.stats().counters(), server_.stats().gauges(), response); return Http::Code::OK; @@ -504,14 +515,16 @@ std::string AdminImpl::statsAsJson(const std::map& all_st } Http::Code AdminImpl::handlerQuitQuitQuit(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*) { server_.shutdown(); response.add("OK\n"); return Http::Code::OK; } Http::Code AdminImpl::handlerListenerInfo(absl::string_view, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&) { + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*) { response_headers.insertContentType().value().setReference( Http::Headers::get().ContentTypeValues.Json); std::list listeners; @@ -523,7 +536,7 @@ Http::Code AdminImpl::handlerListenerInfo(absl::string_view, Http::HeaderMap& re } Http::Code AdminImpl::handlerCerts(absl::string_view, Http::HeaderMap&, Buffer::Instance& response, - HandlerInfo&) { + Http::StreamDecoderFilterCallbacks*) { // This set is used to track distinct certificates. We may have multiple listeners, upstreams, etc // using the same cert. std::unordered_set context_info_set; @@ -542,7 +555,8 @@ Http::Code AdminImpl::handlerCerts(absl::string_view, Http::HeaderMap&, Buffer:: } Http::Code AdminImpl::handlerRuntime(absl::string_view url, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&) { + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*) { const Http::Utility::QueryParams params = Http::Utility::parseQueryString(url); response_headers.insertContentType().value().setReference( Http::Headers::get().ContentTypeValues.Json); @@ -629,43 +643,9 @@ std::string AdminImpl::runtimeAsJson( return strbuf.GetString(); } -Http::Code AdminImpl::handlerHystrixEventStream(absl::string_view, - Http::HeaderMap& response_headers, - Buffer::Instance&, HandlerInfo& handler_info) { - - response_headers.insertContentType().value().setReference( - Http::Headers::get().ContentTypeValues.TextEventStream); - response_headers.insertCacheControl().value().setReference( - Http::Headers::get().CacheControlValues.NoCache); - response_headers.insertConnection().value().setReference( - Http::Headers::get().ConnectionValues.Close); - response_headers.insertAccessControlAllowHeaders().value().setReference( - Http::Headers::get().AccessControlAllowHeadersValue.AccessControlAllowHeadersHystrix); - response_headers.insertAccessControlAllowOrigin().value().setReference( - Http::Headers::get().AccessControlAllowOriginValue.All); - response_headers.insertNoChunks().value().setReference("0"); - - Extensions::StatSinks::Common::HystrixHandlerInfoImpl& hystrix_handler_info = - dynamic_cast(handler_info); - - // TODO (@trabetti) : what should we do when a sink not present? - // is this error message to user enough? - // does it make sense to response with 503? - if (server_.registerToHystrixSink(hystrix_handler_info.callbacks_)) { - ENVOY_LOG(debug, "start sending data to hystrix dashboard on port {}", - hystrix_handler_info.callbacks_->connection()->localAddress()->asString()); - return Http::Code::OK; - } else { - ENVOY_LOG( - warn, - "Hystrix sink is not set up in config file, could not establish connection on port {}", - hystrix_handler_info.callbacks_->connection()->localAddress()->asString()); - return Http::Code::ServiceUnavailable; - } -} - Http::Code AdminImpl::handlerRuntimeModify(absl::string_view url, Http::HeaderMap&, - Buffer::Instance& response, HandlerInfo&) { + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*) { const Http::Utility::QueryParams params = Http::Utility::parseQueryString(url); if (params.empty()) { response.add("usage: /runtime_modify?key1=value1&key2=value2&keyN=valueN\n"); @@ -688,16 +668,10 @@ void AdminFilter::onComplete() { Buffer::OwnedImpl response; Http::HeaderMapPtr header_map{new Http::HeaderMapImpl}; RELEASE_ASSERT(request_headers_); - Http::Code code; + Http::Code code = parent_.runCallback(path, *request_headers_, *header_map, response, callbacks_); bool end_stream = true; - if (path.find("/hystrix_event_stream") == std::string::npos) { - handler_info_ = std::make_unique(); - code = parent_.runCallback(path, *request_headers_, *header_map, response, *handler_info_); - } else { - handler_info_ = - std::make_unique(callbacks_); - code = parent_.runCallback(path, *request_headers_, *header_map, response, *handler_info_); + if (path.find("/hystrix_event_stream") != std::string::npos) { end_stream = false; } header_map->insertStatus().value(std::to_string(enumToInt(code))); @@ -766,8 +740,9 @@ AdminImpl::AdminImpl(const std::string& access_log_path, const std::string& prof {"/runtime", "print runtime values", MAKE_ADMIN_HANDLER(handlerRuntime), false, false}, {"/runtime_modify", "modify runtime values", MAKE_ADMIN_HANDLER(handlerRuntimeModify), false, true}, - {"/hystrix_event_stream", "send hystrix event stream", - MAKE_ADMIN_HANDLER(handlerHystrixEventStream), false, false}}, + // {"/hystrix_event_stream", "send hystrix event stream", + // MAKE_ADMIN_HANDLER(handlerHystrixEventStream), false, false} + }, // TODO(jsedgwick) add /runtime_reset endpoint that removes all admin-set values listener_(*this, std::move(listener_scope)) { @@ -810,7 +785,7 @@ void AdminImpl::createFilterChain(Http::FilterChainFactoryCallbacks& callbacks) Http::Code AdminImpl::runCallback(absl::string_view path_and_query, const Http::HeaderMap& request_headers, Http::HeaderMap& response_headers, Buffer::Instance& response, - HandlerInfo& handler_info) { + Http::StreamDecoderFilterCallbacks* callbacks) { Http::Code code = Http::Code::OK; bool found_handler = false; @@ -828,7 +803,7 @@ Http::Code AdminImpl::runCallback(absl::string_view path_and_query, handler.prefix_, method); } } - code = handler.handler_(path_and_query, response_headers, response, handler_info); + code = handler.handler_(path_and_query, response_headers, response, callbacks); found_handler = true; break; } @@ -838,7 +813,7 @@ Http::Code AdminImpl::runCallback(absl::string_view path_and_query, // Extra space is emitted below to have "invalid path." be a separate sentence in the // 404 output from "admin commands are:" in handlerHelp. response.add("invalid path. "); - handlerHelp(path_and_query, response_headers, response, handler_info); + handlerHelp(path_and_query, response_headers, response, callbacks); code = Http::Code::NotFound; } @@ -857,7 +832,7 @@ std::vector AdminImpl::sortedHandlers() const { } Http::Code AdminImpl::handlerHelp(absl::string_view, Http::HeaderMap&, Buffer::Instance& response, - HandlerInfo&) { + Http::StreamDecoderFilterCallbacks*) { response.add("admin commands are:\n"); // Prefix order is used during searching, but for printing do them in alpha order. @@ -868,7 +843,8 @@ Http::Code AdminImpl::handlerHelp(absl::string_view, Http::HeaderMap&, Buffer::I } Http::Code AdminImpl::handlerAdminHome(absl::string_view, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&) { + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*) { response_headers.insertContentType().value().setReference( Http::Headers::get().ContentTypeValues.Html); diff --git a/source/server/http/admin.h b/source/server/http/admin.h index 1664ec00812b6..9cf99dfc2277e 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -26,23 +26,11 @@ #include "server/http/config_tracker_impl.h" -#include "extensions/stat_sinks/common/hystrix/hystrix.h" - #include "absl/strings/string_view.h" namespace Envoy { namespace Server { -/** - * This class contains data which will be sent from admin filter to a handler - * and build a class which contains the relevant data. - */ -class HandlerInfoImpl : public HandlerInfo { -public: - HandlerInfoImpl(){}; - virtual ~HandlerInfoImpl(){}; -}; - /** * Implementation of Server::Admin. */ @@ -58,7 +46,7 @@ class AdminImpl : public Admin, Http::Code runCallback(absl::string_view path_and_query, const Http::HeaderMap& request_headers, Http::HeaderMap& response_headers, Buffer::Instance& response, - HandlerInfo& handler_info); + Http::StreamDecoderFilterCallbacks* callbacks); const Network::Socket& socket() override { return *socket_; } Network::Socket& mutable_socket() { return *socket_; } Network::ListenerConfig& listener() { return listener_; } @@ -159,53 +147,52 @@ class AdminImpl : public Admin, * URL handlers. */ Http::Code handlerAdminHome(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&); + Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); Http::Code handlerCerts(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&); + Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); Http::Code handlerClusters(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&); + Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); Http::Code handlerConfigDump(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&) const; + Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*) const; Http::Code handlerCpuProfiler(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&); + Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); Http::Code handlerHealthcheckFail(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, - HandlerInfo&); + Http::StreamDecoderFilterCallbacks*); Http::Code handlerHealthcheckOk(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, - HandlerInfo&); + Http::StreamDecoderFilterCallbacks*); Http::Code handlerHelp(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&); + Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); Http::Code handlerHotRestartVersion(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, - HandlerInfo&); + Http::StreamDecoderFilterCallbacks*); Http::Code handlerListenerInfo(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, - HandlerInfo&); + Http::StreamDecoderFilterCallbacks*); Http::Code handlerLogging(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&); - Http::Code handlerMain(const std::string& path, Buffer::Instance& response, HandlerInfo&); + Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); + Http::Code handlerMain(const std::string& path, Buffer::Instance& response, + Http::StreamDecoderFilterCallbacks*); Http::Code handlerQuitQuitQuit(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, - HandlerInfo&); + Http::StreamDecoderFilterCallbacks*); Http::Code handlerResetCounters(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, - HandlerInfo&); + Http::StreamDecoderFilterCallbacks*); Http::Code handlerServerInfo(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&); + Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); Http::Code handlerStats(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&); + Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); Http::Code handlerPrometheusStats(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, - HandlerInfo&); + Http::StreamDecoderFilterCallbacks*); Http::Code handlerRuntime(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, HandlerInfo&); + Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); Http::Code handlerRuntimeModify(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, - HandlerInfo&); - Http::Code handlerHystrixEventStream(absl::string_view path_and_query, - Http::HeaderMap& response_headers, Buffer::Instance&, - HandlerInfo& handler_info); + Http::StreamDecoderFilterCallbacks*); class AdminListener : public Network::ListenerConfig { public: @@ -278,7 +265,6 @@ class AdminFilter : public Http::StreamDecoderFilter, Logger::LoggableenableTimer(config_->statsFlushInterval()); } -bool InstanceImpl::registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) { - for (const auto& sink : config_->statsSinks()) { - // TODO: is there a better way to find the hystrix sink? - Extensions::StatSinks::Common::HystrixNameSpace::HystrixSink* hystrix_sink = - dynamic_cast(sink.get()); - if (hystrix_sink != nullptr) { - hystrix_sink->registerConnection(callbacks); - return true; - } - } - return false; -} - void InstanceImpl::unregisterHystrixSink() { for (const auto& sink : config_->statsSinks()) { // TODO: is there a better way to find the hystrix sink? diff --git a/source/server/server.h b/source/server/server.h index 74472139fa007..b02b4f9ec9471 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -169,7 +169,7 @@ class InstanceImpl : Logger::Loggable, public Instance { const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } std::chrono::milliseconds statsFlushInterval() override { return config_->statsFlushInterval(); } - bool registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) override; + // bool registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) override; void unregisterHystrixSink() override; private: diff --git a/test/extensions/stats_sinks/common/hystrix/hystrix_test.cc b/test/extensions/stats_sinks/common/hystrix/hystrix_test.cc index b4f52a643811e..f84881f068da1 100644 --- a/test/extensions/stats_sinks/common/hystrix/hystrix_test.cc +++ b/test/extensions/stats_sinks/common/hystrix/hystrix_test.cc @@ -121,9 +121,9 @@ TEST_F(HystrixSinkTest, BasicFlow) { } // //std::string rolling_map = sink_->getStats().printRollingWindow(); - // absl::string_view rolling_map = sink_->getStats().printRollingWindow(); - // absl::string_view::size_type pos = rolling_map.find("cluster.test_cluster.total"); - // EXPECT_NE(absl::string_view::npos, pos); + std::string rolling_map = sink_->getStats().printRollingWindow(); + std::size_t pos = rolling_map.find("cluster.test_cluster.total"); + EXPECT_NE(std::string::npos, pos); // //EXPECT_NE(absl::string_view::npos, map.find("cluster.test_cluster.total")); std::string data_message = TestUtility::bufferToString(buffer); diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index 6b1d6828b30b1..775cd8a736466 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -288,7 +288,7 @@ class MockInstance : public Instance { MOCK_METHOD0(threadLocal, ThreadLocal::Instance&()); MOCK_METHOD0(localInfo, const LocalInfo::LocalInfo&()); MOCK_METHOD0(statsFlushInterval, std::chrono::milliseconds()); - MOCK_METHOD1(registerToHystrixSink, bool(Http::StreamDecoderFilterCallbacks* callbacks)); + // MOCK_METHOD1(registerToHystrixSink, bool(Http::StreamDecoderFilterCallbacks* callbacks)); MOCK_METHOD0(unregisterHystrixSink, void()); testing::NiceMock thread_local_; diff --git a/test/server/http/admin_test.cc b/test/server/http/admin_test.cc index 95866c4deaa13..432c6f9ce16eb 100644 --- a/test/server/http/admin_test.cc +++ b/test/server/http/admin_test.cc @@ -90,10 +90,10 @@ class AdminInstanceTest : public testing::TestWithParam callbacks; request_headers_.insertMethod().value(method.data(), method.size()); return admin_.runCallback(path_and_query, request_headers_, response_headers, response, - handler_info); + &callbacks); } Http::Code getCallback(absl::string_view path_and_query, Http::HeaderMap& response_headers, @@ -155,11 +155,11 @@ TEST_P(AdminInstanceTest, AdminBadProfiler) { Http::HeaderMapImpl header_map; const absl::string_view post = Http::Headers::get().MethodValues.Post; request_headers_.insertMethod().value(post.data(), post.size()); - HandlerInfoImpl handler_info; + NiceMock callbacks; EXPECT_NO_LOGS( EXPECT_EQ(Http::Code::InternalServerError, admin_bad_profile_path.runCallback("/cpuprofiler?enable=y", request_headers_, - header_map, data, handler_info))); + header_map, data, &callbacks))); EXPECT_FALSE(Profiler::Cpu::profilerEnabled()); } @@ -184,7 +184,9 @@ TEST_P(AdminInstanceTest, AdminBadAddressOutPath) { TEST_P(AdminInstanceTest, CustomHandler) { auto callback = [](absl::string_view, Http::HeaderMap&, Buffer::Instance&, - HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; + Http::StreamDecoderFilterCallbacks*) -> Http::Code { + return Http::Code::Accepted; + }; // Test removable handler. EXPECT_NO_LOGS(EXPECT_TRUE(admin_.addHandler("/foo/bar", "hello", callback, true, false))); @@ -211,7 +213,9 @@ TEST_P(AdminInstanceTest, CustomHandler) { TEST_P(AdminInstanceTest, RejectHandlerWithXss) { auto callback = [](absl::string_view, Http::HeaderMap&, Buffer::Instance&, - HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; + Http::StreamDecoderFilterCallbacks*) -> Http::Code { + return Http::Code::Accepted; + }; EXPECT_LOG_CONTAINS("error", "filter \"/foo\" contains invalid character '<'", EXPECT_FALSE(admin_.addHandler("/foo", "hello", @@ -220,7 +224,9 @@ TEST_P(AdminInstanceTest, RejectHandlerWithXss) { TEST_P(AdminInstanceTest, RejectHandlerWithEmbeddedQuery) { auto callback = [](absl::string_view, Http::HeaderMap&, Buffer::Instance&, - HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; + Http::StreamDecoderFilterCallbacks*) -> Http::Code { + return Http::Code::Accepted; + }; EXPECT_LOG_CONTAINS("error", "filter \"/bar?queryShouldNotBeInPrefix\" contains invalid character '?'", EXPECT_FALSE(admin_.addHandler("/bar?queryShouldNotBeInPrefix", "hello", @@ -229,7 +235,9 @@ TEST_P(AdminInstanceTest, RejectHandlerWithEmbeddedQuery) { TEST_P(AdminInstanceTest, EscapeHelpTextWithPunctuation) { auto callback = [](absl::string_view, Http::HeaderMap&, Buffer::Instance&, - HandlerInfo&) -> Http::Code { return Http::Code::Accepted; }; + Http::StreamDecoderFilterCallbacks*) -> Http::Code { + return Http::Code::Accepted; + }; // It's OK to have help text with HTML characters in it, but when we render the home // page they need to be escaped. From aac876d870b1c872980edf73af7731ccf092aa2c Mon Sep 17 00:00:00 2001 From: trabetti Date: Tue, 17 Apr 2018 11:33:13 +0300 Subject: [PATCH 20/25] review comments Signed-off-by: trabetti --- .../stat_sinks/common/hystrix/BUILD | 22 ------ source/extensions/stat_sinks/hystrix/BUILD | 15 +++- .../extensions/stat_sinks/hystrix/config.cc | 8 +-- source/extensions/stat_sinks/hystrix/config.h | 4 +- .../{common => }/hystrix/hystrix.cc | 70 +++++++++---------- .../stat_sinks/{common => }/hystrix/hystrix.h | 42 +++++------ source/server/BUILD | 2 +- source/server/http/admin.cc | 2 +- source/server/server.cc | 6 +- .../stats_sinks/common/hystrix/BUILD | 22 ------ test/extensions/stats_sinks/hystrix/BUILD | 14 ++++ .../stats_sinks/hystrix/config_test.cc | 8 +-- .../{common => }/hystrix/hystrix_test.cc | 8 +-- 13 files changed, 100 insertions(+), 123 deletions(-) delete mode 100644 source/extensions/stat_sinks/common/hystrix/BUILD rename source/extensions/stat_sinks/{common => }/hystrix/hystrix.cc (84%) rename source/extensions/stat_sinks/{common => }/hystrix/hystrix.h (79%) delete mode 100644 test/extensions/stats_sinks/common/hystrix/BUILD rename test/extensions/stats_sinks/{common => }/hystrix/hystrix_test.cc (97%) diff --git a/source/extensions/stat_sinks/common/hystrix/BUILD b/source/extensions/stat_sinks/common/hystrix/BUILD deleted file mode 100644 index 06dfdea8dcdc9..0000000000000 --- a/source/extensions/stat_sinks/common/hystrix/BUILD +++ /dev/null @@ -1,22 +0,0 @@ -licenses(["notice"]) # Apache 2 - -load( - "//bazel:envoy_build_system.bzl", - "envoy_cc_library", - "envoy_package", -) - -envoy_package() - -envoy_cc_library( - name = "hystrix_lib", - srcs = ["hystrix.cc"], - hdrs = ["hystrix.h"], - deps = [ - "//include/envoy/server:instance_interface", - "//include/envoy/stats:stats_interface", - "//source/common/buffer:buffer_lib", - "//source/common/common:logger_lib", - "//source/server/http:admin_lib", - ], -) diff --git a/source/extensions/stat_sinks/hystrix/BUILD b/source/extensions/stat_sinks/hystrix/BUILD index aceb5fd4ceb66..ccf336a5e7f1d 100644 --- a/source/extensions/stat_sinks/hystrix/BUILD +++ b/source/extensions/stat_sinks/hystrix/BUILD @@ -18,8 +18,21 @@ envoy_cc_library( "//source/common/network:address_lib", "//source/common/network:resolver_lib", "//source/extensions/stat_sinks:well_known_names", - "//source/extensions/stat_sinks/common/hystrix:hystrix_lib", + "//source/extensions/stat_sinks/hystrix:hystrix_lib", "//source/server:configuration_lib", "@envoy_api//envoy/config/metrics/v2:stats_cc", ], ) + +envoy_cc_library( + name = "hystrix_lib", + srcs = ["hystrix.cc"], + hdrs = ["hystrix.h"], + deps = [ + "//include/envoy/server:instance_interface", + "//include/envoy/stats:stats_interface", + "//source/common/buffer:buffer_lib", + "//source/common/common:logger_lib", + "//source/server/http:admin_lib", + ], +) diff --git a/source/extensions/stat_sinks/hystrix/config.cc b/source/extensions/stat_sinks/hystrix/config.cc index bc404a4643e5f..b42b1e1798180 100644 --- a/source/extensions/stat_sinks/hystrix/config.cc +++ b/source/extensions/stat_sinks/hystrix/config.cc @@ -6,17 +6,17 @@ #include "common/network/resolver_impl.h" -#include "extensions/stat_sinks/common/hystrix/hystrix.h" +#include "extensions/stat_sinks/hystrix/hystrix.h" #include "extensions/stat_sinks/well_known_names.h" namespace Envoy { namespace Extensions { namespace StatSinks { -namespace HystrixNameSpace { +namespace Hystrix { Stats::SinkPtr HystrixSinkFactory::createStatsSink(const Protobuf::Message&, Server::Instance& server) { - return std::make_unique(server); + return std::make_unique(server); } ProtobufTypes::MessagePtr HystrixSinkFactory::createEmptyConfigProto() { @@ -32,7 +32,7 @@ std::string HystrixSinkFactory::name() { return StatsSinkNames::get().HYSTRIX; } static Registry::RegisterFactory register_; -} // namespace HystrixNameSpace +} // namespace Hystrix } // namespace StatSinks } // namespace Extensions } // namespace Envoy diff --git a/source/extensions/stat_sinks/hystrix/config.h b/source/extensions/stat_sinks/hystrix/config.h index 0df98e6c45234..2f3f7c37f8783 100644 --- a/source/extensions/stat_sinks/hystrix/config.h +++ b/source/extensions/stat_sinks/hystrix/config.h @@ -9,7 +9,7 @@ namespace Envoy { namespace Extensions { namespace StatSinks { -namespace HystrixNameSpace { +namespace Hystrix { class HystrixSinkFactory : Logger::Loggable, public Server::Configuration::StatsSinkFactory { @@ -23,7 +23,7 @@ class HystrixSinkFactory : Logger::Loggable, std::string name() override; }; -} // namespace HystrixNameSpace +} // namespace Hystrix } // namespace StatSinks } // namespace Extensions } // namespace Envoy diff --git a/source/extensions/stat_sinks/common/hystrix/hystrix.cc b/source/extensions/stat_sinks/hystrix/hystrix.cc similarity index 84% rename from source/extensions/stat_sinks/common/hystrix/hystrix.cc rename to source/extensions/stat_sinks/hystrix/hystrix.cc index 8907ef8cb99c9..4f46d29ae0547 100644 --- a/source/extensions/stat_sinks/common/hystrix/hystrix.cc +++ b/source/extensions/stat_sinks/hystrix/hystrix.cc @@ -1,4 +1,4 @@ -#include "extensions/stat_sinks/common/hystrix/hystrix.h" +#include "extensions/stat_sinks/hystrix/hystrix.h" #include #include @@ -15,40 +15,39 @@ namespace Envoy { namespace Extensions { namespace StatSinks { -namespace Common { -const uint64_t Hystrix::DEFAULT_NUM_OF_BUCKETS; +const uint64_t HystrixStatCache::DEFAULT_NUM_OF_BUCKETS; // Add new value to rolling window, in place of oldest one. -void Hystrix::pushNewValue(const std::string& key, uint64_t value) { +void HystrixStatCache::pushNewValue(const std::string& key, uint64_t value) { // Create vector if do not exist. // TODO trabetti: why resize + value param didn't work without the if? if (rolling_stats_map_.find(key) == rolling_stats_map_.end()) { - rolling_stats_map_[key].resize(num_of_buckets_, value); + rolling_stats_map_[key].resize(window_size_, value); } else { rolling_stats_map_[key][current_index_] = value; } } -uint64_t Hystrix::getRollingValue(absl::string_view cluster_name, absl::string_view stats) { +uint64_t HystrixStatCache::getRollingValue(absl::string_view cluster_name, absl::string_view stat) { std::string key; - key = absl::StrCat("cluster.", cluster_name, ".", stats); + key = absl::StrCat("cluster.", cluster_name, ".", stat); if (rolling_stats_map_.find(key) != rolling_stats_map_.end()) { // If the counter was reset, the result is negative // better return 0, will be back to normal once one rolling window passes. if (rolling_stats_map_[key][current_index_] < - rolling_stats_map_[key][(current_index_ + 1) % num_of_buckets_]) { + rolling_stats_map_[key][(current_index_ + 1) % window_size_]) { return 0; } else { return rolling_stats_map_[key][current_index_] - - rolling_stats_map_[key][(current_index_ + 1) % num_of_buckets_]; + rolling_stats_map_[key][(current_index_ + 1) % window_size_]; } } else { return 0; } } -void Hystrix::CreateCounterNameLookupForCluster(const std::string& cluster_name) { +void HystrixStatCache::CreateCounterNameLookupForCluster(const std::string& cluster_name) { // Building lookup name map for all specific cluster values. // Every call to the updateRollingWindowMap function should get the appropriate name from the map. std::string cluster_name_with_prefix = absl::StrCat("cluster.", cluster_name, "."); @@ -77,8 +76,8 @@ void Hystrix::CreateCounterNameLookupForCluster(const std::string& cluster_name) counter_name_lookup[cluster_name]["total"] = absl::StrCat(cluster_name_with_prefix, "total"); } -void Hystrix::updateRollingWindowMap(std::map current_stat_values, - std::string cluster_name) { +void HystrixStatCache::updateRollingWindowMap(std::map current_stat_values, + std::string cluster_name) { if (counter_name_lookup.find(cluster_name) == counter_name_lookup.end()) { CreateCounterNameLookupForCluster(cluster_name); @@ -120,20 +119,21 @@ void Hystrix::updateRollingWindowMap(std::map current_sta ENVOY_LOG(trace, "{}", printRollingWindow()); } -void Hystrix::resetRollingWindow() { rolling_stats_map_.clear(); } +void HystrixStatCache::resetRollingWindow() { rolling_stats_map_.clear(); } -void Hystrix::addStringToStream(absl::string_view key, absl::string_view value, - std::stringstream& info) { +void HystrixStatCache::addStringToStream(absl::string_view key, absl::string_view value, + std::stringstream& info) { std::string quoted_value = absl::StrCat("\"", value, "\""); addInfoToStream(key, quoted_value, info); } -void Hystrix::addIntToStream(absl::string_view key, uint64_t value, std::stringstream& info) { +void HystrixStatCache::addIntToStream(absl::string_view key, uint64_t value, + std::stringstream& info) { addInfoToStream(key, std::to_string(value), info); } -void Hystrix::addInfoToStream(absl::string_view key, absl::string_view value, - std::stringstream& info) { +void HystrixStatCache::addInfoToStream(absl::string_view key, absl::string_view value, + std::stringstream& info) { if (!info.str().empty()) { info << ", "; } @@ -141,9 +141,9 @@ void Hystrix::addInfoToStream(absl::string_view key, absl::string_view value, info << added_info; } -void Hystrix::addHystrixCommand(std::stringstream& ss, absl::string_view cluster_name, - uint64_t max_concurrent_requests, uint64_t reporting_hosts, - uint64_t rolling_window) { +void HystrixStatCache::addHystrixCommand(std::stringstream& ss, absl::string_view cluster_name, + uint64_t max_concurrent_requests, uint64_t reporting_hosts, + uint64_t rolling_window) { std::stringstream cluster_info; std::time_t currentTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); addStringToStream("type", "HystrixCommand", cluster_info); @@ -214,9 +214,9 @@ void Hystrix::addHystrixCommand(std::stringstream& ss, absl::string_view cluster ss << "data: {" << cluster_info.str() << "}" << std::endl << std::endl; } -void Hystrix::addHystrixThreadPool(std::stringstream& ss, absl::string_view cluster_name, - uint64_t queue_size, uint64_t reporting_hosts, - uint64_t rolling_window) { +void HystrixStatCache::addHystrixThreadPool(std::stringstream& ss, absl::string_view cluster_name, + uint64_t queue_size, uint64_t reporting_hosts, + uint64_t rolling_window) { std::stringstream cluster_info; addIntToStream("currentPoolSize", 0, cluster_info); @@ -239,22 +239,22 @@ void Hystrix::addHystrixThreadPool(std::stringstream& ss, absl::string_view clus ss << "data: {" << cluster_info.str() << "}" << std::endl << std::endl; } -void Hystrix::getClusterStats(std::stringstream& ss, absl::string_view cluster_name, - uint64_t max_concurrent_requests, uint64_t reporting_hosts, - uint64_t rolling_window) { +void HystrixStatCache::getClusterStats(std::stringstream& ss, absl::string_view cluster_name, + uint64_t max_concurrent_requests, uint64_t reporting_hosts, + uint64_t rolling_window) { addHystrixCommand(ss, cluster_name, max_concurrent_requests, reporting_hosts, rolling_window); addHystrixThreadPool(ss, cluster_name, max_concurrent_requests, reporting_hosts, rolling_window); } -const std::string Hystrix::printRollingWindow() const { +const std::string HystrixStatCache::printRollingWindow() const { std::stringstream out_str; for (auto stats_map_itr = rolling_stats_map_.begin(); stats_map_itr != rolling_stats_map_.end(); ++stats_map_itr) { out_str << stats_map_itr->first << " | "; - RollingStats rolling_stats = stats_map_itr->second; - for (auto specific_stat_vec_itr = rolling_stats.begin(); - specific_stat_vec_itr != rolling_stats.end(); ++specific_stat_vec_itr) { + RollingWindow rolling_window = stats_map_itr->second; + for (auto specific_stat_vec_itr = rolling_window.begin(); + specific_stat_vec_itr != rolling_window.end(); ++specific_stat_vec_itr) { out_str << *specific_stat_vec_itr << " | "; } out_str << std::endl; @@ -262,8 +262,9 @@ const std::string Hystrix::printRollingWindow() const { return out_str.str(); } -namespace HystrixNameSpace { -HystrixSink::HystrixSink(Server::Instance& server) : stats_(new Hystrix()), server_(&server) { +namespace Hystrix { +HystrixSink::HystrixSink(Server::Instance& server) + : stats_(new HystrixStatCache()), server_(&server) { Server::Admin& admin = server_->admin(); ENVOY_LOG(debug, "adding hystrix_event_stream endpoint to enable connection to hystrix dashboard"); @@ -343,8 +344,7 @@ void HystrixSink::registerConnection(Http::StreamDecoderFilterCallbacks* callbac // TODO (@trabetti) is this correct way - to set nullptr? void HystrixSink::unregisterConnection() { callbacks_ = nullptr; } -} // namespace HystrixNameSpace -} // namespace Common +} // namespace Hystrix } // namespace StatSinks } // namespace Extensions } // namespace Envoy diff --git a/source/extensions/stat_sinks/common/hystrix/hystrix.h b/source/extensions/stat_sinks/hystrix/hystrix.h similarity index 79% rename from source/extensions/stat_sinks/common/hystrix/hystrix.h rename to source/extensions/stat_sinks/hystrix/hystrix.h index 92a58219da887..0ba35f8825e57 100644 --- a/source/extensions/stat_sinks/common/hystrix/hystrix.h +++ b/source/extensions/stat_sinks/hystrix/hystrix.h @@ -9,17 +9,17 @@ namespace Envoy { namespace Extensions { namespace StatSinks { -namespace Common { -typedef std::vector RollingStats; -typedef std::map RollingStatsMap; +typedef std::vector RollingWindow; +typedef std::map RollingStatsMap; -class Hystrix : public Logger::Loggable { +class HystrixStatCache : public Logger::Loggable { public: - Hystrix() : current_index_(DEFAULT_NUM_OF_BUCKETS), num_of_buckets_(DEFAULT_NUM_OF_BUCKETS + 1){}; + HystrixStatCache() + : current_index_(DEFAULT_NUM_OF_BUCKETS), window_size_(DEFAULT_NUM_OF_BUCKETS + 1){}; - Hystrix(uint64_t num_of_buckets) - : current_index_(num_of_buckets), num_of_buckets_(num_of_buckets + 1){}; + HystrixStatCache(uint64_t num_of_buckets) + : current_index_(num_of_buckets), window_size_(num_of_buckets + 1){}; /** * Add new value to top of rolling window, pushing out the oldest value. @@ -29,7 +29,7 @@ class Hystrix : public Logger::Loggable { /** * Increment pointer of next value to add to rolling window. */ - void incCounter() { current_index_ = (current_index_ + 1) % num_of_buckets_; } + void incCounter() { current_index_ = (current_index_ + 1) % window_size_; } /** * Generate the streams to be sent to hystrix dashboard. @@ -57,7 +57,7 @@ class Hystrix : public Logger::Loggable { /** * Get the statistic's value change over the rolling window time frame. */ - uint64_t getRollingValue(absl::string_view cluster_name, absl::string_view stats); + uint64_t getRollingValue(absl::string_view cluster_name, absl::string_view stat); private: /** @@ -97,15 +97,15 @@ class Hystrix : public Logger::Loggable { RollingStatsMap rolling_stats_map_; uint64_t current_index_; - uint64_t num_of_buckets_; + const uint64_t window_size_; // TODO(trabetti): do we want this to be configurable through the HystrixSink in config file? static const uint64_t DEFAULT_NUM_OF_BUCKETS = 10; std::map> counter_name_lookup; }; -typedef std::unique_ptr HystrixPtr; +typedef std::unique_ptr HystrixPtr; -namespace HystrixNameSpace { +namespace Hystrix { class HystrixSink : public Stats::Sink, public Logger::Loggable { public: @@ -113,14 +113,11 @@ class HystrixSink : public Stats::Sink, public Logger::Loggable HystrixSinkPtr; -} // namespace HystrixNameSpace -} // namespace Common +} // namespace Hystrix } // namespace StatSinks } // namespace Extensions } // namespace Envoy diff --git a/source/server/BUILD b/source/server/BUILD index 282c3f5748d84..f449ccc2cde5b 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -272,8 +272,8 @@ envoy_cc_library( "//source/common/singleton:manager_impl_lib", "//source/common/stats:thread_local_store_lib", "//source/common/upstream:cluster_manager_lib", - "//source/extensions/stat_sinks/common/hystrix:hystrix_lib", "//source/extensions/stat_sinks/common/statsd:statsd_lib", + "//source/extensions/stat_sinks/hystrix:hystrix_lib", "//source/server/http:admin_lib", "@envoy_api//envoy/config/bootstrap/v2:bootstrap_cc", ], diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index f097916708182..b5e762db85a9a 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -691,7 +691,7 @@ void AdminFilter::onComplete() { callbacks_->encodeHeaders(std::move(header_map), end_stream && response.length() == 0); if (response.length() > 0) { - callbacks_->encodeData(response, true); + callbacks_->encodeData(response, end_stream); } } diff --git a/source/server/server.cc b/source/server/server.cc index de78a759ff397..fe0ff886ce688 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -37,7 +37,7 @@ #include "server/guarddog_impl.h" #include "server/test_hooks.h" -#include "extensions/stat_sinks/common/hystrix/hystrix.h" +#include "extensions/stat_sinks/hystrix/hystrix.h" namespace Envoy { namespace Server { @@ -149,8 +149,8 @@ void InstanceImpl::flushStats() { void InstanceImpl::unregisterHystrixSink() { for (const auto& sink : config_->statsSinks()) { // TODO: is there a better way to find the hystrix sink? - Extensions::StatSinks::Common::HystrixNameSpace::HystrixSink* hystrix_sink = - dynamic_cast(sink.get()); + Extensions::StatSinks::Hystrix::HystrixSink* hystrix_sink = + dynamic_cast(sink.get()); if (hystrix_sink != nullptr) { // TODO (@trabetti) : will want to move to a vector of connections, // need a parameter (callback, hope it will work) to identify which connection to remove diff --git a/test/extensions/stats_sinks/common/hystrix/BUILD b/test/extensions/stats_sinks/common/hystrix/BUILD deleted file mode 100644 index 1d99ec4e13fb0..0000000000000 --- a/test/extensions/stats_sinks/common/hystrix/BUILD +++ /dev/null @@ -1,22 +0,0 @@ -licenses(["notice"]) # Apache 2 - -load( - "//bazel:envoy_build_system.bzl", - "envoy_cc_test", - "envoy_package", -) - -envoy_package() - -envoy_cc_test( - name = "hystrix_test", - srcs = ["hystrix_test.cc"], - deps = [ - "//source/common/stats:stats_lib", - "//source/extensions/stat_sinks/common/hystrix:hystrix_lib", - "//test/mocks/server:server_mocks", - "//test/mocks/stats:stats_mocks", - "//test/mocks/upstream:upstream_mocks", - "//test/test_common:utility_lib", - ], -) diff --git a/test/extensions/stats_sinks/hystrix/BUILD b/test/extensions/stats_sinks/hystrix/BUILD index 88fd181ba24be..67a69c5304c4b 100644 --- a/test/extensions/stats_sinks/hystrix/BUILD +++ b/test/extensions/stats_sinks/hystrix/BUILD @@ -25,3 +25,17 @@ envoy_extension_cc_test( "//test/test_common:utility_lib", ], ) + +envoy_extension_cc_test( + name = "hystrix_test", + srcs = ["hystrix_test.cc"], + extension_name = "envoy.stat_sinks.hystrix", + deps = [ + "//source/common/stats:stats_lib", + "//source/extensions/stat_sinks/hystrix:hystrix_lib", + "//test/mocks/server:server_mocks", + "//test/mocks/stats:stats_mocks", + "//test/mocks/upstream:upstream_mocks", + "//test/test_common:utility_lib", + ], +) diff --git a/test/extensions/stats_sinks/hystrix/config_test.cc b/test/extensions/stats_sinks/hystrix/config_test.cc index 6091ab759e6af..520b36ae51a28 100644 --- a/test/extensions/stats_sinks/hystrix/config_test.cc +++ b/test/extensions/stats_sinks/hystrix/config_test.cc @@ -3,8 +3,8 @@ #include "common/protobuf/utility.h" -#include "extensions/stat_sinks/common/hystrix/hystrix.h" #include "extensions/stat_sinks/hystrix/config.h" +#include "extensions/stat_sinks/hystrix/hystrix.h" #include "extensions/stat_sinks/well_known_names.h" #include "test/mocks/server/mocks.h" @@ -23,7 +23,7 @@ using testing::_; namespace Envoy { namespace Extensions { namespace StatSinks { -namespace HystrixNameSpace { +namespace Hystrix { TEST(StatsConfigTest, ValidHystrixSink) { const std::string name = StatsSinkNames::get().HYSTRIX; @@ -40,10 +40,10 @@ TEST(StatsConfigTest, ValidHystrixSink) { NiceMock server; Stats::SinkPtr sink = factory->createStatsSink(*message, server); EXPECT_NE(sink, nullptr); - EXPECT_NE(dynamic_cast(sink.get()), nullptr); + EXPECT_NE(dynamic_cast(sink.get()), nullptr); } -} // namespace HystrixNameSpace +} // namespace Hystrix } // namespace StatSinks } // namespace Extensions } // namespace Envoy diff --git a/test/extensions/stats_sinks/common/hystrix/hystrix_test.cc b/test/extensions/stats_sinks/hystrix/hystrix_test.cc similarity index 97% rename from test/extensions/stats_sinks/common/hystrix/hystrix_test.cc rename to test/extensions/stats_sinks/hystrix/hystrix_test.cc index f84881f068da1..62b0e50399717 100644 --- a/test/extensions/stats_sinks/common/hystrix/hystrix_test.cc +++ b/test/extensions/stats_sinks/hystrix/hystrix_test.cc @@ -4,7 +4,7 @@ #include "common/stats/stats_impl.h" -#include "extensions/stat_sinks/common/hystrix/hystrix.h" +#include "extensions/stat_sinks/hystrix/hystrix.h" #include "test/mocks/server/mocks.h" #include "test/mocks/stats/mocks.h" @@ -23,8 +23,7 @@ using testing::_; namespace Envoy { namespace Extensions { namespace StatSinks { -namespace Common { -namespace HystrixNameSpace { +namespace Hystrix { class HystrixSinkTest : public testing::Test { public: @@ -186,8 +185,7 @@ TEST_F(HystrixSinkTest, Disconnect) { EXPECT_EQ(buffer.length(), 0); } -} // namespace HystrixNameSpace -} // namespace Common +} // namespace Hystrix } // namespace StatSinks } // namespace Extensions } // namespace Envoy From 9a3c0bf22fd7b54c0dbd9d74106768975d25bc04 Mon Sep 17 00:00:00 2001 From: trabetti Date: Tue, 17 Apr 2018 15:00:57 +0300 Subject: [PATCH 21/25] support multiple connections Signed-off-by: trabetti --- include/envoy/server/instance.h | 2 +- .../extensions/stat_sinks/hystrix/hystrix.cc | 32 +++++++++++++------ .../extensions/stat_sinks/hystrix/hystrix.h | 6 ++-- source/server/config_validation/server.h | 2 +- source/server/http/admin.cc | 2 -- source/server/http/admin.h | 6 ++-- source/server/server.cc | 4 +-- source/server/server.h | 2 +- .../stats_sinks/hystrix/hystrix_test.cc | 2 +- test/mocks/server/mocks.h | 2 +- 10 files changed, 36 insertions(+), 24 deletions(-) diff --git a/include/envoy/server/instance.h b/include/envoy/server/instance.h index f2e82535720f9..b350d21788a37 100644 --- a/include/envoy/server/instance.h +++ b/include/envoy/server/instance.h @@ -190,7 +190,7 @@ class Instance { /** * @ Unregisters a callbacks connection to Hystrix sink. */ - virtual void unregisterHystrixSink() PURE; + virtual void unregisterHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks_to_remove) PURE; }; } // namespace Server diff --git a/source/extensions/stat_sinks/hystrix/hystrix.cc b/source/extensions/stat_sinks/hystrix/hystrix.cc index 4f46d29ae0547..0d1ea0d82ce1b 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.cc +++ b/source/extensions/stat_sinks/hystrix/hystrix.cc @@ -298,7 +298,7 @@ Http::Code HystrixSink::handlerHystrixEventStream(absl::string_view, void HystrixSink::beginFlush() { current_stat_values_.clear(); } void HystrixSink::flushCounter(const Stats::Counter& counter, uint64_t) { - if (callbacks_ == nullptr) { + if (callbacks_list_.empty()) { return; } if (counter.name().find("upstream_rq_") != std::string::npos) { @@ -307,7 +307,7 @@ void HystrixSink::flushCounter(const Stats::Counter& counter, uint64_t) { } // void HystrixSink::flushGauge(const Gauge& gauge, uint64_t value); void HystrixSink::endFlush() { - if (callbacks_ == nullptr) + if (callbacks_list_.empty()) return; stats_->incCounter(); for (auto& cluster : server_->clusterManager().clusters()) { @@ -328,21 +328,33 @@ void HystrixSink::endFlush() { server_->statsFlushInterval().count()); } Buffer::OwnedImpl data; - data.add(ss.str()); - callbacks_->encodeData(data, false); + for (auto callbacks : callbacks_list_) { + data.add(ss.str()); + callbacks->encodeData(data, false); + } // send keep alive ping + // TODO (@trabetti) : is it ok to send together with data? Buffer::OwnedImpl ping_data; - ping_data.add(":\n\n"); - callbacks_->encodeData(ping_data, false); + for (auto callbacks : callbacks_list_) { + ping_data.add(":\n\n"); + callbacks->encodeData(ping_data, false); + } } -void HystrixSink::registerConnection(Http::StreamDecoderFilterCallbacks* callbacks) { - callbacks_ = callbacks; +void HystrixSink::registerConnection(Http::StreamDecoderFilterCallbacks* callbacks_to_register) { + callbacks_list_.emplace_back(callbacks_to_register); } -// TODO (@trabetti) is this correct way - to set nullptr? -void HystrixSink::unregisterConnection() { callbacks_ = nullptr; } +void HystrixSink::unregisterConnection(Http::StreamDecoderFilterCallbacks* callbacks_to_remove) { + for (auto it = callbacks_list_.begin(); it != callbacks_list_.end();) { + if ((*it)->streamId() == callbacks_to_remove->streamId()) { + it = callbacks_list_.erase(it); + } else { + ++it; + } + } +} } // namespace Hystrix } // namespace StatSinks diff --git a/source/extensions/stat_sinks/hystrix/hystrix.h b/source/extensions/stat_sinks/hystrix/hystrix.h index 0ba35f8825e57..e4e4885ae2b59 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.h +++ b/source/extensions/stat_sinks/hystrix/hystrix.h @@ -123,16 +123,16 @@ class HystrixSink : public Stats::Sink, public Logger::Loggable callbacks_list_{}; Server::Instance* server_; std::map current_stat_values_; }; diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index 102f9ccb51d0d..be3fa7388af82 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -91,7 +91,7 @@ class ValidationInstance : Logger::Loggable, std::chrono::milliseconds statsFlushInterval() override { return config_->statsFlushInterval(); } // bool registerToHystrixSink(Http::StreamDecoderFilterCallbacks*) override { NOT_IMPLEMENTED; } - void unregisterHystrixSink() override { NOT_IMPLEMENTED; } + void unregisterHystrixSink(Http::StreamDecoderFilterCallbacks*) override { NOT_IMPLEMENTED; } // Server::ListenerComponentFactory std::vector createNetworkFilterFactoryList( diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index b5e762db85a9a..6e1aff5082798 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -740,8 +740,6 @@ AdminImpl::AdminImpl(const std::string& access_log_path, const std::string& prof {"/runtime", "print runtime values", MAKE_ADMIN_HANDLER(handlerRuntime), false, false}, {"/runtime_modify", "modify runtime values", MAKE_ADMIN_HANDLER(handlerRuntimeModify), false, true}, - // {"/hystrix_event_stream", "send hystrix event stream", - // MAKE_ADMIN_HANDLER(handlerHystrixEventStream), false, false} }, // TODO(jsedgwick) add /runtime_reset endpoint that removes all admin-set values diff --git a/source/server/http/admin.h b/source/server/http/admin.h index 9cf99dfc2277e..1f5bcb3f41648 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -95,7 +95,9 @@ class AdminImpl : public Admin, bool proxy100Continue() const override { return false; } const Http::Http1Settings& http1Settings() const override { return http1_settings_; } - void unregisterHystrixConnection() { server_.unregisterHystrixSink(); } + void unregisterHystrixConnection(Http::StreamDecoderFilterCallbacks* callbacks) { + server_.unregisterHystrixSink(callbacks); + } private: /** @@ -246,7 +248,7 @@ class AdminFilter : public Http::StreamDecoderFilter, Logger::LoggableenableTimer(config_->statsFlushInterval()); } -void InstanceImpl::unregisterHystrixSink() { +void InstanceImpl::unregisterHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks_to_remove) { for (const auto& sink : config_->statsSinks()) { // TODO: is there a better way to find the hystrix sink? Extensions::StatSinks::Hystrix::HystrixSink* hystrix_sink = @@ -155,7 +155,7 @@ void InstanceImpl::unregisterHystrixSink() { // TODO (@trabetti) : will want to move to a vector of connections, // need a parameter (callback, hope it will work) to identify which connection to remove // also better to return success value. - hystrix_sink->unregisterConnection(); + hystrix_sink->unregisterConnection(callbacks_to_remove); return; } } diff --git a/source/server/server.h b/source/server/server.h index b02b4f9ec9471..c514c84d2b991 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -170,7 +170,7 @@ class InstanceImpl : Logger::Loggable, public Instance { std::chrono::milliseconds statsFlushInterval() override { return config_->statsFlushInterval(); } // bool registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) override; - void unregisterHystrixSink() override; + void unregisterHystrixSink(Http::StreamDecoderFilterCallbacks*) override; private: void flushStats(); diff --git a/test/extensions/stats_sinks/hystrix/hystrix_test.cc b/test/extensions/stats_sinks/hystrix/hystrix_test.cc index 62b0e50399717..8277d95cdd49a 100644 --- a/test/extensions/stats_sinks/hystrix/hystrix_test.cc +++ b/test/extensions/stats_sinks/hystrix/hystrix_test.cc @@ -178,7 +178,7 @@ TEST_F(HystrixSinkTest, Disconnect) { // disconnect buffer.drain(buffer.length()); - sink_->unregisterConnection(); + sink_->unregisterConnection(&callbacks_); sink_->beginFlush(); sink_->flushCounter(success_counter, 1); sink_->endFlush(); diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index 775cd8a736466..18ece3c4a2462 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -289,7 +289,7 @@ class MockInstance : public Instance { MOCK_METHOD0(localInfo, const LocalInfo::LocalInfo&()); MOCK_METHOD0(statsFlushInterval, std::chrono::milliseconds()); // MOCK_METHOD1(registerToHystrixSink, bool(Http::StreamDecoderFilterCallbacks* callbacks)); - MOCK_METHOD0(unregisterHystrixSink, void()); + MOCK_METHOD1(unregisterHystrixSink, void(Http::StreamDecoderFilterCallbacks*)); testing::NiceMock thread_local_; Stats::IsolatedStoreImpl stats_store_; From bb876ee141069371ad0624bc3efc08074a0d7602 Mon Sep 17 00:00:00 2001 From: trabetti Date: Wed, 18 Apr 2018 11:34:55 +0300 Subject: [PATCH 22/25] passing AdminFilter to callback, adding on_destroy_callbacks Signed-off-by: trabetti --- include/envoy/server/admin.h | 7 +- include/envoy/server/instance.h | 7 -- .../extensions/stat_sinks/hystrix/hystrix.cc | 19 ++++- .../extensions/stat_sinks/hystrix/hystrix.h | 3 +- source/server/config_validation/server.h | 2 - source/server/http/admin.cc | 72 +++++++++---------- source/server/http/admin.h | 55 +++++++------- source/server/server.cc | 15 ---- source/server/server.h | 2 - test/mocks/server/mocks.h | 2 - test/server/http/admin_test.cc | 25 +++---- test/server/server_test.cc | 8 --- 12 files changed, 88 insertions(+), 129 deletions(-) diff --git a/include/envoy/server/admin.h b/include/envoy/server/admin.h index ffc62d856cc13..3c44f85fa203f 100644 --- a/include/envoy/server/admin.h +++ b/include/envoy/server/admin.h @@ -16,6 +16,7 @@ namespace Envoy { namespace Server { +class AdminFilter; /** * This macro is used to add handlers to the Admin HTTP Endpoint. It builds * a callback that executes X when the specified admin handler is hit. This macro can be @@ -24,8 +25,8 @@ namespace Server { */ #define MAKE_ADMIN_HANDLER(X) \ [this](absl::string_view path_and_query, Http::HeaderMap& response_headers, \ - Buffer::Instance& data, Http::StreamDecoderFilterCallbacks* callbacks) -> Http::Code { \ - return X(path_and_query, response_headers, data, callbacks); \ + Buffer::Instance& data, Server::AdminFilter& admin_filter) -> Http::Code { \ + return X(path_and_query, response_headers, data, admin_filter); \ } /** @@ -45,7 +46,7 @@ class Admin { */ typedef std::function + AdminFilter& admin_filter)> HandlerCb; diff --git a/include/envoy/server/instance.h b/include/envoy/server/instance.h index b350d21788a37..139b59326e447 100644 --- a/include/envoy/server/instance.h +++ b/include/envoy/server/instance.h @@ -184,13 +184,6 @@ class Instance { * @return the flush interval of stats sinks. */ virtual std::chrono::milliseconds statsFlushInterval() PURE; - - // TODO (@trabetti) : for the method below - too specific? what is a better way? it is called on - // callback destroy. - /** - * @ Unregisters a callbacks connection to Hystrix sink. - */ - virtual void unregisterHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks_to_remove) PURE; }; } // namespace Server diff --git a/source/extensions/stat_sinks/hystrix/hystrix.cc b/source/extensions/stat_sinks/hystrix/hystrix.cc index 0d1ea0d82ce1b..59d7c8b43b888 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.cc +++ b/source/extensions/stat_sinks/hystrix/hystrix.cc @@ -275,7 +275,7 @@ HystrixSink::HystrixSink(Server::Instance& server) Http::Code HystrixSink::handlerHystrixEventStream(absl::string_view, Http::HeaderMap& response_headers, Buffer::Instance&, - Http::StreamDecoderFilterCallbacks* callbacks) { + Server::AdminFilter& admin_filter) { response_headers.insertContentType().value().setReference( Http::Headers::get().ContentTypeValues.TextEventStream); @@ -289,9 +289,22 @@ Http::Code HystrixSink::handlerHystrixEventStream(absl::string_view, Http::Headers::get().AccessControlAllowOriginValue.All); response_headers.insertNoChunks().value().setReference("0"); - registerConnection(callbacks); + Http::StreamDecoderFilterCallbacks* stream_decoder_filter_callbacks = + admin_filter.getDecoderFilterCallbacks(); + + registerConnection(stream_decoder_filter_callbacks); + + // Separated out just so it's easier to understand + auto on_destroy_callback = [this, stream_decoder_filter_callbacks]() { + // Unregister the callbacks from the sink so data is no longer encoded through them. + unregisterConnection(stream_decoder_filter_callbacks); + }; + + // Add the callback to the admin_filter list of callbacks + admin_filter.addOnDestroyCallback(std::move(on_destroy_callback)); + ENVOY_LOG(debug, "start sending data to hystrix dashboard on port {}", - callbacks->connection()->localAddress()->asString()); + stream_decoder_filter_callbacks->connection()->localAddress()->asString()); return Http::Code::OK; } diff --git a/source/extensions/stat_sinks/hystrix/hystrix.h b/source/extensions/stat_sinks/hystrix/hystrix.h index e4e4885ae2b59..9a62fc1888966 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.h +++ b/source/extensions/stat_sinks/hystrix/hystrix.h @@ -111,8 +111,7 @@ class HystrixSink : public Stats::Sink, public Logger::Loggable, const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } std::chrono::milliseconds statsFlushInterval() override { return config_->statsFlushInterval(); } - // bool registerToHystrixSink(Http::StreamDecoderFilterCallbacks*) override { NOT_IMPLEMENTED; } - void unregisterHystrixSink(Http::StreamDecoderFilterCallbacks*) override { NOT_IMPLEMENTED; } // Server::ListenerComponentFactory std::vector createNetworkFilterFactoryList( diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 6e1aff5082798..4e86c0e1bfd12 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -150,6 +150,16 @@ Http::FilterTrailersStatus AdminFilter::decodeTrailers(Http::HeaderMap&) { return Http::FilterTrailersStatus::StopIteration; } +void AdminFilter::onDestroy() { + for (auto callback : on_destroy_callbacks_) { + callback(); + } +} + +void AdminFilter::addOnDestroyCallback(std::function cb) { + on_destroy_callbacks_.push_back(cb); +} + bool AdminImpl::changeLogLevel(const Http::Utility::QueryParams& params) { if (params.size() != 1) { return false; @@ -222,8 +232,7 @@ void AdminImpl::addCircuitSettings(const std::string& cluster_name, const std::s } Http::Code AdminImpl::handlerClusters(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) { + Buffer::Instance& response, AdminFilter&) { response.add(fmt::format("version_info::{}\n", server_.clusterManager().versionInfo())); for (auto& cluster : server_.clusterManager().clusters()) { @@ -281,8 +290,7 @@ Http::Code AdminImpl::handlerClusters(absl::string_view, Http::HeaderMap&, // TODO(jsedgwick) Use query params to list available dumps, selectively dump, etc Http::Code AdminImpl::handlerConfigDump(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) const { + Buffer::Instance& response, AdminFilter&) const { envoy::admin::v2::ConfigDump dump; auto& config_dump_map = *(dump.mutable_configs()); for (const auto& key_callback_pair : config_tracker_.getCallbacksMap()) { @@ -298,8 +306,7 @@ Http::Code AdminImpl::handlerConfigDump(absl::string_view, Http::HeaderMap&, } Http::Code AdminImpl::handlerCpuProfiler(absl::string_view url, Http::HeaderMap&, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) { + Buffer::Instance& response, AdminFilter&) { Http::Utility::QueryParams query_params = Http::Utility::parseQueryString(url); if (query_params.size() != 1 || query_params.begin()->first != "enable" || (query_params.begin()->second != "y" && query_params.begin()->second != "n")) { @@ -323,31 +330,27 @@ Http::Code AdminImpl::handlerCpuProfiler(absl::string_view url, Http::HeaderMap& } Http::Code AdminImpl::handlerHealthcheckFail(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) { + Buffer::Instance& response, AdminFilter&) { server_.failHealthcheck(true); response.add("OK\n"); return Http::Code::OK; } Http::Code AdminImpl::handlerHealthcheckOk(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) { + Buffer::Instance& response, AdminFilter&) { server_.failHealthcheck(false); response.add("OK\n"); return Http::Code::OK; } Http::Code AdminImpl::handlerHotRestartVersion(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) { + Buffer::Instance& response, AdminFilter&) { response.add(server_.hotRestart().version()); return Http::Code::OK; } Http::Code AdminImpl::handlerLogging(absl::string_view url, Http::HeaderMap&, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) { + Buffer::Instance& response, AdminFilter&) { Http::Utility::QueryParams query_params = Http::Utility::parseQueryString(url); Http::Code rc = Http::Code::OK; @@ -373,8 +376,7 @@ Http::Code AdminImpl::handlerLogging(absl::string_view url, Http::HeaderMap&, } Http::Code AdminImpl::handlerResetCounters(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) { + Buffer::Instance& response, AdminFilter&) { for (const Stats::CounterSharedPtr& counter : server_.stats().counters()) { counter->reset(); } @@ -384,8 +386,7 @@ Http::Code AdminImpl::handlerResetCounters(absl::string_view, Http::HeaderMap&, } Http::Code AdminImpl::handlerServerInfo(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) { + Buffer::Instance& response, AdminFilter&) { time_t current_time = time(nullptr); response.add(fmt::format("envoy {} {} {} {} {}\n", VersionInfo::version(), server_.healthCheckFailed() ? "draining" : "live", @@ -396,8 +397,7 @@ Http::Code AdminImpl::handlerServerInfo(absl::string_view, Http::HeaderMap&, } Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& response_headers, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks* callbacks) { + Buffer::Instance& response, AdminFilter& admin_filter) { // We currently don't support timers locally (only via statsd) so just group all the counters // and gauges together, alpha sort them, and spit them out. Http::Code rc = Http::Code::OK; @@ -424,7 +424,7 @@ Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& respo Http::Headers::get().ContentTypeValues.Json); response.add(AdminImpl::statsAsJson(all_stats)); } else if (format_key == "format" && format_value == "prometheus") { - return handlerPrometheusStats(url, response_headers, response, callbacks); + return handlerPrometheusStats(url, response_headers, response, admin_filter); } else { response.add("usage: /stats?format=json \n"); response.add("\n"); @@ -435,8 +435,7 @@ Http::Code AdminImpl::handlerStats(absl::string_view url, Http::HeaderMap& respo } Http::Code AdminImpl::handlerPrometheusStats(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) { + Buffer::Instance& response, AdminFilter&) { PrometheusStatsFormatter::statsAsPrometheus(server_.stats().counters(), server_.stats().gauges(), response); return Http::Code::OK; @@ -515,16 +514,14 @@ std::string AdminImpl::statsAsJson(const std::map& all_st } Http::Code AdminImpl::handlerQuitQuitQuit(absl::string_view, Http::HeaderMap&, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) { + Buffer::Instance& response, AdminFilter&) { server_.shutdown(); response.add("OK\n"); return Http::Code::OK; } Http::Code AdminImpl::handlerListenerInfo(absl::string_view, Http::HeaderMap& response_headers, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) { + Buffer::Instance& response, AdminFilter&) { response_headers.insertContentType().value().setReference( Http::Headers::get().ContentTypeValues.Json); std::list listeners; @@ -536,7 +533,7 @@ Http::Code AdminImpl::handlerListenerInfo(absl::string_view, Http::HeaderMap& re } Http::Code AdminImpl::handlerCerts(absl::string_view, Http::HeaderMap&, Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) { + AdminFilter&) { // This set is used to track distinct certificates. We may have multiple listeners, upstreams, etc // using the same cert. std::unordered_set context_info_set; @@ -555,8 +552,7 @@ Http::Code AdminImpl::handlerCerts(absl::string_view, Http::HeaderMap&, Buffer:: } Http::Code AdminImpl::handlerRuntime(absl::string_view url, Http::HeaderMap& response_headers, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) { + Buffer::Instance& response, AdminFilter&) { const Http::Utility::QueryParams params = Http::Utility::parseQueryString(url); response_headers.insertContentType().value().setReference( Http::Headers::get().ContentTypeValues.Json); @@ -644,8 +640,7 @@ std::string AdminImpl::runtimeAsJson( } Http::Code AdminImpl::handlerRuntimeModify(absl::string_view url, Http::HeaderMap&, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) { + Buffer::Instance& response, AdminFilter&) { const Http::Utility::QueryParams params = Http::Utility::parseQueryString(url); if (params.empty()) { response.add("usage: /runtime_modify?key1=value1&key2=value2&keyN=valueN\n"); @@ -668,7 +663,7 @@ void AdminFilter::onComplete() { Buffer::OwnedImpl response; Http::HeaderMapPtr header_map{new Http::HeaderMapImpl}; RELEASE_ASSERT(request_headers_); - Http::Code code = parent_.runCallback(path, *request_headers_, *header_map, response, callbacks_); + Http::Code code = parent_.runCallback(path, *request_headers_, *header_map, response, *this); bool end_stream = true; if (path.find("/hystrix_event_stream") != std::string::npos) { @@ -783,7 +778,7 @@ void AdminImpl::createFilterChain(Http::FilterChainFactoryCallbacks& callbacks) Http::Code AdminImpl::runCallback(absl::string_view path_and_query, const Http::HeaderMap& request_headers, Http::HeaderMap& response_headers, Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks* callbacks) { + AdminFilter& admin_filter) { Http::Code code = Http::Code::OK; bool found_handler = false; @@ -801,7 +796,7 @@ Http::Code AdminImpl::runCallback(absl::string_view path_and_query, handler.prefix_, method); } } - code = handler.handler_(path_and_query, response_headers, response, callbacks); + code = handler.handler_(path_and_query, response_headers, response, admin_filter); found_handler = true; break; } @@ -811,7 +806,7 @@ Http::Code AdminImpl::runCallback(absl::string_view path_and_query, // Extra space is emitted below to have "invalid path." be a separate sentence in the // 404 output from "admin commands are:" in handlerHelp. response.add("invalid path. "); - handlerHelp(path_and_query, response_headers, response, callbacks); + handlerHelp(path_and_query, response_headers, response, admin_filter); code = Http::Code::NotFound; } @@ -830,7 +825,7 @@ std::vector AdminImpl::sortedHandlers() const { } Http::Code AdminImpl::handlerHelp(absl::string_view, Http::HeaderMap&, Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) { + AdminFilter&) { response.add("admin commands are:\n"); // Prefix order is used during searching, but for printing do them in alpha order. @@ -841,8 +836,7 @@ Http::Code AdminImpl::handlerHelp(absl::string_view, Http::HeaderMap&, Buffer::I } Http::Code AdminImpl::handlerAdminHome(absl::string_view, Http::HeaderMap& response_headers, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) { + Buffer::Instance& response, AdminFilter&) { response_headers.insertContentType().value().setReference( Http::Headers::get().ContentTypeValues.Html); diff --git a/source/server/http/admin.h b/source/server/http/admin.h index 1f5bcb3f41648..28c91b6adf32c 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -31,6 +31,7 @@ namespace Envoy { namespace Server { +class AdminFilter; /** * Implementation of Server::Admin. */ @@ -46,7 +47,7 @@ class AdminImpl : public Admin, Http::Code runCallback(absl::string_view path_and_query, const Http::HeaderMap& request_headers, Http::HeaderMap& response_headers, Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks* callbacks); + AdminFilter& admin_filter); const Network::Socket& socket() override { return *socket_; } Network::Socket& mutable_socket() { return *socket_; } Network::ListenerConfig& listener() { return listener_; } @@ -95,10 +96,6 @@ class AdminImpl : public Admin, bool proxy100Continue() const override { return false; } const Http::Http1Settings& http1Settings() const override { return http1_settings_; } - void unregisterHystrixConnection(Http::StreamDecoderFilterCallbacks* callbacks) { - server_.unregisterHystrixSink(callbacks); - } - private: /** * Individual admin handler including prefix, help text, and callback. @@ -149,52 +146,50 @@ class AdminImpl : public Admin, * URL handlers. */ Http::Code handlerAdminHome(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); + Buffer::Instance& response, AdminFilter&); Http::Code handlerCerts(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); + Buffer::Instance& response, AdminFilter&); Http::Code handlerClusters(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); + Buffer::Instance& response, AdminFilter&); Http::Code handlerConfigDump(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*) const; + Buffer::Instance& response, AdminFilter&) const; Http::Code handlerCpuProfiler(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); + Buffer::Instance& response, AdminFilter&); Http::Code handlerHealthcheckFail(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*); + AdminFilter&); Http::Code handlerHealthcheckOk(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*); + AdminFilter&); Http::Code handlerHelp(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); + Buffer::Instance& response, AdminFilter&); Http::Code handlerHotRestartVersion(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*); + AdminFilter&); Http::Code handlerListenerInfo(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*); + AdminFilter&); Http::Code handlerLogging(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); - Http::Code handlerMain(const std::string& path, Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*); + Buffer::Instance& response, AdminFilter&); + Http::Code handlerMain(const std::string& path, Buffer::Instance& response, AdminFilter&); Http::Code handlerQuitQuitQuit(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*); + AdminFilter&); Http::Code handlerResetCounters(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*); + AdminFilter&); Http::Code handlerServerInfo(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); + Buffer::Instance& response, AdminFilter&); Http::Code handlerStats(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); + Buffer::Instance& response, AdminFilter&); Http::Code handlerPrometheusStats(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*); + AdminFilter&); Http::Code handlerRuntime(absl::string_view path_and_query, Http::HeaderMap& response_headers, - Buffer::Instance& response, Http::StreamDecoderFilterCallbacks*); + Buffer::Instance& response, AdminFilter&); Http::Code handlerRuntimeModify(absl::string_view path_and_query, Http::HeaderMap& response_headers, Buffer::Instance& response, - Http::StreamDecoderFilterCallbacks*); + AdminFilter&); class AdminListener : public Network::ListenerConfig { public: @@ -247,9 +242,10 @@ class AdminFilter : public Http::StreamDecoderFilter, Logger::Loggable cb); + Http::StreamDecoderFilterCallbacks* getDecoderFilterCallbacks() { return callbacks_; } + const Http::HeaderMap* getRequestHeaders() { return request_headers_; } // Http::StreamDecoderFilter Http::FilterHeadersStatus decodeHeaders(Http::HeaderMap& headers, bool end_stream) override; Http::FilterDataStatus decodeData(Buffer::Instance& data, bool end_stream) override; @@ -267,6 +263,7 @@ class AdminFilter : public Http::StreamDecoderFilter, Logger::Loggable> on_destroy_callbacks_; }; /** diff --git a/source/server/server.cc b/source/server/server.cc index d93142bec58fc..35e809a1adc36 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -146,21 +146,6 @@ void InstanceImpl::flushStats() { stat_flush_timer_->enableTimer(config_->statsFlushInterval()); } -void InstanceImpl::unregisterHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks_to_remove) { - for (const auto& sink : config_->statsSinks()) { - // TODO: is there a better way to find the hystrix sink? - Extensions::StatSinks::Hystrix::HystrixSink* hystrix_sink = - dynamic_cast(sink.get()); - if (hystrix_sink != nullptr) { - // TODO (@trabetti) : will want to move to a vector of connections, - // need a parameter (callback, hope it will work) to identify which connection to remove - // also better to return success value. - hystrix_sink->unregisterConnection(callbacks_to_remove); - return; - } - } -} - void InstanceImpl::getParentStats(HotRestart::GetParentStatsInfo& info) { info.memory_allocated_ = Memory::Stats::totalCurrentlyAllocated(); info.num_connections_ = numConnections(); diff --git a/source/server/server.h b/source/server/server.h index c514c84d2b991..d077d776ff829 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -169,8 +169,6 @@ class InstanceImpl : Logger::Loggable, public Instance { const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } std::chrono::milliseconds statsFlushInterval() override { return config_->statsFlushInterval(); } - // bool registerToHystrixSink(Http::StreamDecoderFilterCallbacks* callbacks) override; - void unregisterHystrixSink(Http::StreamDecoderFilterCallbacks*) override; private: void flushStats(); diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index 18ece3c4a2462..4f063e4aebc64 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -288,8 +288,6 @@ class MockInstance : public Instance { MOCK_METHOD0(threadLocal, ThreadLocal::Instance&()); MOCK_METHOD0(localInfo, const LocalInfo::LocalInfo&()); MOCK_METHOD0(statsFlushInterval, std::chrono::milliseconds()); - // MOCK_METHOD1(registerToHystrixSink, bool(Http::StreamDecoderFilterCallbacks* callbacks)); - MOCK_METHOD1(unregisterHystrixSink, void(Http::StreamDecoderFilterCallbacks*)); testing::NiceMock thread_local_; Stats::IsolatedStoreImpl stats_store_; diff --git a/test/server/http/admin_test.cc b/test/server/http/admin_test.cc index 32aa2dd3959eb..75ed662748a55 100644 --- a/test/server/http/admin_test.cc +++ b/test/server/http/admin_test.cc @@ -81,7 +81,7 @@ class AdminInstanceTest : public testing::TestWithParam callbacks; request_headers_.insertMethod().value(method.data(), method.size()); return admin_.runCallback(path_and_query, request_headers_, response_headers, response, - &callbacks); + admin_filter_); } Http::Code getCallback(absl::string_view path_and_query, Http::HeaderMap& response_headers, @@ -114,6 +113,7 @@ class AdminInstanceTest : public testing::TestWithParam callbacks; EXPECT_NO_LOGS( EXPECT_EQ(Http::Code::InternalServerError, admin_bad_profile_path.runCallback("/cpuprofiler?enable=y", request_headers_, - header_map, data, &callbacks))); + header_map, data, admin_filter_))); EXPECT_FALSE(Profiler::Cpu::profilerEnabled()); } @@ -184,9 +183,7 @@ TEST_P(AdminInstanceTest, AdminBadAddressOutPath) { TEST_P(AdminInstanceTest, CustomHandler) { auto callback = [](absl::string_view, Http::HeaderMap&, Buffer::Instance&, - Http::StreamDecoderFilterCallbacks*) -> Http::Code { - return Http::Code::Accepted; - }; + AdminFilter&) -> Http::Code { return Http::Code::Accepted; }; // Test removable handler. EXPECT_NO_LOGS(EXPECT_TRUE(admin_.addHandler("/foo/bar", "hello", callback, true, false))); @@ -213,9 +210,7 @@ TEST_P(AdminInstanceTest, CustomHandler) { TEST_P(AdminInstanceTest, RejectHandlerWithXss) { auto callback = [](absl::string_view, Http::HeaderMap&, Buffer::Instance&, - Http::StreamDecoderFilterCallbacks*) -> Http::Code { - return Http::Code::Accepted; - }; + AdminFilter&) -> Http::Code { return Http::Code::Accepted; }; EXPECT_LOG_CONTAINS("error", "filter \"/foo\" contains invalid character '<'", EXPECT_FALSE(admin_.addHandler("/foo", "hello", @@ -224,9 +219,7 @@ TEST_P(AdminInstanceTest, RejectHandlerWithXss) { TEST_P(AdminInstanceTest, RejectHandlerWithEmbeddedQuery) { auto callback = [](absl::string_view, Http::HeaderMap&, Buffer::Instance&, - Http::StreamDecoderFilterCallbacks*) -> Http::Code { - return Http::Code::Accepted; - }; + AdminFilter&) -> Http::Code { return Http::Code::Accepted; }; EXPECT_LOG_CONTAINS("error", "filter \"/bar?queryShouldNotBeInPrefix\" contains invalid character '?'", EXPECT_FALSE(admin_.addHandler("/bar?queryShouldNotBeInPrefix", "hello", @@ -235,9 +228,7 @@ TEST_P(AdminInstanceTest, RejectHandlerWithEmbeddedQuery) { TEST_P(AdminInstanceTest, EscapeHelpTextWithPunctuation) { auto callback = [](absl::string_view, Http::HeaderMap&, Buffer::Instance&, - Http::StreamDecoderFilterCallbacks*) -> Http::Code { - return Http::Code::Accepted; - }; + AdminFilter&) -> Http::Code { return Http::Code::Accepted; }; // It's OK to have help text with HTML characters in it, but when we render the home // page they need to be escaped. diff --git a/test/server/server_test.cc b/test/server/server_test.cc index 16888beb7b514..80f62cb4c9188 100644 --- a/test/server/server_test.cc +++ b/test/server/server_test.cc @@ -135,14 +135,6 @@ TEST_P(ServerInstanceImplTest, V2ConfigOnly) { } } -// TODO (@trabetti) : test registerToHystrixSink/unregisterHystrixSink -// TEST_P(ServerInstanceImplTest, registerSink) { -// InSequence s; -// -// server_->registerToHystrixSink(&callbacks); -// server_->unregisterHystrixSink(); -//} - TEST_P(ServerInstanceImplTest, V1ConfigFallback) { options_.service_cluster_name_ = "some_cluster_name"; options_.service_node_name_ = "some_node_name"; From f5e21f20c0f7df0187cd2eac132ca10e78ab0cfa Mon Sep 17 00:00:00 2001 From: trabetti Date: Mon, 23 Apr 2018 16:29:06 +0300 Subject: [PATCH 23/25] fix review comments Signed-off-by: trabetti --- api/envoy/config/metrics/v2/stats.proto | 3 +- .../extensions/stat_sinks/hystrix/config.cc | 9 ++- .../extensions/stat_sinks/hystrix/hystrix.cc | 73 +++++++++---------- .../extensions/stat_sinks/hystrix/hystrix.h | 19 +++-- source/server/http/admin.cc | 4 +- .../stats_sinks/hystrix/hystrix_test.cc | 2 +- 6 files changed, 55 insertions(+), 55 deletions(-) diff --git a/api/envoy/config/metrics/v2/stats.proto b/api/envoy/config/metrics/v2/stats.proto index 04401eefb5233..ecd58fae2c77c 100644 --- a/api/envoy/config/metrics/v2/stats.proto +++ b/api/envoy/config/metrics/v2/stats.proto @@ -184,7 +184,8 @@ message StatsdSink { string prefix = 3; } -// can it be empty? +// Stats configuration proto schema for built-in *envoy.hystrix* sink. +// The sink emits stats in SSE format for use with Hystrix dashboard message HystrixSink { int64 num_of_buckets = 1; } diff --git a/source/extensions/stat_sinks/hystrix/config.cc b/source/extensions/stat_sinks/hystrix/config.cc index b42b1e1798180..8c547c4417cba 100644 --- a/source/extensions/stat_sinks/hystrix/config.cc +++ b/source/extensions/stat_sinks/hystrix/config.cc @@ -14,9 +14,14 @@ namespace Extensions { namespace StatSinks { namespace Hystrix { -Stats::SinkPtr HystrixSinkFactory::createStatsSink(const Protobuf::Message&, +Stats::SinkPtr HystrixSinkFactory::createStatsSink(const Protobuf::Message& config, Server::Instance& server) { - return std::make_unique(server); + const auto& hystrix_sink = + MessageUtil::downcastAndValidate(config); + if (hystrix_sink.num_of_buckets() == 0) { // if not set + return std::make_unique(server); + } + return std::make_unique(server, hystrix_sink.num_of_buckets()); } ProtobufTypes::MessagePtr HystrixSinkFactory::createEmptyConfigProto() { diff --git a/source/extensions/stat_sinks/hystrix/hystrix.cc b/source/extensions/stat_sinks/hystrix/hystrix.cc index 59d7c8b43b888..d8877a2dbf13b 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.cc +++ b/source/extensions/stat_sinks/hystrix/hystrix.cc @@ -30,8 +30,7 @@ void HystrixStatCache::pushNewValue(const std::string& key, uint64_t value) { } uint64_t HystrixStatCache::getRollingValue(absl::string_view cluster_name, absl::string_view stat) { - std::string key; - key = absl::StrCat("cluster.", cluster_name, ".", stat); + std::string key = absl::StrCat("cluster.", cluster_name, ".", stat); if (rolling_stats_map_.find(key) != rolling_stats_map_.end()) { // If the counter was reset, the result is negative // better return 0, will be back to normal once one rolling window passes. @@ -51,12 +50,6 @@ void HystrixStatCache::CreateCounterNameLookupForCluster(const std::string& clus // Building lookup name map for all specific cluster values. // Every call to the updateRollingWindowMap function should get the appropriate name from the map. std::string cluster_name_with_prefix = absl::StrCat("cluster.", cluster_name, "."); - counter_name_lookup[cluster_name]["upstream_rq_timeout"] = - absl::StrCat(cluster_name_with_prefix, "upstream_rq_timeout"); - counter_name_lookup[cluster_name]["upstream_rq_per_try_timeout"] = - absl::StrCat(cluster_name_with_prefix, "upstream_rq_per_try_timeout"); - counter_name_lookup[cluster_name]["timeouts"] = - absl::StrCat(cluster_name_with_prefix, "timeouts"); counter_name_lookup[cluster_name]["upstream_rq_5xx"] = absl::StrCat(cluster_name_with_prefix, "upstream_rq_5xx"); counter_name_lookup[cluster_name]["retry.upstream_rq_5xx"] = @@ -69,15 +62,16 @@ void HystrixStatCache::CreateCounterNameLookupForCluster(const std::string& clus counter_name_lookup[cluster_name]["upstream_rq_2xx"] = absl::StrCat(cluster_name_with_prefix, "upstream_rq_2xx"); counter_name_lookup[cluster_name]["success"] = absl::StrCat(cluster_name_with_prefix, "success"); - counter_name_lookup[cluster_name]["upstream_rq_pending_overflow"] = - absl::StrCat(cluster_name_with_prefix, "upstream_rq_pending_overflow"); counter_name_lookup[cluster_name]["rejected"] = absl::StrCat(cluster_name_with_prefix, "rejected"); - counter_name_lookup[cluster_name]["total"] = absl::StrCat(cluster_name_with_prefix, "total"); + counter_name_lookup[cluster_name]["timeouts"] = + absl::StrCat(cluster_name_with_prefix, "timeouts"); + counter_name_lookup[cluster_name]["total"] = absl::StrCat(cluster_name_with_prefix, "total"); } -void HystrixStatCache::updateRollingWindowMap(std::map current_stat_values, - std::string cluster_name) { +void HystrixStatCache::updateRollingWindowMap(Upstream::ClusterInfoConstSharedPtr cluster_info, Stats::Store& stats) { + std::string cluster_name = cluster_info->name(); + Upstream::ClusterStats& cluster_stats = cluster_info->stats(); if (counter_name_lookup.find(cluster_name) == counter_name_lookup.end()) { CreateCounterNameLookupForCluster(cluster_name); @@ -86,8 +80,8 @@ void HystrixStatCache::updateRollingWindowMap(std::map cu // Combining timeouts+retries - retries are counted as separate requests // (alternative: each request including the retries counted as 1). uint64_t timeouts = - current_stat_values[counter_name_lookup[cluster_name]["upstream_rq_timeout"]] + - current_stat_values[counter_name_lookup[cluster_name]["upstream_rq_per_try_timeout"]]; + cluster_stats.upstream_rq_timeout_.value() + + cluster_stats.upstream_rq_per_try_timeout_.value(); pushNewValue(counter_name_lookup[cluster_name]["timeouts"], timeouts); @@ -96,19 +90,19 @@ void HystrixStatCache::updateRollingWindowMap(std::map cu // since timeouts are 504 (or 408), deduce them from here ("-" sign). // Timeout retries were not counted here anyway. uint64_t errors = - current_stat_values[counter_name_lookup[cluster_name]["upstream_rq_5xx"]] + - current_stat_values[counter_name_lookup[cluster_name]["retry.upstream_rq_5xx"]] + - current_stat_values[counter_name_lookup[cluster_name]["upstream_rq_4xx"]] + - current_stat_values[counter_name_lookup[cluster_name]["retry.upstream_rq_4xx"]] - - current_stat_values[counter_name_lookup[cluster_name]["upstream_rq_timeout"]]; + stats.counter(counter_name_lookup[cluster_name]["upstream_rq_5xx"]).value() + + stats.counter(counter_name_lookup[cluster_name]["retry.upstream_rq_5xx"]).value() + + stats.counter(counter_name_lookup[cluster_name]["upstream_rq_4xx"]).value() + + stats.counter(counter_name_lookup[cluster_name]["retry.upstream_rq_4xx"]).value() - + cluster_stats.upstream_rq_timeout_.value(); pushNewValue(counter_name_lookup[cluster_name]["errors"], errors); - uint64_t success = current_stat_values[counter_name_lookup[cluster_name]["upstream_rq_2xx"]]; + uint64_t success = stats.counter(counter_name_lookup[cluster_name]["upstream_rq_2xx"]).value(); pushNewValue(counter_name_lookup[cluster_name]["success"], success); uint64_t rejected = - current_stat_values[counter_name_lookup[cluster_name]["upstream_rq_pending_overflow"]]; + cluster_stats.upstream_rq_pending_overflow_.value(); pushNewValue(counter_name_lookup[cluster_name]["rejected"], rejected); // should not take from upstream_rq_total since it is updated before its components, @@ -263,14 +257,23 @@ const std::string HystrixStatCache::printRollingWindow() const { } namespace Hystrix { +HystrixSink::HystrixSink(Server::Instance& server, const uint64_t num_of_buckets) + : stats_(new HystrixStatCache(num_of_buckets)), server_(server) { +init(); +} + HystrixSink::HystrixSink(Server::Instance& server) - : stats_(new HystrixStatCache()), server_(&server) { - Server::Admin& admin = server_->admin(); + : stats_(new HystrixStatCache()), server_(server) { + init(); +} + +void HystrixSink::init() { + Server::Admin& admin = server_.admin(); ENVOY_LOG(debug, "adding hystrix_event_stream endpoint to enable connection to hystrix dashboard"); admin.addHandler("/hystrix_event_stream", "send hystrix event stream", MAKE_ADMIN_HANDLER(handlerHystrixEventStream), false, false); -}; +} Http::Code HystrixSink::handlerHystrixEventStream(absl::string_view, Http::HeaderMap& response_headers, @@ -310,24 +313,15 @@ Http::Code HystrixSink::handlerHystrixEventStream(absl::string_view, void HystrixSink::beginFlush() { current_stat_values_.clear(); } -void HystrixSink::flushCounter(const Stats::Counter& counter, uint64_t) { - if (callbacks_list_.empty()) { - return; - } - if (counter.name().find("upstream_rq_") != std::string::npos) { - current_stat_values_[counter.name()] = counter.value(); - } -} -// void HystrixSink::flushGauge(const Gauge& gauge, uint64_t value); void HystrixSink::endFlush() { if (callbacks_list_.empty()) return; stats_->incCounter(); - for (auto& cluster : server_->clusterManager().clusters()) { - stats_->updateRollingWindowMap(current_stat_values_, cluster.second.get().info()->name()); + for (auto& cluster : server_.clusterManager().clusters()) { + stats_->updateRollingWindowMap(cluster.second.get().info(), server_.stats()); } std::stringstream ss; - for (auto& cluster : server_->clusterManager().clusters()) { + for (auto& cluster : server_.clusterManager().clusters()) { stats_->getClusterStats( ss, cluster.second.get().info()->name(), cluster.second.get() @@ -335,10 +329,10 @@ void HystrixSink::endFlush() { ->resourceManager(Upstream::ResourcePriority::Default) .pendingRequests() .max(), - server_->stats() + server_.stats() .gauge("cluster." + cluster.second.get().info()->name() + ".membership_total") .value(), - server_->statsFlushInterval().count()); + server_.statsFlushInterval().count()); } Buffer::OwnedImpl data; for (auto callbacks : callbacks_list_) { @@ -363,6 +357,7 @@ void HystrixSink::unregisterConnection(Http::StreamDecoderFilterCallbacks* callb for (auto it = callbacks_list_.begin(); it != callbacks_list_.end();) { if ((*it)->streamId() == callbacks_to_remove->streamId()) { it = callbacks_list_.erase(it); + break; } else { ++it; } diff --git a/source/extensions/stat_sinks/hystrix/hystrix.h b/source/extensions/stat_sinks/hystrix/hystrix.h index 9a62fc1888966..13139846f2f96 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.h +++ b/source/extensions/stat_sinks/hystrix/hystrix.h @@ -18,8 +18,7 @@ class HystrixStatCache : public Logger::Loggable { HystrixStatCache() : current_index_(DEFAULT_NUM_OF_BUCKETS), window_size_(DEFAULT_NUM_OF_BUCKETS + 1){}; - HystrixStatCache(uint64_t num_of_buckets) - : current_index_(num_of_buckets), window_size_(num_of_buckets + 1){}; + HystrixStatCache(uint64_t num_of_buckets) : current_index_(num_of_buckets), window_size_(num_of_buckets + 1){}; /** * Add new value to top of rolling window, pushing out the oldest value. @@ -41,9 +40,8 @@ class HystrixStatCache : public Logger::Loggable { /** * Calculate values needed to create the stream and write into the map. */ - void updateRollingWindowMap(std::map current_stat_values, - const std::string cluster_name); - + void updateRollingWindowMap(Upstream::ClusterInfoConstSharedPtr cluster_info, Stats::Store& stats); +; /** * Clear map. */ @@ -103,22 +101,23 @@ class HystrixStatCache : public Logger::Loggable { std::map> counter_name_lookup; }; -typedef std::unique_ptr HystrixPtr; +typedef std::unique_ptr HystrixStatCachePtr; namespace Hystrix { class HystrixSink : public Stats::Sink, public Logger::Loggable { public: + HystrixSink(Server::Instance& server, uint64_t num_of_buckets); HystrixSink(Server::Instance& server); Http::Code handlerHystrixEventStream(absl::string_view, Http::HeaderMap& response_headers, Buffer::Instance&, Server::AdminFilter& admin_filter); + void init(); void beginFlush() override; - void flushCounter(const Stats::Counter& counter, uint64_t delta) override; + void flushCounter(const Stats::Counter&, uint64_t) override{}; void flushGauge(const Stats::Gauge&, uint64_t) override{}; void endFlush() override; void onHistogramComplete(const Stats::Histogram&, uint64_t) override{}; - // TODO (@trabetti) : support multiple connections /** * register a new connection */ @@ -130,9 +129,9 @@ class HystrixSink : public Stats::Sink, public Logger::Loggable callbacks_list_{}; - Server::Instance* server_; + Server::Instance& server_; std::map current_stat_values_; }; diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 4e86c0e1bfd12..0e1cb239cdb00 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -151,13 +151,13 @@ Http::FilterTrailersStatus AdminFilter::decodeTrailers(Http::HeaderMap&) { } void AdminFilter::onDestroy() { - for (auto callback : on_destroy_callbacks_) { + for (const auto& callback : on_destroy_callbacks_) { callback(); } } void AdminFilter::addOnDestroyCallback(std::function cb) { - on_destroy_callbacks_.push_back(cb); + on_destroy_callbacks_.push_back(std::move(cb)); } bool AdminImpl::changeLogLevel(const Http::Utility::QueryParams& params) { diff --git a/test/extensions/stats_sinks/hystrix/hystrix_test.cc b/test/extensions/stats_sinks/hystrix/hystrix_test.cc index 8277d95cdd49a..0226d4f6f8dc1 100644 --- a/test/extensions/stats_sinks/hystrix/hystrix_test.cc +++ b/test/extensions/stats_sinks/hystrix/hystrix_test.cc @@ -27,7 +27,7 @@ namespace Hystrix { class HystrixSinkTest : public testing::Test { public: - HystrixSinkTest() { sink_.reset(new HystrixSink(server_)); } + HystrixSinkTest() { sink_.reset(new HystrixSink(server_, 10)); } absl::string_view getStreamField(absl::string_view dataMessage, absl::string_view key) { absl::string_view::size_type key_pos = dataMessage.find(key); From 98de40a9e9315f62fd031f26cec88031d34f7895 Mon Sep 17 00:00:00 2001 From: Eliran Roffe Date: Mon, 23 Apr 2018 02:05:36 -0400 Subject: [PATCH 24/25] Set end_stream as an attribute of AdminFilter Signed-off-by: Eliran Roffe Signed-off-by: Eliran Roffe --- source/server/http/admin.cc | 8 ++------ source/server/http/admin.h | 4 ++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 0e1cb239cdb00..98f00a5d51beb 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -664,11 +664,7 @@ void AdminFilter::onComplete() { Http::HeaderMapPtr header_map{new Http::HeaderMapImpl}; RELEASE_ASSERT(request_headers_); Http::Code code = parent_.runCallback(path, *request_headers_, *header_map, response, *this); - bool end_stream = true; - if (path.find("/hystrix_event_stream") != std::string::npos) { - end_stream = false; - } header_map->insertStatus().value(std::to_string(enumToInt(code))); const auto& headers = Http::Headers::get(); if (header_map->ContentType() == nullptr) { @@ -683,10 +679,10 @@ void AdminFilter::onComplete() { // Under no circumstance should browsers sniff content-type. header_map->addReference(headers.XContentTypeOptions, headers.XContentTypeOptionValues.Nosniff); - callbacks_->encodeHeaders(std::move(header_map), end_stream && response.length() == 0); + callbacks_->encodeHeaders(std::move(header_map), end_stream_on_complete_ && response.length() == 0); if (response.length() > 0) { - callbacks_->encodeData(response, end_stream); + callbacks_->encodeData(response, end_stream_on_complete_); } } diff --git a/source/server/http/admin.h b/source/server/http/admin.h index 28c91b6adf32c..3217e0fe05482 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -253,6 +253,9 @@ class AdminFilter : public Http::StreamDecoderFilter, Logger::Loggable> on_destroy_callbacks_; + bool end_stream_on_complete_ = true; }; /** From 09dd340ecdbad70acecdc04cf96c133e37c62662 Mon Sep 17 00:00:00 2001 From: Eliran Roffe Date: Mon, 23 Apr 2018 05:01:49 -0400 Subject: [PATCH 25/25] Move request_headers to admin_filter when calling runCallback function Signed-off-by: Eliran Roffe --- source/extensions/stat_sinks/hystrix/hystrix.cc | 17 +++++++++-------- source/extensions/stat_sinks/hystrix/hystrix.h | 7 ++++--- source/server/http/admin.cc | 9 +++++---- source/server/http/admin.h | 9 +++------ test/server/http/admin_test.cc | 12 ++++++------ 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/source/extensions/stat_sinks/hystrix/hystrix.cc b/source/extensions/stat_sinks/hystrix/hystrix.cc index d8877a2dbf13b..8a7f441f9d85e 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.cc +++ b/source/extensions/stat_sinks/hystrix/hystrix.cc @@ -66,10 +66,11 @@ void HystrixStatCache::CreateCounterNameLookupForCluster(const std::string& clus absl::StrCat(cluster_name_with_prefix, "rejected"); counter_name_lookup[cluster_name]["timeouts"] = absl::StrCat(cluster_name_with_prefix, "timeouts"); - counter_name_lookup[cluster_name]["total"] = absl::StrCat(cluster_name_with_prefix, "total"); + counter_name_lookup[cluster_name]["total"] = absl::StrCat(cluster_name_with_prefix, "total"); } -void HystrixStatCache::updateRollingWindowMap(Upstream::ClusterInfoConstSharedPtr cluster_info, Stats::Store& stats) { +void HystrixStatCache::updateRollingWindowMap(Upstream::ClusterInfoConstSharedPtr cluster_info, + Stats::Store& stats) { std::string cluster_name = cluster_info->name(); Upstream::ClusterStats& cluster_stats = cluster_info->stats(); @@ -79,9 +80,8 @@ void HystrixStatCache::updateRollingWindowMap(Upstream::ClusterInfoConstSharedPt // Combining timeouts+retries - retries are counted as separate requests // (alternative: each request including the retries counted as 1). - uint64_t timeouts = - cluster_stats.upstream_rq_timeout_.value() + - cluster_stats.upstream_rq_per_try_timeout_.value(); + uint64_t timeouts = cluster_stats.upstream_rq_timeout_.value() + + cluster_stats.upstream_rq_per_try_timeout_.value(); pushNewValue(counter_name_lookup[cluster_name]["timeouts"], timeouts); @@ -101,8 +101,7 @@ void HystrixStatCache::updateRollingWindowMap(Upstream::ClusterInfoConstSharedPt uint64_t success = stats.counter(counter_name_lookup[cluster_name]["upstream_rq_2xx"]).value(); pushNewValue(counter_name_lookup[cluster_name]["success"], success); - uint64_t rejected = - cluster_stats.upstream_rq_pending_overflow_.value(); + uint64_t rejected = cluster_stats.upstream_rq_pending_overflow_.value(); pushNewValue(counter_name_lookup[cluster_name]["rejected"], rejected); // should not take from upstream_rq_total since it is updated before its components, @@ -259,7 +258,7 @@ const std::string HystrixStatCache::printRollingWindow() const { namespace Hystrix { HystrixSink::HystrixSink(Server::Instance& server, const uint64_t num_of_buckets) : stats_(new HystrixStatCache(num_of_buckets)), server_(server) { -init(); + init(); } HystrixSink::HystrixSink(Server::Instance& server) @@ -297,6 +296,8 @@ Http::Code HystrixSink::handlerHystrixEventStream(absl::string_view, registerConnection(stream_decoder_filter_callbacks); + admin_filter.setEndStreamOnComplete(false); // set streaming + // Separated out just so it's easier to understand auto on_destroy_callback = [this, stream_decoder_filter_callbacks]() { // Unregister the callbacks from the sink so data is no longer encoded through them. diff --git a/source/extensions/stat_sinks/hystrix/hystrix.h b/source/extensions/stat_sinks/hystrix/hystrix.h index 13139846f2f96..3b20607d22a64 100644 --- a/source/extensions/stat_sinks/hystrix/hystrix.h +++ b/source/extensions/stat_sinks/hystrix/hystrix.h @@ -18,7 +18,8 @@ class HystrixStatCache : public Logger::Loggable { HystrixStatCache() : current_index_(DEFAULT_NUM_OF_BUCKETS), window_size_(DEFAULT_NUM_OF_BUCKETS + 1){}; - HystrixStatCache(uint64_t num_of_buckets) : current_index_(num_of_buckets), window_size_(num_of_buckets + 1){}; + HystrixStatCache(uint64_t num_of_buckets) + : current_index_(num_of_buckets), window_size_(num_of_buckets + 1){}; /** * Add new value to top of rolling window, pushing out the oldest value. @@ -40,8 +41,8 @@ class HystrixStatCache : public Logger::Loggable { /** * Calculate values needed to create the stream and write into the map. */ - void updateRollingWindowMap(Upstream::ClusterInfoConstSharedPtr cluster_info, Stats::Store& stats); -; + void updateRollingWindowMap(Upstream::ClusterInfoConstSharedPtr cluster_info, + Stats::Store& stats); /** * Clear map. */ diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index 98f00a5d51beb..6ff4aaf104ca1 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -663,7 +663,7 @@ void AdminFilter::onComplete() { Buffer::OwnedImpl response; Http::HeaderMapPtr header_map{new Http::HeaderMapImpl}; RELEASE_ASSERT(request_headers_); - Http::Code code = parent_.runCallback(path, *request_headers_, *header_map, response, *this); + Http::Code code = parent_.runCallback(path, *header_map, response, *this); header_map->insertStatus().value(std::to_string(enumToInt(code))); const auto& headers = Http::Headers::get(); @@ -679,7 +679,8 @@ void AdminFilter::onComplete() { // Under no circumstance should browsers sniff content-type. header_map->addReference(headers.XContentTypeOptions, headers.XContentTypeOptionValues.Nosniff); - callbacks_->encodeHeaders(std::move(header_map), end_stream_on_complete_ && response.length() == 0); + callbacks_->encodeHeaders(std::move(header_map), + end_stream_on_complete_ && response.length() == 0); if (response.length() > 0) { callbacks_->encodeData(response, end_stream_on_complete_); @@ -772,7 +773,6 @@ void AdminImpl::createFilterChain(Http::FilterChainFactoryCallbacks& callbacks) } Http::Code AdminImpl::runCallback(absl::string_view path_and_query, - const Http::HeaderMap& request_headers, Http::HeaderMap& response_headers, Buffer::Instance& response, AdminFilter& admin_filter) { Http::Code code = Http::Code::OK; @@ -786,7 +786,8 @@ Http::Code AdminImpl::runCallback(absl::string_view path_and_query, for (const UrlHandler& handler : handlers_) { if (path_and_query.compare(0, query_index, handler.prefix_) == 0) { if (handler.mutates_server_state_) { - const absl::string_view method = request_headers.Method()->value().getStringView(); + const absl::string_view method = + admin_filter.getRequestHeaders()->Method()->value().getStringView(); if (method != Http::Headers::get().MethodValues.Post) { ENVOY_LOG(warn, "admin path \"{}\" mutates state, method={} rather than POST", handler.prefix_, method); diff --git a/source/server/http/admin.h b/source/server/http/admin.h index 3217e0fe05482..80fa895b1ba1d 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -45,9 +45,8 @@ class AdminImpl : public Admin, const std::string& address_out_path, Network::Address::InstanceConstSharedPtr address, Server::Instance& server, Stats::ScopePtr&& listener_scope); - Http::Code runCallback(absl::string_view path_and_query, const Http::HeaderMap& request_headers, - Http::HeaderMap& response_headers, Buffer::Instance& response, - AdminFilter& admin_filter); + Http::Code runCallback(absl::string_view path_and_query, Http::HeaderMap& response_headers, + Buffer::Instance& response, AdminFilter& admin_filter); const Network::Socket& socket() override { return *socket_; } Network::Socket& mutable_socket() { return *socket_; } Network::ListenerConfig& listener() { return listener_; } @@ -253,9 +252,7 @@ class AdminFilter : public Http::StreamDecoderFilter, Logger::Loggable