• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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