Imported Upstream version 1.22.0
[platform/upstream/grpc.git] / src / core / ext / filters / client_channel / lb_policy_registry.cc
1 /*
2  *
3  * Copyright 2015 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18
19 #include <grpc/support/port_platform.h>
20
21 #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
22
23 #include <string.h>
24
25 #include "src/core/lib/gpr/string.h"
26 #include "src/core/lib/gprpp/inlined_vector.h"
27
28 namespace grpc_core {
29
30 namespace {
31
32 class RegistryState {
33  public:
34   RegistryState() {}
35
36   void RegisterLoadBalancingPolicyFactory(
37       UniquePtr<LoadBalancingPolicyFactory> factory) {
38     for (size_t i = 0; i < factories_.size(); ++i) {
39       GPR_ASSERT(strcmp(factories_[i]->name(), factory->name()) != 0);
40     }
41     factories_.push_back(std::move(factory));
42   }
43
44   LoadBalancingPolicyFactory* GetLoadBalancingPolicyFactory(
45       const char* name) const {
46     for (size_t i = 0; i < factories_.size(); ++i) {
47       if (strcmp(name, factories_[i]->name()) == 0) {
48         return factories_[i].get();
49       }
50     }
51     return nullptr;
52   }
53
54  private:
55   InlinedVector<UniquePtr<LoadBalancingPolicyFactory>, 10> factories_;
56 };
57
58 RegistryState* g_state = nullptr;
59
60 }  // namespace
61
62 //
63 // LoadBalancingPolicyRegistry::Builder
64 //
65
66 void LoadBalancingPolicyRegistry::Builder::InitRegistry() {
67   if (g_state == nullptr) g_state = New<RegistryState>();
68 }
69
70 void LoadBalancingPolicyRegistry::Builder::ShutdownRegistry() {
71   Delete(g_state);
72   g_state = nullptr;
73 }
74
75 void LoadBalancingPolicyRegistry::Builder::RegisterLoadBalancingPolicyFactory(
76     UniquePtr<LoadBalancingPolicyFactory> factory) {
77   InitRegistry();
78   g_state->RegisterLoadBalancingPolicyFactory(std::move(factory));
79 }
80
81 //
82 // LoadBalancingPolicyRegistry
83 //
84
85 OrphanablePtr<LoadBalancingPolicy>
86 LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(
87     const char* name, LoadBalancingPolicy::Args args) {
88   GPR_ASSERT(g_state != nullptr);
89   // Find factory.
90   LoadBalancingPolicyFactory* factory =
91       g_state->GetLoadBalancingPolicyFactory(name);
92   if (factory == nullptr) return nullptr;  // Specified name not found.
93   // Create policy via factory.
94   return factory->CreateLoadBalancingPolicy(std::move(args));
95 }
96
97 bool LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(
98     const char* name, bool* requires_config) {
99   GPR_ASSERT(g_state != nullptr);
100   auto* factory = g_state->GetLoadBalancingPolicyFactory(name);
101   if (factory == nullptr) {
102     return false;
103   }
104   if (requires_config != nullptr) {
105     grpc_error* error = GRPC_ERROR_NONE;
106     // Check if the load balancing policy allows an empty config
107     *requires_config =
108         factory->ParseLoadBalancingConfig(nullptr, &error) == nullptr;
109     GRPC_ERROR_UNREF(error);
110   }
111   return true;
112 }
113
114 namespace {
115 // Returns the JSON node of policy (with both policy name and config content)
116 // given the JSON node of a LoadBalancingConfig array.
117 grpc_json* ParseLoadBalancingConfigHelper(const grpc_json* lb_config_array,
118                                           grpc_error** error) {
119   GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
120   char* error_msg;
121   if (lb_config_array == nullptr || lb_config_array->type != GRPC_JSON_ARRAY) {
122     gpr_asprintf(&error_msg, "field:%s error:type should be array",
123                  lb_config_array->key);
124     *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
125     gpr_free(error_msg);
126     return nullptr;
127   }
128   const char* field_name = lb_config_array->key;
129   // Find the first LB policy that this client supports.
130   for (const grpc_json* lb_config = lb_config_array->child;
131        lb_config != nullptr; lb_config = lb_config->next) {
132     if (lb_config->type != GRPC_JSON_OBJECT) {
133       gpr_asprintf(&error_msg,
134                    "field:%s error:child entry should be of type object",
135                    field_name);
136       *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
137       gpr_free(error_msg);
138       return nullptr;
139     }
140     grpc_json* policy = nullptr;
141     for (grpc_json* field = lb_config->child; field != nullptr;
142          field = field->next) {
143       if (field->key == nullptr || field->type != GRPC_JSON_OBJECT) {
144         gpr_asprintf(&error_msg,
145                      "field:%s error:child entry should be of type object",
146                      field_name);
147         *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
148         gpr_free(error_msg);
149         return nullptr;
150       }
151       if (policy != nullptr) {
152         gpr_asprintf(&error_msg, "field:%s error:oneOf violation", field_name);
153         *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
154         gpr_free(error_msg);
155         return nullptr;
156       }  // Violate "oneof" type.
157       policy = field;
158     }
159     if (policy == nullptr) {
160       gpr_asprintf(&error_msg, "field:%s error:no policy found in child entry",
161                    field_name);
162       *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
163       gpr_free(error_msg);
164       return nullptr;
165     }
166     // If we support this policy, then select it.
167     if (LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(policy->key,
168                                                                nullptr)) {
169       return policy;
170     }
171   }
172   gpr_asprintf(&error_msg, "field:%s error:No known policy", field_name);
173   *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
174   gpr_free(error_msg);
175   return nullptr;
176 }
177 }  // namespace
178
179 RefCountedPtr<LoadBalancingPolicy::Config>
180 LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(const grpc_json* json,
181                                                       grpc_error** error) {
182   GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
183   GPR_ASSERT(g_state != nullptr);
184   const grpc_json* policy = ParseLoadBalancingConfigHelper(json, error);
185   if (policy == nullptr) {
186     return nullptr;
187   } else {
188     GPR_DEBUG_ASSERT(*error == GRPC_ERROR_NONE && json != nullptr);
189     // Find factory.
190     LoadBalancingPolicyFactory* factory =
191         g_state->GetLoadBalancingPolicyFactory(policy->key);
192     if (factory == nullptr) {
193       char* msg;
194       gpr_asprintf(&msg, "field:%s error:Factory not found to create policy",
195                    json->key);
196       *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
197       gpr_free(msg);
198       return nullptr;
199     }
200     // Parse load balancing config via factory.
201     return factory->ParseLoadBalancingConfig(policy, error);
202   }
203 }
204
205 }  // namespace grpc_core