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