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