1 //
2 // Copyright 2022 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/ext/xds/xds_cluster_specifier_plugin.h"
20 
21 #include <stddef.h>
22 
23 #include <map>
24 #include <utility>
25 
26 #include "absl/status/statusor.h"
27 #include "absl/strings/str_cat.h"
28 #include "absl/types/variant.h"
29 #include "upb/base/status.h"
30 #include "upb/json/encode.h"
31 #include "upb/upb.hpp"
32 
33 #include <grpc/support/json.h>
34 #include <grpc/support/log.h>
35 
36 #include "src/core/lib/json/json.h"
37 #include "src/core/lib/json/json_reader.h"
38 #include "src/proto/grpc/lookup/v1/rls_config.upb.h"
39 #include "src/proto/grpc/lookup/v1/rls_config.upbdefs.h"
40 
41 namespace grpc_core {
42 
43 //
44 // XdsRouteLookupClusterSpecifierPlugin
45 //
46 
ConfigProtoName() const47 absl::string_view XdsRouteLookupClusterSpecifierPlugin::ConfigProtoName()
48     const {
49   return "grpc.lookup.v1.RouteLookupClusterSpecifier";
50 }
51 
PopulateSymtab(upb_DefPool * symtab) const52 void XdsRouteLookupClusterSpecifierPlugin::PopulateSymtab(
53     upb_DefPool* symtab) const {
54   grpc_lookup_v1_RouteLookupConfig_getmsgdef(symtab);
55 }
56 
GenerateLoadBalancingPolicyConfig(XdsExtension extension,upb_Arena * arena,upb_DefPool * symtab,ValidationErrors * errors) const57 Json XdsRouteLookupClusterSpecifierPlugin::GenerateLoadBalancingPolicyConfig(
58     XdsExtension extension, upb_Arena* arena, upb_DefPool* symtab,
59     ValidationErrors* errors) const {
60   absl::string_view* serialized_plugin_config =
61       absl::get_if<absl::string_view>(&extension.value);
62   if (serialized_plugin_config == nullptr) {
63     errors->AddError("could not parse plugin config");
64     return {};
65   }
66   const auto* specifier = grpc_lookup_v1_RouteLookupClusterSpecifier_parse(
67       serialized_plugin_config->data(), serialized_plugin_config->size(),
68       arena);
69   if (specifier == nullptr) {
70     errors->AddError("could not parse plugin config");
71     return {};
72   }
73   const auto* plugin_config =
74       grpc_lookup_v1_RouteLookupClusterSpecifier_route_lookup_config(specifier);
75   if (plugin_config == nullptr) {
76     ValidationErrors::ScopedField field(errors, ".route_lookup_config");
77     errors->AddError("field not present");
78     return {};
79   }
80   upb::Status status;
81   const upb_MessageDef* msg_type =
82       grpc_lookup_v1_RouteLookupConfig_getmsgdef(symtab);
83   size_t json_size = upb_JsonEncode(plugin_config, msg_type, symtab, 0, nullptr,
84                                     0, status.ptr());
85   if (json_size == static_cast<size_t>(-1)) {
86     errors->AddError(absl::StrCat("failed to dump proto to JSON: ",
87                                   upb_Status_ErrorMessage(status.ptr())));
88     return {};
89   }
90   void* buf = upb_Arena_Malloc(arena, json_size + 1);
91   upb_JsonEncode(plugin_config, msg_type, symtab, 0,
92                  reinterpret_cast<char*>(buf), json_size + 1, status.ptr());
93   auto json = JsonParse(reinterpret_cast<char*>(buf));
94   GPR_ASSERT(json.ok());
95   return Json::FromArray({Json::FromObject(
96       {{"rls_experimental",
97         Json::FromObject({
98             {"routeLookupConfig", std::move(*json)},
99             {"childPolicy",
100              Json::FromArray({
101                  Json::FromObject({{"cds_experimental", Json::FromObject({})}}),
102              })},
103             {"childPolicyConfigTargetFieldName", Json::FromString("cluster")},
104         })}})});
105 }
106 
107 //
108 // XdsClusterSpecifierPluginRegistry
109 //
110 
XdsClusterSpecifierPluginRegistry()111 XdsClusterSpecifierPluginRegistry::XdsClusterSpecifierPluginRegistry() {
112   RegisterPlugin(std::make_unique<XdsRouteLookupClusterSpecifierPlugin>());
113 }
114 
RegisterPlugin(std::unique_ptr<XdsClusterSpecifierPluginImpl> plugin)115 void XdsClusterSpecifierPluginRegistry::RegisterPlugin(
116     std::unique_ptr<XdsClusterSpecifierPluginImpl> plugin) {
117   absl::string_view name = plugin->ConfigProtoName();
118   registry_[name] = std::move(plugin);
119 }
120 
121 const XdsClusterSpecifierPluginImpl*
GetPluginForType(absl::string_view config_proto_type_name) const122 XdsClusterSpecifierPluginRegistry::GetPluginForType(
123     absl::string_view config_proto_type_name) const {
124   auto it = registry_.find(config_proto_type_name);
125   if (it == registry_.end()) return nullptr;
126   return it->second.get();
127 }
128 
PopulateSymtab(upb_DefPool * symtab) const129 void XdsClusterSpecifierPluginRegistry::PopulateSymtab(
130     upb_DefPool* symtab) const {
131   for (const auto& p : registry_) {
132     p.second->PopulateSymtab(symtab);
133   }
134 }
135 
136 }  // namespace grpc_core
137