1
#pragma once
2

            
3
#include "envoy/config/cluster/v3/cluster.pb.h"
4
#include "envoy/extensions/load_balancing_policies/common/v3/common.pb.h"
5
#include "envoy/extensions/load_balancing_policies/common/v3/common.pb.validate.h"
6
#include "envoy/extensions/load_balancing_policies/subset/v3/subset.pb.h"
7
#include "envoy/extensions/load_balancing_policies/subset/v3/subset.pb.validate.h"
8
#include "envoy/upstream/load_balancer.h"
9

            
10
namespace Envoy {
11
namespace Upstream {
12

            
13
using SubsetLbConfigProto = envoy::extensions::load_balancing_policies::subset::v3::Subset;
14
using ClusterProto = envoy::config::cluster::v3::Cluster;
15
using LegacySubsetLbConfigProto = envoy::config::cluster::v3::Cluster::LbSubsetConfig;
16

            
17
/**
18
 * This is part of subset load balancer config and is used to store config of single selector.
19
 */
20
class SubsetSelector {
21
public:
22
  SubsetSelector(const Protobuf::RepeatedPtrField<std::string>& selector_keys,
23
                 envoy::config::cluster::v3::Cluster::LbSubsetConfig::LbSubsetSelector::
24
                     LbSubsetSelectorFallbackPolicy fallback_policy,
25
                 const Protobuf::RepeatedPtrField<std::string>& fallback_keys_subset,
26
                 bool single_host_per_subset);
27

            
28
  // SubsetSelector
29
1319
  const std::set<std::string>& selectorKeys() const { return selector_keys_; }
30
  envoy::config::cluster::v3::Cluster::LbSubsetConfig::LbSubsetSelector::
31
      LbSubsetSelectorFallbackPolicy
32
230
      fallbackPolicy() const {
33
230
    return fallback_policy_;
34
230
  }
35
228
  const std::set<std::string>& fallbackKeysSubset() const { return fallback_keys_subset_; }
36
748
  bool singleHostPerSubset() const { return single_host_per_subset_; }
37

            
38
private:
39
  const std::set<std::string> selector_keys_;
40
  const std::set<std::string> fallback_keys_subset_;
41
  // Keep small members (bools and enums) at the end of class, to reduce alignment overhead.
42
  const envoy::config::cluster::v3::Cluster::LbSubsetConfig::LbSubsetSelector::
43
      LbSubsetSelectorFallbackPolicy fallback_policy_;
44
  const bool single_host_per_subset_ : 1;
45
};
46

            
47
using SubsetSelectorPtr = std::shared_ptr<SubsetSelector>;
48

            
49
/**
50
 * Load Balancer subset configuration.
51
 */
52
class LoadBalancerSubsetInfo {
53
public:
54
176
  virtual ~LoadBalancerSubsetInfo() = default;
55

            
56
  /**
57
   * @return bool true if load balancer subsets are configured.
58
   */
59
  virtual bool isEnabled() const PURE;
60

            
61
  /**
62
   * @return LbSubsetFallbackPolicy the fallback policy used when
63
   * route metadata does not match any subset.
64
   */
65
  virtual envoy::config::cluster::v3::Cluster::LbSubsetConfig::LbSubsetFallbackPolicy
66
  fallbackPolicy() const PURE;
67

            
68
  /**
69
   * @return LbSubsetMetadataFallbackPolicy the fallback policy used to try different route metadata
70
   * until a host is found
71
   */
72
  virtual envoy::config::cluster::v3::Cluster::LbSubsetConfig::LbSubsetMetadataFallbackPolicy
73
  metadataFallbackPolicy() const PURE;
74

            
75
  /**
76
   * @return Protobuf::Struct the struct describing the metadata for a
77
   *         host to be included in the default subset.
78
   */
79
  virtual const Protobuf::Struct& defaultSubset() const PURE;
80

            
81
  /*
82
   * @return const std:vector<std:set<std::string>>& a vector of
83
   * sorted keys used to define load balancer subsets.
84
   */
85
  virtual const std::vector<SubsetSelectorPtr>& subsetSelectors() const PURE;
86

            
87
  /*
88
   * @return bool whether routing to subsets should take locality weights into account.
89
   */
90
  virtual bool localityWeightAware() const PURE;
91

            
92
  /*
93
   * @return bool whether the locality weights should be scaled to compensate for the
94
   * fraction of hosts removed from the original host set.
95
   */
96
  virtual bool scaleLocalityWeight() const PURE;
97

            
98
  /*
99
   * @return bool whether to attempt to select a host from the entire cluster if host
100
   * selection from the fallback subset fails.
101
   */
102
  virtual bool panicModeAny() const PURE;
103

            
104
  /*
105
   * @return bool whether matching metadata should attempt to match against any of the
106
   * elements in a list value defined in endpoint metadata.
107
   */
108
  virtual bool listAsAny() const PURE;
109

            
110
  /*
111
   * @return bool whether redundant key/value pairs is allowed in the request metadata.
112
   */
113
  virtual bool allowRedundantKeys() const PURE;
114
};
115

            
116
using LoadBalancerSubsetInfoPtr = std::unique_ptr<LoadBalancerSubsetInfo>;
117

            
118
/**
119
 * Both the legacy and extension subset proto configs are converted to this class.
120
 */
121
class LoadBalancerSubsetInfoImpl : public LoadBalancerSubsetInfo {
122
public:
123
  using FallbackPolicy =
124
      envoy::config::cluster::v3::Cluster::LbSubsetConfig::LbSubsetFallbackPolicy;
125
  using MetadataFallbackPolicy =
126
      envoy::config::cluster::v3::Cluster::LbSubsetConfig::LbSubsetMetadataFallbackPolicy;
127
  using SubsetFallbackPolicy = envoy::config::cluster::v3::Cluster::LbSubsetConfig::
128
      LbSubsetSelector::LbSubsetSelectorFallbackPolicy;
129

            
130
  LoadBalancerSubsetInfoImpl(const SubsetLbConfigProto& subset_config)
131
4
      : default_subset_(subset_config.default_subset()),
132
4
        fallback_policy_(static_cast<FallbackPolicy>(subset_config.fallback_policy())),
133
        metadata_fallback_policy_(
134
4
            static_cast<MetadataFallbackPolicy>(subset_config.metadata_fallback_policy())),
135
4
        enabled_(!subset_config.subset_selectors().empty()),
136
4
        locality_weight_aware_(subset_config.locality_weight_aware()),
137
4
        scale_locality_weight_(subset_config.scale_locality_weight()),
138
4
        panic_mode_any_(subset_config.panic_mode_any()), list_as_any_(subset_config.list_as_any()),
139
4
        allow_redundant_keys_(subset_config.allow_redundant_keys()) {
140
11
    for (const auto& subset : subset_config.subset_selectors()) {
141
11
      if (!subset.keys().empty()) {
142
11
        subset_selectors_.emplace_back(std::make_shared<SubsetSelector>(
143
11
            subset.keys(), static_cast<SubsetFallbackPolicy>(subset.fallback_policy()),
144
11
            subset.fallback_keys_subset(), subset.single_host_per_subset()));
145
11
      }
146
11
    }
147

            
148
4
    if (allow_redundant_keys_) {
149
      // Sort subset selectors by number of keys, descending. This will ensure that the longest
150
      // matching subset selector will be at the beginning of the list.
151
1
      std::stable_sort(subset_selectors_.begin(), subset_selectors_.end(),
152
7
                       [](const SubsetSelectorPtr& a, const SubsetSelectorPtr& b) -> bool {
153
7
                         return a->selectorKeys().size() > b->selectorKeys().size();
154
7
                       });
155
1
    }
156
4
  }
157

            
158
  LoadBalancerSubsetInfoImpl(const LegacySubsetLbConfigProto& subset_config)
159
58
      : default_subset_(subset_config.default_subset()),
160
58
        fallback_policy_(subset_config.fallback_policy()),
161
58
        metadata_fallback_policy_(subset_config.metadata_fallback_policy()),
162
58
        enabled_(!subset_config.subset_selectors().empty()),
163
58
        locality_weight_aware_(subset_config.locality_weight_aware()),
164
58
        scale_locality_weight_(subset_config.scale_locality_weight()),
165
58
        panic_mode_any_(subset_config.panic_mode_any()), list_as_any_(subset_config.list_as_any()) {
166
60
    for (const auto& subset : subset_config.subset_selectors()) {
167
60
      if (!subset.keys().empty()) {
168
60
        subset_selectors_.emplace_back(std::make_shared<SubsetSelector>(
169
60
            subset.keys(), subset.fallback_policy(), subset.fallback_keys_subset(),
170
60
            subset.single_host_per_subset()));
171
60
      }
172
60
    }
173
58
  }
174
  LoadBalancerSubsetInfoImpl()
175
      : LoadBalancerSubsetInfoImpl(
176
            envoy::config::cluster::v3::Cluster::LbSubsetConfig::default_instance()) {}
177

            
178
  // Upstream::LoadBalancerSubsetInfo
179
2
  bool isEnabled() const override { return enabled_; }
180
99
  FallbackPolicy fallbackPolicy() const override { return fallback_policy_; }
181
97
  MetadataFallbackPolicy metadataFallbackPolicy() const override {
182
97
    return metadata_fallback_policy_;
183
97
  }
184
197
  const Protobuf::Struct& defaultSubset() const override { return default_subset_; }
185
109
  const std::vector<SubsetSelectorPtr>& subsetSelectors() const override {
186
109
    return subset_selectors_;
187
109
  }
188
97
  bool localityWeightAware() const override { return locality_weight_aware_; }
189
97
  bool scaleLocalityWeight() const override { return scale_locality_weight_; }
190
97
  bool panicModeAny() const override { return panic_mode_any_; }
191
97
  bool listAsAny() const override { return list_as_any_; }
192
97
  bool allowRedundantKeys() const override { return allow_redundant_keys_; }
193

            
194
private:
195
  const Protobuf::Struct default_subset_;
196
  std::vector<SubsetSelectorPtr> subset_selectors_;
197
  // Keep small members (bools and enums) at the end of class, to reduce alignment overhead.
198
  const FallbackPolicy fallback_policy_;
199
  const MetadataFallbackPolicy metadata_fallback_policy_;
200
  const bool enabled_ : 1;
201
  const bool locality_weight_aware_ : 1;
202
  const bool scale_locality_weight_ : 1;
203
  const bool panic_mode_any_ : 1;
204
  const bool list_as_any_ : 1;
205
  const bool allow_redundant_keys_{};
206
};
207

            
208
using DefaultLoadBalancerSubsetInfo = ConstSingleton<LoadBalancerSubsetInfoImpl>;
209

            
210
class SubsetLoadBalancerConfig : public Upstream::LoadBalancerConfig {
211
public:
212
  SubsetLoadBalancerConfig(Server::Configuration::ServerFactoryContext& factory_context,
213
                           const SubsetLbConfigProto& config, absl::Status& creation_status);
214
  SubsetLoadBalancerConfig(Server::Configuration::ServerFactoryContext& factory_context,
215
                           const ClusterProto& cluster, absl::Status& creation_status);
216
  SubsetLoadBalancerConfig(LoadBalancerSubsetInfoPtr subset_info,
217
                           TypedLoadBalancerFactory* child_factory,
218
                           LoadBalancerConfigPtr child_config);
219

            
220
51
  absl::Status validateEndpoints(const PriorityState& priorities) const override {
221
51
    if (child_lb_config_ != nullptr) {
222
51
      return child_lb_config_->validateEndpoints(priorities);
223
51
    }
224
    return absl::OkStatus();
225
51
  }
226

            
227
  Upstream::ThreadAwareLoadBalancerPtr
228
  createLoadBalancer(const Upstream::ClusterInfo& cluster_info,
229
                     const Upstream::PrioritySet& child_priority_set, Runtime::Loader& runtime,
230
483
                     Random::RandomGenerator& random, TimeSource& time_source) const {
231
483
    return child_lb_factory_->create(
232
483
        makeOptRefFromPtr<const Upstream::LoadBalancerConfig>(child_lb_config_.get()), cluster_info,
233
483
        child_priority_set, runtime, random, time_source);
234
483
  }
235
10
  std::string childLoadBalancerName() const { return child_lb_factory_->name(); }
236

            
237
2100
  const LoadBalancerSubsetInfo& subsetInfo() const { return *subset_info_; }
238

            
239
private:
240
  LoadBalancerSubsetInfoPtr subset_info_;
241
  Upstream::TypedLoadBalancerFactory* child_lb_factory_{};
242
  Upstream::LoadBalancerConfigPtr child_lb_config_;
243
};
244

            
245
} // namespace Upstream
246
} // namespace Envoy