Coverage Report

Created: 2023-11-12 09:30

/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