Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prefer the load_balancing_policy cluster field over lb_policy #18419

Merged
merged 9 commits into from
Oct 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions api/envoy/config/cluster/v3/cluster.proto
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ message Cluster {

// Use the new :ref:`load_balancing_policy
// <envoy_v3_api_field_config.cluster.v3.Cluster.load_balancing_policy>` field to determine the LB policy.
// [#next-major-version: In the v3 API, we should consider deprecating the lb_policy field
// and instead using the new load_balancing_policy field as the one and only mechanism for
// configuring this.]
// This has been deprecated in favor of using the :ref:`load_balancing_policy
// <envoy_v3_api_field_config.cluster.v3.Cluster.load_balancing_policy>` field without
// setting any value in :ref:`lb_policy<envoy_v3_api_field_config.cluster.v3.Cluster.lb_policy>`.
LOAD_BALANCING_POLICY_CONFIG = 7;
}

Expand Down Expand Up @@ -1044,9 +1044,8 @@ message Cluster {
// servers of this cluster.
repeated Filter filters = 40;

// New mechanism for LB policy configuration. Used only if the
// :ref:`lb_policy<envoy_v3_api_field_config.cluster.v3.Cluster.lb_policy>` field has the value
// :ref:`LOAD_BALANCING_POLICY_CONFIG<envoy_v3_api_enum_value_config.cluster.v3.Cluster.LbPolicy.LOAD_BALANCING_POLICY_CONFIG>`.
// If this field is set and is supported by the client, it will supersede the value of
// :ref:`lb_policy<envoy_v3_api_field_config.cluster.v3.Cluster.lb_policy>`.
LoadBalancingPolicy load_balancing_policy = 41;

// [#not-implemented-hide:]
Expand Down
135 changes: 72 additions & 63 deletions source/common/upstream/upstream_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -836,72 +836,42 @@ ClusterInfoImpl::ClusterInfoImpl(
"HttpProtocolOptions can be specified");
}

switch (config.lb_policy()) {
case envoy::config::cluster::v3::Cluster::ROUND_ROBIN:
lb_type_ = LoadBalancerType::RoundRobin;
break;
case envoy::config::cluster::v3::Cluster::LEAST_REQUEST:
lb_type_ = LoadBalancerType::LeastRequest;
break;
case envoy::config::cluster::v3::Cluster::RANDOM:
lb_type_ = LoadBalancerType::Random;
break;
case envoy::config::cluster::v3::Cluster::RING_HASH:
lb_type_ = LoadBalancerType::RingHash;
break;
case envoy::config::cluster::v3::Cluster::MAGLEV:
lb_type_ = LoadBalancerType::Maglev;
break;
case envoy::config::cluster::v3::Cluster::CLUSTER_PROVIDED:
if (config.has_lb_subset_config()) {
throw EnvoyException(
fmt::format("cluster: LB policy {} cannot be combined with lb_subset_config",
envoy::config::cluster::v3::Cluster::LbPolicy_Name(config.lb_policy())));
}

lb_type_ = LoadBalancerType::ClusterProvided;
break;
case envoy::config::cluster::v3::Cluster::LOAD_BALANCING_POLICY_CONFIG: {
if (config.has_lb_subset_config()) {
throw EnvoyException(
fmt::format("cluster: LB policy {} cannot be combined with lb_subset_config",
envoy::config::cluster::v3::Cluster::LbPolicy_Name(config.lb_policy())));
}

if (config.has_common_lb_config()) {
throw EnvoyException(
fmt::format("cluster: LB policy {} cannot be combined with common_lb_config",
envoy::config::cluster::v3::Cluster::LbPolicy_Name(config.lb_policy())));
}

if (!config.has_load_balancing_policy()) {
throw EnvoyException(
fmt::format("cluster: LB policy {} requires load_balancing_policy to be set",
envoy::config::cluster::v3::Cluster::LbPolicy_Name(config.lb_policy())));
}

for (const auto& policy : config.load_balancing_policy().policies()) {
TypedLoadBalancerFactory* factory =
Config::Utility::getAndCheckFactory<TypedLoadBalancerFactory>(
policy.typed_extension_config(), /*is_optional=*/true);
if (factory != nullptr) {
load_balancing_policy_ = policy;
load_balancer_factory_ = factory;
break;
// If load_balancing_policy is set we will use it directly, ignoring lb_policy.
if (config.has_load_balancing_policy()) {
configureLbPolicies(config);
} else {
switch (config.lb_policy()) {
case envoy::config::cluster::v3::Cluster::ROUND_ROBIN:
lb_type_ = LoadBalancerType::RoundRobin;
break;
case envoy::config::cluster::v3::Cluster::LEAST_REQUEST:
lb_type_ = LoadBalancerType::LeastRequest;
break;
case envoy::config::cluster::v3::Cluster::RANDOM:
lb_type_ = LoadBalancerType::Random;
break;
case envoy::config::cluster::v3::Cluster::RING_HASH:
lb_type_ = LoadBalancerType::RingHash;
break;
case envoy::config::cluster::v3::Cluster::MAGLEV:
lb_type_ = LoadBalancerType::Maglev;
break;
case envoy::config::cluster::v3::Cluster::CLUSTER_PROVIDED:
if (config.has_lb_subset_config()) {
throw EnvoyException(
fmt::format("cluster: LB policy {} cannot be combined with lb_subset_config",
envoy::config::cluster::v3::Cluster::LbPolicy_Name(config.lb_policy())));
}
}

if (load_balancer_factory_ == nullptr) {
throw EnvoyException(fmt::format(
"Didn't find a registered load balancer factory implementation for cluster: '{}'",
name_));
lb_type_ = LoadBalancerType::ClusterProvided;
break;
case envoy::config::cluster::v3::Cluster::LOAD_BALANCING_POLICY_CONFIG: {
configureLbPolicies(config);
break;
}
default:
NOT_REACHED_GCOVR_EXCL_LINE;
}

lb_type_ = LoadBalancerType::LoadBalancingPolicyConfig;
break;
}
default:
NOT_REACHED_GCOVR_EXCL_LINE;
}

if (config.lb_subset_config().locality_weight_aware() &&
Expand Down Expand Up @@ -960,6 +930,45 @@ ClusterInfoImpl::ClusterInfoImpl(
}
}

// Configures the load balancer based on config.load_balancing_policy
void ClusterInfoImpl::configureLbPolicies(const envoy::config::cluster::v3::Cluster& config) {
if (config.has_lb_subset_config()) {
throw EnvoyException(
fmt::format("cluster: LB policy {} cannot be combined with lb_subset_config",
envoy::config::cluster::v3::Cluster::LbPolicy_Name(config.lb_policy())));
}

if (config.has_common_lb_config()) {
throw EnvoyException(
fmt::format("cluster: LB policy {} cannot be combined with common_lb_config",
envoy::config::cluster::v3::Cluster::LbPolicy_Name(config.lb_policy())));
}

if (!config.has_load_balancing_policy()) {
throw EnvoyException(
fmt::format("cluster: LB policy {} requires load_balancing_policy to be set",
envoy::config::cluster::v3::Cluster::LbPolicy_Name(config.lb_policy())));
}

for (const auto& policy : config.load_balancing_policy().policies()) {
TypedLoadBalancerFactory* factory =
Config::Utility::getAndCheckFactory<TypedLoadBalancerFactory>(
policy.typed_extension_config(), /*is_optional=*/true);
if (factory != nullptr) {
load_balancing_policy_ = policy;
load_balancer_factory_ = factory;
break;
}
}

if (load_balancer_factory_ == nullptr) {
throw EnvoyException(fmt::format(
"Didn't find a registered load balancer factory implementation for cluster: '{}'", name_));
}

lb_type_ = LoadBalancerType::LoadBalancingPolicyConfig;
}

ProtocolOptionsConfigConstSharedPtr
ClusterInfoImpl::extensionProtocolOptions(const std::string& name) const {
auto i = extension_protocol_options_.find(name);
Expand Down
1 change: 1 addition & 0 deletions source/common/upstream/upstream_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,7 @@ class ClusterInfoImpl : public ClusterInfo,
const envoy::config::core::v3::HttpProtocolOptions& commonHttpProtocolOptions() const override {
return http_protocol_options_->common_http_protocol_options_;
}
void configureLbPolicies(const envoy::config::cluster::v3::Cluster& config);
ProtocolOptionsConfigConstSharedPtr
extensionProtocolOptions(const std::string& name) const override;
LoadBalancerType lbType() const override { return lb_type_; }
Expand Down
2 changes: 2 additions & 0 deletions test/common/upstream/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,8 @@ envoy_cc_test(
"//test/mocks/upstream:cluster_manager_mocks",
"//test/mocks/upstream:health_checker_mocks",
"//test/mocks/upstream:priority_set_mocks",
"//test/mocks/upstream:thread_aware_load_balancer_mocks",
"//test/mocks/upstream:typed_load_balancer_factory_mocks",
"//test/test_common:registry_lib",
"//test/test_common:test_runtime_lib",
"//test/test_common:utility_lib",
Expand Down
134 changes: 134 additions & 0 deletions test/common/upstream/upstream_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
#include "test/mocks/upstream/cluster_manager.h"
#include "test/mocks/upstream/health_checker.h"
#include "test/mocks/upstream/priority_set.h"
#include "test/mocks/upstream/thread_aware_load_balancer.h"
#include "test/mocks/upstream/typed_load_balancer_factory.h"
#include "test/test_common/environment.h"
#include "test/test_common/registry.h"
#include "test/test_common/test_runtime.h"
Expand Down Expand Up @@ -2079,6 +2081,138 @@ TEST_F(StaticClusterImplTest, UnsupportedLBType) {
EnvoyException, "invalid value \"fakelbtype\"");
}

// load_balancing_policy should be used when lb_policy is set to LOAD_BALANCING_POLICY_CONFIG.
TEST_F(StaticClusterImplTest, LoadBalancingPolicyWithLbPolicy) {
const std::string yaml = R"EOF(
name: staticcluster
connect_timeout: 0.25s
type: static
lb_policy: LOAD_BALANCING_POLICY_CONFIG
load_balancing_policy:
policies:
- typed_extension_config:
name: custom_lb
typed_config:
"@type": type.googleapis.com/google.protobuf.Struct
value:
foo: "bar"
load_assignment:
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 10.0.0.1
port_value: 11001
)EOF";

NiceMock<MockTypedLoadBalancerFactory> factory;
EXPECT_CALL(factory, name()).WillRepeatedly(Return("custom_lb"));
Registry::InjectFactory<TypedLoadBalancerFactory> registered_factory(factory);

envoy::config::cluster::v3::Cluster cluster_config = parseClusterFromV3Yaml(yaml);
Envoy::Stats::ScopePtr scope = stats_.createScope(fmt::format(
"cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name()
: cluster_config.alt_stat_name()));
Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context(
admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_,
singleton_manager_, tls_, validation_visitor_, *api_, options_);
StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), true);
cluster.initialize([] {});

EXPECT_EQ(1UL, cluster.prioritySet().hostSetsPerPriority()[0]->healthyHosts().size());
EXPECT_EQ(LoadBalancerType::LoadBalancingPolicyConfig, cluster.info()->lbType());
EXPECT_TRUE(cluster.info()->addedViaApi());
}

// load_balancing_policy should also be used when lb_policy is set to something else besides
// LOAD_BALANCING_POLICY_CONFIG.
TEST_F(StaticClusterImplTest, LoadBalancingPolicyWithOtherLbPolicy) {
const std::string yaml = R"EOF(
name: staticcluster
connect_timeout: 0.25s
type: static
lb_policy: ROUND_ROBIN
load_balancing_policy:
policies:
- typed_extension_config:
name: custom_lb
typed_config:
"@type": type.googleapis.com/google.protobuf.Struct
value:
foo: "bar"
load_assignment:
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 10.0.0.1
port_value: 11001
)EOF";

NiceMock<MockTypedLoadBalancerFactory> factory;
EXPECT_CALL(factory, name()).WillRepeatedly(Return("custom_lb"));
Registry::InjectFactory<TypedLoadBalancerFactory> registered_factory(factory);

envoy::config::cluster::v3::Cluster cluster_config = parseClusterFromV3Yaml(yaml);
Envoy::Stats::ScopePtr scope = stats_.createScope(fmt::format(
"cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name()
: cluster_config.alt_stat_name()));
Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context(
admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_,
singleton_manager_, tls_, validation_visitor_, *api_, options_);
StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), true);
cluster.initialize([] {});

EXPECT_EQ(1UL, cluster.prioritySet().hostSetsPerPriority()[0]->healthyHosts().size());
EXPECT_EQ(LoadBalancerType::LoadBalancingPolicyConfig, cluster.info()->lbType());
EXPECT_TRUE(cluster.info()->addedViaApi());
}

// load_balancing_policy should also be used when lb_policy is omitted.
TEST_F(StaticClusterImplTest, LoadBalancingPolicyWithoutLbPolicy) {
const std::string yaml = R"EOF(
name: staticcluster
connect_timeout: 0.25s
type: static
load_balancing_policy:
policies:
- typed_extension_config:
name: custom_lb
typed_config:
"@type": type.googleapis.com/google.protobuf.Struct
value:
foo: "bar"
load_assignment:
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 10.0.0.1
port_value: 11001
)EOF";

NiceMock<MockTypedLoadBalancerFactory> factory;
EXPECT_CALL(factory, name()).WillRepeatedly(Return("custom_lb"));
Registry::InjectFactory<TypedLoadBalancerFactory> registered_factory(factory);

envoy::config::cluster::v3::Cluster cluster_config = parseClusterFromV3Yaml(yaml);
Envoy::Stats::ScopePtr scope = stats_.createScope(fmt::format(
"cluster.{}.", cluster_config.alt_stat_name().empty() ? cluster_config.name()
: cluster_config.alt_stat_name()));
Envoy::Server::Configuration::TransportSocketFactoryContextImpl factory_context(
admin_, ssl_context_manager_, *scope, cm_, local_info_, dispatcher_, stats_,
singleton_manager_, tls_, validation_visitor_, *api_, options_);
StaticClusterImpl cluster(cluster_config, runtime_, factory_context, std::move(scope), true);
cluster.initialize([] {});

EXPECT_EQ(1UL, cluster.prioritySet().hostSetsPerPriority()[0]->healthyHosts().size());
EXPECT_EQ(LoadBalancerType::LoadBalancingPolicyConfig, cluster.info()->lbType());
EXPECT_TRUE(cluster.info()->addedViaApi());
}

TEST_F(StaticClusterImplTest, MalformedHostIP) {
const std::string yaml = R"EOF(
name: name
Expand Down
10 changes: 10 additions & 0 deletions test/mocks/upstream/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ envoy_cc_mock(
":thread_aware_load_balancer_mocks",
":thread_local_cluster_mocks",
":transport_socket_match_mocks",
":typed_load_balancer_factory_mocks",
"//envoy/http:async_client_interface",
"//envoy/upstream:cluster_factory_interface",
"//envoy/upstream:cluster_manager_interface",
Expand Down Expand Up @@ -204,6 +205,15 @@ envoy_cc_mock(
],
)

envoy_cc_mock(
name = "typed_load_balancer_factory_mocks",
srcs = ["typed_load_balancer_factory.cc"],
hdrs = ["typed_load_balancer_factory.h"],
deps = [
"//envoy/upstream:load_balancer_interface",
],
)

envoy_cc_mock(
name = "thread_local_cluster_mocks",
srcs = ["thread_local_cluster.cc"],
Expand Down
10 changes: 10 additions & 0 deletions test/mocks/upstream/typed_load_balancer_factory.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include "typed_load_balancer_factory.h"

namespace Envoy {
namespace Upstream {
MockTypedLoadBalancerFactory::MockTypedLoadBalancerFactory() = default;

MockTypedLoadBalancerFactory::~MockTypedLoadBalancerFactory() = default;

} // namespace Upstream
} // namespace Envoy
Loading