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