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