diff --git a/docs/root/operations/admin.rst b/docs/root/operations/admin.rst index 8d53d4c613ad0..6c785184a3cdb 100644 --- a/docs/root/operations/admin.rst +++ b/docs/root/operations/admin.rst @@ -194,6 +194,25 @@ modify different aspects of the server: field, use the mask query parameter documented above. If you want only a subset of fields from the repeated resource, use both as documented below. +.. _operations_admin_interface_config_dump_by_name_regex: + +.. http:get:: /config_dump?name_regex={} + + Dump only the currently loaded configurations whose names match the specified regex. Can be used with + both `resource` and `mask` query parameters. + + For example, ``/config_dump?name_regex=.*substring.*`` would return all resource types + whose name field matches the given regex. + + Per resource, the matched name field is: + + - :ref:`envoy.config.listener.v3.Listener.name ` + - :ref:`envoy.config.route.v3.RouteConfiguration.name ` + - :ref:`envoy.config.route.v3.ScopedRouteConfiguration.name ` + - :ref:`envoy.config.cluster.v3.Cluster.name ` + - :ref:`envoy.extensions.transport_sockets.tls.v3.Secret ` + - :ref:`envoy.config.endpoint.v3.ClusterLoadAssignment ` + .. _operations_admin_interface_config_dump_by_resource_and_mask: .. http:get:: /config_dump?resource={}&mask={} diff --git a/envoy/server/BUILD b/envoy/server/BUILD index f3f9208b141be..ccd2478deea7d 100644 --- a/envoy/server/BUILD +++ b/envoy/server/BUILD @@ -53,6 +53,7 @@ envoy_cc_library( name = "config_tracker_interface", hdrs = ["config_tracker.h"], deps = [ + "//envoy/common:matchers_interface", "//source/common/common:non_copyable", "//source/common/protobuf", ], diff --git a/envoy/server/config_tracker.h b/envoy/server/config_tracker.h index 62932be2c853c..72fe2a88e2564 100644 --- a/envoy/server/config_tracker.h +++ b/envoy/server/config_tracker.h @@ -4,6 +4,7 @@ #include #include +#include "envoy/common/matchers.h" #include "envoy/common/pure.h" #include "source/common/common/non_copyable.h" @@ -21,7 +22,9 @@ namespace Server { */ class ConfigTracker { public: - using Cb = std::function; + // The passed StringMatcher will be matched against the `name` field of whatever + // proto is returned. + using Cb = std::function; using CbsMap = std::map; /** diff --git a/source/common/common/matchers.h b/source/common/common/matchers.h index 396203ce5b50d..bdc284f2d480f 100644 --- a/source/common/common/matchers.h +++ b/source/common/common/matchers.h @@ -76,6 +76,11 @@ class DoubleMatcher : public ValueMatcher { const envoy::type::matcher::v3::DoubleMatcher matcher_; }; +class UniversalStringMatcher : public StringMatcher { +public: + bool match(absl::string_view) const override { return true; } +}; + class StringMatcherImpl : public ValueMatcher, public StringMatcher { public: explicit StringMatcherImpl(const envoy::type::matcher::v3::StringMatcher& matcher); diff --git a/source/common/config/config_provider_impl.cc b/source/common/config/config_provider_impl.cc index 172c5305ee57d..b25115b40314d 100644 --- a/source/common/config/config_provider_impl.cc +++ b/source/common/config/config_provider_impl.cc @@ -52,8 +52,9 @@ bool ConfigSubscriptionInstance::checkAndApplyConfigUpdate(const Protobuf::Messa ConfigProviderManagerImplBase::ConfigProviderManagerImplBase(Server::Admin& admin, const std::string& config_name) { - config_tracker_entry_ = - admin.getConfigTracker().add(config_name, [this] { return dumpConfigs(); }); + config_tracker_entry_ = admin.getConfigTracker().add( + config_name, + [this](const Matchers::StringMatcher& name_matcher) { return dumpConfigs(name_matcher); }); // ConfigTracker keys must be unique. We are asserting that no one has stolen the key // from us, since the returned entry will be nullptr if the key already exists. RELEASE_ASSERT(config_tracker_entry_, ""); diff --git a/source/common/config/config_provider_impl.h b/source/common/config/config_provider_impl.h index 8db998ebab884..7cbdfd4955128 100644 --- a/source/common/config/config_provider_impl.h +++ b/source/common/config/config_provider_impl.h @@ -383,7 +383,8 @@ class ConfigProviderManagerImplBase : public ConfigProviderManager, public Singl * @return ProtobufTypes::MessagePtr the config dump proto corresponding to the associated * config providers. */ - virtual ProtobufTypes::MessagePtr dumpConfigs() const PURE; + virtual ProtobufTypes::MessagePtr + dumpConfigs(const Matchers::StringMatcher& name_matcher) const PURE; protected: // Ordered set for deterministic config dump output. diff --git a/source/common/router/rds_impl.cc b/source/common/router/rds_impl.cc index d9a712217775d..670b5e1268cc0 100644 --- a/source/common/router/rds_impl.cc +++ b/source/common/router/rds_impl.cc @@ -328,7 +328,9 @@ void RdsRouteConfigProviderImpl::requestVirtualHostsUpdate( RouteConfigProviderManagerImpl::RouteConfigProviderManagerImpl(Server::Admin& admin) { config_tracker_entry_ = - admin.getConfigTracker().add("routes", [this] { return dumpRouteConfigs(); }); + admin.getConfigTracker().add("routes", [this](const Matchers::StringMatcher& matcher) { + return dumpRouteConfigs(matcher); + }); // ConfigTracker keys must be unique. We are asserting that no one has stolen the "routes" key // from us, since the returned entry will be nullptr if the key already exists. RELEASE_ASSERT(config_tracker_entry_, ""); @@ -379,7 +381,8 @@ RouteConfigProviderPtr RouteConfigProviderManagerImpl::createStaticRouteConfigPr } std::unique_ptr -RouteConfigProviderManagerImpl::dumpRouteConfigs() const { +RouteConfigProviderManagerImpl::dumpRouteConfigs( + const Matchers::StringMatcher& name_matcher) const { auto config_dump = std::make_unique(); for (const auto& element : dynamic_route_config_providers_) { @@ -391,6 +394,9 @@ RouteConfigProviderManagerImpl::dumpRouteConfigs() const { ASSERT(subscription->route_config_provider_opt_.has_value()); if (subscription->routeConfigUpdate()->configInfo()) { + if (!name_matcher.match(subscription->routeConfigUpdate()->protobufConfiguration().name())) { + continue; + } auto* dynamic_config = config_dump->mutable_dynamic_route_configs()->Add(); dynamic_config->set_version_info(subscription->routeConfigUpdate()->configVersion()); dynamic_config->mutable_route_config()->PackFrom( @@ -402,6 +408,9 @@ RouteConfigProviderManagerImpl::dumpRouteConfigs() const { for (const auto& provider : static_route_config_providers_) { ASSERT(provider->configInfo()); + if (!name_matcher.match(provider->configInfo().value().config_.name())) { + continue; + } auto* static_config = config_dump->mutable_static_route_configs()->Add(); static_config->mutable_route_config()->PackFrom( API_RECOVER_ORIGINAL(provider->configInfo().value().config_)); diff --git a/source/common/router/rds_impl.h b/source/common/router/rds_impl.h index 9e79763fc1ddd..4c4df57d4482b 100644 --- a/source/common/router/rds_impl.h +++ b/source/common/router/rds_impl.h @@ -242,7 +242,8 @@ class RouteConfigProviderManagerImpl : public RouteConfigProviderManager, public: RouteConfigProviderManagerImpl(Server::Admin& admin); - std::unique_ptr dumpRouteConfigs() const; + std::unique_ptr + dumpRouteConfigs(const Matchers::StringMatcher& name_matcher) const; // RouteConfigProviderManager RouteConfigProviderSharedPtr createRdsRouteConfigProvider( diff --git a/source/common/router/scoped_rds.cc b/source/common/router/scoped_rds.cc index fe005aa16953f..13cee6e2449e0 100644 --- a/source/common/router/scoped_rds.cc +++ b/source/common/router/scoped_rds.cc @@ -521,7 +521,8 @@ ScopedRdsConfigProvider::ScopedRdsConfigProvider( ScopedRdsConfigSubscriptionSharedPtr&& subscription) : MutableConfigProviderCommonBase(std::move(subscription), ConfigProvider::ApiType::Delta) {} -ProtobufTypes::MessagePtr ScopedRoutesConfigProviderManager::dumpConfigs() const { +ProtobufTypes::MessagePtr +ScopedRoutesConfigProviderManager::dumpConfigs(const Matchers::StringMatcher& name_matcher) const { auto config_dump = std::make_unique(); for (const auto& element : configSubscriptions()) { auto subscription = element.second.lock(); @@ -535,6 +536,9 @@ ProtobufTypes::MessagePtr ScopedRoutesConfigProviderManager::dumpConfigs() const dynamic_config->set_name(typed_subscription->name()); const ScopedRouteMap& scoped_route_map = typed_subscription->scopedRouteMap(); for (const auto& it : scoped_route_map) { + if (!name_matcher.match(it.second->configProto().name())) { + continue; + } dynamic_config->mutable_scoped_route_configs()->Add()->PackFrom( API_RECOVER_ORIGINAL(it.second->configProto())); } @@ -550,6 +554,9 @@ ProtobufTypes::MessagePtr ScopedRoutesConfigProviderManager::dumpConfigs() const auto* inline_config = config_dump->mutable_inline_scoped_route_configs()->Add(); inline_config->set_name(static_cast(provider)->name()); for (const auto& config_proto : protos_info.value().config_protos_) { + if (!name_matcher.match(config_proto->name())) { + continue; + } inline_config->mutable_scoped_route_configs()->Add()->PackFrom( API_RECOVER_ORIGINAL(*config_proto)); } diff --git a/source/common/router/scoped_rds.h b/source/common/router/scoped_rds.h index 65898e81bda34..b0e5690bee8e8 100644 --- a/source/common/router/scoped_rds.h +++ b/source/common/router/scoped_rds.h @@ -275,7 +275,7 @@ class ScopedRoutesConfigProviderManager : public Envoy::Config::ConfigProviderMa ~ScopedRoutesConfigProviderManager() override = default; // Envoy::Config::ConfigProviderManagerImplBase - ProtobufTypes::MessagePtr dumpConfigs() const override; + ProtobufTypes::MessagePtr dumpConfigs(const Matchers::StringMatcher& name_matcher) const override; // Envoy::Config::ConfigProviderManager Envoy::Config::ConfigProviderPtr diff --git a/source/common/secret/secret_manager_impl.cc b/source/common/secret/secret_manager_impl.cc index 0d583128c6c43..5238ff77ae9e8 100644 --- a/source/common/secret/secret_manager_impl.cc +++ b/source/common/secret/secret_manager_impl.cc @@ -19,8 +19,10 @@ namespace Envoy { namespace Secret { SecretManagerImpl::SecretManagerImpl(Server::ConfigTracker& config_tracker) - : config_tracker_entry_(config_tracker.add("secrets", [this] { return dumpSecretConfigs(); })) { -} + : config_tracker_entry_( + config_tracker.add("secrets", [this](const Matchers::StringMatcher& name_matcher) { + return dumpSecretConfigs(name_matcher); + })) {} void SecretManagerImpl::addStaticSecret( const envoy::extensions::transport_sockets::tls::v3::Secret& secret) { switch (secret.type_case()) { @@ -151,7 +153,8 @@ GenericSecretConfigProviderSharedPtr SecretManagerImpl::findOrCreateGenericSecre secret_provider_context); } -ProtobufTypes::MessagePtr SecretManagerImpl::dumpSecretConfigs() { +ProtobufTypes::MessagePtr +SecretManagerImpl::dumpSecretConfigs(const Matchers::StringMatcher& name_matcher) { // TODO(htuch): unlike other config providers, we're recreating the original // Secrets below. This makes it hard to support API_RECOVER_ORIGINAL()-style // recovery of the original config message. As a result, for now we're @@ -161,52 +164,64 @@ ProtobufTypes::MessagePtr SecretManagerImpl::dumpSecretConfigs() { // Handle static tls key/cert providers. for (const auto& cert_iter : static_tls_certificate_providers_) { const auto& tls_cert = cert_iter.second; - auto static_secret = config_dump->mutable_static_secrets()->Add(); - static_secret->set_name(cert_iter.first); ASSERT(tls_cert != nullptr); envoy::extensions::transport_sockets::tls::v3::Secret dump_secret; dump_secret.set_name(cert_iter.first); dump_secret.mutable_tls_certificate()->MergeFrom(*tls_cert->secret()); + if (!name_matcher.match(dump_secret.name())) { + continue; + } MessageUtil::redact(dump_secret); + auto static_secret = config_dump->mutable_static_secrets()->Add(); + static_secret->set_name(cert_iter.first); static_secret->mutable_secret()->PackFrom(dump_secret); } // Handle static certificate validation context providers. for (const auto& context_iter : static_certificate_validation_context_providers_) { const auto& validation_context = context_iter.second; - auto static_secret = config_dump->mutable_static_secrets()->Add(); - static_secret->set_name(context_iter.first); ASSERT(validation_context != nullptr); envoy::extensions::transport_sockets::tls::v3::Secret dump_secret; dump_secret.set_name(context_iter.first); dump_secret.mutable_validation_context()->MergeFrom(*validation_context->secret()); + if (!name_matcher.match(dump_secret.name())) { + continue; + } + auto static_secret = config_dump->mutable_static_secrets()->Add(); + static_secret->set_name(context_iter.first); static_secret->mutable_secret()->PackFrom(dump_secret); } // Handle static session keys providers. for (const auto& context_iter : static_session_ticket_keys_providers_) { const auto& session_ticket_keys = context_iter.second; - auto static_secret = config_dump->mutable_static_secrets()->Add(); - static_secret->set_name(context_iter.first); ASSERT(session_ticket_keys != nullptr); envoy::extensions::transport_sockets::tls::v3::Secret dump_secret; dump_secret.set_name(context_iter.first); for (const auto& key : session_ticket_keys->secret()->keys()) { dump_secret.mutable_session_ticket_keys()->add_keys()->MergeFrom(key); } + if (!name_matcher.match(dump_secret.name())) { + continue; + } MessageUtil::redact(dump_secret); + auto static_secret = config_dump->mutable_static_secrets()->Add(); + static_secret->set_name(context_iter.first); static_secret->mutable_secret()->PackFrom(dump_secret); } // Handle static generic secret providers. for (const auto& secret_iter : static_generic_secret_providers_) { const auto& generic_secret = secret_iter.second; - auto static_secret = config_dump->mutable_static_secrets()->Add(); - static_secret->set_name(secret_iter.first); ASSERT(generic_secret != nullptr); envoy::extensions::transport_sockets::tls::v3::Secret dump_secret; dump_secret.set_name(secret_iter.first); dump_secret.mutable_generic_secret()->MergeFrom(*generic_secret->secret()); + if (!name_matcher.match(dump_secret.name())) { + continue; + } + auto static_secret = config_dump->mutable_static_secrets()->Add(); + static_secret->set_name(secret_iter.first); MessageUtil::redact(dump_secret); static_secret->mutable_secret()->PackFrom(dump_secret); } @@ -216,25 +231,28 @@ ProtobufTypes::MessagePtr SecretManagerImpl::dumpSecretConfigs() { for (const auto& cert_secrets : providers) { const auto& secret_data = cert_secrets->secretData(); const auto& tls_cert = cert_secrets->secret(); - envoy::admin::v3::SecretsConfigDump::DynamicSecret* dump_secret; const bool secret_ready = tls_cert != nullptr; - if (secret_ready) { - dump_secret = config_dump->mutable_dynamic_active_secrets()->Add(); - } else { - dump_secret = config_dump->mutable_dynamic_warming_secrets()->Add(); - } - dump_secret->set_name(secret_data.resource_name_); envoy::extensions::transport_sockets::tls::v3::Secret secret; secret.set_name(secret_data.resource_name_); ProtobufWkt::Timestamp last_updated_ts; TimestampUtil::systemClockToTimestamp(secret_data.last_updated_, last_updated_ts); - dump_secret->set_version_info(secret_data.version_info_); - *dump_secret->mutable_last_updated() = last_updated_ts; secret.set_name(secret_data.resource_name_); if (secret_ready) { secret.mutable_tls_certificate()->MergeFrom(*tls_cert); } + if (!name_matcher.match(secret.name())) { + continue; + } MessageUtil::redact(secret); + envoy::admin::v3::SecretsConfigDump::DynamicSecret* dump_secret; + if (secret_ready) { + dump_secret = config_dump->mutable_dynamic_active_secrets()->Add(); + } else { + dump_secret = config_dump->mutable_dynamic_warming_secrets()->Add(); + } + dump_secret->set_name(secret_data.resource_name_); + dump_secret->set_version_info(secret_data.version_info_); + *dump_secret->mutable_last_updated() = last_updated_ts; dump_secret->mutable_secret()->PackFrom(secret); } @@ -243,23 +261,28 @@ ProtobufTypes::MessagePtr SecretManagerImpl::dumpSecretConfigs() { for (const auto& validation_context_secret : context_secret_provider) { const auto& secret_data = validation_context_secret->secretData(); const auto& validation_context = validation_context_secret->secret(); - envoy::admin::v3::SecretsConfigDump::DynamicSecret* dump_secret; const bool secret_ready = validation_context != nullptr; + + envoy::extensions::transport_sockets::tls::v3::Secret secret; + if (secret_ready) { - dump_secret = config_dump->mutable_dynamic_active_secrets()->Add(); - } else { - dump_secret = config_dump->mutable_dynamic_warming_secrets()->Add(); + secret.mutable_validation_context()->MergeFrom(*validation_context); } - dump_secret->set_name(secret_data.resource_name_); - envoy::extensions::transport_sockets::tls::v3::Secret secret; secret.set_name(secret_data.resource_name_); + if (!name_matcher.match(secret.name())) { + continue; + } ProtobufWkt::Timestamp last_updated_ts; + envoy::admin::v3::SecretsConfigDump::DynamicSecret* dump_secret; TimestampUtil::systemClockToTimestamp(secret_data.last_updated_, last_updated_ts); - dump_secret->set_version_info(secret_data.version_info_); - *dump_secret->mutable_last_updated() = last_updated_ts; if (secret_ready) { - secret.mutable_validation_context()->MergeFrom(*validation_context); + dump_secret = config_dump->mutable_dynamic_active_secrets()->Add(); + } else { + dump_secret = config_dump->mutable_dynamic_warming_secrets()->Add(); } + dump_secret->set_version_info(secret_data.version_info_); + *dump_secret->mutable_last_updated() = last_updated_ts; + dump_secret->set_name(secret_data.resource_name_); dump_secret->mutable_secret()->PackFrom(secret); } @@ -268,23 +291,26 @@ ProtobufTypes::MessagePtr SecretManagerImpl::dumpSecretConfigs() { for (const auto& stek_secrets : stek_providers) { const auto& secret_data = stek_secrets->secretData(); const auto& tls_stek = stek_secrets->secret(); - envoy::admin::v3::SecretsConfigDump::DynamicSecret* dump_secret; const bool secret_ready = tls_stek != nullptr; + envoy::extensions::transport_sockets::tls::v3::Secret secret; + secret.set_name(secret_data.resource_name_); + if (secret_ready) { + secret.mutable_session_ticket_keys()->MergeFrom(*tls_stek); + } + if (!name_matcher.match(secret.name())) { + continue; + } + ProtobufWkt::Timestamp last_updated_ts; + TimestampUtil::systemClockToTimestamp(secret_data.last_updated_, last_updated_ts); + envoy::admin::v3::SecretsConfigDump::DynamicSecret* dump_secret; if (secret_ready) { dump_secret = config_dump->mutable_dynamic_active_secrets()->Add(); } else { dump_secret = config_dump->mutable_dynamic_warming_secrets()->Add(); } dump_secret->set_name(secret_data.resource_name_); - envoy::extensions::transport_sockets::tls::v3::Secret secret; - secret.set_name(secret_data.resource_name_); - ProtobufWkt::Timestamp last_updated_ts; - TimestampUtil::systemClockToTimestamp(secret_data.last_updated_, last_updated_ts); dump_secret->set_version_info(secret_data.version_info_); *dump_secret->mutable_last_updated() = last_updated_ts; - if (secret_ready) { - secret.mutable_session_ticket_keys()->MergeFrom(*tls_stek); - } MessageUtil::redact(secret); dump_secret->mutable_secret()->PackFrom(secret); } @@ -294,23 +320,26 @@ ProtobufTypes::MessagePtr SecretManagerImpl::dumpSecretConfigs() { for (const auto& provider : generic_secret_providers) { const auto& secret_data = provider->secretData(); const auto& generic_secret = provider->secret(); - envoy::admin::v3::SecretsConfigDump::DynamicSecret* dump_secret; const bool secret_ready = generic_secret != nullptr; + envoy::extensions::transport_sockets::tls::v3::Secret secret; + secret.set_name(secret_data.resource_name_); + if (secret_ready) { + secret.mutable_generic_secret()->MergeFrom(*generic_secret); + } + if (!name_matcher.match(secret.name())) { + continue; + } + ProtobufWkt::Timestamp last_updated_ts; + TimestampUtil::systemClockToTimestamp(secret_data.last_updated_, last_updated_ts); + envoy::admin::v3::SecretsConfigDump::DynamicSecret* dump_secret; if (secret_ready) { dump_secret = config_dump->mutable_dynamic_active_secrets()->Add(); } else { dump_secret = config_dump->mutable_dynamic_warming_secrets()->Add(); } dump_secret->set_name(secret_data.resource_name_); - envoy::extensions::transport_sockets::tls::v3::Secret secret; - secret.set_name(secret_data.resource_name_); - ProtobufWkt::Timestamp last_updated_ts; - TimestampUtil::systemClockToTimestamp(secret_data.last_updated_, last_updated_ts); dump_secret->set_version_info(secret_data.version_info_); *dump_secret->mutable_last_updated() = last_updated_ts; - if (secret_ready) { - secret.mutable_generic_secret()->MergeFrom(*generic_secret); - } MessageUtil::redact(secret); dump_secret->mutable_secret()->PackFrom(secret); } diff --git a/source/common/secret/secret_manager_impl.h b/source/common/secret/secret_manager_impl.h index 66ca9e4435818..4537c80705c36 100644 --- a/source/common/secret/secret_manager_impl.h +++ b/source/common/secret/secret_manager_impl.h @@ -68,7 +68,7 @@ class SecretManagerImpl : public SecretManager { Server::Configuration::TransportSocketFactoryContext& secret_provider_context) override; private: - ProtobufTypes::MessagePtr dumpSecretConfigs(); + ProtobufTypes::MessagePtr dumpSecretConfigs(const Matchers::StringMatcher& name_matcher); template class DynamicSecretProviders : public Logger::Loggable { diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index d9d38bd573f38..b62c3261076d1 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -272,7 +272,10 @@ ClusterManagerImpl::ClusterManagerImpl( cm_stats_(generateStats(stats)), init_helper_(*this, [this](ClusterManagerCluster& cluster) { onClusterInit(cluster); }), config_tracker_entry_( - admin.getConfigTracker().add("clusters", [this] { return dumpClusterConfigs(); })), + admin.getConfigTracker().add("clusters", + [this](const Matchers::StringMatcher& name_matcher) { + return dumpClusterConfigs(name_matcher); + })), time_source_(main_thread_dispatcher.timeSource()), dispatcher_(main_thread_dispatcher), http_context_(http_context), router_context_(router_context), cluster_stat_names_(stats.symbolTable()), @@ -1045,11 +1048,15 @@ ClusterManagerImpl::addThreadLocalClusterUpdateCallbacks(ClusterUpdateCallbacks& return std::make_unique(cb, cluster_manager.update_callbacks_); } -ProtobufTypes::MessagePtr ClusterManagerImpl::dumpClusterConfigs() { +ProtobufTypes::MessagePtr +ClusterManagerImpl::dumpClusterConfigs(const Matchers::StringMatcher& name_matcher) { auto config_dump = std::make_unique(); config_dump->set_version_info(cds_api_ != nullptr ? cds_api_->versionInfo() : ""); for (const auto& active_cluster_pair : active_clusters_) { const auto& cluster = *active_cluster_pair.second; + if (!name_matcher.match(cluster.cluster_config_.name())) { + continue; + } if (!cluster.added_via_api_) { auto& static_cluster = *config_dump->mutable_static_clusters()->Add(); static_cluster.mutable_cluster()->PackFrom(API_RECOVER_ORIGINAL(cluster.cluster_config_)); @@ -1066,6 +1073,9 @@ ProtobufTypes::MessagePtr ClusterManagerImpl::dumpClusterConfigs() { for (const auto& warming_cluster_pair : warming_clusters_) { const auto& cluster = *warming_cluster_pair.second; + if (!name_matcher.match(cluster.cluster_config_.name())) { + continue; + } auto& dynamic_cluster = *config_dump->mutable_dynamic_warming_clusters()->Add(); dynamic_cluster.set_version_info(cluster.version_info_); dynamic_cluster.mutable_cluster()->PackFrom(API_RECOVER_ORIGINAL(cluster.cluster_config_)); diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index defc0af2c5d79..c3d436eb3f9ac 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -565,7 +565,7 @@ class ClusterManagerImpl : public ClusterManager, Logger::Loggable +buildNameMatcher(const Http::Utility::QueryParams& params) { + const auto name_regex = Utility::queryParam(params, "name_regex"); + if (!name_regex.has_value()) { + return std::make_unique(); + } + envoy::type::matcher::v3::RegexMatcher matcher; + *matcher.mutable_google_re2() = envoy::type::matcher::v3::RegexMatcher::GoogleRE2(); + matcher.set_regex(*name_regex); + TRY_ASSERT_MAIN_THREAD + return Regex::Utility::parseRegex(matcher); + END_TRY + catch (EnvoyException& e) { + return absl::InvalidArgumentError( + absl::StrCat("Error while parsing name_regex from ", *name_regex, ": ", e.what())); + } +} + } // namespace ConfigDumpHandler::ConfigDumpHandler(ConfigTracker& config_tracker, Server::Instance& server) @@ -143,14 +164,20 @@ Http::Code ConfigDumpHandler::handlerConfigDump(absl::string_view url, const auto resource = resourceParam(query_params); const auto mask = maskParam(query_params); const bool include_eds = shouldIncludeEdsInDump(query_params); + const absl::StatusOr name_matcher = buildNameMatcher(query_params); + if (!name_matcher.ok()) { + response.add(name_matcher.status().ToString()); + response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Text); + return Http::Code::BadRequest; + } envoy::admin::v3::ConfigDump dump; absl::optional> err; if (resource.has_value()) { - err = addResourceToDump(dump, mask, resource.value(), include_eds); + err = addResourceToDump(dump, mask, resource.value(), **name_matcher, include_eds); } else { - err = addAllConfigToDump(dump, mask, include_eds); + err = addAllConfigToDump(dump, mask, **name_matcher, include_eds); } if (err.has_value()) { response_headers.addReference(Http::Headers::get().XContentTypeOptions, @@ -166,22 +193,24 @@ Http::Code ConfigDumpHandler::handlerConfigDump(absl::string_view url, return Http::Code::OK; } -absl::optional> -ConfigDumpHandler::addResourceToDump(envoy::admin::v3::ConfigDump& dump, - const absl::optional& mask, - const std::string& resource, bool include_eds) const { +absl::optional> ConfigDumpHandler::addResourceToDump( + envoy::admin::v3::ConfigDump& dump, const absl::optional& mask, + const std::string& resource, const Matchers::StringMatcher& name_matcher, + bool include_eds) const { Envoy::Server::ConfigTracker::CbsMap callbacks_map = config_tracker_.getCallbacksMap(); if (include_eds) { // TODO(mattklein123): Add ability to see warming clusters in admin output. auto all_clusters = server_.clusterManager().clusters(); if (!all_clusters.active_clusters_.empty()) { - callbacks_map.emplace("endpoint", [this] { return dumpEndpointConfigs(); }); + callbacks_map.emplace("endpoint", [this](const Matchers::StringMatcher& name_matcher) { + return dumpEndpointConfigs(name_matcher); + }); } } for (const auto& [name, callback] : callbacks_map) { UNREFERENCED_PARAMETER(name); - ProtobufTypes::MessagePtr message = callback(); + ProtobufTypes::MessagePtr message = callback(name_matcher); ASSERT(message); auto field_descriptor = message->GetDescriptor()->FindFieldByName(resource); @@ -219,22 +248,23 @@ ConfigDumpHandler::addResourceToDump(envoy::admin::v3::ConfigDump& dump, std::make_pair(Http::Code::NotFound, fmt::format("{} not found in config dump", resource))}; } -absl::optional> -ConfigDumpHandler::addAllConfigToDump(envoy::admin::v3::ConfigDump& dump, - const absl::optional& mask, - bool include_eds) const { +absl::optional> ConfigDumpHandler::addAllConfigToDump( + envoy::admin::v3::ConfigDump& dump, const absl::optional& mask, + const Matchers::StringMatcher& name_matcher, bool include_eds) const { Envoy::Server::ConfigTracker::CbsMap callbacks_map = config_tracker_.getCallbacksMap(); if (include_eds) { // TODO(mattklein123): Add ability to see warming clusters in admin output. auto all_clusters = server_.clusterManager().clusters(); if (!all_clusters.active_clusters_.empty()) { - callbacks_map.emplace("endpoint", [this] { return dumpEndpointConfigs(); }); + callbacks_map.emplace("endpoint", [this](const Matchers::StringMatcher& name_matcher) { + return dumpEndpointConfigs(name_matcher); + }); } } for (const auto& [name, callback] : callbacks_map) { UNREFERENCED_PARAMETER(name); - ProtobufTypes::MessagePtr message = callback(); + ProtobufTypes::MessagePtr message = callback(name_matcher); ASSERT(message); if (mask.has_value()) { @@ -255,7 +285,8 @@ ConfigDumpHandler::addAllConfigToDump(envoy::admin::v3::ConfigDump& dump, return absl::nullopt; } -ProtobufTypes::MessagePtr ConfigDumpHandler::dumpEndpointConfigs() const { +ProtobufTypes::MessagePtr +ConfigDumpHandler::dumpEndpointConfigs(const Matchers::StringMatcher& name_matcher) const { auto endpoint_config_dump = std::make_unique(); // TODO(mattklein123): Add ability to see warming clusters in admin output. auto all_clusters = server_.clusterManager().clusters(); @@ -270,6 +301,9 @@ ProtobufTypes::MessagePtr ConfigDumpHandler::dumpEndpointConfigs() const { } else { cluster_load_assignment.set_cluster_name(cluster_info->name()); } + if (!name_matcher.match(cluster_load_assignment.cluster_name())) { + continue; + } auto& policy = *cluster_load_assignment.mutable_policy(); for (auto& host_set : cluster.prioritySet().hostSetsPerPriority()) { @@ -303,7 +337,6 @@ ProtobufTypes::MessagePtr ConfigDumpHandler::dumpEndpointConfigs() const { } } } - if (cluster_info->addedViaApi()) { auto& dynamic_endpoint = *endpoint_config_dump->mutable_dynamic_endpoint_configs()->Add(); dynamic_endpoint.mutable_endpoint_config()->PackFrom(cluster_load_assignment); diff --git a/source/server/admin/config_dump_handler.h b/source/server/admin/config_dump_handler.h index a5f024f288d6e..d64801eaa8211 100644 --- a/source/server/admin/config_dump_handler.h +++ b/source/server/admin/config_dump_handler.h @@ -29,7 +29,7 @@ class ConfigDumpHandler : public HandlerContextBase { private: absl::optional> addAllConfigToDump(envoy::admin::v3::ConfigDump& dump, const absl::optional& mask, - bool include_eds) const; + const Matchers::StringMatcher& name_matcher, bool include_eds) const; /** * Add the config matching the passed resource to the passed config dump. * @return absl::nullopt on success, else the Http::Code and an error message that should be added @@ -37,7 +37,8 @@ class ConfigDumpHandler : public HandlerContextBase { */ absl::optional> addResourceToDump(envoy::admin::v3::ConfigDump& dump, const absl::optional& mask, - const std::string& resource, bool include_eds) const; + const std::string& resource, const Matchers::StringMatcher& name_matcher, + bool include_eds) const; /** * Helper methods to add endpoints config @@ -45,7 +46,7 @@ class ConfigDumpHandler : public HandlerContextBase { void addLbEndpoint(const Upstream::HostSharedPtr& host, envoy::config::endpoint::v3::LocalityLbEndpoints& locality_lb_endpoint) const; - ProtobufTypes::MessagePtr dumpEndpointConfigs() const; + ProtobufTypes::MessagePtr dumpEndpointConfigs(const Matchers::StringMatcher& name_matcher) const; ConfigTracker& config_tracker_; }; diff --git a/source/server/listener_manager_impl.cc b/source/server/listener_manager_impl.cc index d339a6cae5bc5..b5de079b49fcc 100644 --- a/source/server/listener_manager_impl.cc +++ b/source/server/listener_manager_impl.cc @@ -255,7 +255,10 @@ ListenerManagerImpl::ListenerManagerImpl(Instance& server, : server_(server), factory_(listener_factory), scope_(server.stats().createScope("listener_manager.")), stats_(generateStats(*scope_)), config_tracker_entry_(server.admin().getConfigTracker().add( - "listeners", [this] { return dumpListenerConfigs(); })), + "listeners", + [this](const Matchers::StringMatcher& name_matcher) { + return dumpListenerConfigs(name_matcher); + })), enable_dispatcher_stats_(enable_dispatcher_stats), quic_stat_names_(server_.stats().symbolTable()) { for (uint32_t i = 0; i < server.options().concurrency(); i++) { @@ -264,7 +267,8 @@ ListenerManagerImpl::ListenerManagerImpl(Instance& server, } } -ProtobufTypes::MessagePtr ListenerManagerImpl::dumpListenerConfigs() { +ProtobufTypes::MessagePtr +ListenerManagerImpl::dumpListenerConfigs(const Matchers::StringMatcher& name_matcher) { auto config_dump = std::make_unique(); config_dump->set_version_info(lds_api_ != nullptr ? lds_api_->versionInfo() : ""); @@ -273,6 +277,9 @@ ProtobufTypes::MessagePtr ListenerManagerImpl::dumpListenerConfigs() { absl::flat_hash_map listener_map; for (const auto& listener : active_listeners_) { + if (!name_matcher.match(listener->config().name())) { + continue; + } if (listener->blockRemove()) { auto& static_listener = *config_dump->mutable_static_listeners()->Add(); static_listener.mutable_listener()->PackFrom(API_RECOVER_ORIGINAL(listener->config())); @@ -297,6 +304,9 @@ ProtobufTypes::MessagePtr ListenerManagerImpl::dumpListenerConfigs() { } for (const auto& listener : warming_listeners_) { + if (!name_matcher.match(listener->config().name())) { + continue; + } DynamicListener* dynamic_listener = getOrCreateDynamicListener(listener->name(), *config_dump, listener_map); DynamicListenerState* dump_listener = dynamic_listener->mutable_warming_state(); @@ -304,6 +314,9 @@ ProtobufTypes::MessagePtr ListenerManagerImpl::dumpListenerConfigs() { } for (const auto& draining_listener : draining_listeners_) { + if (!name_matcher.match(draining_listener.listener_->config().name())) { + continue; + } const auto& listener = draining_listener.listener_; DynamicListener* dynamic_listener = getOrCreateDynamicListener(listener->name(), *config_dump, listener_map); diff --git a/source/server/listener_manager_impl.h b/source/server/listener_manager_impl.h index 536184784c64e..eefde4c3af893 100644 --- a/source/server/listener_manager_impl.h +++ b/source/server/listener_manager_impl.h @@ -231,7 +231,7 @@ class ListenerManagerImpl : public ListenerManager, Logger::Loggable overridden_listener, ListenerImpl& listener, ListenerCompletionCallback completion_callback); - ProtobufTypes::MessagePtr dumpListenerConfigs(); + ProtobufTypes::MessagePtr dumpListenerConfigs(const Matchers::StringMatcher& name_matcher); static ListenerManagerStats generateStats(Stats::Scope& scope); static bool hasListenerWithAddress(const ListenerList& list, const Network::Address::Instance& address); diff --git a/source/server/server.cc b/source/server/server.cc index 5b8520e2fffc4..9dc4000dac77b 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -530,8 +530,8 @@ void InstanceImpl::initialize(const Options& options, } else { ENVOY_LOG(warn, "No admin address given, so no admin HTTP server started."); } - config_tracker_entry_ = - admin_->getConfigTracker().add("bootstrap", [this] { return dumpBootstrapConfig(); }); + config_tracker_entry_ = admin_->getConfigTracker().add( + "bootstrap", [this](const Matchers::StringMatcher&) { return dumpBootstrapConfig(); }); if (initial_config.admin().address()) { admin_->addListenerToHandler(handler_.get()); } diff --git a/test/common/config/config_provider_impl_test.cc b/test/common/config/config_provider_impl_test.cc index 6b3d6d36cc0f7..f269aaa77939d 100644 --- a/test/common/config/config_provider_impl_test.cc +++ b/test/common/config/config_provider_impl_test.cc @@ -140,8 +140,12 @@ class DummyConfigProviderManager : public ConfigProviderManagerImplBase { ~DummyConfigProviderManager() override = default; + ProtobufTypes::MessagePtr dumpConfigs() const { + return dumpConfigs(Matchers::UniversalStringMatcher()); + } + // Envoy::Config::ConfigProviderManagerImplBase - ProtobufTypes::MessagePtr dumpConfigs() const override { + ProtobufTypes::MessagePtr dumpConfigs(const Matchers::StringMatcher&) const override { auto config_dump = std::make_unique(); for (const auto& element : configSubscriptions()) { auto subscription = element.second.lock(); @@ -411,7 +415,8 @@ TEST_F(ConfigProviderImplTest, ConfigDump) { initialize(); // Empty dump first. auto message_ptr = - server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["dummy"](); + server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["dummy"]( + Matchers::UniversalStringMatcher()); const auto& dummy_config_dump = static_cast(*message_ptr); @@ -430,7 +435,8 @@ TEST_F(ConfigProviderImplTest, ConfigDump) { ConfigProviderPtr static_config = provider_manager_->createStaticConfigProvider( parseDummyConfigFromYaml(config_yaml), server_factory_context_, ConfigProviderManager::NullOptionalArg()); - message_ptr = server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["dummy"](); + message_ptr = server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["dummy"]( + Matchers::UniversalStringMatcher()); const auto& dummy_config_dump2 = static_cast(*message_ptr); TestUtility::loadFromYaml(R"EOF( @@ -457,7 +463,8 @@ TEST_F(ConfigProviderImplTest, ConfigDump) { const auto decoded_resources = TestUtility::decodeResources({dummy_config}, "a"); subscription.onConfigUpdate(decoded_resources.refvec_, "v1"); - message_ptr = server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["dummy"](); + message_ptr = server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["dummy"]( + Matchers::UniversalStringMatcher()); const auto& dummy_config_dump3 = static_cast(*message_ptr); TestUtility::loadFromYaml(R"EOF( @@ -475,7 +482,8 @@ TEST_F(ConfigProviderImplTest, ConfigDump) { ConfigProviderPtr static_config2 = provider_manager_->createStaticConfigProvider( parseDummyConfigFromYaml("a: another static dummy config"), server_factory_context_, ConfigProviderManager::NullOptionalArg()); - message_ptr = server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["dummy"](); + message_ptr = server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["dummy"]( + Matchers::UniversalStringMatcher()); const auto& dummy_config_dump4 = static_cast(*message_ptr); TestUtility::loadFromYaml(R"EOF( @@ -602,8 +610,12 @@ class DeltaDummyConfigProviderManager : public ConfigProviderManagerImplBase { DeltaDummyConfigProviderManager(Server::Admin& admin) : ConfigProviderManagerImplBase(admin, "dummy") {} + ProtobufTypes::MessagePtr dumpConfigs() const { + return dumpConfigs(Matchers::UniversalStringMatcher()); + } + // Envoy::Config::ConfigProviderManagerImplBase - ProtobufTypes::MessagePtr dumpConfigs() const override { + ProtobufTypes::MessagePtr dumpConfigs(const Matchers::StringMatcher&) const override { auto config_dump = std::make_unique(); for (const auto& element : configSubscriptions()) { auto subscription = element.second.lock(); diff --git a/test/common/router/BUILD b/test/common/router/BUILD index d3be91e2bf11d..ed6f767661b50 100644 --- a/test/common/router/BUILD +++ b/test/common/router/BUILD @@ -101,6 +101,7 @@ envoy_cc_test( "//source/common/router:rds_lib", "//source/server/admin:admin_lib", "//test/mocks/local_info:local_info_mocks", + "//test/mocks/matcher:matcher_mocks", "//test/mocks/protobuf:protobuf_mocks", "//test/mocks/server:instance_mocks", "//test/mocks/thread_local:thread_local_mocks", @@ -145,6 +146,7 @@ envoy_cc_test( "//source/server/admin:admin_lib", "//test/mocks/config:config_mocks", "//test/mocks/init:init_mocks", + "//test/mocks/matcher:matcher_mocks", "//test/mocks/protobuf:protobuf_mocks", "//test/mocks/router:router_mocks", "//test/mocks/server:instance_mocks", diff --git a/test/common/router/rds_impl_test.cc b/test/common/router/rds_impl_test.cc index 20e4f8ed863aa..6ed9e8f1985f9 100644 --- a/test/common/router/rds_impl_test.cc +++ b/test/common/router/rds_impl_test.cc @@ -16,6 +16,7 @@ #include "test/mocks/init/mocks.h" #include "test/mocks/local_info/mocks.h" +#include "test/mocks/matcher/mocks.h" #include "test/mocks/protobuf/mocks.h" #include "test/mocks/server/instance.h" #include "test/mocks/thread_local/mocks.h" @@ -37,6 +38,9 @@ namespace Envoy { namespace Router { namespace { +using ::Envoy::Matchers::MockStringMatcher; +using ::Envoy::Matchers::UniversalStringMatcher; + envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager parseHttpConnectionManagerFromYaml(const std::string& yaml_string) { envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager @@ -753,8 +757,10 @@ parseRouteConfigurationFromV3Yaml(const std::string& yaml, bool avoid_boosting = } TEST_F(RouteConfigProviderManagerImplTest, ConfigDump) { + UniversalStringMatcher universal_name_matcher; auto message_ptr = - server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["routes"](); + server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["routes"]( + universal_name_matcher); const auto& route_config_dump = TestUtility::downcastAndValidate(*message_ptr); @@ -785,8 +791,8 @@ name: foo route_config_provider_manager_->createStaticRouteConfigProvider( parseRouteConfigurationFromV3Yaml(config_yaml), OptionalHttpFilters(), server_factory_context_, validation_visitor_); - message_ptr = - server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["routes"](); + message_ptr = server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["routes"]( + universal_name_matcher); const auto& route_config_dump2 = TestUtility::downcastAndValidate(*message_ptr); TestUtility::loadFromYaml(R"EOF( @@ -833,8 +839,8 @@ name: foo EXPECT_CALL(init_watcher_, ready()); rds_callbacks_->onConfigUpdate(decoded_resources.refvec_, response1.version_info()); - message_ptr = - server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["routes"](); + message_ptr = server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["routes"]( + universal_name_matcher); const auto& route_config_dump3 = TestUtility::downcastAndValidate(*message_ptr); TestUtility::loadFromYaml(R"EOF( @@ -863,6 +869,51 @@ name: foo )EOF", expected_route_config_dump); EXPECT_EQ(expected_route_config_dump.DebugString(), route_config_dump3.DebugString()); + + MockStringMatcher mock_name_matcher; + EXPECT_CALL(mock_name_matcher, match("foo")).WillOnce(Return(true)); + EXPECT_CALL(mock_name_matcher, match("foo_route_config")).WillOnce(Return(false)); + message_ptr = server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["routes"]( + mock_name_matcher); + const auto& route_config_dump4 = + TestUtility::downcastAndValidate(*message_ptr); + TestUtility::loadFromYaml(R"EOF( +static_route_configs: + - route_config: + "@type": type.googleapis.com/envoy.config.route.v3.RouteConfiguration + name: foo + virtual_hosts: + - name: bar + domains: ["*"] + routes: + - match: { prefix: "/" } + route: { cluster: baz } + last_updated: + seconds: 1234567891 + nanos: 234000000 +)EOF", + expected_route_config_dump); + EXPECT_EQ(expected_route_config_dump.DebugString(), route_config_dump4.DebugString()); + + EXPECT_CALL(mock_name_matcher, match("foo")).WillOnce(Return(false)); + EXPECT_CALL(mock_name_matcher, match("foo_route_config")).WillOnce(Return(true)); + message_ptr = server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["routes"]( + mock_name_matcher); + const auto& route_config_dump5 = + TestUtility::downcastAndValidate(*message_ptr); + TestUtility::loadFromYaml(R"EOF( +dynamic_route_configs: + - version_info: "1" + route_config: + "@type": type.googleapis.com/envoy.config.route.v3.RouteConfiguration + name: foo_route_config + virtual_hosts: + last_updated: + seconds: 1234567891 + nanos: 234000000 +)EOF", + expected_route_config_dump); + EXPECT_EQ(expected_route_config_dump.DebugString(), route_config_dump5.DebugString()); } TEST_F(RouteConfigProviderManagerImplTest, Basic) { @@ -910,8 +961,10 @@ name: foo_route_config EXPECT_NE(provider3, provider_); server_factory_context_.cluster_manager_.subscription_factory_.callbacks_->onConfigUpdate( decoded_resources.refvec_, "provider3"); - EXPECT_EQ(2UL, - route_config_provider_manager_->dumpRouteConfigs()->dynamic_route_configs().size()); + UniversalStringMatcher universal_name_matcher; + EXPECT_EQ(2UL, route_config_provider_manager_->dumpRouteConfigs(universal_name_matcher) + ->dynamic_route_configs() + .size()); provider_.reset(); provider2.reset(); @@ -919,7 +972,8 @@ name: foo_route_config // All shared_ptrs to the provider pointed at by provider1, and provider2 have been deleted, so // now we should only have the provider pointed at by provider3. auto dynamic_route_configs = - route_config_provider_manager_->dumpRouteConfigs()->dynamic_route_configs(); + route_config_provider_manager_->dumpRouteConfigs(universal_name_matcher) + ->dynamic_route_configs(); EXPECT_EQ(1UL, dynamic_route_configs.size()); // Make sure the left one is provider3 @@ -927,8 +981,9 @@ name: foo_route_config provider3.reset(); - EXPECT_EQ(0UL, - route_config_provider_manager_->dumpRouteConfigs()->dynamic_route_configs().size()); + EXPECT_EQ(0UL, route_config_provider_manager_->dumpRouteConfigs(universal_name_matcher) + ->dynamic_route_configs() + .size()); } TEST_F(RouteConfigProviderManagerImplTest, SameProviderOnTwoInitManager) { @@ -999,8 +1054,10 @@ TEST_F(RouteConfigProviderManagerImplTest, OnConfigUpdateWrongSize) { // Regression test for https://github.com/envoyproxy/envoy/issues/7939 TEST_F(RouteConfigProviderManagerImplTest, ConfigDumpAfterConfigRejected) { + UniversalStringMatcher universal_name_matcher; auto message_ptr = - server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["routes"](); + server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["routes"]( + universal_name_matcher); const auto& route_config_dump = TestUtility::downcastAndValidate(*message_ptr); @@ -1055,8 +1112,8 @@ version_info: '1' rds_callbacks_->onConfigUpdate(decoded_resources.refvec_, response1.version_info()), EnvoyException, "Only a single wildcard domain is permitted in route foo_route_config"); - message_ptr = - server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["routes"](); + message_ptr = server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["routes"]( + universal_name_matcher); const auto& route_config_dump3 = TestUtility::downcastAndValidate(*message_ptr); TestUtility::loadFromYaml(R"EOF( diff --git a/test/common/router/scoped_rds_test.cc b/test/common/router/scoped_rds_test.cc index 2277495a54d53..8be75180cec13 100644 --- a/test/common/router/scoped_rds_test.cc +++ b/test/common/router/scoped_rds_test.cc @@ -19,6 +19,7 @@ #include "source/common/router/scoped_rds.h" #include "test/mocks/config/mocks.h" +#include "test/mocks/matcher/mocks.h" #include "test/mocks/protobuf/mocks.h" #include "test/mocks/router/mocks.h" #include "test/mocks/server/instance.h" @@ -30,6 +31,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +using testing::_; using testing::AnyNumber; using testing::Eq; using testing::InSequence; @@ -43,6 +45,9 @@ namespace Envoy { namespace Router { namespace { +using ::Envoy::Matchers::MockStringMatcher; +using ::Envoy::Matchers::UniversalStringMatcher; + using ::Envoy::Http::TestRequestHeaderMapImpl; envoy::config::route::v3::ScopedRouteConfiguration @@ -753,8 +758,10 @@ TEST_F(ScopedRdsTest, ConfigDump) { setup(); init_watcher_.expectReady(); context_init_manager_.initialize(init_watcher_); + UniversalStringMatcher universal_matcher; auto message_ptr = - server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["route_scopes"](); + server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["route_scopes"]( + universal_matcher); const auto& scoped_routes_config_dump = TestUtility::downcastAndValidate( *message_ptr); @@ -803,7 +810,8 @@ stat_prefix: foo inline_scoped_route_configs_yaml)), server_factory_context_, context_init_manager_, "foo.", *config_provider_manager_); message_ptr = - server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["route_scopes"](); + server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["route_scopes"]( + universal_matcher); const auto& scoped_routes_config_dump2 = TestUtility::downcastAndValidate( *message_ptr); @@ -874,12 +882,76 @@ route_configuration_name: dynamic-foo-route-config )EOF", expected_config_dump); message_ptr = - server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["route_scopes"](); + server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["route_scopes"]( + universal_matcher); const auto& scoped_routes_config_dump3 = TestUtility::downcastAndValidate( *message_ptr); EXPECT_THAT(expected_config_dump, ProtoEq(scoped_routes_config_dump3)); + NiceMock mock_matcher; + EXPECT_CALL(mock_matcher, match("foo")).WillOnce(Return(true)); + EXPECT_CALL(mock_matcher, match("foo2")).WillOnce(Return(false)); + EXPECT_CALL(mock_matcher, match("dynamic-foo")).WillOnce(Return(false)); + TestUtility::loadFromYaml(R"EOF( +inline_scoped_route_configs: + - name: foo-scoped-routes + scoped_route_configs: + - name: foo + "@type": type.googleapis.com/envoy.api.v2.ScopedRouteConfiguration + route_configuration_name: foo-route-config + key: + fragments: { string_key: "172.10.10.10" } + last_updated: + seconds: 1234567891 + nanos: 234000000 +dynamic_scoped_route_configs: + - name: foo_scoped_routes + last_updated: + seconds: 1234567891 + nanos: 567000000 + version_info: "1" +)EOF", + expected_config_dump); + message_ptr = + server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["route_scopes"]( + mock_matcher); + const auto& scoped_routes_config_dump4 = + TestUtility::downcastAndValidate( + *message_ptr); + EXPECT_THAT(expected_config_dump, ProtoEq(scoped_routes_config_dump4)); + + EXPECT_CALL(mock_matcher, match("foo")).WillOnce(Return(false)); + EXPECT_CALL(mock_matcher, match("foo2")).WillOnce(Return(false)); + EXPECT_CALL(mock_matcher, match("dynamic-foo")).WillOnce(Return(true)); + TestUtility::loadFromYaml(R"EOF( +inline_scoped_route_configs: + - name: foo-scoped-routes + last_updated: + seconds: 1234567891 + nanos: 234000000 +dynamic_scoped_route_configs: + - name: foo_scoped_routes + scoped_route_configs: + - name: dynamic-foo + "@type": type.googleapis.com/envoy.api.v2.ScopedRouteConfiguration + route_configuration_name: dynamic-foo-route-config + key: + fragments: { string_key: "172.30.30.10" } + last_updated: + seconds: 1234567891 + nanos: 567000000 + version_info: "1" +)EOF", + expected_config_dump); + message_ptr = + server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["route_scopes"]( + mock_matcher); + const auto& scoped_routes_config_dump5 = + TestUtility::downcastAndValidate( + *message_ptr); + EXPECT_THAT(expected_config_dump, ProtoEq(scoped_routes_config_dump5)); + srds_subscription_->onConfigUpdate({}, "2"); TestUtility::loadFromYaml(R"EOF( inline_scoped_route_configs: @@ -907,11 +979,12 @@ route_configuration_name: dynamic-foo-route-config )EOF", expected_config_dump); message_ptr = - server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["route_scopes"](); - const auto& scoped_routes_config_dump4 = + server_factory_context_.admin_.config_tracker_.config_tracker_callbacks_["route_scopes"]( + universal_matcher); + const auto& scoped_routes_config_dump6 = TestUtility::downcastAndValidate( *message_ptr); - EXPECT_THAT(expected_config_dump, ProtoEq(scoped_routes_config_dump4)); + EXPECT_THAT(expected_config_dump, ProtoEq(scoped_routes_config_dump6)); } // Tests that SRDS only allows creation of delta static config providers. diff --git a/test/common/secret/BUILD b/test/common/secret/BUILD index 6161773b7b8c2..3f1488d0118fa 100644 --- a/test/common/secret/BUILD +++ b/test/common/secret/BUILD @@ -19,6 +19,7 @@ envoy_cc_test( "//source/common/secret:secret_manager_impl_lib", "//source/common/ssl:certificate_validation_context_config_impl_lib", "//source/common/ssl:tls_certificate_config_impl_lib", + "//test/mocks/matcher:matcher_mocks", "//test/mocks/server:config_tracker_mocks", "//test/mocks/server:instance_mocks", "//test/mocks/server:transport_socket_factory_context_mocks", diff --git a/test/common/secret/secret_manager_impl_test.cc b/test/common/secret/secret_manager_impl_test.cc index cf9d9fb2d9604..0c36a3c4ba404 100644 --- a/test/common/secret/secret_manager_impl_test.cc +++ b/test/common/secret/secret_manager_impl_test.cc @@ -15,6 +15,7 @@ #include "source/common/ssl/tls_certificate_config_impl.h" #include "test/mocks/event/mocks.h" +#include "test/mocks/matcher/mocks.h" #include "test/mocks/server/config_tracker.h" #include "test/mocks/server/instance.h" #include "test/mocks/server/transport_socket_factory_context.h" @@ -26,18 +27,23 @@ #include "gtest/gtest.h" using testing::ReturnRef; +using testing::StrictMock; namespace Envoy { namespace Secret { namespace { +using ::Envoy::Matchers::MockStringMatcher; + class SecretManagerImplTest : public testing::Test, public Logger::Loggable { protected: SecretManagerImplTest() : api_(Api::createApiForTest()), dispatcher_(api_->allocateDispatcher("test_thread")) {} - void checkConfigDump(const std::string& expected_dump_yaml) { - auto message_ptr = config_tracker_.config_tracker_callbacks_["secrets"](); + void checkConfigDump( + const std::string& expected_dump_yaml, + const Matchers::StringMatcher& name_matcher = Matchers::UniversalStringMatcher()) { + auto message_ptr = config_tracker_.config_tracker_callbacks_["secrets"](name_matcher); const auto& secrets_config_dump = dynamic_cast(*message_ptr); envoy::admin::v3::SecretsConfigDump expected_secrets_config_dump; @@ -498,6 +504,9 @@ name: "abc.com" inline_string: "[redacted]" )EOF"; checkConfigDump(expected_secrets_config_dump); + StrictMock mock_matcher; + EXPECT_CALL(mock_matcher, match("abc.com")).WillOnce(Return(false)); + checkConfigDump("{}", mock_matcher); // Add a dynamic tls validation context provider. time_system_.setSystemTime(std::chrono::milliseconds(1234567899000)); @@ -547,6 +556,9 @@ name: "abc.com.validation" inline_string: "DUMMY_INLINE_STRING_TRUSTED_CA" )EOF"; checkConfigDump(updated_config_dump); + EXPECT_CALL(mock_matcher, match("abc.com")).WillOnce(Return(false)); + EXPECT_CALL(mock_matcher, match("abc.com.validation")).WillOnce(Return(false)); + checkConfigDump("{}", mock_matcher); // Add a dynamic tls session ticket encryption keys context provider. time_system_.setSystemTime(std::chrono::milliseconds(1234567899000)); @@ -609,6 +621,10 @@ name: "abc.com.stek" - inline_bytes: "W3JlZGFjdGVkXQ==" )EOF"; checkConfigDump(TestEnvironment::substitute(updated_once_more_config_dump)); + EXPECT_CALL(mock_matcher, match("abc.com")).WillOnce(Return(false)); + EXPECT_CALL(mock_matcher, match("abc.com.validation")).WillOnce(Return(false)); + EXPECT_CALL(mock_matcher, match("abc.com.stek")).WillOnce(Return(false)); + checkConfigDump("{}", mock_matcher); // Add a dynamic generic secret provider. time_system_.setSystemTime(std::chrono::milliseconds(1234567900000)); @@ -682,6 +698,11 @@ name: "signing_key" inline_string: "[redacted]" )EOF"; checkConfigDump(TestEnvironment::substitute(config_dump_with_generic_secret)); + EXPECT_CALL(mock_matcher, match("abc.com")).WillOnce(Return(false)); + EXPECT_CALL(mock_matcher, match("abc.com.validation")).WillOnce(Return(false)); + EXPECT_CALL(mock_matcher, match("abc.com.stek")).WillOnce(Return(false)); + EXPECT_CALL(mock_matcher, match("signing_key")).WillOnce(Return(false)); + checkConfigDump("{}", mock_matcher); } TEST_F(SecretManagerImplTest, ConfigDumpHandlerWarmingSecrets) { @@ -722,6 +743,9 @@ TEST_F(SecretManagerImplTest, ConfigDumpHandlerWarmingSecrets) { name: "abc.com" )EOF"; checkConfigDump(expected_secrets_config_dump); + StrictMock mock_matcher; + EXPECT_CALL(mock_matcher, match("abc.com")).WillOnce(Return(false)); + checkConfigDump("{}", mock_matcher); time_system_.setSystemTime(std::chrono::milliseconds(1234567899000)); auto context_secret_provider = secret_manager->findOrCreateCertificateValidationContextProvider( @@ -746,6 +770,9 @@ TEST_F(SecretManagerImplTest, ConfigDumpHandlerWarmingSecrets) { name: "abc.com.validation" )EOF"; checkConfigDump(updated_config_dump); + EXPECT_CALL(mock_matcher, match("abc.com")).WillOnce(Return(false)); + EXPECT_CALL(mock_matcher, match("abc.com.validation")).WillOnce(Return(false)); + checkConfigDump("{}", mock_matcher); time_system_.setSystemTime(std::chrono::milliseconds(1234567899000)); auto stek_secret_provider = secret_manager->findOrCreateTlsSessionTicketKeysContextProvider( @@ -777,6 +804,10 @@ TEST_F(SecretManagerImplTest, ConfigDumpHandlerWarmingSecrets) { name: "abc.com.stek" )EOF"; checkConfigDump(updated_once_more_config_dump); + EXPECT_CALL(mock_matcher, match("abc.com")).WillOnce(Return(false)); + EXPECT_CALL(mock_matcher, match("abc.com.validation")).WillOnce(Return(false)); + EXPECT_CALL(mock_matcher, match("abc.com.stek")).WillOnce(Return(false)); + checkConfigDump("{}", mock_matcher); time_system_.setSystemTime(std::chrono::milliseconds(1234567900000)); auto generic_secret_provider = secret_manager->findOrCreateGenericSecretProvider( @@ -815,6 +846,11 @@ TEST_F(SecretManagerImplTest, ConfigDumpHandlerWarmingSecrets) { name: "signing_key" )EOF"; checkConfigDump(config_dump_with_generic_secret); + EXPECT_CALL(mock_matcher, match("abc.com")).WillOnce(Return(false)); + EXPECT_CALL(mock_matcher, match("abc.com.validation")).WillOnce(Return(false)); + EXPECT_CALL(mock_matcher, match("abc.com.stek")).WillOnce(Return(false)); + EXPECT_CALL(mock_matcher, match("signing_key")).WillOnce(Return(false)); + checkConfigDump("{}", mock_matcher); } TEST_F(SecretManagerImplTest, ConfigDumpHandlerStaticSecrets) { @@ -889,6 +925,11 @@ name: "abc.com.nopassword" inline_string: "[redacted]" )EOF"; checkConfigDump(expected_config_dump); + StrictMock mock_matcher; + EXPECT_CALL(mock_matcher, match(testing::HasSubstr("abc.com"))) + .Times(2) + .WillRepeatedly(Return(false)); + checkConfigDump("{}", mock_matcher); } TEST_F(SecretManagerImplTest, ConfigDumpHandlerStaticValidationContext) { @@ -934,6 +975,9 @@ name: "abc.com.validation" inline_string: "DUMMY_INLINE_STRING_TRUSTED_CA" )EOF"; checkConfigDump(expected_config_dump); + StrictMock mock_matcher; + EXPECT_CALL(mock_matcher, match("abc.com.validation")).WillOnce(Return(false)); + checkConfigDump("{}", mock_matcher); } TEST_F(SecretManagerImplTest, ConfigDumpHandlerStaticSessionTicketsContext) { @@ -983,6 +1027,9 @@ name: "abc.com.stek" - inline_bytes: "W3JlZGFjdGVkXQ==" )EOF"; checkConfigDump(TestEnvironment::substitute(expected_config_dump)); + StrictMock mock_matcher; + EXPECT_CALL(mock_matcher, match("abc.com.stek")).WillOnce(Return(false)); + checkConfigDump("{}", mock_matcher); } TEST_F(SecretManagerImplTest, ConfigDumpHandlerStaticGenericSecret) { @@ -1009,6 +1056,9 @@ name: "signing_key" inline_bytes: "W3JlZGFjdGVkXQ==" )EOF"; checkConfigDump(TestEnvironment::substitute(expected_config_dump)); + StrictMock mock_matcher; + EXPECT_CALL(mock_matcher, match("signing_key")).WillOnce(Return(false)); + checkConfigDump("{}", mock_matcher); } } // namespace diff --git a/test/common/upstream/BUILD b/test/common/upstream/BUILD index 16fd761aa7394..fa7a3a5287e87 100644 --- a/test/common/upstream/BUILD +++ b/test/common/upstream/BUILD @@ -42,6 +42,7 @@ envoy_cc_test( ":test_cluster_manager", "//source/common/router:context_lib", "//source/extensions/transport_sockets/tls:config", + "//test/mocks/matcher:matcher_mocks", "//test/mocks/upstream:cds_api_mocks", "//test/mocks/upstream:cluster_priority_set_mocks", "//test/mocks/upstream:cluster_real_priority_set_mocks", diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index 7fed2c6567b49..fa5f863ae53ff 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -11,6 +11,7 @@ #include "test/common/upstream/test_cluster_manager.h" #include "test/mocks/http/conn_pool.h" +#include "test/mocks/matcher/mocks.h" #include "test/mocks/upstream/cds_api.h" #include "test/mocks/upstream/cluster_priority_set.h" #include "test/mocks/upstream/cluster_real_priority_set.h" @@ -138,8 +139,10 @@ class ClusterManagerImplTest : public testing::Test { .value()); } - void checkConfigDump(const std::string& expected_dump_yaml) { - auto message_ptr = admin_.config_tracker_.config_tracker_callbacks_["clusters"](); + void checkConfigDump( + const std::string& expected_dump_yaml, + const Matchers::StringMatcher& name_matcher = Matchers::UniversalStringMatcher()) { + auto message_ptr = admin_.config_tracker_.config_tracker_callbacks_["clusters"](name_matcher); const auto& clusters_config_dump = dynamic_cast(*message_ptr); @@ -280,6 +283,15 @@ TEST_F(ClusterManagerImplTest, MultipleProtocolCluster) { dynamic_active_clusters: dynamic_warming_clusters: )EOF"); + + Matchers::MockStringMatcher mock_matcher; + EXPECT_CALL(mock_matcher, match("http12_cluster")).WillOnce(Return(false)); + checkConfigDump(R"EOF( +static_clusters: +dynamic_active_clusters: +dynamic_warming_clusters: +)EOF", + mock_matcher); } TEST_F(ClusterManagerImplTest, OutlierEventLog) { diff --git a/test/integration/BUILD b/test/integration/BUILD index 5d4083e23c05f..7e6d0990dbf6a 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -30,6 +30,7 @@ envoy_cc_test_library( ], deps = [ ":http_integration_lib", + "//source/common/common:matchers_lib", "//source/common/config:protobuf_link_hacks", "//source/common/config:version_converter_lib", "//source/common/protobuf:utility_lib", diff --git a/test/integration/ads_integration.cc b/test/integration/ads_integration.cc index dfa3b81539b94..f356c7cd6cc25 100644 --- a/test/integration/ads_integration.cc +++ b/test/integration/ads_integration.cc @@ -8,6 +8,7 @@ #include "envoy/config/route/v3/route.pb.h" #include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" +#include "source/common/common/matchers.h" #include "source/common/config/protobuf_link_hacks.h" #include "source/common/protobuf/protobuf.h" #include "source/common/protobuf/utility.h" @@ -259,20 +260,20 @@ void AdsIntegrationTest::testBasicFlow() { } envoy::admin::v3::ClustersConfigDump AdsIntegrationTest::getClustersConfigDump() { - auto message_ptr = - test_server_->server().admin().getConfigTracker().getCallbacksMap().at("clusters")(); + auto message_ptr = test_server_->server().admin().getConfigTracker().getCallbacksMap().at( + "clusters")(Matchers::UniversalStringMatcher()); return dynamic_cast(*message_ptr); } envoy::admin::v3::ListenersConfigDump AdsIntegrationTest::getListenersConfigDump() { - auto message_ptr = - test_server_->server().admin().getConfigTracker().getCallbacksMap().at("listeners")(); + auto message_ptr = test_server_->server().admin().getConfigTracker().getCallbacksMap().at( + "listeners")(Matchers::UniversalStringMatcher()); return dynamic_cast(*message_ptr); } envoy::admin::v3::RoutesConfigDump AdsIntegrationTest::getRoutesConfigDump() { - auto message_ptr = - test_server_->server().admin().getConfigTracker().getCallbacksMap().at("routes")(); + auto message_ptr = test_server_->server().admin().getConfigTracker().getCallbacksMap().at( + "routes")(Matchers::UniversalStringMatcher()); return dynamic_cast(*message_ptr); } diff --git a/test/integration/base_integration_test.cc b/test/integration/base_integration_test.cc index 43fa0b6d9add1..4c96771a61456 100644 --- a/test/integration/base_integration_test.cc +++ b/test/integration/base_integration_test.cc @@ -323,7 +323,7 @@ void BaseIntegrationTest::registerTestServerPorts(const std::vector std::string getListenerDetails(Envoy::Server::Instance& server) { const auto& cbs_maps = server.admin().getConfigTracker().getCallbacksMap(); - ProtobufTypes::MessagePtr details = cbs_maps.at("listeners")(); + ProtobufTypes::MessagePtr details = cbs_maps.at("listeners")(Matchers::UniversalStringMatcher()); auto listener_info = Protobuf::down_cast(*details); return MessageUtil::getYamlStringFromMessage(listener_info.dynamic_listeners(0).error_state()); } diff --git a/test/integration/integration_admin_test.cc b/test/integration/integration_admin_test.cc index 65da9c1fb119a..b694e98c55ec8 100644 --- a/test/integration/integration_admin_test.cc +++ b/test/integration/integration_admin_test.cc @@ -398,6 +398,17 @@ TEST_P(IntegrationAdminTest, Admin) { TestUtility::loadFromJson(response->body(), config_dump_with_eds); EXPECT_EQ(7, config_dump_with_eds.configs_size()); + EXPECT_EQ("200", request("admin", "GET", "/config_dump?name_regex=route_config_0", response)); + EXPECT_EQ("application/json", ContentType(response)); + envoy::admin::v3::ConfigDump name_filtered_config_dump; + TestUtility::loadFromJson(response->body(), name_filtered_config_dump); + EXPECT_EQ(6, config_dump.configs_size()); + + // SecretsConfigDump should have been totally filtered away. + secret_config_dump.Clear(); + name_filtered_config_dump.configs(5).UnpackTo(&secret_config_dump); + EXPECT_EQ(secret_config_dump.static_secrets().size(), 0); + // Validate that the "inboundonly" does not stop the default listener. response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "POST", "/drain_listeners?inboundonly", "", diff --git a/test/mocks/matcher/BUILD b/test/mocks/matcher/BUILD index 0d9f3e97bda91..f6eb9386d93e8 100644 --- a/test/mocks/matcher/BUILD +++ b/test/mocks/matcher/BUILD @@ -12,6 +12,7 @@ envoy_cc_mock( name = "matcher_mocks", hdrs = ["mocks.h"], deps = [ + "//source/common/common:matchers_lib", "//source/common/matcher:matcher_lib", ], ) diff --git a/test/mocks/matcher/mocks.h b/test/mocks/matcher/mocks.h index ab2aee120ed08..46483563ed62c 100644 --- a/test/mocks/matcher/mocks.h +++ b/test/mocks/matcher/mocks.h @@ -1,5 +1,6 @@ #pragma once +#include "source/common/common/matchers.h" #include "source/common/matcher/matcher.h" #include "gmock/gmock.h" @@ -18,4 +19,11 @@ class MockMatchTreeValidationVisitor : public MatchTreeValidationVisitor& host, const std::string& hostname TEST_P(AdminInstanceTest, ConfigDump) { Buffer::OwnedImpl response; Http::TestResponseHeaderMapImpl header_map; - auto entry = admin_.getConfigTracker().add("foo", [] { + auto entry = admin_.getConfigTracker().add("foo", [](const Matchers::StringMatcher&) { auto msg = std::make_unique(); msg->set_value("bar"); return msg; @@ -60,26 +60,29 @@ TEST_P(AdminInstanceTest, ConfigDump) { TEST_P(AdminInstanceTest, ConfigDumpMaintainsOrder) { // Add configs in random order and validate config_dump dumps in the order. - auto bootstrap_entry = admin_.getConfigTracker().add("bootstrap", [] { - auto msg = std::make_unique(); - msg->set_value("bootstrap_config"); - return msg; - }); - auto route_entry = admin_.getConfigTracker().add("routes", [] { + auto bootstrap_entry = + admin_.getConfigTracker().add("bootstrap", [](const Matchers::StringMatcher&) { + auto msg = std::make_unique(); + msg->set_value("bootstrap_config"); + return msg; + }); + auto route_entry = admin_.getConfigTracker().add("routes", [](const Matchers::StringMatcher&) { auto msg = std::make_unique(); msg->set_value("routes_config"); return msg; }); - auto listener_entry = admin_.getConfigTracker().add("listeners", [] { - auto msg = std::make_unique(); - msg->set_value("listeners_config"); - return msg; - }); - auto cluster_entry = admin_.getConfigTracker().add("clusters", [] { - auto msg = std::make_unique(); - msg->set_value("clusters_config"); - return msg; - }); + auto listener_entry = + admin_.getConfigTracker().add("listeners", [](const Matchers::StringMatcher&) { + auto msg = std::make_unique(); + msg->set_value("listeners_config"); + return msg; + }); + auto cluster_entry = + admin_.getConfigTracker().add("clusters", [](const Matchers::StringMatcher&) { + auto msg = std::make_unique(); + msg->set_value("clusters_config"); + return msg; + }); const std::string expected_json = R"EOF({ "configs": [ { @@ -369,7 +372,7 @@ TEST_P(AdminInstanceTest, ConfigDumpWithLocalityEndpoint) { TEST_P(AdminInstanceTest, ConfigDumpFiltersByResource) { Buffer::OwnedImpl response; Http::TestResponseHeaderMapImpl header_map; - auto listeners = admin_.getConfigTracker().add("listeners", [] { + auto listeners = admin_.getConfigTracker().add("listeners", [](const Matchers::StringMatcher&) { auto msg = std::make_unique(); auto dyn_listener = msg->add_dynamic_listeners(); dyn_listener->set_name("foo"); @@ -394,10 +397,11 @@ TEST_P(AdminInstanceTest, ConfigDumpFiltersByResource) { EXPECT_EQ(expected_json, output); } -// Test that using the resource query parameter filters the config dump including EDS. -// We add both static and dynamic endpoint config to the dump, but expect only -// dynamic in the JSON with ?resource=dynamic_endpoint_configs. -TEST_P(AdminInstanceTest, ConfigDumpWithEndpointFiltersByResource) { +// Test that using the resource and name_regex query parameters filter the config dump including +// EDS. We add both static and dynamic endpoint config to the dump, but expect only dynamic in the +// JSON with ?resource=dynamic_endpoint_configs, and only the one named `fake_cluster_2` with +// ?name_regex=fake_cluster_2. +TEST_P(AdminInstanceTest, ConfigDumpWithEndpointFiltersByResourceAndName) { Upstream::ClusterManager::ClusterInfoMaps cluster_maps; ON_CALL(server_.cluster_manager_, clusters()).WillByDefault(ReturnPointee(&cluster_maps)); @@ -479,6 +483,57 @@ TEST_P(AdminInstanceTest, ConfigDumpWithEndpointFiltersByResource) { } )EOF"; EXPECT_EQ(expected_json, output); + + // Check that endpoints dump uses the name_matcher. + Buffer::OwnedImpl response2; + EXPECT_EQ(Http::Code::OK, getCallback("/config_dump?include_eds=true&name_regex=fake_cluster_2", + header_map, response2)); + const std::string expected_json2 = R"EOF({ + "configs": [ + { + "@type": "type.googleapis.com/envoy.admin.v3.EndpointsConfigDump", + "static_endpoint_configs": [ + { + "endpoint_config": { + "@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment", + "cluster_name": "fake_cluster_2", + "endpoints": [ + { + "locality": {}, + "lb_endpoints": [ + { + "endpoint": { + "address": { + "socket_address": { + "address": "1.2.3.5", + "port_value": 8 + } + }, + "health_check_config": { + "port_value": 1, + "hostname": "test_hostname_healthcheck" + }, + "hostname": "boo.com" + }, + "health_status": "HEALTHY", + "metadata": {}, + "load_balancing_weight": 3 + } + ], + "priority": 4 + } + ], + "policy": { + "overprovisioning_factor": 140 + } + } + } + ] + } + ] +} +)EOF"; + EXPECT_EQ(expected_json2, response2.toString()); } // Test that using the mask query parameter filters the config dump. @@ -487,7 +542,7 @@ TEST_P(AdminInstanceTest, ConfigDumpWithEndpointFiltersByResource) { TEST_P(AdminInstanceTest, ConfigDumpFiltersByMask) { Buffer::OwnedImpl response; Http::TestResponseHeaderMapImpl header_map; - auto listeners = admin_.getConfigTracker().add("listeners", [] { + auto listeners = admin_.getConfigTracker().add("listeners", [](const Matchers::StringMatcher&) { auto msg = std::make_unique(); auto dyn_listener = msg->add_dynamic_listeners(); dyn_listener->set_name("foo"); @@ -516,7 +571,49 @@ TEST_P(AdminInstanceTest, ConfigDumpFiltersByMask) { EXPECT_EQ(expected_json, output); } -ProtobufTypes::MessagePtr testDumpClustersConfig() { +TEST_P(AdminInstanceTest, ConfigDumpFiltersByNameRegex) { + Buffer::OwnedImpl response; + Http::TestResponseHeaderMapImpl header_map; + auto listeners = + admin_.getConfigTracker().add("listeners", [](const Matchers::StringMatcher& name_matcher) { + auto msg = std::make_unique(); + if (name_matcher.match("bar")) { + auto dyn_listener = msg->add_dynamic_listeners(); + dyn_listener->set_name("bar"); + } + if (name_matcher.match("foo")) { + auto dyn_listener = msg->add_dynamic_listeners(); + dyn_listener->set_name("foo"); + } + return msg; + }); + const std::string expected_json = R"EOF({ + "configs": [ + { + "@type": "type.googleapis.com/envoy.admin.v3.ListenersConfigDump", + "dynamic_listeners": [ + { + "name": "bar" + } + ] + } + ] +} +)EOF"; + EXPECT_EQ(Http::Code::OK, getCallback("/config_dump?name_regex=.*a.*", header_map, response)); + std::string output = response.toString(); + EXPECT_EQ(expected_json, output); +} + +TEST_P(AdminInstanceTest, InvalidRegexIsBadRequest) { + Buffer::OwnedImpl response; + Http::TestResponseHeaderMapImpl header_map; + EXPECT_EQ(Http::Code::BadRequest, getCallback("/config_dump?name_regex=[", header_map, response)); + std::string output = response.toString(); + EXPECT_THAT(output, testing::HasSubstr("Error while parsing name_regex")); +} + +ProtobufTypes::MessagePtr testDumpClustersConfig(const Matchers::StringMatcher&) { auto msg = std::make_unique(); auto* static_cluster = msg->add_static_clusters(); envoy::config::cluster::v3::Cluster inner_cluster; @@ -581,7 +678,7 @@ TEST_P(AdminInstanceTest, ConfigDumpNonExistentMask) { TEST_P(AdminInstanceTest, ConfigDumpNonExistentResource) { Buffer::OwnedImpl response; Http::TestResponseHeaderMapImpl header_map; - auto listeners = admin_.getConfigTracker().add("listeners", [] { + auto listeners = admin_.getConfigTracker().add("listeners", [](const Matchers::StringMatcher&) { auto msg = std::make_unique(); msg->set_value("listeners_config"); return msg; @@ -594,7 +691,7 @@ TEST_P(AdminInstanceTest, ConfigDumpNonExistentResource) { TEST_P(AdminInstanceTest, ConfigDumpResourceNotRepeated) { Buffer::OwnedImpl response; Http::TestResponseHeaderMapImpl header_map; - auto clusters = admin_.getConfigTracker().add("clusters", [] { + auto clusters = admin_.getConfigTracker().add("clusters", [](const Matchers::StringMatcher&) { auto msg = std::make_unique(); msg->set_version_info("foo"); return msg; @@ -606,7 +703,7 @@ TEST_P(AdminInstanceTest, ConfigDumpResourceNotRepeated) { TEST_P(AdminInstanceTest, InvalidFieldMaskWithResourceDoesNotCrash) { Buffer::OwnedImpl response; Http::TestResponseHeaderMapImpl header_map; - auto clusters = admin_.getConfigTracker().add("clusters", [] { + auto clusters = admin_.getConfigTracker().add("clusters", [](const Matchers::StringMatcher&) { auto msg = std::make_unique(); auto* static_cluster = msg->add_static_clusters(); envoy::config::cluster::v3::Cluster inner_cluster; @@ -633,7 +730,7 @@ TEST_P(AdminInstanceTest, InvalidFieldMaskWithResourceDoesNotCrash) { TEST_P(AdminInstanceTest, InvalidFieldMaskWithoutResourceDoesNotCrash) { Buffer::OwnedImpl response; Http::TestResponseHeaderMapImpl header_map; - auto bootstrap = admin_.getConfigTracker().add("bootstrap", [] { + auto bootstrap = admin_.getConfigTracker().add("bootstrap", [](const Matchers::StringMatcher&) { auto msg = std::make_unique(); auto* bootstrap = msg->mutable_bootstrap(); bootstrap->mutable_node()->add_extensions()->set_name("ext1"); diff --git a/test/server/admin/config_tracker_impl_test.cc b/test/server/admin/config_tracker_impl_test.cc index a666d59a1fbdd..8835c53188295 100644 --- a/test/server/admin/config_tracker_impl_test.cc +++ b/test/server/admin/config_tracker_impl_test.cc @@ -1,3 +1,4 @@ +#include "source/common/common/matchers.h" #include "source/server/admin/config_tracker_impl.h" #include "test/mocks/common.h" @@ -11,7 +12,7 @@ class ConfigTrackerImplTest : public testing::Test { public: ConfigTrackerImplTest() : cbs_map(tracker.getCallbacksMap()) { EXPECT_TRUE(cbs_map.empty()); - test_cb = [this] { + test_cb = [this](const Matchers::StringMatcher&) { called = true; return test_msg(); }; @@ -33,7 +34,7 @@ TEST_F(ConfigTrackerImplTest, Basic) { auto entry_owner = tracker.add("test_key", test_cb); EXPECT_EQ(1, cbs_map.size()); EXPECT_NE(nullptr, entry_owner); - EXPECT_NE(nullptr, cbs_map.begin()->second()); + EXPECT_NE(nullptr, cbs_map.begin()->second(Matchers::UniversalStringMatcher())); EXPECT_TRUE(called); } @@ -61,8 +62,8 @@ TEST_F(ConfigTrackerImplTest, AddDuplicate) { TEST_F(ConfigTrackerImplTest, OperationsWithinCallback) { ConfigTracker::EntryOwnerPtr owner1, owner2; - owner1 = tracker.add("test_key", [&] { - owner2 = tracker.add("test_key2", [&] { + owner1 = tracker.add("test_key", [&](const Matchers::StringMatcher&) { + owner2 = tracker.add("test_key2", [&](const Matchers::StringMatcher&) { owner1.reset(); return test_msg(); }); @@ -70,10 +71,10 @@ TEST_F(ConfigTrackerImplTest, OperationsWithinCallback) { }); EXPECT_EQ(1, cbs_map.size()); EXPECT_NE(nullptr, owner1); - EXPECT_NE(nullptr, cbs_map.at("test_key")()); + EXPECT_NE(nullptr, cbs_map.at("test_key")(Matchers::UniversalStringMatcher())); EXPECT_EQ(2, cbs_map.size()); EXPECT_NE(nullptr, owner2); - EXPECT_NE(nullptr, cbs_map.at("test_key2")()); + EXPECT_NE(nullptr, cbs_map.at("test_key2")(Matchers::UniversalStringMatcher())); EXPECT_EQ(1, cbs_map.size()); EXPECT_EQ(0, cbs_map.count("test_key")); } diff --git a/test/server/config_validation/BUILD b/test/server/config_validation/BUILD index 2292906cddb94..51ebda4ae37c4 100644 --- a/test/server/config_validation/BUILD +++ b/test/server/config_validation/BUILD @@ -133,6 +133,7 @@ envoy_cc_test_library( deps = [ ":xds_fuzz_proto_cc_proto", ":xds_verifier_lib", + "//source/common/common:matchers_lib", "//test/fuzz:utility_lib", "//test/integration:http_integration_lib", "@envoy_api//envoy/admin/v3:pkg_cc_proto", diff --git a/test/server/config_validation/xds_fuzz.cc b/test/server/config_validation/xds_fuzz.cc index 3b4d23f4b1575..653acd8db012b 100644 --- a/test/server/config_validation/xds_fuzz.cc +++ b/test/server/config_validation/xds_fuzz.cc @@ -6,6 +6,8 @@ #include "envoy/config/listener/v3/listener.pb.h" #include "envoy/config/route/v3/route.pb.h" +#include "source/common/common/matchers.h" + namespace Envoy { // Helper functions to build API responses. @@ -380,8 +382,8 @@ void XdsFuzzTest::verifyState() { } envoy::admin::v3::ListenersConfigDump XdsFuzzTest::getListenersConfigDump() { - auto message_ptr = - test_server_->server().admin().getConfigTracker().getCallbacksMap().at("listeners")(); + auto message_ptr = test_server_->server().admin().getConfigTracker().getCallbacksMap().at( + "listeners")(Matchers::UniversalStringMatcher()); return dynamic_cast(*message_ptr); } @@ -393,7 +395,7 @@ std::vector XdsFuzzTest::getRoutes return {}; } - auto message_ptr = map.at("routes")(); + auto message_ptr = map.at("routes")(Matchers::UniversalStringMatcher()); auto dump = dynamic_cast(*message_ptr); // Since the route config dump gives the RouteConfigurations as an Any, go through and cast them diff --git a/test/server/listener_manager_impl_test.cc b/test/server/listener_manager_impl_test.cc index 489bca559ace7..0ca3c14cd8a49 100644 --- a/test/server/listener_manager_impl_test.cc +++ b/test/server/listener_manager_impl_test.cc @@ -26,6 +26,7 @@ #include "source/extensions/transport_sockets/tls/ssl_socket.h" #include "test/mocks/init/mocks.h" +#include "test/mocks/matcher/mocks.h" #include "test/server/utility.h" #include "test/test_common/network_utility.h" #include "test/test_common/registry.h" @@ -1153,7 +1154,8 @@ name: foo TEST_F(ListenerManagerImplTest, AddOrUpdateListener) { time_system_.setSystemTime(std::chrono::milliseconds(1001001001001)); - + NiceMock mock_matcher; + ON_CALL(mock_matcher, match(_)).WillByDefault(Return(false)); InSequence s; auto* lds_api = new MockLdsApi(); @@ -1201,6 +1203,13 @@ version_info: version1 seconds: 1001001001 nanos: 1000000 )EOF"); + EXPECT_CALL(*lds_api, versionInfo()).WillOnce(Return("version1")); + checkConfigDump(R"EOF( +version_info: version1 +static_listeners: +dynamic_listeners: +)EOF", + mock_matcher); // Update duplicate should be a NOP. EXPECT_FALSE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "", true)); @@ -1245,6 +1254,13 @@ version_info: version2 seconds: 2002002002 nanos: 2000000 )EOF"); + EXPECT_CALL(*lds_api, versionInfo()).WillOnce(Return("version2")); + checkConfigDump(R"EOF( +version_info: version2 +static_listeners: +dynamic_listeners: +)EOF", + mock_matcher); // Validate that workers_started stat is zero before calling startWorkers. EXPECT_EQ(0, server_.stats_store_ @@ -1320,6 +1336,14 @@ version_info: version3 nanos: 2000000 )EOF"); + EXPECT_CALL(*lds_api, versionInfo()).WillOnce(Return("version3")); + checkConfigDump(R"EOF( +version_info: version3 +static_listeners: +dynamic_listeners: +)EOF", + mock_matcher); + EXPECT_CALL(*worker_, removeListener(_, _)); listener_foo_update1->drain_manager_->drain_sequence_completion_(); checkStats(__LINE__, 1, 2, 0, 0, 1, 1, 0); @@ -1415,6 +1439,14 @@ version_info: version5 nanos: 5000000 )EOF"); + EXPECT_CALL(*lds_api, versionInfo()).WillOnce(Return("version5")); + checkConfigDump(R"EOF( +version_info: version5 +static_listeners: +dynamic_listeners: +)EOF", + mock_matcher); + // Update a duplicate baz that is currently warming. EXPECT_FALSE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(listener_baz_yaml), "", true)); checkStats(__LINE__, 3, 2, 0, 1, 2, 0, 0); diff --git a/test/server/listener_manager_impl_test.h b/test/server/listener_manager_impl_test.h index 26b19f70e672d..38793de484a2d 100644 --- a/test/server/listener_manager_impl_test.h +++ b/test/server/listener_manager_impl_test.h @@ -262,8 +262,11 @@ class ListenerManagerImplTest : public testing::Test { .value()); } - void checkConfigDump(const std::string& expected_dump_yaml) { - auto message_ptr = server_.admin_.config_tracker_.config_tracker_callbacks_["listeners"](); + void checkConfigDump( + const std::string& expected_dump_yaml, + const Matchers::StringMatcher& name_matcher = Matchers::UniversalStringMatcher()) { + auto message_ptr = + server_.admin_.config_tracker_.config_tracker_callbacks_["listeners"](name_matcher); const auto& listeners_config_dump = dynamic_cast(*message_ptr);