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 <grpc/support/port_platform.h>
18 
19 #include "src/core/lib/load_balancing/lb_policy_registry.h"
20 
21 #include <algorithm>
22 #include <initializer_list>
23 #include <map>
24 #include <string>
25 #include <utility>
26 #include <vector>
27 
28 #include "absl/status/status.h"
29 #include "absl/strings/str_cat.h"
30 #include "absl/strings/str_format.h"
31 #include "absl/strings/str_join.h"
32 #include "absl/strings/string_view.h"
33 
34 #include <grpc/support/json.h>
35 #include <grpc/support/log.h>
36 
37 #include "src/core/lib/load_balancing/lb_policy.h"
38 
39 namespace grpc_core {
40 
41 //
42 // LoadBalancingPolicyRegistry::Builder
43 //
44 
RegisterLoadBalancingPolicyFactory(std::unique_ptr<LoadBalancingPolicyFactory> factory)45 void LoadBalancingPolicyRegistry::Builder::RegisterLoadBalancingPolicyFactory(
46     std::unique_ptr<LoadBalancingPolicyFactory> factory) {
47   gpr_log(GPR_DEBUG, "registering LB policy factory for \"%s\"",
48           std::string(factory->name()).c_str());
49   GPR_ASSERT(factories_.find(factory->name()) == factories_.end());
50   factories_.emplace(factory->name(), std::move(factory));
51 }
52 
Build()53 LoadBalancingPolicyRegistry LoadBalancingPolicyRegistry::Builder::Build() {
54   LoadBalancingPolicyRegistry out;
55   out.factories_ = std::move(factories_);
56   return out;
57 }
58 
59 //
60 // LoadBalancingPolicyRegistry
61 //
62 
63 LoadBalancingPolicyFactory*
GetLoadBalancingPolicyFactory(absl::string_view name) const64 LoadBalancingPolicyRegistry::GetLoadBalancingPolicyFactory(
65     absl::string_view name) const {
66   auto it = factories_.find(name);
67   if (it == factories_.end()) return nullptr;
68   return it->second.get();
69 }
70 
71 OrphanablePtr<LoadBalancingPolicy>
CreateLoadBalancingPolicy(absl::string_view name,LoadBalancingPolicy::Args args) const72 LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
73     absl::string_view name, LoadBalancingPolicy::Args args) const {
74   // Find factory.
75   LoadBalancingPolicyFactory* factory = GetLoadBalancingPolicyFactory(name);
76   if (factory == nullptr) return nullptr;  // Specified name not found.
77   // Create policy via factory.
78   return factory->CreateLoadBalancingPolicy(std::move(args));
79 }
80 
LoadBalancingPolicyExists(absl::string_view name,bool * requires_config) const81 bool LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(
82     absl::string_view name, bool* requires_config) const {
83   auto* factory = GetLoadBalancingPolicyFactory(name);
84   if (factory == nullptr) return false;
85   // If requested, check if the load balancing policy allows an empty config.
86   if (requires_config != nullptr) {
87     auto config = factory->ParseLoadBalancingConfig(Json::FromObject({}));
88     *requires_config = !config.ok();
89   }
90   return true;
91 }
92 
93 // Returns the JSON node of policy (with both policy name and config content)
94 // given the JSON node of a LoadBalancingConfig array.
95 absl::StatusOr<Json::Object::const_iterator>
ParseLoadBalancingConfigHelper(const Json & lb_config_array) const96 LoadBalancingPolicyRegistry::ParseLoadBalancingConfigHelper(
97     const Json& lb_config_array) const {
98   if (lb_config_array.type() != Json::Type::kArray) {
99     return absl::InvalidArgumentError("type should be array");
100   }
101   // Find the first LB policy that this client supports.
102   std::vector<absl::string_view> policies_tried;
103   for (const Json& lb_config : lb_config_array.array()) {
104     if (lb_config.type() != Json::Type::kObject) {
105       return absl::InvalidArgumentError("child entry should be of type object");
106     }
107     if (lb_config.object().empty()) {
108       return absl::InvalidArgumentError("no policy found in child entry");
109     }
110     if (lb_config.object().size() > 1) {
111       return absl::InvalidArgumentError("oneOf violation");
112     }
113     auto it = lb_config.object().begin();
114     if (it->second.type() != Json::Type::kObject) {
115       return absl::InvalidArgumentError("child entry should be of type object");
116     }
117     // If we support this policy, then select it.
118     if (LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(
119             it->first.c_str(), nullptr)) {
120       return it;
121     }
122     policies_tried.push_back(it->first);
123   }
124   return absl::FailedPreconditionError(absl::StrCat(
125       "No known policies in list: ", absl::StrJoin(policies_tried, " ")));
126 }
127 
128 absl::StatusOr<RefCountedPtr<LoadBalancingPolicy::Config>>
ParseLoadBalancingConfig(const Json & json) const129 LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(const Json& json) const {
130   auto policy = ParseLoadBalancingConfigHelper(json);
131   if (!policy.ok()) return policy.status();
132   // Find factory.
133   LoadBalancingPolicyFactory* factory =
134       GetLoadBalancingPolicyFactory((*policy)->first.c_str());
135   if (factory == nullptr) {
136     return absl::FailedPreconditionError(absl::StrFormat(
137         "Factory not found for policy \"%s\"", (*policy)->first));
138   }
139   // Parse load balancing config via factory.
140   return factory->ParseLoadBalancingConfig((*policy)->second);
141 }
142 
143 }  // namespace grpc_core
144