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