Coverage Report

Created: 2023-11-12 09:30

/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