Line data Source code
1 : #include "source/extensions/clusters/strict_dns/strict_dns_cluster.h"
2 :
3 : #include "envoy/common/exception.h"
4 : #include "envoy/config/cluster/v3/cluster.pb.h"
5 : #include "envoy/config/endpoint/v3/endpoint.pb.h"
6 : #include "envoy/config/endpoint/v3/endpoint_components.pb.h"
7 :
8 : namespace Envoy {
9 : namespace Upstream {
10 :
11 : StrictDnsClusterImpl::StrictDnsClusterImpl(const envoy::config::cluster::v3::Cluster& cluster,
12 : ClusterFactoryContext& context,
13 : Network::DnsResolverSharedPtr dns_resolver)
14 : : BaseDynamicClusterImpl(cluster, context), load_assignment_(cluster.load_assignment()),
15 : local_info_(context.serverFactoryContext().localInfo()), dns_resolver_(dns_resolver),
16 : dns_refresh_rate_ms_(
17 : std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(cluster, dns_refresh_rate, 5000))),
18 0 : respect_dns_ttl_(cluster.respect_dns_ttl()) {
19 0 : failure_backoff_strategy_ =
20 0 : Config::Utility::prepareDnsRefreshStrategy<envoy::config::cluster::v3::Cluster>(
21 0 : cluster, dns_refresh_rate_ms_.count(),
22 0 : context.serverFactoryContext().api().randomGenerator());
23 :
24 0 : std::list<ResolveTargetPtr> resolve_targets;
25 0 : const auto& locality_lb_endpoints = load_assignment_.endpoints();
26 0 : for (const auto& locality_lb_endpoint : locality_lb_endpoints) {
27 0 : validateEndpointsForZoneAwareRouting(locality_lb_endpoint);
28 :
29 0 : for (const auto& lb_endpoint : locality_lb_endpoint.lb_endpoints()) {
30 0 : const auto& socket_address = lb_endpoint.endpoint().address().socket_address();
31 0 : if (!socket_address.resolver_name().empty()) {
32 0 : throw EnvoyException("STRICT_DNS clusters must NOT have a custom resolver name set");
33 0 : }
34 :
35 0 : resolve_targets.emplace_back(new ResolveTarget(
36 0 : *this, context.serverFactoryContext().mainThreadDispatcher(), socket_address.address(),
37 0 : socket_address.port_value(), locality_lb_endpoint, lb_endpoint));
38 0 : }
39 0 : }
40 0 : resolve_targets_ = std::move(resolve_targets);
41 0 : dns_lookup_family_ = getDnsLookupFamilyFromCluster(cluster);
42 :
43 0 : overprovisioning_factor_ = PROTOBUF_GET_WRAPPED_OR_DEFAULT(
44 0 : load_assignment_.policy(), overprovisioning_factor, kDefaultOverProvisioningFactor);
45 0 : weighted_priority_health_ = load_assignment_.policy().weighted_priority_health();
46 0 : }
47 :
48 0 : void StrictDnsClusterImpl::startPreInit() {
49 0 : for (const ResolveTargetPtr& target : resolve_targets_) {
50 0 : target->startResolve();
51 0 : }
52 : // If the config provides no endpoints, the cluster is initialized immediately as if all hosts are
53 : // resolved in failure.
54 0 : if (resolve_targets_.empty() || !wait_for_warm_on_init_) {
55 0 : onPreInitComplete();
56 0 : }
57 0 : }
58 :
59 : void StrictDnsClusterImpl::updateAllHosts(const HostVector& hosts_added,
60 : const HostVector& hosts_removed,
61 0 : uint32_t current_priority) {
62 0 : PriorityStateManager priority_state_manager(*this, local_info_, nullptr);
63 : // At this point we know that we are different so make a new host list and notify.
64 : //
65 : // TODO(dio): The uniqueness of a host address resolved in STRICT_DNS cluster per priority is not
66 : // guaranteed. Need a clear agreement on the behavior here, whether it is allowable to have
67 : // duplicated hosts inside a priority. And if we want to enforce this behavior, it should be done
68 : // inside the priority state manager.
69 0 : for (const ResolveTargetPtr& target : resolve_targets_) {
70 0 : priority_state_manager.initializePriorityFor(target->locality_lb_endpoints_);
71 0 : for (const HostSharedPtr& host : target->hosts_) {
72 0 : if (target->locality_lb_endpoints_.priority() == current_priority) {
73 0 : priority_state_manager.registerHostForPriority(host, target->locality_lb_endpoints_);
74 0 : }
75 0 : }
76 0 : }
77 :
78 : // TODO(dio): Add assertion in here.
79 0 : priority_state_manager.updateClusterPrioritySet(
80 0 : current_priority, std::move(priority_state_manager.priorityState()[current_priority].first),
81 0 : hosts_added, hosts_removed, absl::nullopt, weighted_priority_health_,
82 0 : overprovisioning_factor_);
83 0 : }
84 :
85 : StrictDnsClusterImpl::ResolveTarget::ResolveTarget(
86 : StrictDnsClusterImpl& parent, Event::Dispatcher& dispatcher, const std::string& dns_address,
87 : const uint32_t dns_port,
88 : const envoy::config::endpoint::v3::LocalityLbEndpoints& locality_lb_endpoint,
89 : const envoy::config::endpoint::v3::LbEndpoint& lb_endpoint)
90 : : parent_(parent), locality_lb_endpoints_(locality_lb_endpoint), lb_endpoint_(lb_endpoint),
91 : dns_address_(dns_address),
92 : hostname_(lb_endpoint_.endpoint().hostname().empty() ? dns_address_
93 : : lb_endpoint_.endpoint().hostname()),
94 : port_(dns_port),
95 0 : resolve_timer_(dispatcher.createTimer([this]() -> void { startResolve(); })) {}
96 :
97 0 : StrictDnsClusterImpl::ResolveTarget::~ResolveTarget() {
98 0 : if (active_query_) {
99 0 : active_query_->cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned);
100 0 : }
101 0 : }
102 :
103 0 : void StrictDnsClusterImpl::ResolveTarget::startResolve() {
104 0 : ENVOY_LOG(trace, "starting async DNS resolution for {}", dns_address_);
105 0 : parent_.info_->configUpdateStats().update_attempt_.inc();
106 :
107 0 : active_query_ = parent_.dns_resolver_->resolve(
108 0 : dns_address_, parent_.dns_lookup_family_,
109 0 : [this](Network::DnsResolver::ResolutionStatus status,
110 0 : std::list<Network::DnsResponse>&& response) -> void {
111 0 : active_query_ = nullptr;
112 0 : ENVOY_LOG(trace, "async DNS resolution complete for {}", dns_address_);
113 :
114 0 : std::chrono::milliseconds final_refresh_rate = parent_.dns_refresh_rate_ms_;
115 :
116 0 : if (status == Network::DnsResolver::ResolutionStatus::Success) {
117 0 : parent_.info_->configUpdateStats().update_success_.inc();
118 :
119 0 : HostVector new_hosts;
120 0 : std::chrono::seconds ttl_refresh_rate = std::chrono::seconds::max();
121 0 : absl::flat_hash_set<std::string> all_new_hosts;
122 0 : for (const auto& resp : response) {
123 0 : const auto& addrinfo = resp.addrInfo();
124 : // TODO(mattklein123): Currently the DNS interface does not consider port. We need to
125 : // make a new address that has port in it. We need to both support IPv6 as well as
126 : // potentially move port handling into the DNS interface itself, which would work better
127 : // for SRV.
128 0 : ASSERT(addrinfo.address_ != nullptr);
129 0 : auto address = Network::Utility::getAddressWithPort(*(addrinfo.address_), port_);
130 0 : if (all_new_hosts.count(address->asString()) > 0) {
131 0 : continue;
132 0 : }
133 :
134 0 : new_hosts.emplace_back(new HostImpl(
135 0 : parent_.info_, hostname_, address,
136 : // TODO(zyfjeff): Created through metadata shared pool
137 0 : std::make_shared<const envoy::config::core::v3::Metadata>(lb_endpoint_.metadata()),
138 0 : lb_endpoint_.load_balancing_weight().value(), locality_lb_endpoints_.locality(),
139 0 : lb_endpoint_.endpoint().health_check_config(), locality_lb_endpoints_.priority(),
140 0 : lb_endpoint_.health_status(), parent_.time_source_));
141 0 : all_new_hosts.emplace(address->asString());
142 0 : ttl_refresh_rate = min(ttl_refresh_rate, addrinfo.ttl_);
143 0 : }
144 :
145 0 : HostVector hosts_added;
146 0 : HostVector hosts_removed;
147 0 : if (parent_.updateDynamicHostList(new_hosts, hosts_, hosts_added, hosts_removed,
148 0 : all_hosts_, all_new_hosts)) {
149 0 : ENVOY_LOG(debug, "DNS hosts have changed for {}", dns_address_);
150 0 : ASSERT(std::all_of(hosts_.begin(), hosts_.end(), [&](const auto& host) {
151 0 : return host->priority() == locality_lb_endpoints_.priority();
152 0 : }));
153 :
154 : // Update host map for current resolve target.
155 0 : for (const auto& host : hosts_removed) {
156 0 : all_hosts_.erase(host->address()->asString());
157 0 : }
158 0 : for (const auto& host : hosts_added) {
159 0 : all_hosts_.insert({host->address()->asString(), host});
160 0 : }
161 :
162 0 : parent_.updateAllHosts(hosts_added, hosts_removed, locality_lb_endpoints_.priority());
163 0 : } else {
164 0 : parent_.info_->configUpdateStats().update_no_rebuild_.inc();
165 0 : }
166 :
167 : // reset failure backoff strategy because there was a success.
168 0 : parent_.failure_backoff_strategy_->reset();
169 :
170 0 : if (!response.empty() && parent_.respect_dns_ttl_ &&
171 0 : ttl_refresh_rate != std::chrono::seconds(0)) {
172 0 : final_refresh_rate = ttl_refresh_rate;
173 0 : ASSERT(ttl_refresh_rate != std::chrono::seconds::max() &&
174 0 : final_refresh_rate.count() > 0);
175 0 : }
176 0 : ENVOY_LOG(debug, "DNS refresh rate reset for {}, refresh rate {} ms", dns_address_,
177 0 : final_refresh_rate.count());
178 0 : } else {
179 0 : parent_.info_->configUpdateStats().update_failure_.inc();
180 :
181 0 : final_refresh_rate =
182 0 : std::chrono::milliseconds(parent_.failure_backoff_strategy_->nextBackOffMs());
183 0 : ENVOY_LOG(debug, "DNS refresh rate reset for {}, (failure) refresh rate {} ms",
184 0 : dns_address_, final_refresh_rate.count());
185 0 : }
186 :
187 : // If there is an initialize callback, fire it now. Note that if the cluster refers to
188 : // multiple DNS names, this will return initialized after a single DNS resolution
189 : // completes. This is not perfect but is easier to code and unclear if the extra
190 : // complexity is needed so will start with this.
191 0 : parent_.onPreInitComplete();
192 0 : resolve_timer_->enableTimer(final_refresh_rate);
193 0 : });
194 0 : }
195 :
196 : absl::StatusOr<std::pair<ClusterImplBaseSharedPtr, ThreadAwareLoadBalancerPtr>>
197 : StrictDnsClusterFactory::createClusterImpl(const envoy::config::cluster::v3::Cluster& cluster,
198 0 : ClusterFactoryContext& context) {
199 0 : auto selected_dns_resolver = selectDnsResolver(cluster, context);
200 :
201 0 : return std::make_pair(
202 0 : std::make_shared<StrictDnsClusterImpl>(cluster, context, selected_dns_resolver), nullptr);
203 0 : }
204 :
205 : /**
206 : * Static registration for the strict dns cluster factory. @see RegisterFactory.
207 : */
208 : REGISTER_FACTORY(StrictDnsClusterFactory, ClusterFactory);
209 :
210 : } // namespace Upstream
211 : } // namespace Envoy
|