Line data Source code
1 : #pragma once
2 :
3 : #include <memory>
4 : #include <string>
5 :
6 : #include "envoy/common/callback.h"
7 : #include "envoy/config/core/v3/config_source.pb.h"
8 : #include "envoy/config/route/v3/scoped_route.pb.h"
9 : #include "envoy/config/route/v3/scoped_route.pb.validate.h"
10 : #include "envoy/config/subscription.h"
11 : #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h"
12 : #include "envoy/router/route_config_provider_manager.h"
13 : #include "envoy/router/scopes.h"
14 : #include "envoy/service/discovery/v3/discovery.pb.h"
15 : #include "envoy/stats/scope.h"
16 :
17 : #include "source/common/config/config_provider_impl.h"
18 : #include "source/common/config/subscription_base.h"
19 : #include "source/common/init/manager_impl.h"
20 : #include "source/common/router/rds_impl.h"
21 : #include "source/common/router/scoped_config_impl.h"
22 :
23 : namespace Envoy {
24 : namespace Router {
25 :
26 : // Scoped routing configuration utilities.
27 : namespace ScopedRoutesConfigProviderUtil {
28 :
29 : // If enabled in the HttpConnectionManager config, returns a ConfigProvider for scoped routing
30 : // configuration.
31 : Envoy::Config::ConfigProviderPtr create(
32 : const envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
33 : config,
34 : Server::Configuration::ServerFactoryContext& factory_context, Init::Manager& init_manager,
35 : const std::string& stat_prefix,
36 : Envoy::Config::ConfigProviderManager& scoped_routes_config_provider_manager);
37 :
38 : // If enabled in the HttpConnectionManager config, returns a ConfigProvider for scoped routing
39 : // configuration.
40 : ScopeKeyBuilderPtr createScopeKeyBuilder(
41 : const envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager&
42 : config);
43 :
44 : } // namespace ScopedRoutesConfigProviderUtil
45 :
46 : class ScopedRoutesConfigProviderManager;
47 :
48 : // A ConfigProvider for inline scoped routing configuration.
49 : // InlineScopedRoutesConfigProvider is not fully implemented at this point. It doesn't load
50 : // ScopedRouteConfigurations and propagate them to worker threads. If
51 : // InlineScopedRoutesConfigProvider is fully implemented, when it is loading
52 : // ScopedRouteConfiguration, the on demand field should be ignored and all scopes should be loaded
53 : // eagerly.
54 : class InlineScopedRoutesConfigProvider : public Envoy::Config::ImmutableConfigProviderBase {
55 : public:
56 : InlineScopedRoutesConfigProvider(ProtobufTypes::ConstMessagePtrVector&& config_protos,
57 : std::string name,
58 : Server::Configuration::ServerFactoryContext& factory_context,
59 : ScopedRoutesConfigProviderManager& config_provider_manager,
60 : envoy::config::core::v3::ConfigSource rds_config_source);
61 :
62 0 : ~InlineScopedRoutesConfigProvider() override = default;
63 :
64 0 : const std::string& name() const { return name_; }
65 :
66 : // Envoy::Config::ConfigProvider
67 0 : Envoy::Config::ConfigProvider::ConfigProtoVector getConfigProtos() const override {
68 0 : Envoy::Config::ConfigProvider::ConfigProtoVector out_protos;
69 0 : std::for_each(scopes_.begin(), scopes_.end(),
70 0 : [&out_protos](const ScopedRouteInfoConstSharedPtr& scope) {
71 0 : out_protos.push_back(&scope->configProto());
72 0 : });
73 0 : return out_protos;
74 0 : }
75 :
76 0 : std::string getConfigVersion() const override { return ""; }
77 0 : ConfigConstSharedPtr getConfig() const override { return config_; }
78 :
79 : private:
80 : const std::string name_;
81 : const std::vector<ScopedRouteInfoConstSharedPtr> scopes_;
82 : ConfigConstSharedPtr config_;
83 : const envoy::config::core::v3::ConfigSource rds_config_source_;
84 : };
85 :
86 : /**
87 : * All SRDS stats. @see stats_macros.h
88 : */
89 : // clang-format off
90 : #define ALL_SCOPED_RDS_STATS(COUNTER, GAUGE) \
91 : COUNTER(config_reload) \
92 : COUNTER(update_empty) \
93 : GAUGE(all_scopes, Accumulate) \
94 : GAUGE(config_reload_time_ms, NeverImport) \
95 : GAUGE(on_demand_scopes, Accumulate) \
96 : GAUGE(active_scopes, Accumulate)
97 :
98 : // clang-format on
99 :
100 : struct ScopedRdsStats {
101 : ALL_SCOPED_RDS_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT)
102 :
103 0 : static ScopedRdsStats generateStats(const std::string& prefix, Stats::Scope& scope) {
104 0 : return ScopedRdsStats{
105 0 : ALL_SCOPED_RDS_STATS(POOL_COUNTER_PREFIX(scope, prefix), POOL_GAUGE_PREFIX(scope, prefix))};
106 0 : }
107 : };
108 :
109 : // A scoped RDS subscription to be used with the dynamic scoped RDS ConfigProvider.
110 : class ScopedRdsConfigSubscription
111 : : public Envoy::Config::DeltaConfigSubscriptionInstance,
112 : public Envoy::Config::SubscriptionBase<envoy::config::route::v3::ScopedRouteConfiguration> {
113 : public:
114 : using ScopedRouteConfigurationMap =
115 : std::map<std::string, envoy::config::route::v3::ScopedRouteConfiguration>;
116 :
117 : ScopedRdsConfigSubscription(
118 : const envoy::extensions::filters::network::http_connection_manager::v3::ScopedRds& scoped_rds,
119 : const uint64_t manager_identifier, const std::string& name,
120 : Server::Configuration::ServerFactoryContext& factory_context, const std::string& stat_prefix,
121 : envoy::config::core::v3::ConfigSource rds_config_source,
122 : RouteConfigProviderManager& route_config_provider_manager,
123 : ScopedRoutesConfigProviderManager& config_provider_manager);
124 :
125 0 : ~ScopedRdsConfigSubscription() override = default;
126 :
127 0 : const std::string& name() const { return name_; }
128 :
129 0 : const ScopedRouteMap& scopedRouteMap() const { return scoped_route_map_; }
130 :
131 : void
132 : onDemandRdsUpdate(std::shared_ptr<Router::ScopeKey> scope_key,
133 : Event::Dispatcher& thread_local_dispatcher,
134 : Http::RouteConfigUpdatedCallback&& route_config_updated_cb,
135 : std::weak_ptr<Envoy::Config::ConfigSubscriptionCommonBase> weak_subscription);
136 :
137 : private:
138 : // A helper class that takes care of the life cycle management of a RDS route provider and the
139 : // update callback handle.
140 : struct RdsRouteConfigProviderHelper {
141 : RdsRouteConfigProviderHelper(
142 : ScopedRdsConfigSubscription& parent, std::string scope_name,
143 : envoy::extensions::filters::network::http_connection_manager::v3::Rds& rds,
144 : Init::Manager& init_manager);
145 :
146 : RdsRouteConfigProviderHelper(ScopedRdsConfigSubscription& parent, std::string scope_name);
147 :
148 0 : ~RdsRouteConfigProviderHelper() {
149 : // Only remove the rds update when the rds provider has been initialized.
150 0 : if (route_provider_) {
151 0 : parent_.stats_.active_scopes_.dec();
152 0 : }
153 0 : if (on_demand_) {
154 0 : parent_.stats_.on_demand_scopes_.dec();
155 0 : }
156 0 : }
157 0 : ConfigConstSharedPtr routeConfig() { return route_provider_->configCast(); }
158 :
159 : void addOnDemandUpdateCallback(std::function<void()> callback);
160 :
161 : // Runs all the callback from worker thread to continue filter chain.
162 : void runOnDemandUpdateCallback();
163 :
164 : // If route provider has not been initialized, initialize it.
165 : void maybeInitRdsConfigProvider();
166 :
167 : // Initialize route provider and register for rds update.
168 : void initRdsConfigProvider(
169 : envoy::extensions::filters::network::http_connection_manager::v3::Rds& rds,
170 : Init::Manager& init_manager);
171 :
172 : ScopedRdsConfigSubscription& parent_;
173 : std::string scope_name_;
174 : bool on_demand_;
175 : RdsRouteConfigProviderImplSharedPtr route_provider_;
176 : // This handle_ is owned by the route config provider's RDS subscription, when the helper
177 : // destructs, the handle is deleted as well.
178 : Common::CallbackHandlePtr rds_update_callback_handle_;
179 : std::vector<std::function<void()>> on_demand_update_callbacks_;
180 : };
181 :
182 : using RdsRouteConfigProviderHelperPtr = std::unique_ptr<RdsRouteConfigProviderHelper>;
183 :
184 : // Adds or updates scopes, create a new RDS provider for each resource, if an exception is thrown
185 : // during updating, the exception message is collected via the exception messages vector.
186 : // Returns a failed status if the operation was unsuccessful. If successful,
187 : // returns a boolean indicating if any scopes were applied.
188 : absl::StatusOr<bool>
189 : addOrUpdateScopes(const std::vector<Envoy::Config::DecodedResourceRef>& resources,
190 : Init::Manager& init_manager, const std::string& version_info);
191 : // Removes given scopes from the managed set of scopes.
192 : // Returns a list of to be removed helpers which is temporally held in the onConfigUpdate method,
193 : // to make sure new scopes sharing the same RDS source configs could reuse the subscriptions.
194 : std::list<RdsRouteConfigProviderHelperPtr>
195 : removeScopes(const Protobuf::RepeatedPtrField<std::string>& scope_names,
196 : const std::string& version_info);
197 :
198 : // Envoy::Config::DeltaConfigSubscriptionInstance
199 0 : void start() override { subscription_->start({}); }
200 :
201 : // Detect scope name and scope key conflict between added scopes or between added scopes and old
202 : // scopes. Some removed scopes may be in added resources list, instead of being removed, they
203 : // should be updated, so only return scope names that will disappear after update. If conflict
204 : // detected, fill exception_msg with information about scope conflict and return.
205 : Protobuf::RepeatedPtrField<std::string> detectUpdateConflictAndCleanupRemoved(
206 : const std::vector<Envoy::Config::DecodedResourceRef>& added_resources,
207 : const Protobuf::RepeatedPtrField<std::string>& removed_resources, std::string& exception_msg);
208 :
209 : // Envoy::Config::SubscriptionCallbacks
210 :
211 : // NOTE: both delta form and state-of-the-world form onConfigUpdate(resources, version_info) will
212 : // throw an EnvoyException or return failure on any error and essentially reject an update.
213 : absl::Status onConfigUpdate(const std::vector<Envoy::Config::DecodedResourceRef>& resources,
214 : const std::string& version_info) override;
215 : absl::Status onConfigUpdate(const std::vector<Envoy::Config::DecodedResourceRef>& added_resources,
216 : const Protobuf::RepeatedPtrField<std::string>& removed_resources,
217 : const std::string& system_version_info) override;
218 : void onConfigUpdateFailed(Envoy::Config::ConfigUpdateFailureReason reason,
219 0 : const EnvoyException*) override {
220 0 : ASSERT(Envoy::Config::ConfigUpdateFailureReason::ConnectionFailure != reason);
221 0 : DeltaConfigSubscriptionInstance::onConfigUpdateFailed();
222 0 : }
223 : // Propagate RDS updates to ScopeConfigImpl in workers.
224 : void onRdsConfigUpdate(const std::string& scope_name, ConfigConstSharedPtr new_rds_config);
225 :
226 : // ScopedRouteInfo by scope name.
227 : ScopedRouteMap scoped_route_map_;
228 :
229 : // For creating RDS subscriptions.
230 : Server::Configuration::ServerFactoryContext& factory_context_;
231 : const std::string name_;
232 : // Stats must outlive subscription.
233 : Stats::ScopeSharedPtr scope_;
234 : ScopedRdsStats stats_;
235 : Envoy::Config::SubscriptionPtr subscription_;
236 : const envoy::config::core::v3::ConfigSource rds_config_source_;
237 : const std::string stat_prefix_;
238 : RouteConfigProviderManager& route_config_provider_manager_;
239 :
240 : // RdsRouteConfigProvider by scope name.
241 : absl::flat_hash_map<std::string, RdsRouteConfigProviderHelperPtr> route_provider_by_scope_;
242 : // A map of (hash, scope-name), used to detect the key conflict between scopes.
243 : absl::flat_hash_map<uint64_t, std::string> scope_name_by_hash_;
244 : };
245 :
246 : using ScopedRdsConfigSubscriptionSharedPtr = std::shared_ptr<ScopedRdsConfigSubscription>;
247 :
248 : // A ConfigProvider for scoped RDS that dynamically fetches scoped routing configuration via a
249 : // subscription.
250 : class ScopedRdsConfigProvider : public Envoy::Config::MutableConfigProviderCommonBase {
251 : public:
252 : ScopedRdsConfigProvider(ScopedRdsConfigSubscriptionSharedPtr&& subscription);
253 :
254 0 : ScopedRdsConfigSubscription& subscription() const {
255 0 : return *static_cast<ScopedRdsConfigSubscription*>(subscription_.get());
256 0 : }
257 : void onDemandRdsUpdate(std::shared_ptr<Router::ScopeKey> scope_key,
258 : Event::Dispatcher& thread_local_dispatcher,
259 0 : Http::RouteConfigUpdatedCallback&& route_config_updated_cb) const {
260 0 : subscription().onDemandRdsUpdate(
261 0 : std::move(scope_key), thread_local_dispatcher, std::move(route_config_updated_cb),
262 0 : std::weak_ptr<Envoy::Config::ConfigSubscriptionCommonBase>(subscription_));
263 0 : }
264 : };
265 :
266 : // A ConfigProviderManager for scoped routing configuration that creates static/inline and dynamic
267 : // (xds) config providers.
268 : class ScopedRoutesConfigProviderManager : public Envoy::Config::ConfigProviderManagerImplBase {
269 : public:
270 : ScopedRoutesConfigProviderManager(
271 : OptRef<Server::Admin> admin,
272 : Router::RouteConfigProviderManager& route_config_provider_manager)
273 : : Envoy::Config::ConfigProviderManagerImplBase(admin, "route_scopes"),
274 96 : route_config_provider_manager_(route_config_provider_manager) {}
275 :
276 96 : ~ScopedRoutesConfigProviderManager() override = default;
277 :
278 : // Envoy::Config::ConfigProviderManagerImplBase
279 : ProtobufTypes::MessagePtr dumpConfigs(const Matchers::StringMatcher& name_matcher) const override;
280 :
281 : // Envoy::Config::ConfigProviderManager
282 : Envoy::Config::ConfigProviderPtr
283 : createXdsConfigProvider(const Protobuf::Message& config_source_proto,
284 : Server::Configuration::ServerFactoryContext& factory_context,
285 : Init::Manager& init_manager, const std::string& stat_prefix,
286 : const Envoy::Config::ConfigProviderManager::OptionalArg& optarg) override;
287 : Envoy::Config::ConfigProviderPtr
288 : createStaticConfigProvider(const Protobuf::Message&, Server::Configuration::ServerFactoryContext&,
289 0 : const Envoy::Config::ConfigProviderManager::OptionalArg&) override {
290 0 : PANIC("SRDS supports delta updates and requires the use of the createStaticConfigProvider() "
291 0 : "overload that accepts a config proto set as an argument.");
292 0 : }
293 : Envoy::Config::ConfigProviderPtr createStaticConfigProvider(
294 : std::vector<std::unique_ptr<const Protobuf::Message>>&& config_protos,
295 : Server::Configuration::ServerFactoryContext& factory_context,
296 : const Envoy::Config::ConfigProviderManager::OptionalArg& optarg) override;
297 :
298 0 : RouteConfigProviderManager& routeConfigProviderManager() {
299 0 : return route_config_provider_manager_;
300 0 : }
301 :
302 : private:
303 : RouteConfigProviderManager& route_config_provider_manager_;
304 : };
305 :
306 : using ScopedRoutesConfigProviderManagerPtr = std::unique_ptr<ScopedRoutesConfigProviderManager>;
307 : using ScopedRoutesConfigProviderManagerSharedPtr =
308 : std::shared_ptr<ScopedRoutesConfigProviderManager>;
309 :
310 : // The optional argument passed to the ConfigProviderManager::create*() functions.
311 : class ScopedRoutesConfigProviderManagerOptArg
312 : : public Envoy::Config::ConfigProviderManager::OptionalArg {
313 : public:
314 : ScopedRoutesConfigProviderManagerOptArg(
315 : std::string scoped_routes_name,
316 : const envoy::config::core::v3::ConfigSource& rds_config_source)
317 0 : : scoped_routes_name_(std::move(scoped_routes_name)), rds_config_source_(rds_config_source) {}
318 :
319 : const std::string scoped_routes_name_;
320 : const envoy::config::core::v3::ConfigSource& rds_config_source_;
321 : };
322 :
323 : } // namespace Router
324 : } // namespace Envoy
|