2 // Copyright 2019 gRPC authors.
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
8 // http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <grpc/support/port_platform.h>
19 #include "src/core/ext/xds/xds_bootstrap.h"
26 #include "absl/strings/str_cat.h"
27 #include "absl/strings/str_format.h"
28 #include "absl/strings/str_join.h"
29 #include "absl/strings/string_view.h"
31 #include "src/core/lib/gpr/env.h"
32 #include "src/core/lib/gpr/string.h"
33 #include "src/core/lib/iomgr/load_file.h"
34 #include "src/core/lib/slice/slice_internal.h"
38 bool XdsBootstrap::XdsServer::ShouldUseV3() const {
39 return server_features.find("xds_v3") != server_features.end();
44 std::string BootstrapString(const XdsBootstrap& bootstrap) {
45 std::vector<std::string> parts;
46 if (bootstrap.node() != nullptr) {
47 parts.push_back(absl::StrFormat(
58 bootstrap.node()->id, bootstrap.node()->cluster,
59 bootstrap.node()->locality_region, bootstrap.node()->locality_zone,
60 bootstrap.node()->locality_subzone, bootstrap.node()->metadata.Dump()));
63 absl::StrFormat("servers=[\n"
67 bootstrap.server().server_uri));
68 for (const auto& creds : bootstrap.server().channel_creds) {
69 parts.push_back(absl::StrFormat(" {type=\"%s\", config=%s},\n",
70 creds.type, creds.config.Dump()));
72 parts.push_back(" ],\n");
73 if (!bootstrap.server().server_features.empty()) {
74 parts.push_back(absl::StrCat(
76 absl::StrJoin(bootstrap.server().server_features, ", "), "],\n"));
78 parts.push_back(" }\n]");
79 return absl::StrJoin(parts, "");
84 std::unique_ptr<XdsBootstrap> XdsBootstrap::ReadFromFile(XdsClient* client,
87 grpc_core::UniquePtr<char> path(gpr_getenv("GRPC_XDS_BOOTSTRAP"));
88 if (path == nullptr) {
89 *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
90 "Environment variable GRPC_XDS_BOOTSTRAP not defined");
93 if (GRPC_TRACE_FLAG_ENABLED(*tracer)) {
95 "[xds_client %p] Got bootstrap file location from "
96 "GRPC_XDS_BOOTSTRAP environment variable: %s",
100 *error = grpc_load_file(path.get(), /*add_null_terminator=*/true, &contents);
101 if (*error != GRPC_ERROR_NONE) return nullptr;
102 absl::string_view contents_str_view = StringViewFromSlice(contents);
103 if (GRPC_TRACE_FLAG_ENABLED(*tracer)) {
104 gpr_log(GPR_DEBUG, "[xds_client %p] Bootstrap file contents: %s", client,
105 std::string(contents_str_view).c_str());
107 Json json = Json::Parse(contents_str_view, error);
108 grpc_slice_unref_internal(contents);
109 if (*error != GRPC_ERROR_NONE) {
110 grpc_error* error_out = GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(
111 absl::StrCat("Failed to parse bootstrap file ", path.get()).c_str(),
113 GRPC_ERROR_UNREF(*error);
117 std::unique_ptr<XdsBootstrap> result =
118 absl::make_unique<XdsBootstrap>(std::move(json), error);
119 if (*error == GRPC_ERROR_NONE && GRPC_TRACE_FLAG_ENABLED(*tracer)) {
121 "[xds_client %p] Bootstrap config for creating xds client:\n%s",
122 client, BootstrapString(*result).c_str());
127 XdsBootstrap::XdsBootstrap(Json json, grpc_error** error) {
128 if (json.type() != Json::Type::OBJECT) {
129 *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
130 "malformed JSON in bootstrap file");
133 std::vector<grpc_error*> error_list;
134 auto it = json.mutable_object()->find("xds_servers");
135 if (it == json.mutable_object()->end()) {
136 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
137 "\"xds_servers\" field not present"));
138 } else if (it->second.type() != Json::Type::ARRAY) {
139 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
140 "\"xds_servers\" field is not an array"));
142 grpc_error* parse_error = ParseXdsServerList(&it->second);
143 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
145 it = json.mutable_object()->find("node");
146 if (it != json.mutable_object()->end()) {
147 if (it->second.type() != Json::Type::OBJECT) {
148 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
149 "\"node\" field is not an object"));
151 grpc_error* parse_error = ParseNode(&it->second);
152 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
155 *error = GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing xds bootstrap file",
159 grpc_error* XdsBootstrap::ParseXdsServerList(Json* json) {
160 std::vector<grpc_error*> error_list;
161 for (size_t i = 0; i < json->mutable_array()->size(); ++i) {
162 Json& child = json->mutable_array()->at(i);
163 if (child.type() != Json::Type::OBJECT) {
164 error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
165 absl::StrCat("array element ", i, " is not an object").c_str()));
167 grpc_error* parse_error = ParseXdsServer(&child, i);
168 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
171 return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"xds_servers\" array",
175 grpc_error* XdsBootstrap::ParseXdsServer(Json* json, size_t idx) {
176 std::vector<grpc_error*> error_list;
177 servers_.emplace_back();
178 XdsServer& server = servers_[servers_.size() - 1];
179 auto it = json->mutable_object()->find("server_uri");
180 if (it == json->mutable_object()->end()) {
181 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
182 "\"server_uri\" field not present"));
183 } else if (it->second.type() != Json::Type::STRING) {
184 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
185 "\"server_uri\" field is not a string"));
187 server.server_uri = std::move(*it->second.mutable_string_value());
189 it = json->mutable_object()->find("channel_creds");
190 if (it != json->mutable_object()->end()) {
191 if (it->second.type() != Json::Type::ARRAY) {
192 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
193 "\"channel_creds\" field is not an array"));
195 grpc_error* parse_error = ParseChannelCredsArray(&it->second, &server);
196 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
199 it = json->mutable_object()->find("server_features");
200 if (it != json->mutable_object()->end()) {
201 if (it->second.type() != Json::Type::ARRAY) {
202 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
203 "\"server_features\" field is not an array"));
205 grpc_error* parse_error = ParseServerFeaturesArray(&it->second, &server);
206 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
209 // Can't use GRPC_ERROR_CREATE_FROM_VECTOR() here, because the error
210 // string is not static in this case.
211 if (error_list.empty()) return GRPC_ERROR_NONE;
212 grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
213 absl::StrCat("errors parsing index ", idx).c_str());
214 for (size_t i = 0; i < error_list.size(); ++i) {
215 error = grpc_error_add_child(error, error_list[i]);
220 grpc_error* XdsBootstrap::ParseChannelCredsArray(Json* json,
222 std::vector<grpc_error*> error_list;
223 for (size_t i = 0; i < json->mutable_array()->size(); ++i) {
224 Json& child = json->mutable_array()->at(i);
225 if (child.type() != Json::Type::OBJECT) {
226 error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
227 absl::StrCat("array element ", i, " is not an object").c_str()));
229 grpc_error* parse_error = ParseChannelCreds(&child, i, server);
230 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
233 return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"channel_creds\" array",
237 grpc_error* XdsBootstrap::ParseChannelCreds(Json* json, size_t idx,
239 std::vector<grpc_error*> error_list;
240 ChannelCreds channel_creds;
241 auto it = json->mutable_object()->find("type");
242 if (it == json->mutable_object()->end()) {
243 error_list.push_back(
244 GRPC_ERROR_CREATE_FROM_STATIC_STRING("\"type\" field not present"));
245 } else if (it->second.type() != Json::Type::STRING) {
246 error_list.push_back(
247 GRPC_ERROR_CREATE_FROM_STATIC_STRING("\"type\" field is not a string"));
249 channel_creds.type = std::move(*it->second.mutable_string_value());
251 it = json->mutable_object()->find("config");
252 if (it != json->mutable_object()->end()) {
253 if (it->second.type() != Json::Type::OBJECT) {
254 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
255 "\"config\" field is not an object"));
257 channel_creds.config = std::move(it->second);
260 if (!channel_creds.type.empty()) {
261 server->channel_creds.emplace_back(std::move(channel_creds));
263 // Can't use GRPC_ERROR_CREATE_FROM_VECTOR() here, because the error
264 // string is not static in this case.
265 if (error_list.empty()) return GRPC_ERROR_NONE;
266 grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
267 absl::StrCat("errors parsing index ", idx).c_str());
268 for (size_t i = 0; i < error_list.size(); ++i) {
269 error = grpc_error_add_child(error, error_list[i]);
274 grpc_error* XdsBootstrap::ParseServerFeaturesArray(Json* json,
276 std::vector<grpc_error*> error_list;
277 for (size_t i = 0; i < json->mutable_array()->size(); ++i) {
278 Json& child = json->mutable_array()->at(i);
279 if (child.type() == Json::Type::STRING &&
280 child.string_value() == "xds_v3") {
281 // TODO(roth): Remove env var check once we do interop testing and
282 // are sure that the v3 code actually works.
283 grpc_core::UniquePtr<char> enable_str(
284 gpr_getenv("GRPC_XDS_EXPERIMENTAL_V3_SUPPORT"));
285 bool enabled = false;
286 if (gpr_parse_bool_value(enable_str.get(), &enabled) && enabled) {
287 server->server_features.insert(
288 std::move(*child.mutable_string_value()));
292 return GRPC_ERROR_CREATE_FROM_VECTOR(
293 "errors parsing \"server_features\" array", &error_list);
296 grpc_error* XdsBootstrap::ParseNode(Json* json) {
297 std::vector<grpc_error*> error_list;
298 node_ = absl::make_unique<Node>();
299 auto it = json->mutable_object()->find("id");
300 if (it != json->mutable_object()->end()) {
301 if (it->second.type() != Json::Type::STRING) {
302 error_list.push_back(
303 GRPC_ERROR_CREATE_FROM_STATIC_STRING("\"id\" field is not a string"));
305 node_->id = std::move(*it->second.mutable_string_value());
308 it = json->mutable_object()->find("cluster");
309 if (it != json->mutable_object()->end()) {
310 if (it->second.type() != Json::Type::STRING) {
311 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
312 "\"cluster\" field is not a string"));
314 node_->cluster = std::move(*it->second.mutable_string_value());
317 it = json->mutable_object()->find("locality");
318 if (it != json->mutable_object()->end()) {
319 if (it->second.type() != Json::Type::OBJECT) {
320 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
321 "\"locality\" field is not an object"));
323 grpc_error* parse_error = ParseLocality(&it->second);
324 if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
327 it = json->mutable_object()->find("metadata");
328 if (it != json->mutable_object()->end()) {
329 if (it->second.type() != Json::Type::OBJECT) {
330 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
331 "\"metadata\" field is not an object"));
333 node_->metadata = std::move(it->second);
336 return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"node\" object",
340 grpc_error* XdsBootstrap::ParseLocality(Json* json) {
341 std::vector<grpc_error*> error_list;
342 auto it = json->mutable_object()->find("region");
343 if (it != json->mutable_object()->end()) {
344 if (it->second.type() != Json::Type::STRING) {
345 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
346 "\"region\" field is not a string"));
348 node_->locality_region = std::move(*it->second.mutable_string_value());
351 it = json->mutable_object()->find("zone");
352 if (it != json->mutable_object()->end()) {
353 if (it->second.type() != Json::Type::STRING) {
354 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
355 "\"zone\" field is not a string"));
357 node_->locality_zone = std::move(*it->second.mutable_string_value());
360 it = json->mutable_object()->find("subzone");
361 if (it != json->mutable_object()->end()) {
362 if (it->second.type() != Json::Type::STRING) {
363 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
364 "\"subzone\" field is not a string"));
366 node_->locality_subzone = std::move(*it->second.mutable_string_value());
369 return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"locality\" object",
373 } // namespace grpc_core