1 //
2 // Copyright 2019 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_bootstrap_grpc.h"
18
19 #include <grpc/support/json.h>
20 #include <grpc/support/port_platform.h>
21 #include <stdlib.h>
22
23 #include <string>
24 #include <utility>
25 #include <vector>
26
27 #include "absl/status/status.h"
28 #include "absl/status/statusor.h"
29 #include "absl/strings/match.h"
30 #include "absl/strings/str_cat.h"
31 #include "absl/strings/str_format.h"
32 #include "absl/strings/str_join.h"
33 #include "absl/strings/string_view.h"
34 #include "absl/types/optional.h"
35 #include "src/core/util/json/json.h"
36 #include "src/core/util/json/json_object_loader.h"
37 #include "src/core/util/json/json_reader.h"
38 #include "src/core/util/json/json_writer.h"
39 #include "src/core/util/ref_counted_ptr.h"
40 #include "src/core/util/string.h"
41
42 namespace grpc_core {
43
44 //
45 // GrpcXdsBootstrap::GrpcNode::Locality
46 //
47
JsonLoader(const JsonArgs &)48 const JsonLoaderInterface* GrpcXdsBootstrap::GrpcNode::Locality::JsonLoader(
49 const JsonArgs&) {
50 static const auto* loader =
51 JsonObjectLoader<Locality>()
52 .OptionalField("region", &Locality::region)
53 .OptionalField("zone", &Locality::zone)
54 .OptionalField("sub_zone", &Locality::sub_zone)
55 .Finish();
56 return loader;
57 }
58
59 //
60 // GrpcXdsBootstrap::GrpcNode
61 //
62
JsonLoader(const JsonArgs &)63 const JsonLoaderInterface* GrpcXdsBootstrap::GrpcNode::JsonLoader(
64 const JsonArgs&) {
65 static const auto* loader =
66 JsonObjectLoader<GrpcNode>()
67 .OptionalField("id", &GrpcNode::id_)
68 .OptionalField("cluster", &GrpcNode::cluster_)
69 .OptionalField("locality", &GrpcNode::locality_)
70 .OptionalField("metadata", &GrpcNode::metadata_)
71 .Finish();
72 return loader;
73 }
74
75 //
76 // GrpcXdsBootstrap::GrpcAuthority
77 //
78
JsonLoader(const JsonArgs &)79 const JsonLoaderInterface* GrpcXdsBootstrap::GrpcAuthority::JsonLoader(
80 const JsonArgs&) {
81 static const auto* loader =
82 JsonObjectLoader<GrpcAuthority>()
83 .OptionalField(
84 "client_listener_resource_name_template",
85 &GrpcAuthority::client_listener_resource_name_template_)
86 .OptionalField("xds_servers", &GrpcAuthority::servers_)
87 .Finish();
88 return loader;
89 }
90
91 //
92 // GrpcXdsBootstrap
93 //
94
Create(absl::string_view json_string)95 absl::StatusOr<std::unique_ptr<GrpcXdsBootstrap>> GrpcXdsBootstrap::Create(
96 absl::string_view json_string) {
97 auto json = JsonParse(json_string);
98 if (!json.ok()) {
99 return absl::InvalidArgumentError(absl::StrCat(
100 "Failed to parse bootstrap JSON string: ", json.status().ToString()));
101 }
102 // Validate JSON.
103 class XdsJsonArgs final : public JsonArgs {
104 public:
105 bool IsEnabled(absl::string_view key) const override {
106 if (key == "federation") return XdsFederationEnabled();
107 return true;
108 }
109 };
110 auto bootstrap = LoadFromJson<GrpcXdsBootstrap>(*json, XdsJsonArgs());
111 if (!bootstrap.ok()) return bootstrap.status();
112 return std::make_unique<GrpcXdsBootstrap>(std::move(*bootstrap));
113 }
114
JsonLoader(const JsonArgs &)115 const JsonLoaderInterface* GrpcXdsBootstrap::JsonLoader(const JsonArgs&) {
116 static const auto* loader =
117 JsonObjectLoader<GrpcXdsBootstrap>()
118 .Field("xds_servers", &GrpcXdsBootstrap::servers_)
119 .OptionalField("node", &GrpcXdsBootstrap::node_)
120 .OptionalField("certificate_providers",
121 &GrpcXdsBootstrap::certificate_providers_)
122 .OptionalField(
123 "server_listener_resource_name_template",
124 &GrpcXdsBootstrap::server_listener_resource_name_template_)
125 .OptionalField("authorities", &GrpcXdsBootstrap::authorities_,
126 "federation")
127 .OptionalField("client_default_listener_resource_name_template",
128 &GrpcXdsBootstrap::
129 client_default_listener_resource_name_template_,
130 "federation")
131 .Finish();
132 return loader;
133 }
134
JsonPostLoad(const Json &,const JsonArgs &,ValidationErrors * errors)135 void GrpcXdsBootstrap::JsonPostLoad(const Json& /*json*/,
136 const JsonArgs& /*args*/,
137 ValidationErrors* errors) {
138 // Verify that there is at least one server present.
139 {
140 ValidationErrors::ScopedField field(errors, ".xds_servers");
141 if (servers_.empty() && !errors->FieldHasErrors()) {
142 errors->AddError("must be non-empty");
143 }
144 }
145 // Verify that each authority has the right prefix in the
146 // client_listener_resource_name_template field.
147 {
148 ValidationErrors::ScopedField field(errors, ".authorities");
149 for (const auto& p : authorities_) {
150 const std::string& name = p.first;
151 const GrpcAuthority& authority =
152 static_cast<const GrpcAuthority&>(p.second);
153 ValidationErrors::ScopedField field(
154 errors, absl::StrCat("[\"", name,
155 "\"].client_listener_resource_name_template"));
156 std::string expected_prefix = absl::StrCat("xdstp://", name, "/");
157 if (!authority.client_listener_resource_name_template().empty() &&
158 !absl::StartsWith(authority.client_listener_resource_name_template(),
159 expected_prefix)) {
160 errors->AddError(
161 absl::StrCat("field must begin with \"", expected_prefix, "\""));
162 }
163 }
164 }
165 }
166
ToString() const167 std::string GrpcXdsBootstrap::ToString() const {
168 std::vector<std::string> parts;
169 if (node_.has_value()) {
170 parts.push_back(
171 absl::StrFormat("node={\n"
172 " id=\"%s\",\n"
173 " cluster=\"%s\",\n"
174 " locality={\n"
175 " region=\"%s\",\n"
176 " zone=\"%s\",\n"
177 " sub_zone=\"%s\"\n"
178 " },\n"
179 " metadata=%s,\n"
180 "},\n",
181 node_->id(), node_->cluster(), node_->locality_region(),
182 node_->locality_zone(), node_->locality_sub_zone(),
183 JsonDump(Json::FromObject(node_->metadata()))));
184 }
185 parts.push_back(
186 absl::StrFormat("servers=[\n%s\n],\n", JsonDump(servers_[0].ToJson())));
187 if (!client_default_listener_resource_name_template_.empty()) {
188 parts.push_back(absl::StrFormat(
189 "client_default_listener_resource_name_template=\"%s\",\n",
190 client_default_listener_resource_name_template_));
191 }
192 if (!server_listener_resource_name_template_.empty()) {
193 parts.push_back(
194 absl::StrFormat("server_listener_resource_name_template=\"%s\",\n",
195 server_listener_resource_name_template_));
196 }
197 parts.push_back("authorities={\n");
198 for (const auto& entry : authorities_) {
199 parts.push_back(absl::StrFormat(" %s={\n", entry.first));
200 parts.push_back(
201 absl::StrFormat(" client_listener_resource_name_template=\"%s\",\n",
202 entry.second.client_listener_resource_name_template()));
203 std::vector<std::string> server_jsons;
204 for (const XdsServer* server : entry.second.servers()) {
205 server_jsons.emplace_back(
206 JsonDump(static_cast<const GrpcXdsServer*>(server)->ToJson()));
207 }
208 if (!server_jsons.empty()) {
209 parts.push_back(absl::StrFormat(" servers=[\n%s\n],\n",
210 absl::StrJoin(server_jsons, ",\n")));
211 }
212 parts.push_back(" },\n");
213 }
214 parts.push_back("}\n");
215 parts.push_back("certificate_providers={\n");
216 for (const auto& entry : certificate_providers_) {
217 parts.push_back(
218 absl::StrFormat(" %s={\n"
219 " plugin_name=%s\n"
220 " config=%s\n"
221 " },\n",
222 entry.first, entry.second.plugin_name,
223 entry.second.config->ToString()));
224 }
225 parts.push_back("}");
226 return absl::StrJoin(parts, "");
227 }
228
LookupAuthority(const std::string & name) const229 const XdsBootstrap::Authority* GrpcXdsBootstrap::LookupAuthority(
230 const std::string& name) const {
231 auto it = authorities_.find(name);
232 if (it != authorities_.end()) {
233 return &it->second;
234 }
235 return nullptr;
236 }
237
238 } // namespace grpc_core
239