diff --git a/api/envoy/admin/v2alpha/BUILD b/api/envoy/admin/v2alpha/BUILD index 3806af07dd9fe..6b9e6bb44475b 100644 --- a/api/envoy/admin/v2alpha/BUILD +++ b/api/envoy/admin/v2alpha/BUILD @@ -10,6 +10,7 @@ api_proto_library_internal( "//envoy/api/v2:cds", "//envoy/api/v2:lds", "//envoy/api/v2:rds", + "//envoy/api/v2:srds", "//envoy/config/bootstrap/v2:bootstrap", ], ) diff --git a/api/envoy/admin/v2alpha/config_dump.proto b/api/envoy/admin/v2alpha/config_dump.proto index cfc1cdd54d8f5..ba5b36df36c7c 100644 --- a/api/envoy/admin/v2alpha/config_dump.proto +++ b/api/envoy/admin/v2alpha/config_dump.proto @@ -9,6 +9,7 @@ option java_package = "io.envoyproxy.envoy.admin.v2alpha"; import "envoy/api/v2/cds.proto"; import "envoy/api/v2/lds.proto"; import "envoy/api/v2/rds.proto"; +import "envoy/api/v2/srds.proto"; import "envoy/config/bootstrap/v2/bootstrap.proto"; import "google/protobuf/any.proto"; @@ -178,3 +179,43 @@ message RoutesConfigDump { // The dynamically loaded route configs. repeated DynamicRouteConfig dynamic_route_configs = 3 [(gogoproto.nullable) = false]; } + +// Envoy's scoped RDS implementation fills this message with all currently loaded route +// configuration scopes (defined via ScopedRouteConfigurationsSet protos). This message lists both +// the scopes defined inline with the higher order object (i.e., the HttpConnectionManager) and the +// dynamically obtained scopes via the SRDS API. +message ScopedRoutesConfigDump { + message InlineScopedRouteConfigs { + // The name assigned to the scoped route configurations. + string name = 1; + + // The scoped route configurations. + repeated envoy.api.v2.ScopedRouteConfiguration scoped_route_configs = 2; + + // The timestamp when the scoped route config set was last updated. + google.protobuf.Timestamp last_updated = 3; + } + + message DynamicScopedRouteConfigs { + // The name assigned to the scoped route configurations. + string name = 1; + + // This is the per-resource version information. This version is currently taken from the + // :ref:`version_info ` field at the time that + // the scoped routes configuration was loaded. + string version_info = 2; + + // The scoped route configurations. + repeated envoy.api.v2.ScopedRouteConfiguration scoped_route_configs = 3; + + // The timestamp when the scoped route config set was last updated. + google.protobuf.Timestamp last_updated = 4; + } + + // The statically loaded scoped route configs. + repeated InlineScopedRouteConfigs inline_scoped_route_configs = 1 [(gogoproto.nullable) = false]; + + // The dynamically loaded scoped route configs. + repeated DynamicScopedRouteConfigs dynamic_scoped_route_configs = 2 + [(gogoproto.nullable) = false]; +} diff --git a/include/envoy/config/config_provider_manager.h b/include/envoy/config/config_provider_manager.h index 81beb1ecb5fbb..9c9dce88d1071 100644 --- a/include/envoy/config/config_provider_manager.h +++ b/include/envoy/config/config_provider_manager.h @@ -81,7 +81,7 @@ class ConfigProviderManager { * @return ConfigProviderPtr a newly allocated static config provider. */ virtual ConfigProviderPtr - createStaticConfigProvider(std::vector>&& config_protos, + createStaticConfigProvider(ProtobufTypes::ConstMessagePtrVector&& config_protos, Server::Configuration::FactoryContext& factory_context, const OptionalArg& optarg) { UNREFERENCED_PARAMETER(config_protos); diff --git a/include/envoy/router/BUILD b/include/envoy/router/BUILD index 73c4e8bee63a5..3ebe1a1b70e39 100644 --- a/include/envoy/router/BUILD +++ b/include/envoy/router/BUILD @@ -61,6 +61,16 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "scopes_interface", + hdrs = ["scopes.h"], + deps = [ + ":router_interface", + "//include/envoy/config:config_provider_interface", + "//include/envoy/http:header_map_interface", + ], +) + envoy_cc_library( name = "router_ratelimit_interface", hdrs = ["router_ratelimit.h"], diff --git a/include/envoy/router/scopes.h b/include/envoy/router/scopes.h new file mode 100644 index 0000000000000..9eda8e8e3d9b4 --- /dev/null +++ b/include/envoy/router/scopes.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "envoy/config/config_provider.h" +#include "envoy/router/router.h" + +namespace Envoy { +namespace Router { + +/** + * The scoped routing configuration. + */ +class ScopedConfig : public Envoy::Config::ConfigProvider::Config { +public: + ~ScopedConfig() override = default; + + /** + * Based on the incoming HTTP request headers, returns the configuration to use for selecting a + * target route. + * @param headers the request headers to match the scoped routing configuration against. + * @return ConfigConstSharedPtr the router's Config matching the request headers. + */ + virtual ConfigConstSharedPtr getRouteConfig(const Http::HeaderMap& headers) const PURE; +}; + +using ScopedConfigConstSharedPtr = std::shared_ptr; + +} // namespace Router +} // namespace Envoy diff --git a/source/common/config/resources.h b/source/common/config/resources.h index 9f35373b511e3..cb1a343bea814 100644 --- a/source/common/config/resources.h +++ b/source/common/config/resources.h @@ -18,6 +18,8 @@ class TypeUrlValues { const std::string Secret{"type.googleapis.com/envoy.api.v2.auth.Secret"}; const std::string RouteConfiguration{"type.googleapis.com/envoy.api.v2.RouteConfiguration"}; const std::string VirtualHost{"type.googleapis.com/envoy.api.v2.route.VirtualHost"}; + const std::string ScopedRouteConfiguration{ + "type.googleapis.com/envoy.api.v2.ScopedRouteConfiguration"}; }; typedef ConstSingleton TypeUrl; diff --git a/source/common/protobuf/protobuf.h b/source/common/protobuf/protobuf.h index fbeffd9dcb4b4..4f5e7de88c2a1 100644 --- a/source/common/protobuf/protobuf.h +++ b/source/common/protobuf/protobuf.h @@ -45,6 +45,7 @@ namespace ProtobufWkt = google::protobuf; namespace ProtobufTypes { typedef std::unique_ptr MessagePtr; +typedef std::vector> ConstMessagePtrVector; typedef int64_t Int64; diff --git a/source/common/router/BUILD b/source/common/router/BUILD index 6f66df20eac95..3cf5d8568b8e4 100644 --- a/source/common/router/BUILD +++ b/source/common/router/BUILD @@ -136,6 +136,46 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "scoped_config_manager_lib", + srcs = ["scoped_config_manager.cc"], + hdrs = ["scoped_config_manager.h"], + deps = [ + "@envoy_api//envoy/api/v2:srds_cc", + ], +) + +envoy_cc_library( + name = "scoped_config_lib", + srcs = ["scoped_config_impl.cc"], + hdrs = ["scoped_config_impl.h"], + deps = [ + ":config_lib", + ":scoped_config_manager_lib", + "//include/envoy/router:scopes_interface", + "//include/envoy/thread_local:thread_local_interface", + "@envoy_api//envoy/api/v2:srds_cc", + "@envoy_api//envoy/config/filter/network/http_connection_manager/v2:http_connection_manager_cc", + ], +) + +envoy_cc_library( + name = "scoped_rds_lib", + srcs = ["scoped_rds.cc"], + hdrs = ["scoped_rds.h"], + deps = [ + ":scoped_config_lib", + "//include/envoy/config:subscription_interface", + "//include/envoy/stats:stats_interface", + "//source/common/common:assert_lib", + "//source/common/common:minimal_logger_lib", + "//source/common/config:config_provider_lib", + "//source/common/config:subscription_factory_lib", + "@envoy_api//envoy/admin/v2alpha:config_dump_cc", + "@envoy_api//envoy/api/v2:srds_cc", + ], +) + envoy_cc_library( name = "retry_state_lib", srcs = ["retry_state_impl.cc"], diff --git a/source/common/router/scoped_config_impl.cc b/source/common/router/scoped_config_impl.cc new file mode 100644 index 0000000000000..0274381ae8501 --- /dev/null +++ b/source/common/router/scoped_config_impl.cc @@ -0,0 +1,16 @@ +#include "common/router/scoped_config_impl.h" + +namespace Envoy { +namespace Router { + +void ThreadLocalScopedConfigImpl::addOrUpdateRoutingScope(const ScopedRouteInfoConstSharedPtr&) {} + +void ThreadLocalScopedConfigImpl::removeRoutingScope(const std::string&) {} + +Router::ConfigConstSharedPtr +ThreadLocalScopedConfigImpl::getRouteConfig(const Http::HeaderMap&) const { + return std::make_shared(); +} + +} // namespace Router +} // namespace Envoy diff --git a/source/common/router/scoped_config_impl.h b/source/common/router/scoped_config_impl.h new file mode 100644 index 0000000000000..94d376b2a354e --- /dev/null +++ b/source/common/router/scoped_config_impl.h @@ -0,0 +1,53 @@ +#pragma once + +#include "envoy/api/v2/srds.pb.h" +#include "envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.pb.h" +#include "envoy/router/router.h" +#include "envoy/router/scopes.h" +#include "envoy/thread_local/thread_local.h" + +#include "common/router/config_impl.h" +#include "common/router/scoped_config_manager.h" + +namespace Envoy { +namespace Router { + +/** + * TODO(AndresGuedez): implement scoped routing logic. + * + * Each Envoy worker is assigned an instance of this type. When config updates are received, + * addOrUpdateRoutingScope() and removeRoutingScope() are called to update the set of scoped routes. + * + * ConnectionManagerImpl::refreshCachedRoute() will call getRouterConfig() to obtain the + * Router::ConfigConstSharedPtr to use for route selection. + */ +class ThreadLocalScopedConfigImpl : public ScopedConfig, public ThreadLocal::ThreadLocalObject { +public: + ThreadLocalScopedConfigImpl( + envoy::config::filter::network::http_connection_manager::v2::ScopedRoutes::ScopeKeyBuilder + scope_key_builder) + : scope_key_builder_(std::move(scope_key_builder)) {} + + void addOrUpdateRoutingScope(const ScopedRouteInfoConstSharedPtr& scoped_route_info); + void removeRoutingScope(const std::string& scope_name); + + // Envoy::Router::ScopedConfig + Router::ConfigConstSharedPtr getRouteConfig(const Http::HeaderMap& headers) const override; + +private: + const envoy::config::filter::network::http_connection_manager::v2::ScopedRoutes::ScopeKeyBuilder + scope_key_builder_; +}; + +/** + * A NULL implementation of the scoped routing configuration. + */ +class NullScopedConfigImpl : public ScopedConfig { +public: + Router::ConfigConstSharedPtr getRouteConfig(const Http::HeaderMap&) const override { + return std::make_shared(); + } +}; + +} // namespace Router +} // namespace Envoy diff --git a/source/common/router/scoped_config_manager.cc b/source/common/router/scoped_config_manager.cc new file mode 100644 index 0000000000000..2a5b75f3b29ce --- /dev/null +++ b/source/common/router/scoped_config_manager.cc @@ -0,0 +1,22 @@ +#include "common/router/scoped_config_manager.h" + +#include "envoy/common/exception.h" + +#include "common/common/fmt.h" + +namespace Envoy { +namespace Router { + +ScopedRouteInfoConstSharedPtr ScopedConfigManager::addOrUpdateRoutingScope( + const envoy::api::v2::ScopedRouteConfiguration& config_proto, const std::string&) { + auto scoped_route_info = std::make_shared(config_proto); + scoped_route_map_[config_proto.name()] = scoped_route_info; + return scoped_route_info; +} + +bool ScopedConfigManager::removeRoutingScope(const std::string& name) { + return scoped_route_map_.erase(name) == 0; +} + +} // namespace Router +} // namespace Envoy diff --git a/source/common/router/scoped_config_manager.h b/source/common/router/scoped_config_manager.h new file mode 100644 index 0000000000000..5f8dd6fda878c --- /dev/null +++ b/source/common/router/scoped_config_manager.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +#include "envoy/api/v2/srds.pb.h" + +namespace Envoy { +namespace Router { + +// The internal representation of the configuration distributed via the ScopedRouteConfiguration +// proto. +class ScopedRouteInfo { +public: + ScopedRouteInfo(const envoy::api::v2::ScopedRouteConfiguration& config_proto) + : config_proto_(config_proto) {} + + // TODO(AndresGuedez): Add the necessary APIs required for the scoped routing logic. + + const envoy::api::v2::ScopedRouteConfiguration config_proto_; +}; +using ScopedRouteInfoConstSharedPtr = std::shared_ptr; + +// A manager for routing configuration scopes. +// An instance of the manager is owned by each ScopedRdsConfigSubscription. When config updates are +// received (on the main thread), the manager is called to track changes to the set of scoped route +// configurations and build s as needed. +class ScopedConfigManager { +public: + // Ordered map for consistent config dumping. + using ScopedRouteMap = std::map; + + // Adds/updates a routing scope specified via the Scoped RDS API. This scope will be added to the + // set of scopes matched against the scope keys built for each HTTP request. + ScopedRouteInfoConstSharedPtr + addOrUpdateRoutingScope(const envoy::api::v2::ScopedRouteConfiguration& scoped_route_config, + const std::string& version_info); + + // Removes a routing scope from the set of scopes matched against each HTTP request. + bool removeRoutingScope(const std::string& scope_name); + + const ScopedRouteMap& scopedRouteMap() const { return scoped_route_map_; } + +private: + ScopedRouteMap scoped_route_map_; +}; + +} // namespace Router +} // namespace Envoy diff --git a/source/common/router/scoped_rds.cc b/source/common/router/scoped_rds.cc new file mode 100644 index 0000000000000..3a5ac1d76ff20 --- /dev/null +++ b/source/common/router/scoped_rds.cc @@ -0,0 +1,206 @@ +#include "common/router/scoped_rds.h" + +#include + +#include "envoy/admin/v2alpha/config_dump.pb.h" +#include "envoy/api/v2/srds.pb.validate.h" + +#include "common/common/assert.h" +#include "common/common/logger.h" +#include "common/config/subscription_factory.h" + +// Types are deeply nested under Envoy::Config::ConfigProvider; use 'using-directives' across all +// ConfigProvider related types for consistency. +using Envoy::Config::ConfigProvider; +using Envoy::Config::ConfigProviderInstanceType; +using Envoy::Config::ConfigProviderManager; +using Envoy::Config::ConfigProviderPtr; + +namespace Envoy { +namespace Router { + +InlineScopedRoutesConfigProvider::InlineScopedRoutesConfigProvider( + ProtobufTypes::ConstMessagePtrVector&& config_protos, std::string name, + Server::Configuration::FactoryContext& factory_context, + ScopedRoutesConfigProviderManager& config_provider_manager, + envoy::api::v2::core::ConfigSource rds_config_source, + envoy::config::filter::network::http_connection_manager::v2::ScopedRoutes::ScopeKeyBuilder + scope_key_builder) + : Envoy::Config::ImmutableConfigProviderBase(factory_context, config_provider_manager, + ConfigProviderInstanceType::Inline, + ConfigProvider::ApiType::Delta), + name_(std::move(name)), + config_(std::make_shared(std::move(scope_key_builder))), + config_protos_(std::make_move_iterator(config_protos.begin()), + std::make_move_iterator(config_protos.end())), + rds_config_source_(std::move(rds_config_source)) {} + +ScopedRdsConfigSubscription::ScopedRdsConfigSubscription( + const envoy::config::filter::network::http_connection_manager::v2::ScopedRds& scoped_rds, + const uint64_t manager_identifier, const std::string& name, + Server::Configuration::FactoryContext& factory_context, const std::string& stat_prefix, + ScopedRoutesConfigProviderManager& config_provider_manager) + : DeltaConfigSubscriptionInstance( + "SRDS", manager_identifier, config_provider_manager, factory_context.timeSource(), + factory_context.timeSource().systemTime(), factory_context.localInfo()), + name_(name), + scope_(factory_context.scope().createScope(stat_prefix + "scoped_rds." + name + ".")), + stats_({ALL_SCOPED_RDS_STATS(POOL_COUNTER(*scope_))}) { + subscription_ = Envoy::Config::SubscriptionFactory::subscriptionFromConfigSource( + scoped_rds.scoped_rds_config_source(), factory_context.localInfo(), + factory_context.dispatcher(), factory_context.clusterManager(), factory_context.random(), + *scope_, "envoy.api.v2.ScopedRoutesDiscoveryService.FetchScopedRoutes", + "envoy.api.v2.ScopedRoutesDiscoveryService.StreamScopedRoutes", + Grpc::Common::typeUrl( + envoy::api::v2::ScopedRouteConfiguration().GetDescriptor()->full_name()), + factory_context.api()); +} + +void ScopedRdsConfigSubscription::onConfigUpdate( + const Protobuf::RepeatedPtrField& resources, + const std::string& version_info) { + std::vector scoped_routes; + for (const auto& resource_any : resources) { + scoped_routes.emplace_back( + MessageUtil::anyConvert(resource_any)); + } + + std::unordered_set resource_names; + for (const auto& scoped_route : scoped_routes) { + if (!resource_names.insert(scoped_route.name()).second) { + throw EnvoyException( + fmt::format("duplicate scoped route configuration {} found", scoped_route.name())); + } + } + for (const auto& scoped_route : scoped_routes) { + MessageUtil::validate(scoped_route); + } + + // TODO(AndresGuedez): refactor such that it can be shared with other delta APIs (e.g., CDS). + std::vector exception_msgs; + // We need to keep track of which scoped routes we might need to remove. + ScopedConfigManager::ScopedRouteMap scoped_routes_to_remove = + scoped_config_manager_.scopedRouteMap(); + for (auto& scoped_route : scoped_routes) { + const std::string& scoped_route_name = scoped_route.name(); + scoped_routes_to_remove.erase(scoped_route_name); + ScopedRouteInfoConstSharedPtr scoped_route_info = + scoped_config_manager_.addOrUpdateRoutingScope(scoped_route, version_info); + ENVOY_LOG(debug, "srds: add/update scoped_route '{}'", scoped_route_name); + applyDeltaConfigUpdate([scoped_route_info](const ConfigProvider::ConfigConstSharedPtr& config) { + auto* thread_local_scoped_config = const_cast( + static_cast(config.get())); + thread_local_scoped_config->addOrUpdateRoutingScope(scoped_route_info); + }); + } + + for (const auto& scoped_route : scoped_routes_to_remove) { + const std::string scoped_route_name = scoped_route.first; + ENVOY_LOG(debug, "srds: remove scoped route '{}'", scoped_route_name); + scoped_config_manager_.removeRoutingScope(scoped_route_name); + applyDeltaConfigUpdate([scoped_route_name](const ConfigProvider::ConfigConstSharedPtr& config) { + auto* thread_local_scoped_config = const_cast( + static_cast(config.get())); + thread_local_scoped_config->removeRoutingScope(scoped_route_name); + }); + } + + ConfigSubscriptionCommonBase::onConfigUpdate(); + setLastConfigInfo(absl::optional({absl::nullopt, version_info})); + stats_.config_reload_.inc(); +} + +ScopedRdsConfigProvider::ScopedRdsConfigProvider( + ScopedRdsConfigSubscriptionSharedPtr&& subscription, + Server::Configuration::FactoryContext& factory_context, + envoy::api::v2::core::ConfigSource rds_config_source, + const envoy::config::filter::network::http_connection_manager::v2::ScopedRoutes:: + ScopeKeyBuilder& scope_key_builder) + : DeltaMutableConfigProviderBase(std::move(subscription), factory_context, + ConfigProvider::ApiType::Delta), + subscription_(static_cast( + MutableConfigProviderCommonBase::subscription_.get())), + rds_config_source_(std::move(rds_config_source)) { + initialize([scope_key_builder](Event::Dispatcher&) -> ThreadLocal::ThreadLocalObjectSharedPtr { + return std::make_shared(scope_key_builder); + }); +} + +ProtobufTypes::MessagePtr ScopedRoutesConfigProviderManager::dumpConfigs() const { + auto config_dump = std::make_unique(); + for (const auto& element : configSubscriptions()) { + auto subscription = element.second.lock(); + ASSERT(subscription); + + if (subscription->configInfo()) { + auto* dynamic_config = config_dump->mutable_dynamic_scoped_route_configs()->Add(); + dynamic_config->set_version_info(subscription->configInfo().value().last_config_version_); + const ScopedRdsConfigSubscription* typed_subscription = + static_cast(subscription.get()); + dynamic_config->set_name(typed_subscription->name()); + const ScopedConfigManager::ScopedRouteMap& scoped_route_map = + typed_subscription->scopedRouteMap(); + for (const auto& it : scoped_route_map) { + dynamic_config->mutable_scoped_route_configs()->Add()->MergeFrom(it.second->config_proto_); + } + TimestampUtil::systemClockToTimestamp(subscription->lastUpdated(), + *dynamic_config->mutable_last_updated()); + } + } + + for (const auto& provider : immutableConfigProviders(ConfigProviderInstanceType::Inline)) { + const auto protos_info = + provider->configProtoInfoVector(); + ASSERT(protos_info != absl::nullopt); + 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_) { + inline_config->mutable_scoped_route_configs()->Add()->MergeFrom(*config_proto); + } + TimestampUtil::systemClockToTimestamp(provider->lastUpdated(), + *inline_config->mutable_last_updated()); + } + + return config_dump; +} + +ConfigProviderPtr ScopedRoutesConfigProviderManager::createXdsConfigProvider( + const Protobuf::Message& config_source_proto, + Server::Configuration::FactoryContext& factory_context, const std::string& stat_prefix, + const ConfigProviderManager::OptionalArg& optarg) { + ScopedRdsConfigSubscriptionSharedPtr subscription = + ConfigProviderManagerImplBase::getSubscription( + config_source_proto, factory_context.initManager(), + [&config_source_proto, &factory_context, &stat_prefix, + &optarg](const uint64_t manager_identifier, + ConfigProviderManagerImplBase& config_provider_manager) + -> Envoy::Config::ConfigSubscriptionCommonBaseSharedPtr { + const auto& scoped_rds_config_source = dynamic_cast< + const envoy::config::filter::network::http_connection_manager::v2::ScopedRds&>( + config_source_proto); + return std::make_shared( + scoped_rds_config_source, manager_identifier, + static_cast(optarg) + .scoped_routes_name_, + factory_context, stat_prefix, + static_cast(config_provider_manager)); + }); + + const auto& typed_optarg = static_cast(optarg); + return std::make_unique(std::move(subscription), factory_context, + typed_optarg.rds_config_source_, + typed_optarg.scope_key_builder_); +} + +ConfigProviderPtr ScopedRoutesConfigProviderManager::createStaticConfigProvider( + ProtobufTypes::ConstMessagePtrVector&& config_protos, + Server::Configuration::FactoryContext& factory_context, + const ConfigProviderManager::OptionalArg& optarg) { + const auto& typed_optarg = static_cast(optarg); + return absl::make_unique( + std::move(config_protos), typed_optarg.scoped_routes_name_, factory_context, *this, + typed_optarg.rds_config_source_, typed_optarg.scope_key_builder_); +} + +} // namespace Router +} // namespace Envoy diff --git a/source/common/router/scoped_rds.h b/source/common/router/scoped_rds.h new file mode 100644 index 0000000000000..c32ec1012ce5e --- /dev/null +++ b/source/common/router/scoped_rds.h @@ -0,0 +1,190 @@ +#pragma once + +#include + +#include "envoy/api/v2/srds.pb.h" +#include "envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.pb.h" +#include "envoy/config/subscription.h" +#include "envoy/stats/scope.h" + +#include "common/config/config_provider_impl.h" +#include "common/router/scoped_config_impl.h" + +namespace Envoy { +namespace Router { + +class ScopedRoutesConfigProviderManager; + +// A ConfigProvider for inline scoped routing configuration. +class InlineScopedRoutesConfigProvider : public Envoy::Config::ImmutableConfigProviderBase { +public: + InlineScopedRoutesConfigProvider( + ProtobufTypes::ConstMessagePtrVector&& config_protos, std::string name, + Server::Configuration::FactoryContext& factory_context, + ScopedRoutesConfigProviderManager& config_provider_manager, + envoy::api::v2::core::ConfigSource rds_config_source, + envoy::config::filter::network::http_connection_manager::v2::ScopedRoutes::ScopeKeyBuilder + scope_key_builder); + + ~InlineScopedRoutesConfigProvider() override = default; + + const std::string& name() const { return name_; } + + // Envoy::Config::ConfigProvider + Envoy::Config::ConfigProvider::ConfigProtoVector getConfigProtos() const override { + Envoy::Config::ConfigProvider::ConfigProtoVector out_protos; + std::for_each(config_protos_.begin(), config_protos_.end(), + [&out_protos](const std::unique_ptr& message) { + out_protos.push_back(message.get()); + }); + return out_protos; + } + + std::string getConfigVersion() const override { return ""; } + ConfigConstSharedPtr getConfig() const override { return config_; } + +private: + const std::string name_; + ConfigConstSharedPtr config_; + const std::vector> config_protos_; + const envoy::api::v2::core::ConfigSource rds_config_source_; +}; + +/** + * All SRDS stats. @see stats_macros.h + */ +// clang-format off +#define ALL_SCOPED_RDS_STATS(COUNTER) \ + COUNTER(config_reload) \ + COUNTER(update_empty) + +// clang-format on + +struct ScopedRdsStats { + ALL_SCOPED_RDS_STATS(GENERATE_COUNTER_STRUCT) +}; + +// A scoped RDS subscription to be used with the dynamic scoped RDS ConfigProvider. +class ScopedRdsConfigSubscription : public Envoy::Config::DeltaConfigSubscriptionInstance, + Envoy::Config::SubscriptionCallbacks { +public: + using ScopedRouteConfigurationMap = + std::map; + + ScopedRdsConfigSubscription( + const envoy::config::filter::network::http_connection_manager::v2::ScopedRds& scoped_rds, + const uint64_t manager_identifier, const std::string& name, + Server::Configuration::FactoryContext& factory_context, const std::string& stat_prefix, + ScopedRoutesConfigProviderManager& config_provider_manager); + + ~ScopedRdsConfigSubscription() override = default; + + const std::string& name() const { return name_; } + + // Envoy::Config::ConfigSubscriptionCommonBase + void start() override { subscription_->start({}, *this); } + + // Envoy::Config::SubscriptionCallbacks + void onConfigUpdate(const Protobuf::RepeatedPtrField& resources, + const std::string& version_info) override; + void onConfigUpdate(const Protobuf::RepeatedPtrField&, + const Protobuf::RepeatedPtrField&, const std::string&) override { + NOT_IMPLEMENTED_GCOVR_EXCL_LINE; + } + void onConfigUpdateFailed(const EnvoyException*) override { + ConfigSubscriptionCommonBase::onConfigUpdateFailed(); + } + std::string resourceName(const ProtobufWkt::Any& resource) override { + return MessageUtil::anyConvert(resource).name(); + } + const ScopedConfigManager::ScopedRouteMap& scopedRouteMap() const { + return scoped_config_manager_.scopedRouteMap(); + } + +private: + const std::string name_; + std::unique_ptr subscription_; + Stats::ScopePtr scope_; + ScopedRdsStats stats_; + ScopedConfigManager scoped_config_manager_; +}; + +using ScopedRdsConfigSubscriptionSharedPtr = std::shared_ptr; + +// A ConfigProvider for scoped RDS that dynamically fetches scoped routing configuration via a +// subscription. +class ScopedRdsConfigProvider : public Envoy::Config::DeltaMutableConfigProviderBase { +public: + ScopedRdsConfigProvider(ScopedRdsConfigSubscriptionSharedPtr&& subscription, + Server::Configuration::FactoryContext& factory_context, + envoy::api::v2::core::ConfigSource rds_config_source, + const envoy::config::filter::network::http_connection_manager::v2:: + ScopedRoutes::ScopeKeyBuilder& scope_key_builder); + + ScopedRdsConfigSubscription& subscription() { return *subscription_; } + + // getConfig() is overloaded (const/non-const only). Make all base getConfig()s visible to avoid + // compiler warnings. + using DeltaMutableConfigProviderBase::getConfig; + + // Envoy::Config::DeltaMutableConfigProviderBase + Envoy::Config::ConfigSharedPtr getConfig() override { + return std::dynamic_pointer_cast(tls_->get()); + } + +private: + ScopedRdsConfigSubscription* subscription_; + const envoy::api::v2::core::ConfigSource rds_config_source_; +}; + +// A ConfigProviderManager for scoped routing configuration that creates static/inline and dynamic +// (xds) config providers. +class ScopedRoutesConfigProviderManager : public Envoy::Config::ConfigProviderManagerImplBase { +public: + ScopedRoutesConfigProviderManager(Server::Admin& admin) + : Envoy::Config::ConfigProviderManagerImplBase(admin, "route_scopes") {} + + ~ScopedRoutesConfigProviderManager() override = default; + + // Envoy::Config::ConfigProviderManagerImplBase + ProtobufTypes::MessagePtr dumpConfigs() const override; + + // Envoy::Config::ConfigProviderManager + Envoy::Config::ConfigProviderPtr + createXdsConfigProvider(const Protobuf::Message& config_source_proto, + Server::Configuration::FactoryContext& factory_context, + const std::string& stat_prefix, + const Envoy::Config::ConfigProviderManager::OptionalArg& optarg) override; + Envoy::Config::ConfigProviderPtr + createStaticConfigProvider(const Protobuf::Message&, Server::Configuration::FactoryContext&, + const Envoy::Config::ConfigProviderManager::OptionalArg&) override { + ASSERT(false, + "SRDS supports delta updates and requires the use of the createStaticConfigProvider() " + "overload that accepts a config proto set as an argument."); + NOT_REACHED_GCOVR_EXCL_LINE; + } + Envoy::Config::ConfigProviderPtr createStaticConfigProvider( + std::vector>&& config_protos, + Server::Configuration::FactoryContext& factory_context, + const Envoy::Config::ConfigProviderManager::OptionalArg& optarg) override; +}; + +// The optional argument passed to the ConfigProviderManager::create*() functions. +class ScopedRoutesConfigProviderManagerOptArg + : public Envoy::Config::ConfigProviderManager::OptionalArg { +public: + ScopedRoutesConfigProviderManagerOptArg( + std::string scoped_routes_name, const envoy::api::v2::core::ConfigSource& rds_config_source, + const envoy::config::filter::network::http_connection_manager::v2::ScopedRoutes:: + ScopeKeyBuilder& scope_key_builder) + : scoped_routes_name_(std::move(scoped_routes_name)), rds_config_source_(rds_config_source), + scope_key_builder_(scope_key_builder) {} + + const std::string scoped_routes_name_; + const envoy::api::v2::core::ConfigSource& rds_config_source_; + const envoy::config::filter::network::http_connection_manager::v2::ScopedRoutes::ScopeKeyBuilder& + scope_key_builder_; +}; + +} // namespace Router +} // namespace Envoy diff --git a/test/common/router/BUILD b/test/common/router/BUILD index 8f80a247e2f1c..5658a98587499 100644 --- a/test/common/router/BUILD +++ b/test/common/router/BUILD @@ -78,6 +78,22 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "scoped_rds_test", + srcs = ["scoped_rds_test.cc"], + deps = [ + "//source/common/config:utility_lib", + "//source/common/http:message_lib", + "//source/common/json:json_loader_lib", + "//source/common/router:scoped_rds_lib", + "//source/server/http:admin_lib", + "//test/mocks/init:init_mocks", + "//test/mocks/server:server_mocks", + "//test/test_common:simulated_time_system_lib", + "//test/test_common:utility_lib", + ], +) + envoy_cc_test( name = "vhds_test", srcs = ["vhds_test.cc"], diff --git a/test/common/router/scoped_rds_test.cc b/test/common/router/scoped_rds_test.cc new file mode 100644 index 0000000000000..7165ee9a1c7d6 --- /dev/null +++ b/test/common/router/scoped_rds_test.cc @@ -0,0 +1,452 @@ +#include + +#include "envoy/admin/v2alpha/config_dump.pb.h" +#include "envoy/admin/v2alpha/config_dump.pb.validate.h" +#include "envoy/stats/scope.h" + +#include "common/router/scoped_rds.h" + +#include "test/mocks/server/mocks.h" +#include "test/test_common/simulated_time_system.h" + +#include "absl/strings/string_view.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using testing::InSequence; +using testing::Return; + +namespace Envoy { +namespace Router { +namespace { + +envoy::api::v2::ScopedRouteConfiguration +parseScopedRouteConfigurationFromYaml(const std::string& yaml) { + envoy::api::v2::ScopedRouteConfiguration scoped_route_config; + MessageUtil::loadFromYaml(yaml, scoped_route_config); + return scoped_route_config; +} + +void parseScopedRouteConfigurationFromYaml(ProtobufWkt::Any& scoped_route_config, + const std::string& yaml) { + scoped_route_config.PackFrom(parseScopedRouteConfigurationFromYaml(yaml)); +} + +std::vector> +protosToMessageVec(std::vector&& protos) { + std::vector> messages; + for (const auto& proto : protos) { + Protobuf::Message* message = proto.New(); + message->CopyFrom(proto); + messages.push_back(std::unique_ptr(message)); + } + return messages; +} + +class ScopedRoutesTestBase : public testing::Test { +protected: + ScopedRoutesTestBase() { + EXPECT_CALL(factory_context_.admin_.config_tracker_, add_("route_scopes", _)); + config_provider_manager_ = + std::make_unique(factory_context_.admin_); + + const std::string rds_config_yaml = R"EOF( +api_config_source: + api_type: REST + cluster_names: + - foo_rds_cluster + refresh_delay: { seconds: 1, nanos: 0 } +)EOF"; + MessageUtil::loadFromYaml(rds_config_yaml, rds_config_source_); + } + + ~ScopedRoutesTestBase() override { factory_context_.thread_local_.shutdownThread(); } + + void setupMockClusterMap() { + InSequence s; + cluster_map_.emplace("foo_cluster", cluster_); + EXPECT_CALL(factory_context_.cluster_manager_, clusters()).WillOnce(Return(cluster_map_)); + EXPECT_CALL(cluster_, info()); + EXPECT_CALL(*cluster_.info_, addedViaApi()); + EXPECT_CALL(cluster_, info()); + EXPECT_CALL(*cluster_.info_, type()); + } + + Event::SimulatedTimeSystem& timeSystem() { return time_system_; } + + NiceMock factory_context_; + Upstream::ClusterManager::ClusterInfoMap cluster_map_; + Upstream::MockClusterMockPrioritySet cluster_; + std::unique_ptr config_provider_manager_; + Event::SimulatedTimeSystem time_system_; + envoy::api::v2::core::ConfigSource rds_config_source_; +}; + +class ScopedRdsTest : public ScopedRoutesTestBase { +protected: + void setup() { + InSequence s; + + setupMockClusterMap(); + const std::string config_yaml = R"EOF( +name: foo_scoped_routes +scope_key_builder: + fragments: + - header_value_extractor: { name: X-Google-VIP } +rds_config_source: + api_config_source: + api_type: REST + cluster_names: + - foo_cluster + refresh_delay: { seconds: 1, nanos: 0 } +scoped_rds: + scoped_rds_config_source: + api_config_source: + api_type: REST + cluster_names: + - foo_cluster + refresh_delay: { seconds: 1, nanos: 0 } +)EOF"; + envoy::config::filter::network::http_connection_manager::v2::ScopedRoutes scoped_routes_config; + MessageUtil::loadFromYaml(config_yaml, scoped_routes_config); + provider_ = config_provider_manager_->createXdsConfigProvider( + scoped_routes_config.scoped_rds(), factory_context_, "foo.", + ScopedRoutesConfigProviderManagerOptArg(scoped_routes_config.name(), + scoped_routes_config.rds_config_source(), + scoped_routes_config.scope_key_builder())); + subscription_ = &dynamic_cast(*provider_).subscription(); + } + + ScopedRdsConfigSubscription& subscription() const { return *subscription_; } + + ScopedRdsConfigSubscription* subscription_; + Envoy::Config::ConfigProviderPtr provider_; +}; + +TEST_F(ScopedRdsTest, ValidateFail) { + setup(); + + ScopedRdsConfigSubscription& subscription = + dynamic_cast(*provider_).subscription(); + + // 'name' validation: value must be > 1 byte. + const std::string config_yaml = R"EOF( +name: +route_configuration_name: foo_routes +key: + fragments: + - string_key: x-foo-key +)EOF"; + Protobuf::RepeatedPtrField resources; + parseScopedRouteConfigurationFromYaml(*resources.Add(), config_yaml); + EXPECT_THROW(subscription.onConfigUpdate(resources, "1"), ProtoValidationException); + + // 'route_configuration_name' validation: value must be > 1 byte. + const std::string config_yaml2 = R"EOF( +name: foo_scope +route_configuration_name: +key: + fragments: + - string_key: x-foo-key +)EOF"; + Protobuf::RepeatedPtrField resources2; + parseScopedRouteConfigurationFromYaml(*resources2.Add(), config_yaml2); + EXPECT_THROW(subscription.onConfigUpdate(resources2, "1"), ProtoValidationException); + + // 'key' validation: must define at least 1 fragment. + const std::string config_yaml3 = R"EOF( +name: foo_scope +route_configuration_name: foo_routes +key: +)EOF"; + Protobuf::RepeatedPtrField resources3; + parseScopedRouteConfigurationFromYaml(*resources3.Add(), config_yaml3); + EXPECT_THROW(subscription.onConfigUpdate(resources3, "1"), ProtoValidationException); +} + +// Tests that multiple uniquely named resources are allowed in config updates. +TEST_F(ScopedRdsTest, MultipleResources) { + setup(); + + const std::string config_yaml = R"EOF( +name: foo_scope +route_configuration_name: foo_routes +key: + fragments: + - string_key: x-foo-key +)EOF"; + Protobuf::RepeatedPtrField resources; + parseScopedRouteConfigurationFromYaml(*resources.Add(), config_yaml); + const std::string config_yaml2 = R"EOF( +name: foo_scope2 +route_configuration_name: foo_routes +key: + fragments: + - string_key: x-foo-key +)EOF"; + parseScopedRouteConfigurationFromYaml(*resources.Add(), config_yaml2); + EXPECT_NO_THROW(subscription().onConfigUpdate(resources, "1")); + EXPECT_EQ( + 1UL, + factory_context_.scope_.counter("foo.scoped_rds.foo_scoped_routes.config_reload").value()); +} + +// Tests that only one resource is provided during a config update. +TEST_F(ScopedRdsTest, InvalidDuplicateResource) { + setup(); + + const std::string config_yaml = R"EOF( +name: foo_scope +route_configuration_name: foo_routes +key: + fragments: + - string_key: x-foo-key +)EOF"; + Protobuf::RepeatedPtrField resources; + parseScopedRouteConfigurationFromYaml(*resources.Add(), config_yaml); + parseScopedRouteConfigurationFromYaml(*resources.Add(), config_yaml); + EXPECT_THROW_WITH_MESSAGE(subscription().onConfigUpdate(resources, "1"), EnvoyException, + "duplicate scoped route configuration foo_scope found"); +} + +// Tests that defining an invalid cluster in the SRDS config results in an error. +TEST_F(ScopedRdsTest, UnknownCluster) { + const std::string config_yaml = R"EOF( +name: foo_scoped_routes +scope_key_builder: + fragments: + - header_value_extractor: { name: X-Google-VIP } +rds_config_source: + api_config_source: + api_type: REST + cluster_names: + - foo_cluster + refresh_delay: { seconds: 1, nanos: 0 } +scoped_rds: + scoped_rds_config_source: + api_config_source: + api_type: REST + cluster_names: + - foo_cluster + refresh_delay: { seconds: 1, nanos: 0 } +)EOF"; + envoy::config::filter::network::http_connection_manager::v2::ScopedRoutes scoped_routes_config; + MessageUtil::loadFromYaml(config_yaml, scoped_routes_config); + + Upstream::ClusterManager::ClusterInfoMap cluster_map; + EXPECT_CALL(factory_context_.cluster_manager_, clusters()).WillOnce(Return(cluster_map)); + EXPECT_THROW_WITH_MESSAGE( + config_provider_manager_->createXdsConfigProvider( + scoped_routes_config.scoped_rds(), factory_context_, "foo.", + ScopedRoutesConfigProviderManagerOptArg(scoped_routes_config.name(), + scoped_routes_config.rds_config_source(), + scoped_routes_config.scope_key_builder())), + EnvoyException, + "envoy::api::v2::core::ConfigSource must have a statically defined non-EDS " + "cluster: 'foo_cluster' does not exist, was added via api, or is an " + "EDS cluster"); +} + +// Tests a config update failure. +TEST_F(ScopedRdsTest, ConfigUpdateFailure) { + setup(); + + const auto time = std::chrono::milliseconds(1234567891234); + timeSystem().setSystemTime(time); + const EnvoyException ex(fmt::format("config failure")); + // Verify the failure updates the lastUpdated() timestamp. + subscription().onConfigUpdateFailed(&ex); + EXPECT_EQ(std::chrono::time_point_cast(provider_->lastUpdated()) + .time_since_epoch(), + time); +} + +using ScopedRoutesConfigProviderManagerTest = ScopedRoutesTestBase; + +// Tests that the /config_dump handler returns the corresponding scoped routing config. +TEST_F(ScopedRoutesConfigProviderManagerTest, ConfigDump) { + auto message_ptr = + factory_context_.admin_.config_tracker_.config_tracker_callbacks_["route_scopes"](); + const auto& scoped_routes_config_dump = + MessageUtil::downcastAndValidate( + *message_ptr); + + // No routes at all, no last_updated timestamp + envoy::admin::v2alpha::ScopedRoutesConfigDump expected_config_dump; + MessageUtil::loadFromYaml(R"EOF( +inline_scoped_route_configs: +dynamic_scoped_route_configs: +)EOF", + expected_config_dump); + EXPECT_EQ(expected_config_dump.DebugString(), scoped_routes_config_dump.DebugString()); + + const std::string config_yaml = R"EOF( +name: foo +route_configuration_name: foo-route-config +key: + fragments: { string_key: "172.10.10.10" } +)EOF"; + const std::string config_yaml2 = R"EOF( +name: foo2 +route_configuration_name: foo-route-config2 +key: + fragments: { string_key: "172.10.10.20" } +)EOF"; + std::vector> config_protos = + protosToMessageVec({parseScopedRouteConfigurationFromYaml(config_yaml), + parseScopedRouteConfigurationFromYaml(config_yaml2)}); + + timeSystem().setSystemTime(std::chrono::milliseconds(1234567891234)); + + envoy::config::filter::network::http_connection_manager::v2 ::ScopedRoutes::ScopeKeyBuilder + scope_key_builder; + MessageUtil::loadFromYaml(R"EOF( +fragments: + - header_value_extractor: { name: X-Google-VIP } +)EOF", + scope_key_builder); + // Only load the inline scopes. + Envoy::Config::ConfigProviderPtr inline_config = + config_provider_manager_->createStaticConfigProvider( + std::move(config_protos), factory_context_, + ScopedRoutesConfigProviderManagerOptArg("foo-scoped-routes", rds_config_source_, + scope_key_builder)); + message_ptr = factory_context_.admin_.config_tracker_.config_tracker_callbacks_["route_scopes"](); + const auto& scoped_routes_config_dump2 = + MessageUtil::downcastAndValidate( + *message_ptr); + MessageUtil::loadFromYaml(R"EOF( +inline_scoped_route_configs: + - name: foo-scoped-routes + scoped_route_configs: + - name: foo + route_configuration_name: foo-route-config + key: + fragments: { string_key: "172.10.10.10" } + - name: foo2 + route_configuration_name: foo-route-config2 + key: + fragments: { string_key: "172.10.10.20" } + last_updated: + seconds: 1234567891 + nanos: 234000000 +dynamic_scoped_route_configs: +)EOF", + expected_config_dump); + EXPECT_EQ(expected_config_dump.DebugString(), scoped_routes_config_dump2.DebugString()); + + setupMockClusterMap(); + envoy::config::filter::network::http_connection_manager::v2::ScopedRds scoped_rds_config; + const std::string config_source_yaml = R"EOF( +scoped_rds_config_source: + api_config_source: + api_type: REST + cluster_names: + - foo_cluster + refresh_delay: { seconds: 1, nanos: 0 } +)EOF"; + MessageUtil::loadFromYaml(config_source_yaml, scoped_rds_config); + Envoy::Config::ConfigProviderPtr dynamic_provider = + config_provider_manager_->createXdsConfigProvider( + scoped_rds_config, factory_context_, "foo.", + ScopedRoutesConfigProviderManagerOptArg("foo-dynamic-scoped-routes", rds_config_source_, + scope_key_builder)); + + Protobuf::RepeatedPtrField resources; + resources.Add()->PackFrom(parseScopedRouteConfigurationFromYaml(R"EOF( +name: dynamic-foo +route_configuration_name: dynamic-foo-route-config +key: + fragments: { string_key: "172.30.30.10" } +)EOF")); + + timeSystem().setSystemTime(std::chrono::milliseconds(1234567891567)); + ScopedRdsConfigSubscription& subscription = + dynamic_cast(*dynamic_provider).subscription(); + subscription.onConfigUpdate(resources, "1"); + + MessageUtil::loadFromYaml(R"EOF( +inline_scoped_route_configs: + - name: foo-scoped-routes + scoped_route_configs: + - name: foo + route_configuration_name: foo-route-config + key: + fragments: { string_key: "172.10.10.10" } + - name: foo2 + route_configuration_name: foo-route-config2 + key: + fragments: { string_key: "172.10.10.20" } + last_updated: + seconds: 1234567891 + nanos: 234000000 +dynamic_scoped_route_configs: + - name: foo-dynamic-scoped-routes + scoped_route_configs: + - name: dynamic-foo + 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 = factory_context_.admin_.config_tracker_.config_tracker_callbacks_["route_scopes"](); + const auto& scoped_routes_config_dump3 = + MessageUtil::downcastAndValidate( + *message_ptr); + EXPECT_EQ(expected_config_dump.DebugString(), scoped_routes_config_dump3.DebugString()); + + resources.Clear(); + subscription.onConfigUpdate(resources, "2"); + MessageUtil::loadFromYaml(R"EOF( +inline_scoped_route_configs: + - name: foo-scoped-routes + scoped_route_configs: + - name: foo + route_configuration_name: foo-route-config + key: + fragments: { string_key: "172.10.10.10" } + - name: foo2 + route_configuration_name: foo-route-config2 + key: + fragments: { string_key: "172.10.10.20" } + last_updated: + seconds: 1234567891 + nanos: 234000000 +dynamic_scoped_route_configs: + - name: foo-dynamic-scoped-routes + last_updated: + seconds: 1234567891 + nanos: 567000000 + version_info: "2" +)EOF", + expected_config_dump); + message_ptr = factory_context_.admin_.config_tracker_.config_tracker_callbacks_["route_scopes"](); + const auto& scoped_routes_config_dump4 = + MessageUtil::downcastAndValidate( + *message_ptr); + EXPECT_EQ(expected_config_dump.DebugString(), scoped_routes_config_dump4.DebugString()); +} + +using ScopedRoutesConfigProviderManagerDeathTest = ScopedRoutesConfigProviderManagerTest; + +// Tests that SRDS only allows creation of delta static config providers. +TEST_F(ScopedRoutesConfigProviderManagerDeathTest, DeltaStaticConfigProviderOnly) { + // Use match all regex due to lack of distinctive matchable output for coverage test. + EXPECT_DEATH(config_provider_manager_->createStaticConfigProvider( + parseScopedRouteConfigurationFromYaml(R"EOF( +name: dynamic-foo +route_configuration_name: static-foo-route-config +key: + fragments: { string_key: "172.30.30.10" } +)EOF"), + factory_context_, Envoy::Config::ConfigProviderManager::NullOptionalArg()), + ".*"); +} + +} // namespace +} // namespace Router +} // namespace Envoy diff --git a/test/mocks/config/BUILD b/test/mocks/config/BUILD index 287ec6ebf11c2..90f9c88103188 100644 --- a/test/mocks/config/BUILD +++ b/test/mocks/config/BUILD @@ -13,6 +13,7 @@ envoy_cc_mock( srcs = ["mocks.cc"], hdrs = ["mocks.h"], deps = [ + "//include/envoy/config:config_provider_manager_interface", "//include/envoy/config:grpc_mux_interface", "//include/envoy/config:subscription_interface", "//include/envoy/config:xds_grpc_context_interface", diff --git a/test/mocks/config/mocks.h b/test/mocks/config/mocks.h index 5176655a65395..0d0c0df9df344 100644 --- a/test/mocks/config/mocks.h +++ b/test/mocks/config/mocks.h @@ -1,6 +1,7 @@ #pragma once #include "envoy/api/v2/eds.pb.h" +#include "envoy/config/config_provider_manager.h" #include "envoy/config/grpc_mux.h" #include "envoy/config/subscription.h" #include "envoy/config/xds_grpc_context.h" @@ -106,5 +107,26 @@ class MockMutableConfigProviderBase : public MutableConfigProviderBase { ConfigSubscriptionCommonBase& subscription() { return *subscription_.get(); } }; +class MockConfigProviderManager : public ConfigProviderManager { +public: + MockConfigProviderManager() = default; + ~MockConfigProviderManager() override = default; + + MOCK_METHOD4(createXdsConfigProvider, + ConfigProviderPtr(const Protobuf::Message& config_source_proto, + Server::Configuration::FactoryContext& factory_context, + const std::string& stat_prefix, + const Envoy::Config::ConfigProviderManager::OptionalArg& optarg)); + MOCK_METHOD3(createStaticConfigProvider, + ConfigProviderPtr(const Protobuf::Message& config_proto, + Server::Configuration::FactoryContext& factory_context, + const Envoy::Config::ConfigProviderManager::OptionalArg& optarg)); + MOCK_METHOD3( + createStaticConfigProvider, + ConfigProviderPtr(std::vector>&& config_protos, + Server::Configuration::FactoryContext& factory_context, + const Envoy::Config::ConfigProviderManager::OptionalArg& optarg)); +}; + } // namespace Config } // namespace Envoy diff --git a/test/mocks/router/BUILD b/test/mocks/router/BUILD index 76e2c77be171b..c6e3b9065f71e 100644 --- a/test/mocks/router/BUILD +++ b/test/mocks/router/BUILD @@ -19,6 +19,7 @@ envoy_cc_mock( "//include/envoy/router:route_config_provider_manager_interface", "//include/envoy/router:router_interface", "//include/envoy/router:router_ratelimit_interface", + "//include/envoy/router:scopes_interface", "//include/envoy/router:shadow_writer_interface", "//include/envoy/runtime:runtime_interface", "//include/envoy/stats:stats_interface", diff --git a/test/mocks/router/mocks.cc b/test/mocks/router/mocks.cc index 099d1095c7e70..31c8037701ff5 100644 --- a/test/mocks/router/mocks.cc +++ b/test/mocks/router/mocks.cc @@ -109,5 +109,8 @@ MockRoute::~MockRoute() {} MockRouteConfigProviderManager::MockRouteConfigProviderManager() {} MockRouteConfigProviderManager::~MockRouteConfigProviderManager() {} +MockScopedConfig::MockScopedConfig() {} +MockScopedConfig::~MockScopedConfig() {} + } // namespace Router } // namespace Envoy diff --git a/test/mocks/router/mocks.h b/test/mocks/router/mocks.h index c1bd440ef0de8..308da62a36c9e 100644 --- a/test/mocks/router/mocks.h +++ b/test/mocks/router/mocks.h @@ -16,6 +16,7 @@ #include "envoy/router/route_config_provider_manager.h" #include "envoy/router/router.h" #include "envoy/router/router_ratelimit.h" +#include "envoy/router/scopes.h" #include "envoy/router/shadow_writer.h" #include "envoy/runtime/runtime.h" #include "envoy/thread_local/thread_local.h" @@ -362,5 +363,13 @@ class MockRouteConfigProviderManager : public RouteConfigProviderManager { Server::Configuration::FactoryContext& factory_context)); }; +class MockScopedConfig : public ScopedConfig { +public: + MockScopedConfig(); + ~MockScopedConfig(); + + MOCK_CONST_METHOD1(getRouterConfig, ConfigConstSharedPtr(const Http::HeaderMap& headers)); +}; + } // namespace Router } // namespace Envoy diff --git a/tools/spelling_dictionary.txt b/tools/spelling_dictionary.txt index bf29fb00b1961..3c75af4f8f1f6 100644 --- a/tools/spelling_dictionary.txt +++ b/tools/spelling_dictionary.txt @@ -357,6 +357,7 @@ comparator cond condvar conf +configdump conn conns const @@ -787,6 +788,7 @@ usr util utils validator +validators vanishingly var variadic @@ -796,6 +798,7 @@ verifiers versa versioned vhost +vip vptr vptrs wakeup