xref: /aosp_15_r20/external/grpc-grpc/test/core/xds/xds_bootstrap_test.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
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/ext/xds/xds_bootstrap.h"
18 
19 #include <stdio.h>
20 
21 #include <map>
22 #include <memory>
23 #include <string>
24 #include <utility>
25 
26 #include "absl/status/status.h"
27 #include "absl/status/statusor.h"
28 #include "absl/strings/str_format.h"
29 #include "absl/strings/string_view.h"
30 #include "gmock/gmock.h"
31 #include "gtest/gtest.h"
32 
33 #include <grpc/grpc.h>
34 #include <grpc/grpc_security.h>
35 #include <grpc/grpc_security_constants.h>
36 #include <grpc/support/alloc.h>
37 
38 #include "src/core/ext/xds/certificate_provider_store.h"
39 #include "src/core/ext/xds/xds_bootstrap_grpc.h"
40 #include "src/core/lib/config/core_configuration.h"
41 #include "src/core/lib/gpr/tmpfile.h"
42 #include "src/core/lib/gprpp/env.h"
43 #include "src/core/lib/gprpp/ref_counted_ptr.h"
44 #include "src/core/lib/gprpp/validation_errors.h"
45 #include "src/core/lib/json/json.h"
46 #include "src/core/lib/json/json_args.h"
47 #include "src/core/lib/json/json_object_loader.h"
48 #include "src/core/lib/json/json_reader.h"
49 #include "src/core/lib/security/certificate_provider/certificate_provider_factory.h"
50 #include "src/core/lib/security/credentials/channel_creds_registry.h"
51 #include "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h"
52 #include "test/core/util/scoped_env_var.h"
53 #include "test/core/util/test_config.h"
54 
55 namespace grpc_core {
56 namespace testing {
57 namespace {
58 
59 MATCHER_P2(EqXdsServer, name, creds_config_type, "equals XdsServer") {
60   auto* server = static_cast<const GrpcXdsBootstrap::GrpcXdsServer*>(arg);
61   if (!::testing::ExplainMatchResult(::testing::Ne(nullptr), server,
62                                      result_listener)) {
63     return false;
64   }
65   bool ok = ::testing::ExplainMatchResult(name, server->server_uri(),
66                                           result_listener);
67   auto creds_config = server->channel_creds_config();
68   if (!::testing::ExplainMatchResult(::testing::Ne(nullptr), creds_config,
69                                      result_listener)) {
70     return false;
71   }
72   ok |= ::testing::ExplainMatchResult(creds_config_type, creds_config->type(),
73                                       result_listener);
74   return ok;
75 }
76 
TEST(XdsBootstrapTest,Basic)77 TEST(XdsBootstrapTest, Basic) {
78   const char* json_str =
79       "{"
80       "  \"xds_servers\": ["
81       "    {"
82       "      \"server_uri\": \"fake:///lb1\","
83       "      \"channel_creds\": ["
84       "        {"
85       "          \"type\": \"fake\","
86       "          \"ignore\": 0"
87       "        }"
88       "      ],"
89       "      \"ignore\": 0"
90       "    },"
91       "    {"
92       "      \"server_uri\": \"fake:///lb2\","
93       "      \"channel_creds\": ["
94       "        {"
95       "          \"type\": \"fake\","
96       "          \"ignore\": 0"
97       "        }"
98       "      ],"
99       "      \"ignore\": 0"
100       "    }"
101       "  ],"
102       "  \"authorities\": {"
103       "    \"xds.example.com\": {"
104       "      \"client_listener_resource_name_template\": "
105       "\"xdstp://xds.example.com/envoy.config.listener.v3.Listener/grpc/server/"
106       "%s\","
107       "      \"xds_servers\": ["
108       "        {"
109       "          \"server_uri\": \"fake:///xds_server\","
110       "          \"channel_creds\": ["
111       "            {"
112       "              \"type\": \"fake\""
113       "            }"
114       "          ],"
115       "          \"server_features\": [\"xds_v3\"]"
116       "        },"
117       "        {"
118       "          \"server_uri\": \"fake:///xds_server2\","
119       "          \"channel_creds\": ["
120       "            {"
121       "              \"type\": \"fake\""
122       "            }"
123       "          ],"
124       "          \"server_features\": [\"xds_v3\"]"
125       "        }"
126       "      ]"
127       "    },"
128       "    \"xds.example2.com\": {"
129       "      \"client_listener_resource_name_template\": "
130       "\"xdstp://xds.example2.com/envoy.config.listener.v3.Listener/grpc/"
131       "server/%s\","
132       "      \"xds_servers\": ["
133       "        {"
134       "          \"server_uri\": \"fake:///xds_server3\","
135       "          \"channel_creds\": ["
136       "            {"
137       "              \"type\": \"fake\""
138       "            }"
139       "          ],"
140       "          \"server_features\": [\"xds_v3\"]"
141       "        }"
142       "      ]"
143       "    }"
144       "  },"
145       "  \"node\": {"
146       "    \"id\": \"foo\","
147       "    \"cluster\": \"bar\","
148       "    \"locality\": {"
149       "      \"region\": \"milky_way\","
150       "      \"zone\": \"sol_system\","
151       "      \"sub_zone\": \"earth\","
152       "      \"ignore\": {}"
153       "    },"
154       "    \"metadata\": {"
155       "      \"foo\": 1,"
156       "      \"bar\": 2"
157       "    },"
158       "    \"ignore\": \"whee\""
159       "  },"
160       "  \"server_listener_resource_name_template\": \"example/resource\","
161       "  \"ignore\": {}"
162       "}";
163   auto bootstrap_or = GrpcXdsBootstrap::Create(json_str);
164   ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status();
165   auto bootstrap = std::move(*bootstrap_or);
166   EXPECT_THAT(bootstrap->servers(),
167               ::testing::ElementsAre(EqXdsServer("fake:///lb1", "fake")));
168   EXPECT_EQ(bootstrap->authorities().size(), 2);
169   auto* authority = static_cast<const GrpcXdsBootstrap::GrpcAuthority*>(
170       bootstrap->LookupAuthority("xds.example.com"));
171   ASSERT_NE(authority, nullptr);
172   EXPECT_EQ(authority->client_listener_resource_name_template(),
173             "xdstp://xds.example.com/envoy.config.listener.v3.Listener/grpc/"
174             "server/%s");
175   EXPECT_THAT(authority->servers(), ::testing::ElementsAre(EqXdsServer(
176                                         "fake:///xds_server", "fake")));
177   authority = static_cast<const GrpcXdsBootstrap::GrpcAuthority*>(
178       bootstrap->LookupAuthority("xds.example2.com"));
179   ASSERT_NE(authority, nullptr);
180   EXPECT_EQ(authority->client_listener_resource_name_template(),
181             "xdstp://xds.example2.com/envoy.config.listener.v3.Listener/grpc/"
182             "server/%s");
183   EXPECT_THAT(authority->servers(), ::testing::ElementsAre(EqXdsServer(
184                                         "fake:///xds_server3", "fake")));
185   ASSERT_NE(bootstrap->node(), nullptr);
186   EXPECT_EQ(bootstrap->node()->id(), "foo");
187   EXPECT_EQ(bootstrap->node()->cluster(), "bar");
188   EXPECT_EQ(bootstrap->node()->locality_region(), "milky_way");
189   EXPECT_EQ(bootstrap->node()->locality_zone(), "sol_system");
190   EXPECT_EQ(bootstrap->node()->locality_sub_zone(), "earth");
191   EXPECT_THAT(bootstrap->node()->metadata(),
192               ::testing::ElementsAre(
193                   ::testing::Pair(
194                       ::testing::Eq("bar"),
195                       ::testing::AllOf(
196                           ::testing::Property(&Json::type, Json::Type::kNumber),
197                           ::testing::Property(&Json::string, "2"))),
198                   ::testing::Pair(
199                       ::testing::Eq("foo"),
200                       ::testing::AllOf(
201                           ::testing::Property(&Json::type, Json::Type::kNumber),
202                           ::testing::Property(&Json::string, "1")))));
203   EXPECT_EQ(bootstrap->server_listener_resource_name_template(),
204             "example/resource");
205 }
206 
TEST(XdsBootstrapTest,ValidWithoutNode)207 TEST(XdsBootstrapTest, ValidWithoutNode) {
208   const char* json_str =
209       "{"
210       "  \"xds_servers\": ["
211       "    {"
212       "      \"server_uri\": \"fake:///lb\","
213       "      \"channel_creds\": [{\"type\": \"fake\"}]"
214       "    }"
215       "  ]"
216       "}";
217   auto bootstrap_or = GrpcXdsBootstrap::Create(json_str);
218   ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status();
219   auto bootstrap = std::move(*bootstrap_or);
220   EXPECT_THAT(bootstrap->servers(),
221               ::testing::ElementsAre(EqXdsServer("fake:///lb", "fake")));
222   EXPECT_EQ(bootstrap->node(), nullptr);
223 }
224 
TEST(XdsBootstrapTest,InsecureCreds)225 TEST(XdsBootstrapTest, InsecureCreds) {
226   const char* json_str =
227       "{"
228       "  \"xds_servers\": ["
229       "    {"
230       "      \"server_uri\": \"fake:///lb\","
231       "      \"channel_creds\": [{\"type\": \"insecure\"}]"
232       "    }"
233       "  ]"
234       "}";
235   auto bootstrap_or = GrpcXdsBootstrap::Create(json_str);
236   ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status();
237   auto bootstrap = std::move(*bootstrap_or);
238   EXPECT_THAT(bootstrap->servers(),
239               ::testing::ElementsAre(EqXdsServer("fake:///lb", "insecure")));
240   EXPECT_EQ(bootstrap->node(), nullptr);
241 }
242 
TEST(XdsBootstrapTest,GoogleDefaultCreds)243 TEST(XdsBootstrapTest, GoogleDefaultCreds) {
244   // Generate call creds file needed by GoogleDefaultCreds.
245   const char token_str[] =
246       "{ \"client_id\": \"32555999999.apps.googleusercontent.com\","
247       "  \"client_secret\": \"EmssLNjJy1332hD4KFsecret\","
248       "  \"refresh_token\": \"1/Blahblasj424jladJDSGNf-u4Sua3HDA2ngjd42\","
249       "  \"type\": \"authorized_user\"}";
250   char* creds_file_name;
251   FILE* creds_file = gpr_tmpfile("xds_bootstrap_test", &creds_file_name);
252   ASSERT_NE(creds_file_name, nullptr);
253   ASSERT_NE(creds_file, nullptr);
254   ASSERT_EQ(fwrite(token_str, 1, sizeof(token_str), creds_file),
255             sizeof(token_str));
256   fclose(creds_file);
257   SetEnv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, creds_file_name);
258   gpr_free(creds_file_name);
259   // Now run test.
260   const char* json_str =
261       "{"
262       "  \"xds_servers\": ["
263       "    {"
264       "      \"server_uri\": \"fake:///lb\","
265       "      \"channel_creds\": [{\"type\": \"google_default\"}]"
266       "    }"
267       "  ]"
268       "}";
269   auto bootstrap_or = GrpcXdsBootstrap::Create(json_str);
270   ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status();
271   auto bootstrap = std::move(*bootstrap_or);
272   EXPECT_THAT(bootstrap->servers(), ::testing::ElementsAre(EqXdsServer(
273                                         "fake:///lb", "google_default")));
274   EXPECT_EQ(bootstrap->node(), nullptr);
275 }
276 
TEST(XdsBootstrapTest,MissingChannelCreds)277 TEST(XdsBootstrapTest, MissingChannelCreds) {
278   const char* json_str =
279       "{"
280       "  \"xds_servers\": ["
281       "    {"
282       "      \"server_uri\": \"fake:///lb\""
283       "    }"
284       "  ]"
285       "}";
286   auto bootstrap = GrpcXdsBootstrap::Create(json_str);
287   EXPECT_EQ(bootstrap.status().message(),
288             "errors validating JSON: ["
289             "field:xds_servers[0].channel_creds error:field not present]")
290       << bootstrap.status();
291 }
292 
TEST(XdsBootstrapTest,NoKnownChannelCreds)293 TEST(XdsBootstrapTest, NoKnownChannelCreds) {
294   const char* json_str =
295       "{"
296       "  \"xds_servers\": ["
297       "    {"
298       "      \"server_uri\": \"fake:///lb\","
299       "      \"channel_creds\": [{\"type\": \"unknown\"}]"
300       "    }"
301       "  ]"
302       "}";
303   auto bootstrap = GrpcXdsBootstrap::Create(json_str);
304   EXPECT_EQ(bootstrap.status().message(),
305             "errors validating JSON: ["
306             "field:xds_servers[0].channel_creds "
307             "error:no known creds type found]")
308       << bootstrap.status();
309 }
310 
TEST(XdsBootstrapTest,MissingXdsServers)311 TEST(XdsBootstrapTest, MissingXdsServers) {
312   auto bootstrap = GrpcXdsBootstrap::Create("{}");
313   EXPECT_EQ(
314       bootstrap.status().message(),
315       "errors validating JSON: [field:xds_servers error:field not present]")
316       << bootstrap.status();
317 }
318 
TEST(XdsBootstrapTest,EmptyXdsServers)319 TEST(XdsBootstrapTest, EmptyXdsServers) {
320   const char* json_str =
321       "{"
322       "  \"xds_servers\": ["
323       "  ]"
324       "}";
325   auto bootstrap = GrpcXdsBootstrap::Create(json_str);
326   EXPECT_EQ(
327       bootstrap.status().message(),
328       "errors validating JSON: [field:xds_servers error:must be non-empty]")
329       << bootstrap.status();
330 }
331 
TEST(XdsBootstrapTest,TopFieldsWrongTypes)332 TEST(XdsBootstrapTest, TopFieldsWrongTypes) {
333   const char* json_str =
334       "{"
335       "  \"xds_servers\":1,"
336       "  \"node\":1,"
337       "  \"server_listener_resource_name_template\":1,"
338       "  \"certificate_providers\":1"
339       "}";
340   auto bootstrap = GrpcXdsBootstrap::Create(json_str);
341   EXPECT_EQ(
342       bootstrap.status().message(),
343       "errors validating JSON: ["
344       "field:certificate_providers error:is not an object; "
345       "field:node error:is not an object; "
346       "field:server_listener_resource_name_template error:is not a string; "
347       "field:xds_servers error:is not an array]")
348       << bootstrap.status();
349 }
350 
TEST(XdsBootstrapTest,XdsServerMissingFields)351 TEST(XdsBootstrapTest, XdsServerMissingFields) {
352   const char* json_str =
353       "{"
354       "  \"xds_servers\":[{}]"
355       "}";
356   auto bootstrap = GrpcXdsBootstrap::Create(json_str);
357   EXPECT_EQ(bootstrap.status().message(),
358             "errors validating JSON: ["
359             "field:xds_servers[0].channel_creds error:field not present; "
360             "field:xds_servers[0].server_uri error:field not present]")
361       << bootstrap.status();
362 }
363 
TEST(XdsBootstrapTest,XdsServerUriAndCredsWrongTypes)364 TEST(XdsBootstrapTest, XdsServerUriAndCredsWrongTypes) {
365   const char* json_str =
366       "{"
367       "  \"xds_servers\":["
368       "    {"
369       "      \"server_uri\":1,"
370       "      \"channel_creds\":1"
371       "    }"
372       "  ]"
373       "}";
374   auto bootstrap = GrpcXdsBootstrap::Create(json_str);
375   EXPECT_EQ(bootstrap.status().message(),
376             "errors validating JSON: ["
377             "field:xds_servers[0].channel_creds error:is not an array; "
378             "field:xds_servers[0].server_uri error:is not a string]")
379       << bootstrap.status();
380 }
381 
TEST(XdsBootstrapTest,ChannelCredsFieldsWrongTypes)382 TEST(XdsBootstrapTest, ChannelCredsFieldsWrongTypes) {
383   const char* json_str =
384       "{"
385       "  \"xds_servers\":["
386       "    {"
387       "      \"server_uri\":\"foo\","
388       "      \"channel_creds\":["
389       "        {"
390       "          \"type\":0,"
391       "          \"config\":1"
392       "        }"
393       "      ]"
394       "    }"
395       "  ]"
396       "}";
397   auto bootstrap = GrpcXdsBootstrap::Create(json_str);
398   EXPECT_EQ(
399       bootstrap.status().message(),
400       "errors validating JSON: ["
401       "field:xds_servers[0].channel_creds[0].config error:is not an object; "
402       "field:xds_servers[0].channel_creds[0].type error:is not a string]")
403       << bootstrap.status();
404 }
405 
TEST(XdsBootstrapTest,NodeFieldsWrongTypes)406 TEST(XdsBootstrapTest, NodeFieldsWrongTypes) {
407   const char* json_str =
408       "{"
409       "  \"node\":{"
410       "    \"id\":0,"
411       "    \"cluster\":0,"
412       "    \"locality\":0,"
413       "    \"metadata\":0"
414       "  }"
415       "}";
416   auto bootstrap = GrpcXdsBootstrap::Create(json_str);
417   EXPECT_EQ(bootstrap.status().message(),
418             "errors validating JSON: ["
419             "field:node.cluster error:is not a string; "
420             "field:node.id error:is not a string; "
421             "field:node.locality error:is not an object; "
422             "field:node.metadata error:is not an object; "
423             "field:xds_servers error:field not present]")
424       << bootstrap.status();
425 }
426 
TEST(XdsBootstrapTest,LocalityFieldsWrongType)427 TEST(XdsBootstrapTest, LocalityFieldsWrongType) {
428   const char* json_str =
429       "{"
430       "  \"node\":{"
431       "    \"locality\":{"
432       "      \"region\":0,"
433       "      \"zone\":0,"
434       "      \"sub_zone\":0"
435       "    }"
436       "  }"
437       "}";
438   auto bootstrap = GrpcXdsBootstrap::Create(json_str);
439   EXPECT_EQ(bootstrap.status().message(),
440             "errors validating JSON: ["
441             "field:node.locality.region error:is not a string; "
442             "field:node.locality.sub_zone error:is not a string; "
443             "field:node.locality.zone error:is not a string; "
444             "field:xds_servers error:field not present]")
445       << bootstrap.status();
446 }
447 
TEST(XdsBootstrapTest,CertificateProvidersElementWrongType)448 TEST(XdsBootstrapTest, CertificateProvidersElementWrongType) {
449   const char* json_str =
450       "{"
451       "  \"xds_servers\": ["
452       "    {"
453       "      \"server_uri\": \"fake:///lb\","
454       "      \"channel_creds\": [{\"type\": \"fake\"}]"
455       "    }"
456       "  ],"
457       "  \"certificate_providers\": {"
458       "    \"plugin\":1"
459       "  }"
460       "}";
461   auto bootstrap = GrpcXdsBootstrap::Create(json_str);
462   EXPECT_EQ(bootstrap.status().message(),
463             "errors validating JSON: ["
464             "field:certificate_providers[\"plugin\"] error:is not an object]")
465       << bootstrap.status();
466 }
467 
TEST(XdsBootstrapTest,CertificateProvidersPluginNameWrongType)468 TEST(XdsBootstrapTest, CertificateProvidersPluginNameWrongType) {
469   const char* json_str =
470       "{"
471       "  \"xds_servers\": ["
472       "    {"
473       "      \"server_uri\": \"fake:///lb\","
474       "      \"channel_creds\": [{\"type\": \"fake\"}]"
475       "    }"
476       "  ],"
477       "  \"certificate_providers\": {"
478       "    \"plugin\": {"
479       "      \"plugin_name\":1"
480       "    }"
481       "  }"
482       "}";
483   auto bootstrap = GrpcXdsBootstrap::Create(json_str);
484   EXPECT_EQ(bootstrap.status().message(),
485             "errors validating JSON: ["
486             "field:certificate_providers[\"plugin\"].plugin_name error:"
487             "is not a string]")
488       << bootstrap.status();
489 }
490 
TEST(XdsBootstrapTest,CertificateProvidersUnrecognizedPluginName)491 TEST(XdsBootstrapTest, CertificateProvidersUnrecognizedPluginName) {
492   const char* json_str =
493       "{"
494       "  \"xds_servers\": ["
495       "    {"
496       "      \"server_uri\": \"fake:///lb\","
497       "      \"channel_creds\": [{\"type\": \"fake\"}]"
498       "    }"
499       "  ],"
500       "  \"certificate_providers\": {"
501       "    \"plugin\": {"
502       "      \"plugin_name\":\"unknown\""
503       "    }"
504       "  }"
505       "}";
506   auto bootstrap = GrpcXdsBootstrap::Create(json_str);
507   EXPECT_EQ(bootstrap.status().message(),
508             "errors validating JSON: ["
509             "field:certificate_providers[\"plugin\"].plugin_name error:"
510             "Unrecognized plugin name: unknown]")
511       << bootstrap.status();
512 }
513 
TEST(XdsBootstrapTest,AuthorityXdsServerInvalidResourceTemplate)514 TEST(XdsBootstrapTest, AuthorityXdsServerInvalidResourceTemplate) {
515   const char* json_str =
516       "{"
517       "  \"xds_servers\": ["
518       "    {"
519       "      \"server_uri\": \"fake:///lb\","
520       "      \"channel_creds\": [{\"type\": \"fake\"}]"
521       "    }"
522       "  ],"
523       "  \"authorities\": {"
524       "    \"xds.example.com\": {"
525       "      \"client_listener_resource_name_template\": "
526       "\"xds://xds.example.com/envoy.config.listener.v3.Listener/grpc/server/"
527       "%s\","
528       "      \"xds_servers\": ["
529       "        {"
530       "          \"server_uri\": \"fake:///xds_server\","
531       "          \"channel_creds\": ["
532       "            {"
533       "              \"type\": \"fake\""
534       "            }"
535       "          ],"
536       "          \"server_features\": [\"xds_v3\"]"
537       "        }"
538       "      ]"
539       "    }"
540       "  }"
541       "}";
542   auto bootstrap = GrpcXdsBootstrap::Create(json_str);
543   EXPECT_EQ(bootstrap.status().message(),
544             "errors validating JSON: ["
545             "field:authorities[\"xds.example.com\"]"
546             ".client_listener_resource_name_template error:"
547             "field must begin with \"xdstp://xds.example.com/\"]")
548       << bootstrap.status();
549 }
550 
TEST(XdsBootstrapTest,AuthorityXdsServerMissingServerUri)551 TEST(XdsBootstrapTest, AuthorityXdsServerMissingServerUri) {
552   const char* json_str =
553       "{"
554       "  \"xds_servers\": ["
555       "    {"
556       "      \"server_uri\": \"fake:///lb\","
557       "      \"channel_creds\": [{\"type\": \"fake\"}]"
558       "    }"
559       "  ],"
560       "  \"authorities\": {"
561       "    \"xds.example.com\": {"
562       "      \"client_listener_resource_name_template\": "
563       "\"xdstp://xds.example.com/envoy.config.listener.v3.Listener/grpc/server/"
564       "%s\","
565       "      \"xds_servers\":[{}]"
566       "    }"
567       "  }"
568       "}";
569   auto bootstrap = GrpcXdsBootstrap::Create(json_str);
570   EXPECT_EQ(
571       bootstrap.status().message(),
572       "errors validating JSON: ["
573       "field:authorities[\"xds.example.com\"].xds_servers[0].channel_creds "
574       "error:field not present; "
575       "field:authorities[\"xds.example.com\"].xds_servers[0].server_uri "
576       "error:field not present]")
577       << bootstrap.status();
578 }
579 
580 class FakeCertificateProviderFactory : public CertificateProviderFactory {
581  public:
582   class Config : public CertificateProviderFactory::Config {
583    public:
value() const584     int value() const { return value_; }
585 
name() const586     absl::string_view name() const override { return "fake"; }
587 
ToString() const588     std::string ToString() const override {
589       return absl::StrFormat(
590           "{\n"
591           "  value=%d"
592           "}",
593           value_);
594     }
595 
JsonLoader(const JsonArgs &)596     static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
597       static const auto* loader = JsonObjectLoader<Config>()
598                                       .OptionalField("value", &Config::value_)
599                                       .Finish();
600       return loader;
601     }
602 
603    private:
604     int value_;
605   };
606 
name() const607   absl::string_view name() const override { return "fake"; }
608 
609   RefCountedPtr<CertificateProviderFactory::Config>
CreateCertificateProviderConfig(const Json & config_json,const JsonArgs & args,ValidationErrors * errors)610   CreateCertificateProviderConfig(const Json& config_json, const JsonArgs& args,
611                                   ValidationErrors* errors) override {
612     return LoadFromJson<RefCountedPtr<Config>>(config_json, args, errors);
613   }
614 
CreateCertificateProvider(RefCountedPtr<CertificateProviderFactory::Config>)615   RefCountedPtr<grpc_tls_certificate_provider> CreateCertificateProvider(
616       RefCountedPtr<CertificateProviderFactory::Config> /*config*/) override {
617     return nullptr;
618   }
619 };
620 
TEST(XdsBootstrapTest,CertificateProvidersFakePluginParsingError)621 TEST(XdsBootstrapTest, CertificateProvidersFakePluginParsingError) {
622   const char* json_str =
623       "{"
624       "  \"xds_servers\": ["
625       "    {"
626       "      \"server_uri\": \"fake:///lb\","
627       "      \"channel_creds\": [{\"type\": \"fake\"}]"
628       "    }"
629       "  ],"
630       "  \"certificate_providers\": {"
631       "    \"fake_plugin\": {"
632       "      \"plugin_name\": \"fake\","
633       "      \"config\": {"
634       "        \"value\": []"
635       "      }"
636       "    }"
637       "  }"
638       "}";
639   auto bootstrap = GrpcXdsBootstrap::Create(json_str);
640   EXPECT_EQ(bootstrap.status().message(),
641             "errors validating JSON: ["
642             "field:certificate_providers[\"fake_plugin\"].config.value "
643             "error:is not a number]")
644       << bootstrap.status();
645 }
646 
TEST(XdsBootstrapTest,CertificateProvidersFakePluginParsingSuccess)647 TEST(XdsBootstrapTest, CertificateProvidersFakePluginParsingSuccess) {
648   const char* json_str =
649       "{"
650       "  \"xds_servers\": ["
651       "    {"
652       "      \"server_uri\": \"fake:///lb\","
653       "      \"channel_creds\": [{\"type\": \"fake\"}]"
654       "    }"
655       "  ],"
656       "  \"certificate_providers\": {"
657       "    \"fake_plugin\": {"
658       "      \"plugin_name\": \"fake\","
659       "      \"config\": {"
660       "        \"value\": 10"
661       "      }"
662       "    }"
663       "  }"
664       "}";
665   auto bootstrap_or = GrpcXdsBootstrap::Create(json_str);
666   ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status();
667   auto bootstrap = std::move(*bootstrap_or);
668   const CertificateProviderStore::PluginDefinition& fake_plugin =
669       bootstrap->certificate_providers().at("fake_plugin");
670   ASSERT_EQ(fake_plugin.plugin_name, "fake");
671   ASSERT_EQ(fake_plugin.config->name(), "fake");
672   auto* config = static_cast<FakeCertificateProviderFactory::Config*>(
673       fake_plugin.config.get());
674   ASSERT_EQ(config->value(), 10);
675 }
676 
TEST(XdsBootstrapTest,CertificateProvidersFakePluginEmptyConfig)677 TEST(XdsBootstrapTest, CertificateProvidersFakePluginEmptyConfig) {
678   const char* json_str =
679       "{"
680       "  \"xds_servers\": ["
681       "    {"
682       "      \"server_uri\": \"fake:///lb\","
683       "      \"channel_creds\": [{\"type\": \"fake\"}]"
684       "    }"
685       "  ],"
686       "  \"certificate_providers\": {"
687       "    \"fake_plugin\": {"
688       "      \"plugin_name\": \"fake\""
689       "    }"
690       "  }"
691       "}";
692   auto bootstrap_or = GrpcXdsBootstrap::Create(json_str);
693   ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status();
694   auto bootstrap = std::move(*bootstrap_or);
695   const CertificateProviderStore::PluginDefinition& fake_plugin =
696       bootstrap->certificate_providers().at("fake_plugin");
697   ASSERT_EQ(fake_plugin.plugin_name, "fake");
698   ASSERT_EQ(fake_plugin.config->name(), "fake");
699   auto* config = static_cast<FakeCertificateProviderFactory::Config*>(
700       fake_plugin.config.get());
701   ASSERT_EQ(config->value(), 0);
702 }
703 
TEST(XdsBootstrapTest,XdsServerToJsonAndParse)704 TEST(XdsBootstrapTest, XdsServerToJsonAndParse) {
705   const char* json_str =
706       "    {"
707       "      \"server_uri\": \"fake:///lb\","
708       "      \"channel_creds\": ["
709       "        {"
710       "          \"type\": \"fake\","
711       "          \"ignore\": 0"
712       "        }"
713       "      ],"
714       "      \"ignore\": 0"
715       "    }";
716   auto json = JsonParse(json_str);
717   ASSERT_TRUE(json.ok()) << json.status();
718   auto xds_server = LoadFromJson<GrpcXdsBootstrap::GrpcXdsServer>(*json);
719   ASSERT_TRUE(xds_server.ok()) << xds_server.status();
720   Json output = xds_server->ToJson();
721   auto output_xds_server =
722       LoadFromJson<GrpcXdsBootstrap::GrpcXdsServer>(output);
723   ASSERT_TRUE(output_xds_server.ok()) << output_xds_server.status();
724   EXPECT_EQ(*xds_server, *output_xds_server);
725 }
726 
TEST(XdsBootstrapTest,NoXdsServersEnvVar)727 TEST(XdsBootstrapTest, NoXdsServersEnvVar) {
728   ScopedEnvVar fallback_enabled("GRPC_EXPERIMENTAL_XDS_FALLBACK", "1");
729   const char* json_str =
730       "{"
731       "  \"xds_servers\": ["
732       "    {"
733       "      \"server_uri\": \"fake:///lb1\","
734       "      \"channel_creds\": ["
735       "        {"
736       "          \"type\": \"fake\","
737       "          \"ignore\": 0"
738       "        }"
739       "      ],"
740       "      \"ignore\": 0"
741       "    },"
742       "    {"
743       "      \"server_uri\": \"fake:///lb2\","
744       "      \"channel_creds\": ["
745       "        {"
746       "          \"type\": \"fake\","
747       "          \"ignore\": 0"
748       "        }"
749       "      ],"
750       "      \"ignore\": 0"
751       "    }"
752       "  ],"
753       "  \"authorities\": {"
754       "    \"xds.example.com\": {"
755       "      \"client_listener_resource_name_template\": "
756       "\"xdstp://xds.example.com/envoy.config.listener.v3.Listener/grpc/server/"
757       "%s\","
758       "      \"xds_servers\": ["
759       "        {"
760       "          \"server_uri\": \"fake:///xds_server\","
761       "          \"channel_creds\": ["
762       "            {"
763       "              \"type\": \"fake\""
764       "            }"
765       "          ],"
766       "          \"server_features\": [\"xds_v3\"]"
767       "        },"
768       "        {"
769       "          \"server_uri\": \"fake:///xds_server2\","
770       "          \"channel_creds\": ["
771       "            {"
772       "              \"type\": \"fake\""
773       "            }"
774       "          ],"
775       "          \"server_features\": [\"xds_v3\"]"
776       "        }"
777       "      ]"
778       "    }"
779       "  },"
780       "  \"node\": {"
781       "    \"id\": \"foo\","
782       "    \"cluster\": \"bar\","
783       "    \"locality\": {"
784       "      \"region\": \"milky_way\","
785       "      \"zone\": \"sol_system\","
786       "      \"sub_zone\": \"earth\","
787       "      \"ignore\": {}"
788       "    },"
789       "    \"metadata\": {"
790       "      \"foo\": 1,"
791       "      \"bar\": 2"
792       "    },"
793       "    \"ignore\": \"whee\""
794       "  },"
795       "  \"server_listener_resource_name_template\": \"example/resource\","
796       "  \"ignore\": {}"
797       "}";
798   auto bootstrap_or = GrpcXdsBootstrap::Create(json_str);
799   ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status();
800   auto bootstrap = std::move(*bootstrap_or);
801   EXPECT_THAT(bootstrap->servers(),
802               ::testing::ElementsAre(EqXdsServer("fake:///lb1", "fake"),
803                                      EqXdsServer("fake:///lb2", "fake")));
804   auto* authority = static_cast<const GrpcXdsBootstrap::GrpcAuthority*>(
805       bootstrap->LookupAuthority("xds.example.com"));
806   ASSERT_NE(authority, nullptr);
807   EXPECT_THAT(
808       authority->servers(),
809       ::testing::ElementsAre(EqXdsServer("fake:///xds_server", "fake"),
810                              EqXdsServer("fake:///xds_server2", "fake")));
811 }
812 
813 }  // namespace
814 }  // namespace testing
815 }  // namespace grpc_core
816 
main(int argc,char ** argv)817 int main(int argc, char** argv) {
818   ::testing::InitGoogleTest(&argc, argv);
819   grpc::testing::TestEnvironment env(&argc, argv);
820   grpc_core::CoreConfiguration::RegisterBuilder(
821       [](grpc_core::CoreConfiguration::Builder* builder) {
822         builder->certificate_provider_registry()
823             ->RegisterCertificateProviderFactory(
824                 std::make_unique<
825                     grpc_core::testing::FakeCertificateProviderFactory>());
826       });
827   grpc_init();
828   int ret = RUN_ALL_TESTS();
829   grpc_shutdown();
830   return ret;
831 }
832