diff --git a/include/envoy/api/api.h b/include/envoy/api/api.h index 44af42935e7e4..6f397cae01024 100644 --- a/include/envoy/api/api.h +++ b/include/envoy/api/api.h @@ -47,6 +47,11 @@ class Api { * @return a reference to the TimeSource */ virtual TimeSource& timeSource() PURE; + + /** + * @return a constant reference to the root Stats::Scope + */ + virtual const Stats::Scope& rootScope() PURE; }; typedef std::unique_ptr ApiPtr; diff --git a/include/envoy/stats/scope.h b/include/envoy/stats/scope.h index a593827c7c54d..863d5f7d45909 100644 --- a/include/envoy/stats/scope.h +++ b/include/envoy/stats/scope.h @@ -1,12 +1,15 @@ #pragma once #include +#include #include #include "envoy/common/pure.h" #include "envoy/stats/histogram.h" #include "envoy/stats/symbol_table.h" +#include "absl/types/optional.h" + namespace Envoy { namespace Stats { @@ -84,6 +87,27 @@ class Scope { */ virtual Histogram& histogram(const std::string& name) PURE; + /** + * @param The name of the stat, obtained from the SymbolTable. + * @return a reference to a counter within the scope's namespace, if it exists. + */ + virtual absl::optional> + findCounter(StatName name) const PURE; + + /** + * @param The name of the stat, obtained from the SymbolTable. + * @return a reference to a gauge within the scope's namespace, if it exists. + */ + virtual absl::optional> findGauge(StatName name) const PURE; + + /** + * @param The name of the stat, obtained from the SymbolTable. + * @return a reference to a histogram within the scope's namespace, if it + * exists. + */ + virtual absl::optional> + findHistogram(StatName name) const PURE; + /** * @return a reference to the symbol table. */ diff --git a/source/common/api/api_impl.cc b/source/common/api/api_impl.cc index 9f997f377a09b..52666328121ef 100644 --- a/source/common/api/api_impl.cc +++ b/source/common/api/api_impl.cc @@ -9,9 +9,10 @@ namespace Envoy { namespace Api { -Impl::Impl(Thread::ThreadFactory& thread_factory, Stats::Store&, Event::TimeSystem& time_system, - Filesystem::Instance& file_system) - : thread_factory_(thread_factory), time_system_(time_system), file_system_(file_system) {} +Impl::Impl(Thread::ThreadFactory& thread_factory, Stats::Store& store, + Event::TimeSystem& time_system, Filesystem::Instance& file_system) + : thread_factory_(thread_factory), store_(store), time_system_(time_system), + file_system_(file_system) {} Event::DispatcherPtr Impl::allocateDispatcher() { return std::make_unique(*this, time_system_); diff --git a/source/common/api/api_impl.h b/source/common/api/api_impl.h index 61733b4ce53fe..92c57563e893a 100644 --- a/source/common/api/api_impl.h +++ b/source/common/api/api_impl.h @@ -16,7 +16,7 @@ namespace Api { */ class Impl : public Api { public: - Impl(Thread::ThreadFactory& thread_factory, Stats::Store&, Event::TimeSystem& time_system, + Impl(Thread::ThreadFactory& thread_factory, Stats::Store& store, Event::TimeSystem& time_system, Filesystem::Instance& file_system); // Api::Api @@ -25,9 +25,11 @@ class Impl : public Api { Thread::ThreadFactory& threadFactory() override { return thread_factory_; } Filesystem::Instance& fileSystem() override { return file_system_; } TimeSource& timeSource() override { return time_system_; } + const Stats::Scope& rootScope() override { return store_; } private: Thread::ThreadFactory& thread_factory_; + Stats::Store& store_; Event::TimeSystem& time_system_; Filesystem::Instance& file_system_; }; diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index 8e720a6ad8e3e..d658565e63d9a 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -50,6 +50,16 @@ template class IsolatedStatsCache { } private: + friend class IsolatedStoreImpl; + + absl::optional> find(StatName name) const { + auto stat = stats_.find(name); + if (stat == stats_.end()) { + return absl::nullopt; + } + return std::cref(*stat->second.get()); + } + StatNameHashMap> stats_; Allocator alloc_; }; @@ -69,6 +79,16 @@ class IsolatedStoreImpl : public StoreImpl { Histogram& histogram = histograms_.get(name); return histogram; } + absl::optional> findCounter(StatName name) const override { + return counters_.find(name); + } + absl::optional> findGauge(StatName name) const override { + return gauges_.find(name); + } + absl::optional> + findHistogram(StatName name) const override { + return histograms_.find(name); + } // Stats::Store std::vector counters() const override { return counters_.toVector(); } diff --git a/source/common/stats/scope_prefixer.cc b/source/common/stats/scope_prefixer.cc index 521696059e4ad..44314d967de1c 100644 --- a/source/common/stats/scope_prefixer.cc +++ b/source/common/stats/scope_prefixer.cc @@ -44,6 +44,20 @@ Histogram& ScopePrefixer::histogramFromStatName(StatName name) { return scope_.histogramFromStatName(StatName(stat_name_storage.get())); } +absl::optional> +ScopePrefixer::findCounter(StatName name) const { + return scope_.findCounter(name); +} + +absl::optional> ScopePrefixer::findGauge(StatName name) const { + return scope_.findGauge(name); +} + +absl::optional> +ScopePrefixer::findHistogram(StatName name) const { + return scope_.findHistogram(name); +} + void ScopePrefixer::deliverHistogramToSinks(const Histogram& histograms, uint64_t val) { scope_.deliverHistogramToSinks(histograms, val); } diff --git a/source/common/stats/scope_prefixer.h b/source/common/stats/scope_prefixer.h index 66c61072c7f42..51c1828393913 100644 --- a/source/common/stats/scope_prefixer.h +++ b/source/common/stats/scope_prefixer.h @@ -35,6 +35,11 @@ class ScopePrefixer : public Scope { return histogramFromStatName(storage.statName()); } + absl::optional> findCounter(StatName name) const override; + absl::optional> findGauge(StatName name) const override; + absl::optional> + findHistogram(StatName name) const override; + const SymbolTable& symbolTable() const override { return scope_.symbolTable(); } virtual SymbolTable& symbolTable() override { return scope_.symbolTable(); } diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index 03d799509c756..8a31348ca748a 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -316,7 +316,6 @@ StatType& ThreadLocalStoreImpl::ScopeImpl::safeMakeStat( StatMap>* tls_cache, StatNameHashSet* tls_rejected_stats, StatType& null_stat) { - // We do name-rejections on the full name, prior to truncation. if (tls_rejected_stats != nullptr && tls_rejected_stats->find(name) != tls_rejected_stats->end()) { return null_stat; @@ -358,6 +357,18 @@ StatType& ThreadLocalStoreImpl::ScopeImpl::safeMakeStat( return **central_ref; } +template +absl::optional> +ThreadLocalStoreImpl::ScopeImpl::findStatLockHeld( + StatName name, StatMap>& central_cache_map) const { + auto iter = central_cache_map.find(name); + if (iter == central_cache_map.end()) { + return absl::nullopt; + } + + return std::cref(*iter->second.get()); +} + Counter& ThreadLocalStoreImpl::ScopeImpl::counterFromStatName(StatName name) { if (parent_.rejectsAll()) { return parent_.null_counter_; @@ -496,6 +507,27 @@ Histogram& ThreadLocalStoreImpl::ScopeImpl::histogramFromStatName(StatName name) return **central_ref; } +absl::optional> +ThreadLocalStoreImpl::ScopeImpl::findCounter(StatName name) const { + return findStatLockHeld(name, central_cache_.counters_); +} + +absl::optional> +ThreadLocalStoreImpl::ScopeImpl::findGauge(StatName name) const { + return findStatLockHeld(name, central_cache_.gauges_); +} + +absl::optional> +ThreadLocalStoreImpl::ScopeImpl::findHistogram(StatName name) const { + auto iter = central_cache_.histograms_.find(name); + if (iter == central_cache_.histograms_.end()) { + return absl::nullopt; + } + + std::shared_ptr histogram_ref(iter->second); + return std::cref(*histogram_ref.get()); +} + Histogram& ThreadLocalStoreImpl::ScopeImpl::tlsHistogram(StatName name, ParentHistogramImpl& parent) { // tlsHistogram() is generally not called for a histogram that is rejected by diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 4e318bb949fb0..8a062b2269574 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -163,7 +163,40 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo const SymbolTable& symbolTable() const override { return alloc_.symbolTable(); } SymbolTable& symbolTable() override { return alloc_.symbolTable(); } const TagProducer& tagProducer() const { return *tag_producer_; } - + absl::optional> findCounter(StatName name) const override { + absl::optional> found_counter; + Thread::LockGuard lock(lock_); + for (ScopeImpl* scope : scopes_) { + found_counter = scope->findCounter(name); + if (found_counter.has_value()) { + return found_counter; + } + } + return absl::nullopt; + } + absl::optional> findGauge(StatName name) const override { + absl::optional> found_gauge; + Thread::LockGuard lock(lock_); + for (ScopeImpl* scope : scopes_) { + found_gauge = scope->findGauge(name); + if (found_gauge.has_value()) { + return found_gauge; + } + } + return absl::nullopt; + } + absl::optional> + findHistogram(StatName name) const override { + absl::optional> found_histogram; + Thread::LockGuard lock(lock_); + for (ScopeImpl* scope : scopes_) { + found_histogram = scope->findHistogram(name); + if (found_histogram.has_value()) { + return found_histogram; + } + } + return absl::nullopt; + } // Stats::Store std::vector counters() const override; std::vector gauges() const override; @@ -239,6 +272,13 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo NullGaugeImpl& nullGauge(const std::string&) override { return parent_.null_gauge_; } + // NOTE: The find methods assume that `name` is fully-qualified. + // Implementations will not add the scope prefix. + absl::optional> findCounter(StatName name) const override; + absl::optional> findGauge(StatName name) const override; + absl::optional> + findHistogram(StatName name) const override; + template using MakeStatFn = std::function(StatDataAllocator&, StatName name, absl::string_view tag_extracted_name, @@ -262,6 +302,19 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo StatMap>* tls_cache, StatNameHashSet* tls_rejected_stats, StatType& null_stat); + /** + * Looks up an existing stat, populating the local cache if necessary. Does + * not check the TLS or rejects, and does not create a stat if it does not + * exist. + * + * @param name the full name of the stat (not tag extracted). + * @param central_cache_map a map from name to the desired object in the central cache. + * @return a reference to the stat, if it exists. + */ + template + absl::optional> + findStatLockHeld(StatName name, StatMap>& central_cache_map) const; + void extractTagsAndTruncate(StatName& name, std::unique_ptr& truncated_name_storage, std::vector& tags, std::string& tag_extracted_name); @@ -271,7 +324,7 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo const uint64_t scope_id_; ThreadLocalStoreImpl& parent_; StatNameStorage prefix_; - CentralCacheEntry central_cache_; + mutable CentralCacheEntry central_cache_; }; struct TlsCache : public ThreadLocal::ThreadLocalObject { diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index e00ac79745cb8..6954a28147f1e 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -182,12 +182,32 @@ TEST_F(StatsThreadLocalStoreTest, NoTls) { Counter& c1 = store_->counter("c1"); EXPECT_EQ(&c1, &store_->counter("c1")); + StatNameManagedStorage c1_name("c1", symbol_table_); + c1.add(100); + auto found_counter = store_->findCounter(c1_name.statName()); + ASSERT_TRUE(found_counter.has_value()); + EXPECT_EQ(&c1, &found_counter->get()); + EXPECT_EQ(100, found_counter->get().value()); + c1.add(100); + EXPECT_EQ(200, found_counter->get().value()); Gauge& g1 = store_->gauge("g1"); EXPECT_EQ(&g1, &store_->gauge("g1")); + StatNameManagedStorage g1_name("g1", symbol_table_); + g1.set(100); + auto found_gauge = store_->findGauge(g1_name.statName()); + ASSERT_TRUE(found_gauge.has_value()); + EXPECT_EQ(&g1, &found_gauge->get()); + EXPECT_EQ(100, found_gauge->get().value()); + g1.set(0); + EXPECT_EQ(0, found_gauge->get().value()); Histogram& h1 = store_->histogram("h1"); EXPECT_EQ(&h1, &store_->histogram("h1")); + StatNameManagedStorage h1_name("h1", symbol_table_); + auto found_histogram = store_->findHistogram(h1_name.statName()); + ASSERT_TRUE(found_histogram.has_value()); + EXPECT_EQ(&h1, &found_histogram->get()); EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200)); h1.recordValue(200); @@ -210,12 +230,32 @@ TEST_F(StatsThreadLocalStoreTest, Tls) { Counter& c1 = store_->counter("c1"); EXPECT_EQ(&c1, &store_->counter("c1")); + StatNameManagedStorage c1_name("c1", symbol_table_); + c1.add(100); + auto found_counter = store_->findCounter(c1_name.statName()); + ASSERT_TRUE(found_counter.has_value()); + EXPECT_EQ(&c1, &found_counter->get()); + EXPECT_EQ(100, found_counter->get().value()); + c1.add(100); + EXPECT_EQ(200, found_counter->get().value()); Gauge& g1 = store_->gauge("g1"); EXPECT_EQ(&g1, &store_->gauge("g1")); + StatNameManagedStorage g1_name("g1", symbol_table_); + g1.set(100); + auto found_gauge = store_->findGauge(g1_name.statName()); + ASSERT_TRUE(found_gauge.has_value()); + EXPECT_EQ(&g1, &found_gauge->get()); + EXPECT_EQ(100, found_gauge->get().value()); + g1.set(0); + EXPECT_EQ(0, found_gauge->get().value()); Histogram& h1 = store_->histogram("h1"); EXPECT_EQ(&h1, &store_->histogram("h1")); + StatNameManagedStorage h1_name("h1", symbol_table_); + auto found_histogram = store_->findHistogram(h1_name.statName()); + ASSERT_TRUE(found_histogram.has_value()); + EXPECT_EQ(&h1, &found_histogram->get()); EXPECT_EQ(1UL, store_->counters().size()); EXPECT_EQ(&c1, TestUtility::findCounter(*store_, "c1").get()); @@ -244,11 +284,27 @@ TEST_F(StatsThreadLocalStoreTest, BasicScope) { Counter& c2 = scope1->counter("c2"); EXPECT_EQ("c1", c1.name()); EXPECT_EQ("scope1.c2", c2.name()); + StatNameManagedStorage c1_name("c1", symbol_table_); + auto found_counter = store_->findCounter(c1_name.statName()); + ASSERT_TRUE(found_counter.has_value()); + EXPECT_EQ(&c1, &found_counter->get()); + StatNameManagedStorage c2_name("scope1.c2", symbol_table_); + auto found_counter2 = store_->findCounter(c2_name.statName()); + ASSERT_TRUE(found_counter2.has_value()); + EXPECT_EQ(&c2, &found_counter2->get()); Gauge& g1 = store_->gauge("g1"); Gauge& g2 = scope1->gauge("g2"); EXPECT_EQ("g1", g1.name()); EXPECT_EQ("scope1.g2", g2.name()); + StatNameManagedStorage g1_name("g1", symbol_table_); + auto found_gauge = store_->findGauge(g1_name.statName()); + ASSERT_TRUE(found_gauge.has_value()); + EXPECT_EQ(&g1, &found_gauge->get()); + StatNameManagedStorage g2_name("scope1.g2", symbol_table_); + auto found_gauge2 = store_->findGauge(g2_name.statName()); + ASSERT_TRUE(found_gauge2.has_value()); + EXPECT_EQ(&g2, &found_gauge2->get()); Histogram& h1 = store_->histogram("h1"); Histogram& h2 = scope1->histogram("h2"); @@ -258,6 +314,14 @@ TEST_F(StatsThreadLocalStoreTest, BasicScope) { h1.recordValue(100); EXPECT_CALL(sink_, onHistogramComplete(Ref(h2), 200)); h2.recordValue(200); + StatNameManagedStorage h1_name("h1", symbol_table_); + auto found_histogram = store_->findHistogram(h1_name.statName()); + ASSERT_TRUE(found_histogram.has_value()); + EXPECT_EQ(&h1, &found_histogram->get()); + StatNameManagedStorage h2_name("scope1.h2", symbol_table_); + auto found_histogram2 = store_->findHistogram(h2_name.statName()); + ASSERT_TRUE(found_histogram2.has_value()); + EXPECT_EQ(&h2, &found_histogram2->get()); store_->shutdownThreading(); scope1->deliverHistogramToSinks(h1, 100); @@ -311,11 +375,18 @@ TEST_F(StatsThreadLocalStoreTest, NestedScopes) { ScopePtr scope1 = store_->createScope("scope1."); Counter& c1 = scope1->counter("foo.bar"); EXPECT_EQ("scope1.foo.bar", c1.name()); + StatNameManagedStorage c1_name("scope1.foo.bar", symbol_table_); + auto found_counter = store_->findCounter(c1_name.statName()); + ASSERT_TRUE(found_counter.has_value()); + EXPECT_EQ(&c1, &found_counter->get()); ScopePtr scope2 = scope1->createScope("foo."); Counter& c2 = scope2->counter("bar"); EXPECT_NE(&c1, &c2); EXPECT_EQ("scope1.foo.bar", c2.name()); + StatNameManagedStorage c2_name("scope1.foo.bar", symbol_table_); + auto found_counter2 = store_->findCounter(c2_name.statName()); + ASSERT_TRUE(found_counter2.has_value()); // Different allocations point to the same referenced counted backing memory. c1.inc(); diff --git a/test/integration/BUILD b/test/integration/BUILD index ca0f4fdcccaa6..5cb9cdf1c7215 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -91,6 +91,7 @@ envoy_cc_test( ":http_integration_lib", "//source/common/upstream:load_balancer_lib", "//test/config:utility_lib", + "//test/integration/filters:eds_ready_filter_config_lib", "//test/test_common:network_utility_lib", "@envoy_api//envoy/api/v2:eds_cc", ], diff --git a/test/integration/eds_integration_test.cc b/test/integration/eds_integration_test.cc index 1b14651fa5f52..3a3bb1d906699 100644 --- a/test/integration/eds_integration_test.cc +++ b/test/integration/eds_integration_test.cc @@ -260,5 +260,31 @@ TEST_P(EdsIntegrationTest, BatchMemberUpdateCb) { EXPECT_EQ(1, member_update_count); } +TEST_P(EdsIntegrationTest, StatsReadyFilter) { + config_helper_.addFilter("name: eds-ready-filter"); + initializeTest(false); + + // Initial state: no healthy endpoints + EXPECT_EQ(0, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); + BufferingStreamDecoderPtr response = IntegrationUtil::makeSingleRequest( + lookupPort("http"), "GET", "/cluster1", "", downstream_protocol_, version_, "foo.com"); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("500", response->headers().Status()->value().getStringView()); + EXPECT_EQ("EDS not ready", response->body()); + + cleanupUpstreamAndDownstream(); + + // 2/2 healthy endpoints. + setEndpoints(2, 2, 0); + EXPECT_EQ(2, test_server_->gauge("cluster.cluster_0.membership_healthy")->value()); + response = IntegrationUtil::makeSingleRequest(lookupPort("http"), "GET", "/cluster1", "", + downstream_protocol_, version_, "foo.com"); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().Status()->value().getStringView()); + EXPECT_EQ("EDS is ready", response->body()); + + cleanupUpstreamAndDownstream(); +} + } // namespace } // namespace Envoy diff --git a/test/integration/filters/BUILD b/test/integration/filters/BUILD index 5c45dbb2e0947..77d5c0ff83bc3 100644 --- a/test/integration/filters/BUILD +++ b/test/integration/filters/BUILD @@ -37,6 +37,22 @@ envoy_cc_test_library( ], ) +envoy_cc_test_library( + name = "eds_ready_filter_config_lib", + srcs = [ + "eds_ready_filter.cc", + ], + deps = [ + "//include/envoy/http:filter_interface", + "//include/envoy/http:header_map_interface", + "//include/envoy/registry", + "//include/envoy/server:filter_config_interface", + "//source/common/stats:symbol_table_lib", + "//source/extensions/filters/http/common:empty_http_filter_config_lib", + "//source/extensions/filters/http/common:pass_through_filter_lib", + ], +) + envoy_cc_test_library( name = "modify_buffer_filter_config_lib", srcs = [ diff --git a/test/integration/filters/eds_ready_filter.cc b/test/integration/filters/eds_ready_filter.cc new file mode 100644 index 0000000000000..37351fc538685 --- /dev/null +++ b/test/integration/filters/eds_ready_filter.cc @@ -0,0 +1,65 @@ +#include +#include + +#include "envoy/http/filter.h" +#include "envoy/http/header_map.h" +#include "envoy/registry/registry.h" +#include "envoy/server/filter_config.h" + +#include "common/stats/symbol_table_impl.h" + +#include "extensions/filters/http/common/empty_http_filter_config.h" +#include "extensions/filters/http/common/pass_through_filter.h" + +namespace Envoy { + +// A test filter that rejects all requests if EDS isn't healthy yet, and +// responds OK to all requests if it is. +class EdsReadyFilter : public Http::PassThroughFilter { +public: + EdsReadyFilter(const Stats::Scope& root_scope) + : root_scope_(root_scope), + stat_name_("cluster.cluster_0.membership_healthy", + const_cast(root_scope_.symbolTable())) {} + Http::FilterHeadersStatus decodeHeaders(Http::HeaderMap&, bool) override { + absl::optional> gauge = + root_scope_.findGauge(stat_name_.statName()); + if (!gauge.has_value()) { + decoder_callbacks_->sendLocalReply(Envoy::Http::Code::InternalServerError, + "Couldn't find stat", nullptr, absl::nullopt, ""); + return Http::FilterHeadersStatus::StopIteration; + } + if (gauge->get().value() == 0) { + decoder_callbacks_->sendLocalReply(Envoy::Http::Code::InternalServerError, "EDS not ready", + nullptr, absl::nullopt, ""); + return Http::FilterHeadersStatus::StopIteration; + } + decoder_callbacks_->sendLocalReply(Envoy::Http::Code::OK, "EDS is ready", nullptr, + absl::nullopt, ""); + return Http::FilterHeadersStatus::StopIteration; + } + +private: + const Stats::Scope& root_scope_; + Stats::StatNameManagedStorage stat_name_; +}; + +class EdsReadyFilterConfig : public Extensions::HttpFilters::Common::EmptyHttpFilterConfig { +public: + EdsReadyFilterConfig() : EmptyHttpFilterConfig("eds-ready-filter") {} + + Http::FilterFactoryCb + createFilter(const std::string&, + Server::Configuration::FactoryContext& factory_context) override { + return [&factory_context](Http::FilterChainFactoryCallbacks& callbacks) { + callbacks.addStreamFilter( + std::make_shared(factory_context.api().rootScope())); + }; + } +}; + +static Registry::RegisterFactory + register_; + +} // namespace Envoy diff --git a/test/integration/integration.h b/test/integration/integration.h index b1e2d31834628..3f300c4ab7584 100644 --- a/test/integration/integration.h +++ b/test/integration/integration.h @@ -193,6 +193,7 @@ class BaseIntegrationTest : Logger::Loggable { Stats::IsolatedStoreImpl stats_store_; Api::ApiPtr api_; + Api::ApiPtr api_for_server_stat_store_; MockBufferFactory* mock_buffer_factory_; // Will point to the dispatcher's factory. // Functions for testing reloadable config (xDS) diff --git a/test/integration/server.h b/test/integration/server.h index 096cc2dfac0a5..aa1e3865913bb 100644 --- a/test/integration/server.h +++ b/test/integration/server.h @@ -106,6 +106,20 @@ class TestScopeWrapper : public Scope { return histogramFromStatName(storage.statName()); } + absl::optional> findCounter(StatName name) const override { + Thread::LockGuard lock(lock_); + return wrapped_scope_->findCounter(name); + } + absl::optional> findGauge(StatName name) const override { + Thread::LockGuard lock(lock_); + return wrapped_scope_->findGauge(name); + } + absl::optional> + findHistogram(StatName name) const override { + Thread::LockGuard lock(lock_); + return wrapped_scope_->findHistogram(name); + } + const SymbolTable& symbolTable() const override { return wrapped_scope_->symbolTable(); } SymbolTable& symbolTable() override { return wrapped_scope_->symbolTable(); } @@ -152,6 +166,19 @@ class TestIsolatedStoreImpl : public StoreRoot { Thread::LockGuard lock(lock_); return store_.histogram(name); } + absl::optional> findCounter(StatName name) const override { + Thread::LockGuard lock(lock_); + return store_.findCounter(name); + } + absl::optional> findGauge(StatName name) const override { + Thread::LockGuard lock(lock_); + return store_.findGauge(name); + } + absl::optional> + findHistogram(StatName name) const override { + Thread::LockGuard lock(lock_); + return store_.findHistogram(name); + } const SymbolTable& symbolTable() const override { return store_.symbolTable(); } SymbolTable& symbolTable() override { return store_.symbolTable(); } diff --git a/test/mocks/api/mocks.cc b/test/mocks/api/mocks.cc index 2e4c6b6a74fff..b3eb6296544d8 100644 --- a/test/mocks/api/mocks.cc +++ b/test/mocks/api/mocks.cc @@ -12,7 +12,10 @@ using testing::Return; namespace Envoy { namespace Api { -MockApi::MockApi() { ON_CALL(*this, fileSystem()).WillByDefault(ReturnRef(file_system_)); } +MockApi::MockApi() { + ON_CALL(*this, fileSystem()).WillByDefault(ReturnRef(file_system_)); + ON_CALL(*this, rootScope()).WillByDefault(ReturnRef(stats_store_)); +} MockApi::~MockApi() {} diff --git a/test/mocks/api/mocks.h b/test/mocks/api/mocks.h index 042ef53aa2e99..592f7b68675fa 100644 --- a/test/mocks/api/mocks.h +++ b/test/mocks/api/mocks.h @@ -39,6 +39,7 @@ class MockApi : public Api { Event::TimeSystem&)); MOCK_METHOD0(fileSystem, Filesystem::Instance&()); MOCK_METHOD0(threadFactory, Thread::ThreadFactory&()); + MOCK_METHOD0(rootScope, const Stats::Scope&()); testing::NiceMock file_system_; Event::GlobalTimeSystem time_system_; diff --git a/test/mocks/stats/mocks.h b/test/mocks/stats/mocks.h index cd7407e0c95e3..23d6dd2738cd6 100644 --- a/test/mocks/stats/mocks.h +++ b/test/mocks/stats/mocks.h @@ -194,6 +194,11 @@ class MockStore : public SymbolTableProvider, public StoreImpl { MOCK_METHOD1(histogram, Histogram&(const std::string& name)); MOCK_CONST_METHOD0(histograms, std::vector()); + MOCK_CONST_METHOD1(findCounter, absl::optional>(StatName)); + MOCK_CONST_METHOD1(findGauge, absl::optional>(StatName)); + MOCK_CONST_METHOD1(findHistogram, + absl::optional>(StatName)); + Counter& counterFromStatName(StatName name) override { return counter(symbol_table_->toString(name)); }