Line data Source code
1 : #include "source/common/router/rds_impl.h"
2 :
3 : #include <chrono>
4 : #include <cstdint>
5 : #include <memory>
6 : #include <string>
7 :
8 : #include "envoy/admin/v3/config_dump.pb.h"
9 : #include "envoy/config/core/v3/config_source.pb.h"
10 : #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h"
11 : #include "envoy/service/discovery/v3/discovery.pb.h"
12 :
13 : #include "source/common/common/assert.h"
14 : #include "source/common/common/fmt.h"
15 : #include "source/common/config/api_version.h"
16 : #include "source/common/config/utility.h"
17 : #include "source/common/http/header_map_impl.h"
18 : #include "source/common/protobuf/utility.h"
19 : #include "source/common/router/config_impl.h"
20 : #include "source/common/router/route_config_update_receiver_impl.h"
21 :
22 : namespace Envoy {
23 : namespace Router {
24 :
25 : RouteConfigProviderSharedPtr RouteConfigProviderUtil::create(
26 : const envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
27 : config,
28 : Server::Configuration::ServerFactoryContext& factory_context,
29 : ProtobufMessage::ValidationVisitor& validator, Init::Manager& init_manager,
30 140 : const std::string& stat_prefix, RouteConfigProviderManager& route_config_provider_manager) {
31 :
32 140 : switch (config.route_specifier_case()) {
33 74 : case envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager::
34 74 : RouteSpecifierCase::kRouteConfig:
35 74 : return route_config_provider_manager.createStaticRouteConfigProvider(
36 74 : config.route_config(), factory_context, validator);
37 66 : case envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager::
38 66 : RouteSpecifierCase::kRds:
39 66 : return route_config_provider_manager.createRdsRouteConfigProvider(
40 : // At the creation of a RDS route config provider, the factory_context's initManager is
41 : // always valid, though the init manager may go away later when the listener goes away.
42 66 : config.rds(), factory_context, stat_prefix, init_manager);
43 0 : case envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager::
44 0 : RouteSpecifierCase::kScopedRoutes:
45 0 : FALLTHRU; // PANIC
46 0 : case envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager::
47 0 : RouteSpecifierCase::ROUTE_SPECIFIER_NOT_SET:
48 0 : PANIC("not implemented");
49 140 : }
50 0 : PANIC_DUE_TO_CORRUPT_ENUM;
51 0 : }
52 :
53 : StaticRouteConfigProviderImpl::StaticRouteConfigProviderImpl(
54 : const envoy::config::route::v3::RouteConfiguration& config, Rds::ConfigTraits& config_traits,
55 : Server::Configuration::ServerFactoryContext& factory_context,
56 : Rds::RouteConfigProviderManager& route_config_provider_manager)
57 : : base_(config, config_traits, factory_context, route_config_provider_manager),
58 74 : route_config_provider_manager_(route_config_provider_manager) {}
59 :
60 74 : StaticRouteConfigProviderImpl::~StaticRouteConfigProviderImpl() {
61 74 : route_config_provider_manager_.eraseStaticProvider(this);
62 74 : }
63 :
64 393 : ConfigConstSharedPtr StaticRouteConfigProviderImpl::configCast() const {
65 393 : ASSERT(dynamic_cast<const Config*>(StaticRouteConfigProviderImpl::config().get()));
66 393 : return std::static_pointer_cast<const Config>(StaticRouteConfigProviderImpl::config());
67 393 : }
68 :
69 : // TODO(htuch): If support for multiple clusters is added per #1170 cluster_name_
70 : RdsRouteConfigSubscription::RdsRouteConfigSubscription(
71 : RouteConfigUpdatePtr&& config_update,
72 : Envoy::Config::OpaqueResourceDecoderSharedPtr&& resource_decoder,
73 : const envoy::extensions::filters::network::http_connection_manager::v3::Rds& rds,
74 : const uint64_t manager_identifier, Server::Configuration::ServerFactoryContext& factory_context,
75 : const std::string& stat_prefix, Rds::RouteConfigProviderManager& route_config_provider_manager)
76 : : Rds::RdsRouteConfigSubscription(std::move(config_update), std::move(resource_decoder),
77 : rds.config_source(), rds.route_config_name(),
78 : manager_identifier, factory_context, stat_prefix + "rds.",
79 : "RDS", route_config_provider_manager),
80 : config_update_info_(static_cast<RouteConfigUpdateReceiver*>(
81 50 : Rds::RdsRouteConfigSubscription::config_update_info_.get())) {}
82 :
83 50 : RdsRouteConfigSubscription::~RdsRouteConfigSubscription() { config_update_info_.release(); }
84 :
85 : void RdsRouteConfigSubscription::beforeProviderUpdate(
86 36 : std::unique_ptr<Init::ManagerImpl>& noop_init_manager, std::unique_ptr<Cleanup>& resume_rds) {
87 36 : if (config_update_info_->protobufConfigurationCast().has_vhds() &&
88 36 : config_update_info_->vhdsConfigurationChanged()) {
89 0 : ENVOY_LOG(debug,
90 0 : "rds: vhds configuration present/changed, (re)starting vhds: config_name={} hash={}",
91 0 : route_config_name_, routeConfigUpdate()->configHash());
92 0 : ASSERT(config_update_info_->configInfo().has_value());
93 0 : maybeCreateInitManager(routeConfigUpdate()->configInfo().value().version_, noop_init_manager,
94 0 : resume_rds);
95 0 : vhds_subscription_ = std::make_unique<VhdsSubscription>(config_update_info_, factory_context_,
96 0 : stat_prefix_, route_config_provider_);
97 0 : vhds_subscription_->registerInitTargetWithInitManager(
98 0 : noop_init_manager == nullptr ? local_init_manager_ : *noop_init_manager);
99 0 : }
100 36 : }
101 :
102 36 : void RdsRouteConfigSubscription::afterProviderUpdate() {
103 : // RDS update removed VHDS configuration
104 36 : if (!config_update_info_->protobufConfigurationCast().has_vhds()) {
105 36 : vhds_subscription_.release();
106 36 : }
107 :
108 36 : update_callback_manager_.runCallbacks();
109 36 : }
110 :
111 : // Initialize a no-op InitManager in case the one in the factory_context has completed
112 : // initialization. This can happen if an RDS config update for an already established RDS
113 : // subscription contains VHDS configuration.
114 : void RdsRouteConfigSubscription::maybeCreateInitManager(
115 : const std::string& version_info, std::unique_ptr<Init::ManagerImpl>& init_manager,
116 0 : std::unique_ptr<Cleanup>& init_vhds) {
117 0 : if (local_init_manager_.state() == Init::Manager::State::Initialized) {
118 0 : init_manager = std::make_unique<Init::ManagerImpl>(
119 0 : fmt::format("VHDS {}:{}", route_config_name_, version_info));
120 0 : init_vhds = std::make_unique<Cleanup>([this, &init_manager, version_info] {
121 : // For new RDS subscriptions created after listener warming up, we don't wait for them to warm
122 : // up.
123 0 : Init::WatcherImpl noop_watcher(
124 : // Note: we just throw it away.
125 0 : fmt::format("VHDS ConfigUpdate watcher {}:{}", route_config_name_, version_info),
126 0 : []() { /*Do nothing.*/ });
127 0 : init_manager->initialize(noop_watcher);
128 0 : });
129 0 : }
130 0 : }
131 :
132 0 : void RdsRouteConfigSubscription::updateOnDemand(const std::string& aliases) {
133 0 : if (vhds_subscription_.get() == nullptr) {
134 0 : return;
135 0 : }
136 0 : vhds_subscription_->updateOnDemand(aliases);
137 0 : }
138 :
139 : RdsRouteConfigProviderImpl::RdsRouteConfigProviderImpl(
140 : RdsRouteConfigSubscriptionSharedPtr&& subscription,
141 : Server::Configuration::ServerFactoryContext& factory_context)
142 : : base_(subscription, factory_context), config_update_info_(subscription->routeConfigUpdate()),
143 50 : factory_context_(factory_context) {
144 : // The subscription referenced by the 'base_' and by 'this' is the same.
145 : // In it the provider is already set by the 'base_' so it points to that.
146 : // Need to set again to point to 'this'.
147 50 : base_.subscription().routeConfigProvider() = this;
148 50 : }
149 :
150 50 : RdsRouteConfigSubscription& RdsRouteConfigProviderImpl::subscription() {
151 50 : return static_cast<RdsRouteConfigSubscription&>(base_.subscription());
152 50 : }
153 :
154 36 : absl::Status RdsRouteConfigProviderImpl::onConfigUpdate() {
155 36 : auto status = base_.onConfigUpdate();
156 36 : if (!status.ok()) {
157 0 : return status;
158 0 : }
159 :
160 36 : const auto aliases = config_update_info_->resourceIdsInLastVhdsUpdate();
161 : // Regular (non-VHDS) RDS updates don't populate aliases fields in resources.
162 36 : if (aliases.empty()) {
163 36 : return absl::OkStatus();
164 36 : }
165 :
166 0 : const auto config =
167 0 : std::static_pointer_cast<const ConfigImpl>(config_update_info_->parsedConfiguration());
168 : // Notifies connections that RouteConfiguration update has been propagated.
169 : // Callbacks processing is performed in FIFO order. The callback is skipped if alias used in
170 : // the VHDS update request do not match the aliases in the update response
171 0 : for (auto it = config_update_callbacks_.begin(); it != config_update_callbacks_.end();) {
172 0 : auto found = aliases.find(it->alias_);
173 0 : if (found != aliases.end()) {
174 : // TODO(dmitri-d) HeaderMapImpl is expensive, need to profile this
175 0 : auto host_header = Http::RequestHeaderMapImpl::create();
176 0 : host_header->setHost(VhdsSubscription::aliasToDomainName(it->alias_));
177 0 : const bool host_exists = config->virtualHostExists(*host_header);
178 0 : std::weak_ptr<Http::RouteConfigUpdatedCallback> current_cb(it->cb_);
179 0 : it->thread_local_dispatcher_.post([current_cb, host_exists] {
180 0 : if (auto cb = current_cb.lock()) {
181 0 : (*cb)(host_exists);
182 0 : }
183 0 : });
184 0 : it = config_update_callbacks_.erase(it);
185 0 : } else {
186 0 : it++;
187 0 : }
188 0 : }
189 0 : return absl::OkStatus();
190 36 : }
191 :
192 0 : ConfigConstSharedPtr RdsRouteConfigProviderImpl::configCast() const {
193 0 : ASSERT(dynamic_cast<const Config*>(RdsRouteConfigProviderImpl::config().get()));
194 0 : return std::static_pointer_cast<const Config>(RdsRouteConfigProviderImpl::config());
195 0 : }
196 :
197 : // Schedules a VHDS request on the main thread and queues up the callback to use when the VHDS
198 : // response has been propagated to the worker thread that was the request origin.
199 : void RdsRouteConfigProviderImpl::requestVirtualHostsUpdate(
200 : const std::string& for_domain, Event::Dispatcher& thread_local_dispatcher,
201 0 : std::weak_ptr<Http::RouteConfigUpdatedCallback> route_config_updated_cb) {
202 0 : auto alias = VhdsSubscription::domainNameToAlias(
203 0 : config_update_info_->protobufConfigurationCast().name(), for_domain);
204 : // The RdsRouteConfigProviderImpl instance can go away before the dispatcher has a chance to
205 : // execute the callback. still_alive shared_ptr will be deallocated when the current instance of
206 : // the RdsRouteConfigProviderImpl is deallocated; we rely on a weak_ptr to still_alive flag to
207 : // determine if the RdsRouteConfigProviderImpl instance is still valid.
208 0 : factory_context_.mainThreadDispatcher().post([this,
209 0 : maybe_still_alive =
210 0 : std::weak_ptr<bool>(still_alive_),
211 0 : alias, &thread_local_dispatcher,
212 0 : route_config_updated_cb]() -> void {
213 0 : if (maybe_still_alive.lock()) {
214 0 : subscription().updateOnDemand(alias);
215 0 : config_update_callbacks_.push_back({alias, thread_local_dispatcher, route_config_updated_cb});
216 0 : }
217 0 : });
218 0 : }
219 :
220 : RouteConfigProviderManagerImpl::RouteConfigProviderManagerImpl(OptRef<Server::Admin> admin)
221 96 : : manager_(admin, "routes", proto_traits_) {}
222 :
223 : Router::RouteConfigProviderSharedPtr RouteConfigProviderManagerImpl::createRdsRouteConfigProvider(
224 : const envoy::extensions::filters::network::http_connection_manager::v3::Rds& rds,
225 : Server::Configuration::ServerFactoryContext& factory_context, const std::string& stat_prefix,
226 66 : Init::Manager& init_manager) {
227 66 : auto provider = manager_.addDynamicProvider(
228 66 : rds, rds.route_config_name(), init_manager,
229 66 : [&factory_context, &rds, &stat_prefix, this](uint64_t manager_identifier) {
230 50 : auto config_update =
231 50 : std::make_unique<RouteConfigUpdateReceiverImpl>(proto_traits_, factory_context);
232 50 : auto resource_decoder = std::make_shared<
233 50 : Envoy::Config::OpaqueResourceDecoderImpl<envoy::config::route::v3::RouteConfiguration>>(
234 50 : factory_context.messageValidationContext().dynamicValidationVisitor(), "name");
235 50 : auto subscription = std::make_shared<RdsRouteConfigSubscription>(
236 50 : std::move(config_update), std::move(resource_decoder), rds, manager_identifier,
237 50 : factory_context, stat_prefix, manager_);
238 50 : auto provider =
239 50 : std::make_shared<RdsRouteConfigProviderImpl>(std::move(subscription), factory_context);
240 50 : return std::make_pair(provider, &provider->subscription().initTarget());
241 50 : });
242 66 : ASSERT(dynamic_cast<RouteConfigProvider*>(provider.get()));
243 66 : return std::static_pointer_cast<RouteConfigProvider>(provider);
244 66 : }
245 :
246 : RouteConfigProviderPtr RouteConfigProviderManagerImpl::createStaticRouteConfigProvider(
247 : const envoy::config::route::v3::RouteConfiguration& route_config,
248 : Server::Configuration::ServerFactoryContext& factory_context,
249 74 : ProtobufMessage::ValidationVisitor& validator) {
250 74 : auto provider = manager_.addStaticProvider([&factory_context, &validator, &route_config, this]() {
251 74 : ConfigTraitsImpl config_traits(validator);
252 74 : return std::make_unique<StaticRouteConfigProviderImpl>(route_config, config_traits,
253 74 : factory_context, manager_);
254 74 : });
255 74 : ASSERT(dynamic_cast<RouteConfigProvider*>(provider.get()));
256 74 : return RouteConfigProviderPtr(static_cast<RouteConfigProvider*>(provider.release()));
257 74 : }
258 :
259 : } // namespace Router
260 : } // namespace Envoy
|