• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2015 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include "src/core/load_balancing/lb_policy_registry.h"
18 
19 #include <grpc/support/json.h>
20 #include <grpc/support/port_platform.h>
21 
22 #include <algorithm>
23 #include <map>
24 #include <string>
25 #include <utility>
26 #include <vector>
27 
28 #include "absl/log/check.h"
29 #include "absl/log/log.h"
30 #include "absl/status/status.h"
31 #include "absl/strings/str_cat.h"
32 #include "absl/strings/str_format.h"
33 #include "absl/strings/str_join.h"
34 #include "absl/strings/string_view.h"
35 #include "src/core/load_balancing/lb_policy.h"
36 
37 namespace grpc_core {
38 
39 //
40 // LoadBalancingPolicyRegistry::Builder
41 //
42 
RegisterLoadBalancingPolicyFactory(std::unique_ptr<LoadBalancingPolicyFactory> factory)43 void LoadBalancingPolicyRegistry::Builder::RegisterLoadBalancingPolicyFactory(
44     std::unique_ptr<LoadBalancingPolicyFactory> factory) {
45   VLOG(2) << "registering LB policy factory for \"" << factory->name() << "\"";
46   CHECK(factories_.find(factory->name()) == factories_.end());
47   factories_.emplace(factory->name(), std::move(factory));
48 }
49 
Build()50 LoadBalancingPolicyRegistry LoadBalancingPolicyRegistry::Builder::Build() {
51   LoadBalancingPolicyRegistry out;
52   out.factories_ = std::move(factories_);
53   return out;
54 }
55 
56 //
57 // LoadBalancingPolicyRegistry
58 //
59 
60 LoadBalancingPolicyFactory*
GetLoadBalancingPolicyFactory(absl::string_view name) const61 LoadBalancingPolicyRegistry::GetLoadBalancingPolicyFactory(
62     absl::string_view name) const {
63   auto it = factories_.find(name);
64   if (it == factories_.end()) return nullptr;
65   return it->second.get();
66 }
67 
68 OrphanablePtr<LoadBalancingPolicy>
CreateLoadBalancingPolicy(absl::string_view name,LoadBalancingPolicy::Args args) const69 LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
70     absl::string_view name, LoadBalancingPolicy::Args args) const {
71   // Find factory.
72   LoadBalancingPolicyFactory* factory = GetLoadBalancingPolicyFactory(name);
73   if (factory == nullptr) return nullptr;  // Specified name not found.
74   // Create policy via factory.
75   return factory->CreateLoadBalancingPolicy(std::move(args));
76 }
77 
LoadBalancingPolicyExists(absl::string_view name,bool * requires_config) const78 bool LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(
79     absl::string_view name, bool* requires_config) const {
80   auto* factory = GetLoadBalancingPolicyFactory(name);
81   if (factory == nullptr) return false;
82   // If requested, check if the load balancing policy allows an empty config.
83   if (requires_config != nullptr) {
84     auto config = factory->ParseLoadBalancingConfig(Json::FromObject({}));
85     *requires_config = !config.ok();
86   }
87   return true;
88 }
89 
90 // Returns the JSON node of policy (with both policy name and config content)
91 // given the JSON node of a LoadBalancingConfig array.
92 absl::StatusOr<Json::Object::const_iterator>
ParseLoadBalancingConfigHelper(const Json & lb_config_array) const93 LoadBalancingPolicyRegistry::ParseLoadBalancingConfigHelper(
94     const Json& lb_config_array) const {
95   if (lb_config_array.type() != Json::Type::kArray) {
96     return absl::InvalidArgumentError("type should be array");
97   }
98   // Find the first LB policy that this client supports.
99   std::vector<absl::string_view> policies_tried;
100   for (const Json& lb_config : lb_config_array.array()) {
101     if (lb_config.type() != Json::Type::kObject) {
102       return absl::InvalidArgumentError("child entry should be of type object");
103     }
104     if (lb_config.object().empty()) {
105       return absl::InvalidArgumentError("no policy found in child entry");
106     }
107     if (lb_config.object().size() > 1) {
108       return absl::InvalidArgumentError("oneOf violation");
109     }
110     auto it = lb_config.object().begin();
111     if (it->second.type() != Json::Type::kObject) {
112       return absl::InvalidArgumentError("child entry should be of type object");
113     }
114     // If we support this policy, then select it.
115     if (LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(
116             it->first.c_str(), nullptr)) {
117       return it;
118     }
119     policies_tried.push_back(it->first);
120   }
121   return absl::FailedPreconditionError(absl::StrCat(
122       "No known policies in list: ", absl::StrJoin(policies_tried, " ")));
123 }
124 
125 absl::StatusOr<RefCountedPtr<LoadBalancingPolicy::Config>>
ParseLoadBalancingConfig(const Json & json) const126 LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(const Json& json) const {
127   auto policy = ParseLoadBalancingConfigHelper(json);
128   if (!policy.ok()) return policy.status();
129   // Find factory.
130   LoadBalancingPolicyFactory* factory =
131       GetLoadBalancingPolicyFactory((*policy)->first.c_str());
132   if (factory == nullptr) {
133     return absl::FailedPreconditionError(absl::StrFormat(
134         "Factory not found for policy \"%s\"", (*policy)->first));
135   }
136   // Parse load balancing config via factory.
137   return factory->ParseLoadBalancingConfig((*policy)->second);
138 }
139 
140 }  // namespace grpc_core
141