Line data Source code
1 : #include "source/extensions/clusters/logical_dns/logical_dns_cluster.h"
2 :
3 : #include <chrono>
4 : #include <list>
5 : #include <memory>
6 : #include <string>
7 : #include <vector>
8 :
9 : #include "envoy/common/exception.h"
10 : #include "envoy/config/cluster/v3/cluster.pb.h"
11 : #include "envoy/config/core/v3/address.pb.h"
12 : #include "envoy/config/endpoint/v3/endpoint.pb.h"
13 : #include "envoy/stats/scope.h"
14 :
15 : #include "source/common/common/dns_utils.h"
16 : #include "source/common/common/fmt.h"
17 : #include "source/common/config/utility.h"
18 : #include "source/common/network/address_impl.h"
19 : #include "source/common/network/utility.h"
20 : #include "source/common/protobuf/protobuf.h"
21 : #include "source/common/protobuf/utility.h"
22 :
23 : namespace Envoy {
24 : namespace Upstream {
25 :
26 : namespace {
27 : envoy::config::endpoint::v3::ClusterLoadAssignment
28 0 : convertPriority(const envoy::config::endpoint::v3::ClusterLoadAssignment& load_assignment) {
29 0 : envoy::config::endpoint::v3::ClusterLoadAssignment converted;
30 0 : converted.MergeFrom(load_assignment);
31 :
32 : // We convert the priority set by the configuration back to zero. This helps
33 : // ensure that we don't blow up later on when using zone aware routing due
34 : // to a check that all priorities are zero.
35 : //
36 : // Since LOGICAL_DNS is limited to exactly one host declared per load_assignment
37 : // (checked in the ctor in this file), we can safely just rewrite the priority
38 : // to zero.
39 0 : for (auto& endpoint : *converted.mutable_endpoints()) {
40 0 : endpoint.set_priority(0);
41 0 : }
42 :
43 0 : return converted;
44 0 : }
45 : } // namespace
46 :
47 : LogicalDnsCluster::LogicalDnsCluster(const envoy::config::cluster::v3::Cluster& cluster,
48 : ClusterFactoryContext& context,
49 : Network::DnsResolverSharedPtr dns_resolver)
50 : : ClusterImplBase(cluster, context), dns_resolver_(dns_resolver),
51 : dns_refresh_rate_ms_(
52 : std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(cluster, dns_refresh_rate, 5000))),
53 : respect_dns_ttl_(cluster.respect_dns_ttl()),
54 : resolve_timer_(context.serverFactoryContext().mainThreadDispatcher().createTimer(
55 0 : [this]() -> void { startResolve(); })),
56 : local_info_(context.serverFactoryContext().localInfo()),
57 0 : load_assignment_(convertPriority(cluster.load_assignment())) {
58 0 : failure_backoff_strategy_ =
59 0 : Config::Utility::prepareDnsRefreshStrategy<envoy::config::cluster::v3::Cluster>(
60 0 : cluster, dns_refresh_rate_ms_.count(),
61 0 : context.serverFactoryContext().api().randomGenerator());
62 :
63 0 : const envoy::config::core::v3::SocketAddress& socket_address =
64 0 : lbEndpoint().endpoint().address().socket_address();
65 :
66 : // Checked by factory;
67 0 : ASSERT(socket_address.resolver_name().empty());
68 0 : dns_address_ = socket_address.address();
69 0 : dns_port_ = socket_address.port_value();
70 :
71 0 : if (lbEndpoint().endpoint().hostname().empty()) {
72 0 : hostname_ = dns_address_;
73 0 : } else {
74 0 : hostname_ = lbEndpoint().endpoint().hostname();
75 0 : }
76 0 : dns_lookup_family_ = getDnsLookupFamilyFromCluster(cluster);
77 0 : }
78 :
79 0 : void LogicalDnsCluster::startPreInit() {
80 0 : startResolve();
81 0 : if (!wait_for_warm_on_init_) {
82 0 : onPreInitComplete();
83 0 : }
84 0 : }
85 :
86 0 : LogicalDnsCluster::~LogicalDnsCluster() {
87 0 : if (active_dns_query_) {
88 0 : active_dns_query_->cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned);
89 0 : }
90 0 : }
91 :
92 0 : void LogicalDnsCluster::startResolve() {
93 0 : ENVOY_LOG(trace, "starting async DNS resolution for {}", dns_address_);
94 0 : info_->configUpdateStats().update_attempt_.inc();
95 :
96 0 : active_dns_query_ = dns_resolver_->resolve(
97 0 : dns_address_, dns_lookup_family_,
98 0 : [this](Network::DnsResolver::ResolutionStatus status,
99 0 : std::list<Network::DnsResponse>&& response) -> void {
100 0 : active_dns_query_ = nullptr;
101 0 : ENVOY_LOG(trace, "async DNS resolution complete for {}", dns_address_);
102 :
103 0 : std::chrono::milliseconds final_refresh_rate = dns_refresh_rate_ms_;
104 :
105 : // If the DNS resolver successfully resolved with an empty response list, the logical DNS
106 : // cluster does not update. This ensures that a potentially previously resolved address does
107 : // not stabilize back to 0 hosts.
108 0 : if (status == Network::DnsResolver::ResolutionStatus::Success && !response.empty()) {
109 0 : info_->configUpdateStats().update_success_.inc();
110 0 : const auto addrinfo = response.front().addrInfo();
111 : // TODO(mattklein123): Move port handling into the DNS interface.
112 0 : ASSERT(addrinfo.address_ != nullptr);
113 0 : Network::Address::InstanceConstSharedPtr new_address =
114 0 : Network::Utility::getAddressWithPort(*(response.front().addrInfo().address_),
115 0 : dns_port_);
116 0 : auto address_list = DnsUtils::generateAddressList(response, dns_port_);
117 :
118 0 : if (!logical_host_) {
119 0 : logical_host_ = std::make_shared<LogicalHost>(info_, hostname_, new_address,
120 0 : address_list, localityLbEndpoint(),
121 0 : lbEndpoint(), nullptr, time_source_);
122 :
123 0 : const auto& locality_lb_endpoint = localityLbEndpoint();
124 0 : PriorityStateManager priority_state_manager(*this, local_info_, nullptr);
125 0 : priority_state_manager.initializePriorityFor(locality_lb_endpoint);
126 0 : priority_state_manager.registerHostForPriority(logical_host_, locality_lb_endpoint);
127 :
128 0 : const uint32_t priority = locality_lb_endpoint.priority();
129 0 : priority_state_manager.updateClusterPrioritySet(
130 0 : priority, std::move(priority_state_manager.priorityState()[priority].first),
131 0 : absl::nullopt, absl::nullopt, absl::nullopt, absl::nullopt, absl::nullopt);
132 0 : }
133 :
134 0 : if (!current_resolved_address_ ||
135 0 : (*new_address != *current_resolved_address_ ||
136 0 : DnsUtils::listChanged(address_list, current_resolved_address_list_))) {
137 0 : current_resolved_address_ = new_address;
138 0 : current_resolved_address_list_ = address_list;
139 :
140 : // Make sure that we have an updated address for admin display, health
141 : // checking, and creating real host connections.
142 0 : logical_host_->setNewAddresses(new_address, address_list, lbEndpoint());
143 0 : }
144 :
145 : // reset failure backoff strategy because there was a success.
146 0 : failure_backoff_strategy_->reset();
147 :
148 0 : if (respect_dns_ttl_ && addrinfo.ttl_ != std::chrono::seconds(0)) {
149 0 : final_refresh_rate = addrinfo.ttl_;
150 0 : }
151 0 : ENVOY_LOG(debug, "DNS refresh rate reset for {}, refresh rate {} ms", dns_address_,
152 0 : final_refresh_rate.count());
153 0 : } else {
154 0 : info_->configUpdateStats().update_failure_.inc();
155 0 : final_refresh_rate =
156 0 : std::chrono::milliseconds(failure_backoff_strategy_->nextBackOffMs());
157 0 : ENVOY_LOG(debug, "DNS refresh rate reset for {}, (failure) refresh rate {} ms",
158 0 : dns_address_, final_refresh_rate.count());
159 0 : }
160 :
161 0 : onPreInitComplete();
162 0 : resolve_timer_->enableTimer(final_refresh_rate);
163 0 : });
164 0 : }
165 :
166 : absl::StatusOr<std::pair<ClusterImplBaseSharedPtr, ThreadAwareLoadBalancerPtr>>
167 : LogicalDnsClusterFactory::createClusterImpl(const envoy::config::cluster::v3::Cluster& cluster,
168 0 : ClusterFactoryContext& context) {
169 0 : auto selected_dns_resolver = selectDnsResolver(cluster, context);
170 :
171 0 : const auto& load_assignment = cluster.load_assignment();
172 0 : const auto& locality_lb_endpoints = load_assignment.endpoints();
173 0 : if (locality_lb_endpoints.size() != 1 || locality_lb_endpoints[0].lb_endpoints().size() != 1) {
174 0 : if (cluster.has_load_assignment()) {
175 0 : return absl::InvalidArgumentError(
176 0 : "LOGICAL_DNS clusters must have a single locality_lb_endpoint and a single lb_endpoint");
177 0 : } else {
178 0 : return absl::InvalidArgumentError("LOGICAL_DNS clusters must have a single host");
179 0 : }
180 0 : }
181 :
182 0 : const envoy::config::core::v3::SocketAddress& socket_address =
183 0 : locality_lb_endpoints[0].lb_endpoints()[0].endpoint().address().socket_address();
184 0 : if (!socket_address.resolver_name().empty()) {
185 0 : return absl::InvalidArgumentError(
186 0 : "LOGICAL_DNS clusters must NOT have a custom resolver name set");
187 0 : }
188 :
189 0 : return std::make_pair(std::shared_ptr<LogicalDnsCluster>(
190 0 : new LogicalDnsCluster(cluster, context, selected_dns_resolver)),
191 0 : nullptr);
192 0 : }
193 :
194 : /**
195 : * Static registration for the strict dns cluster factory. @see RegisterFactory.
196 : */
197 : REGISTER_FACTORY(LogicalDnsClusterFactory, ClusterFactory);
198 :
199 : } // namespace Upstream
200 : } // namespace Envoy
|