1
#pragma once
2

            
3
#include <cstdint>
4
#include <memory>
5
#include <optional>
6
#include <string>
7
#include <utility>
8
#include <vector>
9

            
10
#include "envoy/common/optref.h"
11
#include "envoy/common/random_generator.h"
12
#include "envoy/common/time.h"
13
#include "envoy/config/core/v3/base.pb.h"
14
#include "envoy/extensions/load_balancing_policies/override_host/v3/override_host.pb.h"
15
#include "envoy/http/header_map.h"
16
#include "envoy/runtime/runtime.h"
17
#include "envoy/upstream/load_balancer.h"
18
#include "envoy/upstream/upstream.h"
19

            
20
#include "source/common/common/logger.h"
21
#include "source/common/config/metadata.h"
22
#include "source/extensions/load_balancing_policies/override_host/override_host_filter_state.h"
23

            
24
#include "absl/status/status.h"
25
#include "absl/status/statusor.h"
26
#include "absl/strings/string_view.h"
27
#include "absl/types/optional.h"
28

            
29
namespace Envoy {
30
namespace Extensions {
31
namespace LoadBalancingPolicies {
32
namespace OverrideHost {
33

            
34
using ::envoy::extensions::load_balancing_policies::override_host::v3::OverrideHost;
35

            
36
using ::Envoy::Random::RandomGenerator;
37
using ::Envoy::Runtime::Loader;
38
using ::Envoy::Server::Configuration::ServerFactoryContext;
39
using ::Envoy::Upstream::ClusterInfo;
40
using ::Envoy::Upstream::ClusterLbStats;
41
using ::Envoy::Upstream::Host;
42
using ::Envoy::Upstream::HostConstSharedPtr;
43
using ::Envoy::Upstream::HostSelectionResponse;
44
using ::Envoy::Upstream::LoadBalancerConfigPtr;
45
using ::Envoy::Upstream::LoadBalancerContext;
46
using ::Envoy::Upstream::LoadBalancerFactorySharedPtr;
47
using ::Envoy::Upstream::LoadBalancerParams;
48
using ::Envoy::Upstream::LoadBalancerPtr;
49
using ::Envoy::Upstream::PrioritySet;
50
using ::Envoy::Upstream::ThreadAwareLoadBalancerPtr;
51
using ::Envoy::Upstream::TypedLoadBalancerFactory;
52

            
53
// Parsed configuration for the dynamic forwarding load balancer. It contains
54
// factory and config for the load balancer specified in the
55
// `fallback_policy` field of the OverrideHost config
56
// proto.
57
class OverrideHostLbConfig : public Upstream::LoadBalancerConfig {
58
public:
59
  struct OverrideSource {
60
    static OverrideSource make(const OverrideHost::OverrideHostSource& config);
61
    absl::optional<Http::LowerCaseString> header_name;
62
    absl::optional<Config::MetadataKey> metadata_key;
63
  };
64

            
65
  static absl::StatusOr<std::unique_ptr<OverrideHostLbConfig>> make(const OverrideHost& config,
66
                                                                    ServerFactoryContext& context);
67

            
68
  ThreadAwareLoadBalancerPtr create(const ClusterInfo& cluster_info,
69
                                    const PrioritySet& priority_set, Loader& runtime,
70
                                    RandomGenerator& random, TimeSource& time_source) const;
71

            
72
47
  const std::vector<OverrideSource>& overrideHostSources() const { return override_host_sources_; }
73
63
  const absl::optional<Config::MetadataKey>& selectedHostKey() const { return selected_host_key_; }
74

            
75
private:
76
  OverrideHostLbConfig(std::vector<OverrideSource>&& override_host_sources,
77
                       absl::optional<Config::MetadataKey>&& selected_host_key,
78
                       TypedLoadBalancerFactory* fallback_load_balancer_factory,
79
                       LoadBalancerConfigPtr&& fallback_load_balancer_config);
80

            
81
  static absl::StatusOr<std::vector<OverrideSource>> makeOverrideSources(
82
      const Protobuf::RepeatedPtrField<OverrideHost::OverrideHostSource>& override_sources);
83

            
84
  // Group the factory and config together to make them const in the
85
  // configuration object.
86
  struct FallbackLbConfig {
87
    TypedLoadBalancerFactory* const load_balancer_factory = nullptr;
88
    const LoadBalancerConfigPtr load_balancer_config;
89
  };
90
  const FallbackLbConfig fallback_picker_lb_config_;
91

            
92
  const std::vector<OverrideSource> override_host_sources_;
93
  const absl::optional<Config::MetadataKey> selected_host_key_;
94
};
95

            
96
// Load balancer for the dynamic forwarding, supporting external endpoint
97
// selection by LbTrafficExtension.
98
// The load balancer uses host list supplied in the request metadata under the
99
// `com.google.envoy.override_host.localities_and_endpoints` key to pick
100
// the next backend. TODO(yavlasov): Add a link to the proto describing the
101
// format of the host list when it is committed.
102
//
103
// If the metadata is not present, it falls back to using the load balancer
104
// specified in the `fallback_policy` field of the
105
// LoadBalancingPolicyConfig config proto.
106
// The metadata is not present in two scenarios:
107
// 1. The Endpoint Picker LbTrafficExtension extension has not been called yet.
108
//    In this case the picked locality is used to call Endpoint Picker specific
109
//    to selected locality.
110
// 2. The Locality Picker extension failed to select a host. In this case, the
111
//    `fallback_policy` is used as a fallback to pick the backend.
112
//
113
// Once the initial locality is picked, the load balancer will use the host list
114
// from the request metadata to pick the next backend.
115
class OverrideHostLoadBalancer : public Upstream::ThreadAwareLoadBalancer,
116
                                 protected Logger::Loggable<Logger::Id::upstream> {
117
public:
118
  OverrideHostLoadBalancer(const OverrideHostLbConfig& config,
119
                           ThreadAwareLoadBalancerPtr fallback_picker_lb)
120
27
      : config_(config), fallback_picker_lb_(std::move(fallback_picker_lb)) {}
121

            
122
  LoadBalancerFactorySharedPtr factory() override;
123

            
124
  absl::Status initialize() override;
125

            
126
private:
127
  // Thread-local LB implementation.
128
  class LoadBalancerImpl : public Upstream::LoadBalancer {
129
  public:
130
    LoadBalancerImpl(const OverrideHostLbConfig& config, LoadBalancerPtr fallback_picker_lb,
131
                     const PrioritySet& priority_set)
132
39
        : config_(config), fallback_picker_lb_(std::move(fallback_picker_lb)),
133
39
          priority_set_(priority_set) {}
134

            
135
    HostConstSharedPtr peekAnotherHost(LoadBalancerContext* context) override;
136

            
137
    HostSelectionResponse chooseHost(LoadBalancerContext* context) override;
138

            
139
1
    OptRef<Http::ConnectionPool::ConnectionLifetimeCallbacks> lifetimeCallbacks() override {
140
      // TODO(yavlasov): Check if this is needed by the fallback LB.
141
1
      return {};
142
1
    }
143

            
144
    absl::optional<Upstream::SelectedPoolAndConnection>
145
1
    selectExistingConnection(LoadBalancerContext*, const Host&, std::vector<uint8_t>&) override {
146
      // This functionality is not supported by dynamic forwarding LB.
147
1
      return std::nullopt;
148
1
    }
149

            
150
  private:
151
    HostConstSharedPtr getEndpoint(OverrideHostFilterState& override_host_state);
152
    HostConstSharedPtr findHost(absl::string_view endpoint);
153

            
154
    HostSelectionResponse chooseHostInternal(LoadBalancerContext* context);
155

            
156
    void addSelectedHostKey(LoadBalancerContext* context, HostSelectionResponse& response);
157

            
158
    // Lookup the list of endpoints selected by the LbTrafficExtension in the
159
    // header or in the request metadata.
160
    // TODO(wbpcode): will absl::InlinedVector be used here be better?
161
    std::vector<std::string> getSelectedHosts(LoadBalancerContext* context);
162

            
163
    // Return a list of endpoints selected by the LbTrafficExtension.
164
    // nullopt if the metadata is not present.
165
    absl::optional<absl::string_view>
166
    getSelectedHostsFromMetadata(const ::envoy::config::core::v3::Metadata& metadata,
167
                                 const Config::MetadataKey& metadata_key);
168

            
169
    // Return a list of endpoints selected by the LbTrafficExtension, specified.
170
    // in the header. nullopt if the header is not present.
171
    absl::optional<absl::string_view>
172
    getSelectedHostsFromHeader(const Http::RequestHeaderMap* header_map,
173
                               const Http::LowerCaseString& header_name);
174

            
175
    const OverrideHostLbConfig& config_;
176
    const LoadBalancerPtr fallback_picker_lb_;
177
    const PrioritySet& priority_set_;
178
  };
179

            
180
  // LoadBalancerFactory implementation shared by worker threads to create
181
  // thread-local LB instances. Shared state in this class MUST be protected by
182
  // appropriate mutexes.
183
  class LoadBalancerFactoryImpl : public Upstream::LoadBalancerFactory {
184
  public:
185
    LoadBalancerFactoryImpl(const OverrideHostLbConfig& config,
186
                            LoadBalancerFactorySharedPtr fallback_picker_lb_factory)
187
27
        : config_(config), fallback_picker_lb_factory_(std::move(fallback_picker_lb_factory)) {}
188

            
189
    // Called by worker threads to create a thread-local load balancer.
190
    LoadBalancerPtr create(LoadBalancerParams params) override;
191

            
192
  private:
193
    // Hosts in the load balancer. Owned by the cluster manager.
194
    const OverrideHostLbConfig& config_;
195
    LoadBalancerFactorySharedPtr fallback_picker_lb_factory_;
196
  };
197

            
198
  const OverrideHostLbConfig& config_;
199
  // Shared factory used to create new thread-local LB implementations.
200
  std::shared_ptr<LoadBalancerFactoryImpl> factory_;
201
  const ThreadAwareLoadBalancerPtr fallback_picker_lb_;
202
};
203

            
204
} // namespace OverrideHost
205
} // namespace LoadBalancingPolicies
206
} // namespace Extensions
207
} // namespace Envoy