Skip to content

Commit b32eabf

Browse files
diohtuch
authored andcommitted
upstream: implement Cluster's load_assignment field (envoyproxy#3864)
This patch implements load_assigment field in CDS' Cluster. This change specifically adds the implementation of the new load_assigment field for clusters with discovery-type: STATIC, STRICT_DNS and LOGICAL_DNS. While adding this load_assigment field implementation to Cluster, this patch also allows specifying optional (active) health check config per specified upstream host. Risk Level: medium Testing: unit tests Docs Changes: This unhides docs for endpoint health check config Release Notes: N/A Fixes envoyproxy#439 Signed-off-by: Dhi Aurrahman <[email protected]>
1 parent 8b3aae8 commit b32eabf

File tree

16 files changed

+1018
-245
lines changed

16 files changed

+1018
-245
lines changed

DEPRECATED.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ A logged warning is expected for each deprecated item that is in deprecation win
1010

1111
* Use of the v1 API is deprecated. See envoy-announce
1212
[email](https://groups.google.com/forum/#!topic/envoy-announce/oPnYMZw8H4U).
13-
* Use of the legacy
13+
* Use of the legacy
1414
[ratelimit.proto](https://github.com/envoyproxy/envoy/blob/b0a518d064c8255e0e20557a8f909b6ff457558f/source/common/ratelimit/ratelimit.proto)
1515
is deprecated, in favor of the proto defined in
1616
[date-plane-api](https://github.com/envoyproxy/envoy/blob/master/api/envoy/service/ratelimit/v2/rls.proto)
@@ -23,6 +23,7 @@ A logged warning is expected for each deprecated item that is in deprecation win
2323
is deprecated. Please use the new `upgrade_configs` in the
2424
[HttpConnectionManager](https://github.com/envoyproxy/envoy/blob/master/api/envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.proto)
2525
instead.
26+
* Setting hosts via `hosts` field in `Cluster` is deprecated. Use `load_assignment` instead.
2627

2728
## Version 1.7.0
2829

api/envoy/api/v2/cds.proto

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,13 @@ message Cluster {
160160
// :ref:`STRICT_DNS<envoy_api_enum_value_Cluster.DiscoveryType.STRICT_DNS>`
161161
// or :ref:`LOGICAL_DNS<envoy_api_enum_value_Cluster.DiscoveryType.LOGICAL_DNS>`,
162162
// then hosts is required.
163-
repeated core.Address hosts = 7;
163+
//
164+
// .. attention::
165+
//
166+
// **This field is deprecated**. Set the
167+
// :ref:`load_assignment<envoy_api_field_Cluster.load_assignment>` field instead.
168+
//
169+
repeated core.Address hosts = 7 [deprecated = true];
164170

165171
// Setting this is required for specifying members of
166172
// :ref:`STATIC<envoy_api_enum_value_Cluster.DiscoveryType.STATIC>`,
@@ -176,7 +182,6 @@ message Cluster {
176182
// :ref:`endpoint assignments<envoy_api_msg_ClusterLoadAssignment>`.
177183
// Setting this overrides :ref:`hosts<envoy_api_field_Cluster.hosts>` values.
178184
//
179-
// [#not-implemented-hide:]
180185
ClusterLoadAssignment load_assignment = 33;
181186

182187
// Optional :ref:`active health checking <arch_overview_health_checking>`

api/envoy/api/v2/endpoint/endpoint.proto

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ message Endpoint {
2929
// and will be resolved via DNS.
3030
core.Address address = 1;
3131

32-
// [#not-implemented-hide:] The optional health check configuration.
32+
// The optional health check configuration.
3333
message HealthCheckConfig {
3434
// Optional alternative health check port value.
3535
//
@@ -40,8 +40,8 @@ message Endpoint {
4040
uint32 port_value = 1;
4141
}
4242

43-
// [#not-implemented-hide:] The optional health check configuration is used as
44-
// configuration for the health checker to contact the health checked host.
43+
// The optional health check configuration is used as configuration for the
44+
// health checker to contact the health checked host.
4545
//
4646
// .. attention::
4747
//

docs/root/configuration/overview/v2_overview.rst

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,14 @@ A minimal fully static bootstrap config is provided below:
9595
connect_timeout: 0.25s
9696
type: STATIC
9797
lb_policy: ROUND_ROBIN
98-
hosts: [{ socket_address: { address: 127.0.0.2, port_value: 1234 }}]
98+
load_assignment:
99+
endpoints:
100+
- lb_endpoints:
101+
- endpoint:
102+
address:
103+
socket_address:
104+
address: 127.0.0.1
105+
port_value: 1234
99106
100107
Mostly static with dynamic EDS
101108
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -150,7 +157,14 @@ on 127.0.0.3:5678 is provided below:
150157
type: STATIC
151158
lb_policy: ROUND_ROBIN
152159
http2_protocol_options: {}
153-
hosts: [{ socket_address: { address: 127.0.0.3, port_value: 5678 }}]
160+
load_assignment:
161+
endpoints:
162+
- lb_endpoints:
163+
- endpoint:
164+
address:
165+
socket_address:
166+
address: 127.0.0.1
167+
port_value: 5678
154168
155169
Notice above that *xds_cluster* is defined to point Envoy at the management server. Even in
156170
an otherwise completely dynamic configurations, some static resources need to
@@ -214,7 +228,14 @@ below:
214228
type: STATIC
215229
lb_policy: ROUND_ROBIN
216230
http2_protocol_options: {}
217-
hosts: [{ socket_address: { address: 127.0.0.3, port_value: 5678 }}]
231+
load_assignment:
232+
endpoints:
233+
- lb_endpoints:
234+
- endpoint:
235+
address:
236+
socket_address:
237+
address: 127.0.0.1
238+
port_value: 5678
218239
219240
The management server could respond to LDS requests with:
220241

@@ -543,11 +564,11 @@ the shared ADS channel.
543564
Management Server Unreachability
544565
--------------------------------
545566

546-
When Envoy instance looses connectivity with the management server, Envoy will latch on to
547-
the previous configuration while actively retrying in the background to reestablish the
548-
connection with the management server.
567+
When an Envoy instance loses connectivity with the management server, Envoy will latch on to
568+
the previous configuration while actively retrying in the background to reestablish the
569+
connection with the management server.
549570

550-
Envoy debug logs the fact that it is not able to establish a connection with the management server
571+
Envoy debug logs the fact that it is not able to establish a connection with the management server
551572
every time it attempts a connection.
552573

553574
:ref:`upstream_cx_connect_fail <config_cluster_manager_cluster_stats>` a cluster level statistic

docs/root/intro/arch_overview/health_checking.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,35 @@ unhealthy, successes required before marking a host healthy, etc.):
2424
maintenance by setting the specified key to any value and waiting for traffic to drain. See
2525
:ref:`redis_key <config_cluster_manager_cluster_hc_redis_key>`.
2626

27+
.. _arch_overview_per_cluster_health_check_config:
28+
29+
Per cluster member health check config
30+
--------------------------------------
31+
32+
If active health checking is configured for an upstream cluster, a specific additional configuration
33+
for each registered member can be specified by setting the
34+
:ref:`HealthCheckConfig<envoy_api_msg_endpoint.Endpoint.HealthCheckConfig>`
35+
in the :ref:`Endpoint<envoy_api_msg_endpoint.Endpoint>` of an :ref:`LbEndpoint<envoy_api_msg_endpoint.LbEndpoint>`
36+
of each defined :ref:`LocalityLbEndpoints<envoy_api_msg_endpoint.LocalityLbEndpoints>` in a
37+
:ref:`ClusterLoadAssignment<envoy_api_msg_ClusterLoadAssignment>`.
38+
39+
An example of setting up :ref:`health check config<envoy_api_msg_endpoint.Endpoint.HealthCheckConfig>`
40+
to set a :ref:`cluster member<envoy_api_msg_endpoint.Endpoint>`'s alternative health check
41+
:ref:`port<envoy_api_field_endpoint.Endpoint.HealthCheckConfig.port_value>` is:
42+
43+
.. code-block:: yaml
44+
45+
load_assignment:
46+
endpoints:
47+
- lb_endpoints:
48+
- endpoint:
49+
health_check_config:
50+
port_value: 8080
51+
address:
52+
socket_address:
53+
address: localhost
54+
port_value: 80
55+
2756
.. _arch_overview_health_check_logging:
2857

2958
Health check event logging

source/common/config/utility.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,5 +225,20 @@ Grpc::AsyncClientFactoryPtr Utility::factoryForGrpcApiConfigSource(
225225
return async_client_manager.factoryForGrpcService(grpc_service, scope, false);
226226
}
227227

228+
envoy::api::v2::ClusterLoadAssignment Utility::translateClusterHosts(
229+
const Protobuf::RepeatedPtrField<envoy::api::v2::core::Address>& hosts) {
230+
envoy::api::v2::ClusterLoadAssignment load_assignment;
231+
envoy::api::v2::endpoint::LocalityLbEndpoints* locality_lb_endpoints =
232+
load_assignment.add_endpoints();
233+
// Since this LocalityLbEndpoints is built from hosts list, set the default weight to 1.
234+
locality_lb_endpoints->mutable_load_balancing_weight()->set_value(1);
235+
for (const envoy::api::v2::core::Address& host : hosts) {
236+
envoy::api::v2::endpoint::LbEndpoint* lb_endpoint = locality_lb_endpoints->add_lb_endpoints();
237+
lb_endpoint->mutable_endpoint()->mutable_address()->MergeFrom(host);
238+
lb_endpoint->mutable_load_balancing_weight()->set_value(1);
239+
}
240+
return load_assignment;
241+
}
242+
228243
} // namespace Config
229244
} // namespace Envoy

source/common/config/utility.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,14 @@ class Utility {
288288
factoryForGrpcApiConfigSource(Grpc::AsyncClientManager& async_client_manager,
289289
const envoy::api::v2::core::ApiConfigSource& api_config_source,
290290
Stats::Scope& scope);
291+
292+
/**
293+
* Translate a set of cluster's hosts into a load assignment configuration.
294+
* @param hosts cluster's list of hosts.
295+
* @return envoy::api::v2::ClusterLoadAssignment a load assignment configuration.
296+
*/
297+
static envoy::api::v2::ClusterLoadAssignment
298+
translateClusterHosts(const Protobuf::RepeatedPtrField<envoy::api::v2::core::Address>& hosts);
291299
};
292300

293301
} // namespace Config

source/common/upstream/eds.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ bool EdsClusterImpl::updateHostsPerLocality(const uint32_t priority, const HostV
140140
info_->name(), host_set.hosts().size(), host_set.priority());
141141

142142
priority_state_manager.updateClusterPrioritySet(priority, std::move(current_hosts_copy),
143-
hosts_added, hosts_removed);
143+
hosts_added, hosts_removed, absl::nullopt);
144144
return true;
145145
}
146146
return false;

source/common/upstream/logical_dns_cluster.cc

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ namespace Upstream {
1818
LogicalDnsCluster::LogicalDnsCluster(const envoy::api::v2::Cluster& cluster,
1919
Runtime::Loader& runtime, Stats::Store& stats,
2020
Ssl::ContextManager& ssl_context_manager,
21+
const LocalInfo::LocalInfo& local_info,
2122
Network::DnsResolverSharedPtr dns_resolver,
2223
ThreadLocal::SlotAllocator& tls, ClusterManager& cm,
2324
Event::Dispatcher& dispatcher, bool added_via_api)
@@ -27,10 +28,19 @@ LogicalDnsCluster::LogicalDnsCluster(const envoy::api::v2::Cluster& cluster,
2728
dns_refresh_rate_ms_(
2829
std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(cluster, dns_refresh_rate, 5000))),
2930
tls_(tls.allocateSlot()),
30-
resolve_timer_(dispatcher.createTimer([this]() -> void { startResolve(); })) {
31-
const auto& hosts = cluster.hosts();
32-
if (hosts.size() != 1) {
33-
throw EnvoyException("logical_dns clusters must have a single host");
31+
resolve_timer_(dispatcher.createTimer([this]() -> void { startResolve(); })),
32+
local_info_(local_info),
33+
load_assignment_(cluster.has_load_assignment()
34+
? cluster.load_assignment()
35+
: Config::Utility::translateClusterHosts(cluster.hosts())) {
36+
const auto& locality_lb_endpoints = load_assignment_.endpoints();
37+
if (locality_lb_endpoints.size() != 1 || locality_lb_endpoints[0].lb_endpoints().size() != 1) {
38+
if (cluster.has_load_assignment()) {
39+
throw EnvoyException(
40+
"LOGICAL_DNS clusters must have a single locality_lb_endpoint and a single lb_endpoint");
41+
} else {
42+
throw EnvoyException("LOGICAL_DNS clusters must have a single host");
43+
}
3444
}
3545

3646
switch (cluster.dns_lookup_family()) {
@@ -47,7 +57,8 @@ LogicalDnsCluster::LogicalDnsCluster(const envoy::api::v2::Cluster& cluster,
4757
NOT_REACHED_GCOVR_EXCL_LINE;
4858
}
4959

50-
const auto& socket_address = hosts[0].socket_address();
60+
const envoy::api::v2::core::SocketAddress& socket_address =
61+
lbEndpoint().endpoint().address().socket_address();
5162
dns_url_ = fmt::format("tcp://{}:{}", socket_address.address(), socket_address.port_value());
5263
hostname_ = Network::Utility::hostFromTcpUrl(dns_url_);
5364
Network::Utility::portFromTcpUrl(dns_url_);
@@ -88,7 +99,8 @@ void LogicalDnsCluster::startResolve() {
8899
current_resolved_address_ = new_address;
89100
// Capture URL to avoid a race with another update.
90101
tls_->runOnAllThreads([this, new_address]() -> void {
91-
tls_->getTyped<PerThreadCurrentHostData>().current_resolved_address_ = new_address;
102+
PerThreadCurrentHostData& data = tls_->getTyped<PerThreadCurrentHostData>();
103+
data.current_resolved_address_ = new_address;
92104
});
93105
}
94106

@@ -107,14 +119,16 @@ void LogicalDnsCluster::startResolve() {
107119
new LogicalHost(info_, hostname_, Network::Utility::getIpv6AnyAddress(), *this));
108120
break;
109121
}
110-
HostVectorSharedPtr new_hosts(new HostVector());
111-
new_hosts->emplace_back(logical_host_);
112-
// Given the current config, only EDS clusters support multiple priorities.
113-
ASSERT(priority_set_.hostSetsPerPriority().size() == 1);
114-
auto& first_host_set = priority_set_.getOrCreateHostSet(0);
115-
first_host_set.updateHosts(new_hosts, createHealthyHostList(*new_hosts),
116-
HostsPerLocalityImpl::empty(), HostsPerLocalityImpl::empty(),
117-
{}, *new_hosts, {});
122+
const auto& locality_lb_endpoint = localityLbEndpoint();
123+
PriorityStateManager priority_state_manager(*this, local_info_);
124+
priority_state_manager.initializePriorityFor(locality_lb_endpoint);
125+
priority_state_manager.registerHostForPriority(logical_host_, locality_lb_endpoint,
126+
lbEndpoint(), absl::nullopt);
127+
128+
const uint32_t priority = locality_lb_endpoint.priority();
129+
priority_state_manager.updateClusterPrioritySet(
130+
priority, std::move(priority_state_manager.priorityState()[priority].first),
131+
absl::nullopt, absl::nullopt, absl::nullopt);
118132
}
119133
}
120134

@@ -131,7 +145,8 @@ Upstream::Host::CreateConnectionData LogicalDnsCluster::LogicalHost::createConne
131145
return {HostImpl::createConnection(dispatcher, *parent_.info_, data.current_resolved_address_,
132146
options),
133147
HostDescriptionConstSharedPtr{
134-
new RealHostDescription(data.current_resolved_address_, shared_from_this())}};
148+
new RealHostDescription(data.current_resolved_address_, parent_.localityLbEndpoint(),
149+
parent_.lbEndpoint(), shared_from_this())}};
135150
}
136151

137152
} // namespace Upstream

source/common/upstream/logical_dns_cluster.h

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class LogicalDnsCluster : public ClusterImplBase {
3030
public:
3131
LogicalDnsCluster(const envoy::api::v2::Cluster& cluster, Runtime::Loader& runtime,
3232
Stats::Store& stats, Ssl::ContextManager& ssl_context_manager,
33+
const LocalInfo::LocalInfo& local_info,
3334
Network::DnsResolverSharedPtr dns_resolver, ThreadLocal::SlotAllocator& tls,
3435
ClusterManager& cm, Event::Dispatcher& dispatcher, bool added_via_api);
3536

@@ -42,9 +43,10 @@ class LogicalDnsCluster : public ClusterImplBase {
4243
struct LogicalHost : public HostImpl {
4344
LogicalHost(ClusterInfoConstSharedPtr cluster, const std::string& hostname,
4445
Network::Address::InstanceConstSharedPtr address, LogicalDnsCluster& parent)
45-
: HostImpl(cluster, hostname, address, envoy::api::v2::core::Metadata::default_instance(),
46-
1, envoy::api::v2::core::Locality().default_instance(),
47-
envoy::api::v2::endpoint::Endpoint::HealthCheckConfig().default_instance()),
46+
: HostImpl(cluster, hostname, address, parent.lbEndpoint().metadata(),
47+
parent.lbEndpoint().load_balancing_weight().value(),
48+
parent.localityLbEndpoint().locality(),
49+
parent.lbEndpoint().endpoint().health_check_config()),
4850
parent_(parent) {}
4951

5052
// Upstream::Host
@@ -57,10 +59,17 @@ class LogicalDnsCluster : public ClusterImplBase {
5759

5860
struct RealHostDescription : public HostDescription {
5961
RealHostDescription(Network::Address::InstanceConstSharedPtr address,
62+
const envoy::api::v2::endpoint::LocalityLbEndpoints& locality_lb_endpoint,
63+
const envoy::api::v2::endpoint::LbEndpoint& lb_endpoint,
6064
HostConstSharedPtr logical_host)
6165
: address_(address), logical_host_(logical_host),
62-
metadata_(std::make_shared<envoy::api::v2::core::Metadata>(
63-
envoy::api::v2::core::Metadata::default_instance())) {}
66+
metadata_(std::make_shared<envoy::api::v2::core::Metadata>(lb_endpoint.metadata())),
67+
health_check_address_(
68+
lb_endpoint.endpoint().health_check_config().port_value() == 0
69+
? address
70+
: Network::Utility::getAddressWithPort(
71+
*address, lb_endpoint.endpoint().health_check_config().port_value())),
72+
locality_lb_endpoint_(locality_lb_endpoint), lb_endpoint_(lb_endpoint) {}
6473

6574
// Upstream:HostDescription
6675
bool canary() const override { return false; }
@@ -81,21 +90,36 @@ class LogicalDnsCluster : public ClusterImplBase {
8190
const std::string& hostname() const override { return logical_host_->hostname(); }
8291
Network::Address::InstanceConstSharedPtr address() const override { return address_; }
8392
const envoy::api::v2::core::Locality& locality() const override {
84-
return envoy::api::v2::core::Locality().default_instance();
93+
return locality_lb_endpoint_.locality();
8594
}
86-
// TODO(dio): To support different address port.
8795
Network::Address::InstanceConstSharedPtr healthCheckAddress() const override {
88-
return address_;
96+
return health_check_address_;
8997
}
98+
uint32_t priority() const { return locality_lb_endpoint_.priority(); }
9099
Network::Address::InstanceConstSharedPtr address_;
91100
HostConstSharedPtr logical_host_;
92101
const std::shared_ptr<envoy::api::v2::core::Metadata> metadata_;
102+
Network::Address::InstanceConstSharedPtr health_check_address_;
103+
const envoy::api::v2::endpoint::LocalityLbEndpoints& locality_lb_endpoint_;
104+
const envoy::api::v2::endpoint::LbEndpoint& lb_endpoint_;
93105
};
94106

95107
struct PerThreadCurrentHostData : public ThreadLocal::ThreadLocalObject {
96108
Network::Address::InstanceConstSharedPtr current_resolved_address_;
97109
};
98110

111+
const envoy::api::v2::endpoint::LocalityLbEndpoints& localityLbEndpoint() const {
112+
// This is checked in the constructor, i.e. at config load time.
113+
ASSERT(load_assignment_.endpoints().size() == 1);
114+
return load_assignment_.endpoints()[0];
115+
}
116+
117+
const envoy::api::v2::endpoint::LbEndpoint& lbEndpoint() const {
118+
// This is checked in the constructor, i.e. at config load time.
119+
ASSERT(localityLbEndpoint().lb_endpoints().size() == 1);
120+
return localityLbEndpoint().lb_endpoints()[0];
121+
}
122+
99123
void startResolve();
100124

101125
// ClusterImplBase
@@ -111,6 +135,8 @@ class LogicalDnsCluster : public ClusterImplBase {
111135
Network::Address::InstanceConstSharedPtr current_resolved_address_;
112136
HostSharedPtr logical_host_;
113137
Network::ActiveDnsQuery* active_dns_query_{};
138+
const LocalInfo::LocalInfo& local_info_;
139+
const envoy::api::v2::ClusterLoadAssignment load_assignment_;
114140
};
115141

116142
} // namespace Upstream

0 commit comments

Comments
 (0)