/proc/self/cwd/source/extensions/clusters/logical_dns/logical_dns_cluster.cc
Line | Count | Source (jump to first uncovered line) |
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(debug, "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(debug, "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 |