1
#include "source/common/router/route_config_update_receiver_impl.h"
2

            
3
#include <string>
4
#include <utility>
5

            
6
#include "envoy/config/route/v3/route.pb.h"
7
#include "envoy/service/discovery/v3/discovery.pb.h"
8

            
9
#include "source/common/common/assert.h"
10
#include "source/common/common/fmt.h"
11
#include "source/common/common/thread.h"
12
#include "source/common/config/resource_name.h"
13
#include "source/common/protobuf/utility.h"
14
#include "source/common/router/config_impl.h"
15

            
16
namespace Envoy {
17
namespace Router {
18

            
19
namespace {
20

            
21
// Resets 'route_config::virtual_hosts' by merging VirtualHost contained in
22
// 'rds_vhosts' and 'vhds_vhosts'.
23
void rebuildRouteConfigVirtualHosts(
24
    const RouteConfigUpdateReceiverImpl::VirtualHostMap& rds_vhosts,
25
    const RouteConfigUpdateReceiverImpl::VirtualHostMap& vhds_vhosts,
26
139
    envoy::config::route::v3::RouteConfiguration& route_config) {
27
139
  route_config.clear_virtual_hosts();
28
139
  for (const auto& vhost : rds_vhosts) {
29
71
    route_config.mutable_virtual_hosts()->Add()->CheckTypeAndMergeFrom(vhost.second);
30
71
  }
31
213
  for (const auto& vhost : vhds_vhosts) {
32
213
    route_config.mutable_virtual_hosts()->Add()->CheckTypeAndMergeFrom(vhost.second);
33
213
  }
34
139
}
35

            
36
} // namespace
37

            
38
403
Rds::ConfigConstSharedPtr ConfigTraitsImpl::createNullConfig() const {
39
403
  return std::make_shared<NullConfigImpl>();
40
403
}
41

            
42
Rds::ConfigConstSharedPtr
43
ConfigTraitsImpl::createConfig(const Protobuf::Message& rc,
44
                               Server::Configuration::ServerFactoryContext& factory_context,
45
9956
                               bool validate_clusters_default) const {
46
9956
  ASSERT(dynamic_cast<const envoy::config::route::v3::RouteConfiguration*>(&rc));
47
9956
  return THROW_OR_RETURN_VALUE(
48
9956
      ConfigImpl::create(static_cast<const envoy::config::route::v3::RouteConfiguration&>(rc),
49
9956
                         factory_context, validator_, validate_clusters_default),
50
9956
      std::shared_ptr<ConfigImpl>);
51
9956
}
52

            
53
bool RouteConfigUpdateReceiverImpl::onRdsUpdate(const Protobuf::Message& rc,
54
471
                                                const std::string& version_info) {
55
471
  uint64_t new_hash = base_.getHash(rc);
56
471
  if (!base_.checkHash(new_hash)) {
57
52
    return false;
58
52
  }
59
419
  auto new_route_config = std::make_unique<envoy::config::route::v3::RouteConfiguration>();
60
419
  new_route_config->CheckTypeAndMergeFrom(rc);
61
419
  const uint64_t new_vhds_config_hash =
62
419
      new_route_config->has_vhds() ? MessageUtil::hash(new_route_config->vhds()) : 0ul;
63
419
  if (new_route_config->has_vhds()) {
64
    // When using VHDS, stash away RDS vhosts, so that they can be merged with VHDS vhosts in
65
    // onVhdsUpdate.
66
78
    if (rds_virtual_hosts_ == nullptr) {
67
73
      rds_virtual_hosts_ = std::make_unique<VirtualHostMap>();
68
73
    } else {
69
5
      rds_virtual_hosts_->clear();
70
5
    }
71
78
    for (const auto& vhost : new_route_config->virtual_hosts()) {
72
36
      rds_virtual_hosts_->emplace(vhost.name(), vhost);
73
36
    }
74
78
    if (vhds_virtual_hosts_ != nullptr && !vhds_virtual_hosts_->empty()) {
75
      // If there are vhosts supplied by VHDS, merge them with RDS vhosts.
76
5
      rebuildRouteConfigVirtualHosts(*rds_virtual_hosts_, *vhds_virtual_hosts_, *new_route_config);
77
5
    }
78
78
  }
79
419
  base_.updateConfig(std::move(new_route_config));
80
419
  base_.updateHash(new_hash);
81
419
  vhds_configuration_changed_ = new_vhds_config_hash != last_vhds_config_hash_;
82
419
  last_vhds_config_hash_ = new_vhds_config_hash;
83

            
84
419
  base_.onUpdateCommon(version_info);
85
419
  return true;
86
471
}
87

            
88
bool RouteConfigUpdateReceiverImpl::onVhdsUpdate(
89
    const VirtualHostRefVector& added_vhosts, std::set<std::string>&& added_resource_ids,
90
    const Protobuf::RepeatedPtrField<std::string>& removed_resources,
91
134
    const std::string& version_info) {
92
134
  std::unique_ptr<VirtualHostMap> vhosts_after_this_update;
93
134
  if (vhds_virtual_hosts_ != nullptr) {
94
68
    vhosts_after_this_update = std::make_unique<VirtualHostMap>(*vhds_virtual_hosts_);
95
82
  } else {
96
66
    vhosts_after_this_update = std::make_unique<VirtualHostMap>();
97
66
  }
98
134
  if (rds_virtual_hosts_ == nullptr) {
99
    rds_virtual_hosts_ = std::make_unique<VirtualHostMap>();
100
  }
101
134
  const bool removed = removeVhosts(*vhosts_after_this_update, removed_resources);
102
134
  const bool updated = updateVhosts(*vhosts_after_this_update, added_vhosts);
103

            
104
134
  auto route_config_after_this_update =
105
134
      std::make_unique<envoy::config::route::v3::RouteConfiguration>();
106
134
  route_config_after_this_update->CheckTypeAndMergeFrom(base_.protobufConfiguration());
107
134
  rebuildRouteConfigVirtualHosts(*rds_virtual_hosts_, *vhosts_after_this_update,
108
134
                                 *route_config_after_this_update);
109

            
110
134
  base_.updateConfig(std::move(route_config_after_this_update));
111
  // No exception, route_config_after_this_update is valid, can update the state.
112
134
  vhds_virtual_hosts_ = std::move(vhosts_after_this_update);
113
134
  resource_ids_in_last_update_ = std::move(added_resource_ids);
114
134
  base_.onUpdateCommon(version_info);
115

            
116
134
  return removed || updated || !resource_ids_in_last_update_.empty();
117
134
}
118

            
119
bool RouteConfigUpdateReceiverImpl::removeVhosts(
120
134
    VirtualHostMap& vhosts, const Protobuf::RepeatedPtrField<std::string>& removed_vhost_names) {
121
134
  bool vhosts_removed = false;
122
134
  for (const auto& vhost_name : removed_vhost_names) {
123
18
    auto found = vhosts.find(vhost_name);
124
18
    if (found != vhosts.end()) {
125
18
      vhosts_removed = true;
126
18
      vhosts.erase(vhost_name);
127
18
    }
128
18
  }
129
134
  return vhosts_removed;
130
134
}
131

            
132
bool RouteConfigUpdateReceiverImpl::updateVhosts(VirtualHostMap& vhosts,
133
134
                                                 const VirtualHostRefVector& added_vhosts) {
134
134
  bool vhosts_added = false;
135
138
  for (const auto& vhost : added_vhosts) {
136
138
    auto found = vhosts.find(vhost.get().name());
137
138
    if (found != vhosts.end()) {
138
10
      vhosts.erase(found);
139
10
    }
140
138
    vhosts.emplace(vhost.get().name(), vhost.get());
141
138
    vhosts_added = true;
142
138
  }
143
134
  return vhosts_added;
144
134
}
145

            
146
} // namespace Router
147
} // namespace Envoy