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