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