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