Merge pull request #20380 from markdroth/xds_client_bootstrap

xds client bootstrap file
reviewable/pr20476/r2^2
Mark D. Roth 5 years ago committed by GitHub
commit 36319502be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      BUILD
  2. 2
      BUILD.gn
  3. 44
      CMakeLists.txt
  4. 50
      Makefile
  5. 15
      build.yaml
  6. 1
      config.m4
  7. 1
      config.w32
  8. 3
      gRPC-Core.podspec
  9. 2
      grpc.gemspec
  10. 2
      grpc.gyp
  11. 2
      package.xml
  12. 68
      src/core/ext/filters/client_channel/lb_policy/xds/xds.cc
  13. 123
      src/core/ext/filters/client_channel/xds/xds_api.cc
  14. 9
      src/core/ext/filters/client_channel/xds/xds_api.h
  15. 452
      src/core/ext/filters/client_channel/xds/xds_bootstrap.cc
  16. 99
      src/core/ext/filters/client_channel/xds/xds_bootstrap.h
  17. 5
      src/core/ext/filters/client_channel/xds/xds_channel.cc
  18. 4
      src/core/ext/filters/client_channel/xds/xds_channel.h
  19. 29
      src/core/ext/filters/client_channel/xds/xds_channel_secure.cc
  20. 52
      src/core/ext/filters/client_channel/xds/xds_client.cc
  21. 11
      src/core/ext/filters/client_channel/xds/xds_client.h
  22. 2
      src/core/lib/json/json.h
  23. 4
      src/core/lib/json/json_string.cc
  24. 1
      src/python/grpcio/grpc_core_dependencies.py
  25. 17
      test/core/client_channel/BUILD
  26. 338
      test/core/client_channel/xds_bootstrap_test.cc
  27. 4
      test/cpp/end2end/BUILD
  28. 22
      test/cpp/end2end/xds_bootstrap.json
  29. 12
      test/cpp/end2end/xds_bootstrap_bad.json
  30. 26
      test/cpp/end2end/xds_end2end_test.cc
  31. 2
      tools/doxygen/Doxyfile.core.internal
  32. 24
      tools/run_tests/generated/tests.json

@ -1256,12 +1256,14 @@ grpc_cc_library(
name = "grpc_xds_client",
srcs = [
"src/core/ext/filters/client_channel/xds/xds_api.cc",
"src/core/ext/filters/client_channel/xds/xds_bootstrap.cc",
"src/core/ext/filters/client_channel/xds/xds_client.cc",
"src/core/ext/filters/client_channel/xds/xds_channel.cc",
"src/core/ext/filters/client_channel/xds/xds_client_stats.cc",
],
hdrs = [
"src/core/ext/filters/client_channel/xds/xds_api.h",
"src/core/ext/filters/client_channel/xds/xds_bootstrap.h",
"src/core/ext/filters/client_channel/xds/xds_client.h",
"src/core/ext/filters/client_channel/xds/xds_channel.h",
"src/core/ext/filters/client_channel/xds/xds_channel_args.h",
@ -1279,12 +1281,14 @@ grpc_cc_library(
name = "grpc_xds_client_secure",
srcs = [
"src/core/ext/filters/client_channel/xds/xds_api.cc",
"src/core/ext/filters/client_channel/xds/xds_bootstrap.cc",
"src/core/ext/filters/client_channel/xds/xds_client.cc",
"src/core/ext/filters/client_channel/xds/xds_channel_secure.cc",
"src/core/ext/filters/client_channel/xds/xds_client_stats.cc",
],
hdrs = [
"src/core/ext/filters/client_channel/xds/xds_api.h",
"src/core/ext/filters/client_channel/xds/xds_bootstrap.h",
"src/core/ext/filters/client_channel/xds/xds_client.h",
"src/core/ext/filters/client_channel/xds/xds_channel.h",
"src/core/ext/filters/client_channel/xds/xds_channel_args.h",

@ -298,6 +298,8 @@ config("grpc_config") {
"src/core/ext/filters/client_channel/subchannel_pool_interface.h",
"src/core/ext/filters/client_channel/xds/xds_api.cc",
"src/core/ext/filters/client_channel/xds/xds_api.h",
"src/core/ext/filters/client_channel/xds/xds_bootstrap.cc",
"src/core/ext/filters/client_channel/xds/xds_bootstrap.h",
"src/core/ext/filters/client_channel/xds/xds_channel.h",
"src/core/ext/filters/client_channel/xds/xds_channel_args.h",
"src/core/ext/filters/client_channel/xds/xds_channel_secure.cc",

@ -731,6 +731,7 @@ add_dependencies(buildtests_cxx transport_security_common_api_test)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx writes_per_rpc_test)
endif()
add_dependencies(buildtests_cxx xds_bootstrap_test)
add_dependencies(buildtests_cxx xds_end2end_test)
add_dependencies(buildtests_cxx bad_streaming_id_bad_client_test)
add_dependencies(buildtests_cxx badreq_bad_client_test)
@ -1310,6 +1311,7 @@ add_library(grpc
src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc
src/core/ext/filters/client_channel/lb_policy/xds/xds.cc
src/core/ext/filters/client_channel/xds/xds_api.cc
src/core/ext/filters/client_channel/xds/xds_bootstrap.cc
src/core/ext/filters/client_channel/xds/xds_channel_secure.cc
src/core/ext/filters/client_channel/xds/xds_client.cc
src/core/ext/filters/client_channel/xds/xds_client_stats.cc
@ -2827,6 +2829,7 @@ add_library(grpc_unsecure
src/core/ext/upb-generated/src/proto/grpc/lb/v1/load_balancer.upb.c
src/core/ext/filters/client_channel/lb_policy/xds/xds.cc
src/core/ext/filters/client_channel/xds/xds_api.cc
src/core/ext/filters/client_channel/xds/xds_bootstrap.cc
src/core/ext/filters/client_channel/xds/xds_channel.cc
src/core/ext/filters/client_channel/xds/xds_client.cc
src/core/ext/filters/client_channel/xds/xds_client_stats.cc
@ -16795,6 +16798,47 @@ endif()
endif (gRPC_BUILD_TESTS)
if (gRPC_BUILD_TESTS)
add_executable(xds_bootstrap_test
test/core/client_channel/xds_bootstrap_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(xds_bootstrap_test
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
PRIVATE ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
PRIVATE ${_gRPC_BENCHMARK_INCLUDE_DIR}
PRIVATE ${_gRPC_CARES_INCLUDE_DIR}
PRIVATE ${_gRPC_GFLAGS_INCLUDE_DIR}
PRIVATE ${_gRPC_PROTOBUF_INCLUDE_DIR}
PRIVATE ${_gRPC_SSL_INCLUDE_DIR}
PRIVATE ${_gRPC_UPB_GENERATED_DIR}
PRIVATE ${_gRPC_UPB_GRPC_GENERATED_DIR}
PRIVATE ${_gRPC_UPB_INCLUDE_DIR}
PRIVATE ${_gRPC_ZLIB_INCLUDE_DIR}
PRIVATE third_party/googletest/googletest/include
PRIVATE third_party/googletest/googletest
PRIVATE third_party/googletest/googlemock/include
PRIVATE third_party/googletest/googlemock
PRIVATE ${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(xds_bootstrap_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
grpc++
grpc
gpr
${_gRPC_GFLAGS_LIBRARIES}
)
endif (gRPC_BUILD_TESTS)
if (gRPC_BUILD_TESTS)
add_executable(xds_end2end_test
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/ads_for_test.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/ads_for_test.grpc.pb.cc

@ -1296,6 +1296,7 @@ transport_connectivity_state_test: $(BINDIR)/$(CONFIG)/transport_connectivity_st
transport_pid_controller_test: $(BINDIR)/$(CONFIG)/transport_pid_controller_test
transport_security_common_api_test: $(BINDIR)/$(CONFIG)/transport_security_common_api_test
writes_per_rpc_test: $(BINDIR)/$(CONFIG)/writes_per_rpc_test
xds_bootstrap_test: $(BINDIR)/$(CONFIG)/xds_bootstrap_test
xds_end2end_test: $(BINDIR)/$(CONFIG)/xds_end2end_test
public_headers_must_be_c89: $(BINDIR)/$(CONFIG)/public_headers_must_be_c89
boringssl_ssl_test: $(BINDIR)/$(CONFIG)/boringssl_ssl_test
@ -1767,6 +1768,7 @@ buildtests_cxx: privatelibs_cxx \
$(BINDIR)/$(CONFIG)/transport_pid_controller_test \
$(BINDIR)/$(CONFIG)/transport_security_common_api_test \
$(BINDIR)/$(CONFIG)/writes_per_rpc_test \
$(BINDIR)/$(CONFIG)/xds_bootstrap_test \
$(BINDIR)/$(CONFIG)/xds_end2end_test \
$(BINDIR)/$(CONFIG)/boringssl_ssl_test \
$(BINDIR)/$(CONFIG)/boringssl_crypto_test \
@ -1937,6 +1939,7 @@ buildtests_cxx: privatelibs_cxx \
$(BINDIR)/$(CONFIG)/transport_pid_controller_test \
$(BINDIR)/$(CONFIG)/transport_security_common_api_test \
$(BINDIR)/$(CONFIG)/writes_per_rpc_test \
$(BINDIR)/$(CONFIG)/xds_bootstrap_test \
$(BINDIR)/$(CONFIG)/xds_end2end_test \
$(BINDIR)/$(CONFIG)/bad_streaming_id_bad_client_test \
$(BINDIR)/$(CONFIG)/badreq_bad_client_test \
@ -2483,6 +2486,8 @@ test_cxx: buildtests_cxx
$(Q) $(BINDIR)/$(CONFIG)/transport_security_common_api_test || ( echo test transport_security_common_api_test failed ; exit 1 )
$(E) "[RUN] Testing writes_per_rpc_test"
$(Q) $(BINDIR)/$(CONFIG)/writes_per_rpc_test || ( echo test writes_per_rpc_test failed ; exit 1 )
$(E) "[RUN] Testing xds_bootstrap_test"
$(Q) $(BINDIR)/$(CONFIG)/xds_bootstrap_test || ( echo test xds_bootstrap_test failed ; exit 1 )
$(E) "[RUN] Testing xds_end2end_test"
$(Q) $(BINDIR)/$(CONFIG)/xds_end2end_test || ( echo test xds_end2end_test failed ; exit 1 )
$(E) "[RUN] Testing bad_streaming_id_bad_client_test"
@ -3853,6 +3858,7 @@ LIBGRPC_SRC = \
src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc \
src/core/ext/filters/client_channel/lb_policy/xds/xds.cc \
src/core/ext/filters/client_channel/xds/xds_api.cc \
src/core/ext/filters/client_channel/xds/xds_bootstrap.cc \
src/core/ext/filters/client_channel/xds/xds_channel_secure.cc \
src/core/ext/filters/client_channel/xds/xds_client.cc \
src/core/ext/filters/client_channel/xds/xds_client_stats.cc \
@ -5322,6 +5328,7 @@ LIBGRPC_UNSECURE_SRC = \
src/core/ext/upb-generated/src/proto/grpc/lb/v1/load_balancer.upb.c \
src/core/ext/filters/client_channel/lb_policy/xds/xds.cc \
src/core/ext/filters/client_channel/xds/xds_api.cc \
src/core/ext/filters/client_channel/xds/xds_bootstrap.cc \
src/core/ext/filters/client_channel/xds/xds_channel.cc \
src/core/ext/filters/client_channel/xds/xds_client.cc \
src/core/ext/filters/client_channel/xds/xds_client_stats.cc \
@ -20023,6 +20030,49 @@ endif
endif
XDS_BOOTSTRAP_TEST_SRC = \
test/core/client_channel/xds_bootstrap_test.cc \
XDS_BOOTSTRAP_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(XDS_BOOTSTRAP_TEST_SRC))))
ifeq ($(NO_SECURE),true)
# You can't build secure targets if you don't have OpenSSL.
$(BINDIR)/$(CONFIG)/xds_bootstrap_test: openssl_dep_error
else
ifeq ($(NO_PROTOBUF),true)
# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.5.0+.
$(BINDIR)/$(CONFIG)/xds_bootstrap_test: protobuf_dep_error
else
$(BINDIR)/$(CONFIG)/xds_bootstrap_test: $(PROTOBUF_DEP) $(XDS_BOOTSTRAP_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
$(E) "[LD] Linking $@"
$(Q) mkdir -p `dirname $@`
$(Q) $(LDXX) $(LDFLAGS) $(XDS_BOOTSTRAP_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/xds_bootstrap_test
endif
endif
$(OBJDIR)/$(CONFIG)/test/core/client_channel/xds_bootstrap_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr.a
deps_xds_bootstrap_test: $(XDS_BOOTSTRAP_TEST_OBJS:.o=.dep)
ifneq ($(NO_SECURE),true)
ifneq ($(NO_DEPS),true)
-include $(XDS_BOOTSTRAP_TEST_OBJS:.o=.dep)
endif
endif
XDS_END2END_TEST_SRC = \
$(GENDIR)/src/proto/grpc/testing/xds/ads_for_test.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/ads_for_test.grpc.pb.cc \
$(GENDIR)/src/proto/grpc/testing/xds/eds_for_test.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/eds_for_test.grpc.pb.cc \

@ -1538,12 +1538,14 @@ filegroups:
- name: grpc_xds_client
headers:
- src/core/ext/filters/client_channel/xds/xds_api.h
- src/core/ext/filters/client_channel/xds/xds_bootstrap.h
- src/core/ext/filters/client_channel/xds/xds_channel.h
- src/core/ext/filters/client_channel/xds/xds_channel_args.h
- src/core/ext/filters/client_channel/xds/xds_client.h
- src/core/ext/filters/client_channel/xds/xds_client_stats.h
src:
- src/core/ext/filters/client_channel/xds/xds_api.cc
- src/core/ext/filters/client_channel/xds/xds_bootstrap.cc
- src/core/ext/filters/client_channel/xds/xds_channel.cc
- src/core/ext/filters/client_channel/xds/xds_client.cc
- src/core/ext/filters/client_channel/xds/xds_client_stats.cc
@ -1554,12 +1556,14 @@ filegroups:
- name: grpc_xds_client_secure
headers:
- src/core/ext/filters/client_channel/xds/xds_api.h
- src/core/ext/filters/client_channel/xds/xds_bootstrap.h
- src/core/ext/filters/client_channel/xds/xds_channel.h
- src/core/ext/filters/client_channel/xds/xds_channel_args.h
- src/core/ext/filters/client_channel/xds/xds_client.h
- src/core/ext/filters/client_channel/xds/xds_client_stats.h
src:
- src/core/ext/filters/client_channel/xds/xds_api.cc
- src/core/ext/filters/client_channel/xds/xds_bootstrap.cc
- src/core/ext/filters/client_channel/xds/xds_channel_secure.cc
- src/core/ext/filters/client_channel/xds/xds_client.cc
- src/core/ext/filters/client_channel/xds/xds_client_stats.cc
@ -6008,6 +6012,17 @@ targets:
- mac
- linux
- posix
- name: xds_bootstrap_test
gtest: true
build: test
language: c++
src:
- test/core/client_channel/xds_bootstrap_test.cc
deps:
- grpc_test_util
- grpc++
- grpc
- gpr
- name: xds_end2end_test
gtest: true
build: test

@ -418,6 +418,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc \
src/core/ext/filters/client_channel/lb_policy/xds/xds.cc \
src/core/ext/filters/client_channel/xds/xds_api.cc \
src/core/ext/filters/client_channel/xds/xds_bootstrap.cc \
src/core/ext/filters/client_channel/xds/xds_channel_secure.cc \
src/core/ext/filters/client_channel/xds/xds_client.cc \
src/core/ext/filters/client_channel/xds/xds_client_stats.cc \

@ -388,6 +388,7 @@ if (PHP_GRPC != "no") {
"src\\core\\ext\\filters\\client_channel\\resolver\\fake\\fake_resolver.cc " +
"src\\core\\ext\\filters\\client_channel\\lb_policy\\xds\\xds.cc " +
"src\\core\\ext\\filters\\client_channel\\xds\\xds_api.cc " +
"src\\core\\ext\\filters\\client_channel\\xds\\xds_bootstrap.cc " +
"src\\core\\ext\\filters\\client_channel\\xds\\xds_channel_secure.cc " +
"src\\core\\ext\\filters\\client_channel\\xds\\xds_client.cc " +
"src\\core\\ext\\filters\\client_channel\\xds\\xds_client_stats.cc " +

@ -556,6 +556,7 @@ Pod::Spec.new do |s|
'src/core/ext/upb-generated/src/proto/grpc/lb/v1/load_balancer.upb.h',
'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h',
'src/core/ext/filters/client_channel/xds/xds_api.h',
'src/core/ext/filters/client_channel/xds/xds_bootstrap.h',
'src/core/ext/filters/client_channel/xds/xds_channel.h',
'src/core/ext/filters/client_channel/xds/xds_channel_args.h',
'src/core/ext/filters/client_channel/xds/xds_client.h',
@ -918,6 +919,7 @@ Pod::Spec.new do |s|
'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc',
'src/core/ext/filters/client_channel/lb_policy/xds/xds.cc',
'src/core/ext/filters/client_channel/xds/xds_api.cc',
'src/core/ext/filters/client_channel/xds/xds_bootstrap.cc',
'src/core/ext/filters/client_channel/xds/xds_channel_secure.cc',
'src/core/ext/filters/client_channel/xds/xds_client.cc',
'src/core/ext/filters/client_channel/xds/xds_client_stats.cc',
@ -1294,6 +1296,7 @@ Pod::Spec.new do |s|
'src/core/ext/upb-generated/src/proto/grpc/lb/v1/load_balancer.upb.h',
'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h',
'src/core/ext/filters/client_channel/xds/xds_api.h',
'src/core/ext/filters/client_channel/xds/xds_bootstrap.h',
'src/core/ext/filters/client_channel/xds/xds_channel.h',
'src/core/ext/filters/client_channel/xds/xds_channel_args.h',
'src/core/ext/filters/client_channel/xds/xds_client.h',

@ -486,6 +486,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/ext/upb-generated/src/proto/grpc/lb/v1/load_balancer.upb.h )
s.files += %w( src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h )
s.files += %w( src/core/ext/filters/client_channel/xds/xds_api.h )
s.files += %w( src/core/ext/filters/client_channel/xds/xds_bootstrap.h )
s.files += %w( src/core/ext/filters/client_channel/xds/xds_channel.h )
s.files += %w( src/core/ext/filters/client_channel/xds/xds_channel_args.h )
s.files += %w( src/core/ext/filters/client_channel/xds/xds_client.h )
@ -848,6 +849,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc )
s.files += %w( src/core/ext/filters/client_channel/lb_policy/xds/xds.cc )
s.files += %w( src/core/ext/filters/client_channel/xds/xds_api.cc )
s.files += %w( src/core/ext/filters/client_channel/xds/xds_bootstrap.cc )
s.files += %w( src/core/ext/filters/client_channel/xds/xds_channel_secure.cc )
s.files += %w( src/core/ext/filters/client_channel/xds/xds_client.cc )
s.files += %w( src/core/ext/filters/client_channel/xds/xds_client_stats.cc )

@ -556,6 +556,7 @@
'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc',
'src/core/ext/filters/client_channel/lb_policy/xds/xds.cc',
'src/core/ext/filters/client_channel/xds/xds_api.cc',
'src/core/ext/filters/client_channel/xds/xds_bootstrap.cc',
'src/core/ext/filters/client_channel/xds/xds_channel_secure.cc',
'src/core/ext/filters/client_channel/xds/xds_client.cc',
'src/core/ext/filters/client_channel/xds/xds_client_stats.cc',
@ -1423,6 +1424,7 @@
'src/core/ext/upb-generated/src/proto/grpc/lb/v1/load_balancer.upb.c',
'src/core/ext/filters/client_channel/lb_policy/xds/xds.cc',
'src/core/ext/filters/client_channel/xds/xds_api.cc',
'src/core/ext/filters/client_channel/xds/xds_bootstrap.cc',
'src/core/ext/filters/client_channel/xds/xds_channel.cc',
'src/core/ext/filters/client_channel/xds/xds_client.cc',
'src/core/ext/filters/client_channel/xds/xds_client_stats.cc',

@ -491,6 +491,7 @@
<file baseinstalldir="/" name="src/core/ext/upb-generated/src/proto/grpc/lb/v1/load_balancer.upb.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/xds/xds_api.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/xds/xds_bootstrap.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/xds/xds_channel.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/xds/xds_channel_args.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/xds/xds_client.h" role="src" />
@ -853,6 +854,7 @@
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/lb_policy/xds/xds.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/xds/xds_api.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/xds/xds_bootstrap.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/xds/xds_channel_secure.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/xds/xds_client.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/xds/xds_client_stats.cc" role="src" />

@ -76,17 +76,13 @@ constexpr char kXds[] = "xds_experimental";
class ParsedXdsConfig : public LoadBalancingPolicy::Config {
public:
ParsedXdsConfig(const char* balancer_name,
RefCountedPtr<LoadBalancingPolicy::Config> child_policy,
ParsedXdsConfig(RefCountedPtr<LoadBalancingPolicy::Config> child_policy,
RefCountedPtr<LoadBalancingPolicy::Config> fallback_policy)
: balancer_name_(balancer_name),
child_policy_(std::move(child_policy)),
: child_policy_(std::move(child_policy)),
fallback_policy_(std::move(fallback_policy)) {}
const char* name() const override { return kXds; }
const char* balancer_name() const { return balancer_name_; };
RefCountedPtr<LoadBalancingPolicy::Config> child_policy() const {
return child_policy_;
}
@ -96,7 +92,6 @@ class ParsedXdsConfig : public LoadBalancingPolicy::Config {
}
private:
const char* balancer_name_ = nullptr;
RefCountedPtr<LoadBalancingPolicy::Config> child_policy_;
RefCountedPtr<LoadBalancingPolicy::Config> fallback_policy_;
};
@ -370,12 +365,6 @@ class XdsLb : public LoadBalancingPolicy {
void ShutdownLocked() override;
// Parses the xds config given the JSON node of the first child of XdsConfig.
// If parsing succeeds, updates \a balancer_name, and updates \a
// child_policy_config_ and \a fallback_policy_config_ if they are also
// found. Does nothing upon failure.
void ParseLbConfig(const ParsedXdsConfig* xds_config);
// Methods for dealing with fallback state.
void MaybeCancelFallbackAtStartupChecks();
static void OnFallbackTimerLocked(void* arg, grpc_error* error);
@ -387,9 +376,6 @@ class XdsLb : public LoadBalancingPolicy {
// Name of the backend server to connect to.
const char* server_name_ = nullptr;
// Name of the balancer to connect to.
UniquePtr<char> balancer_name_;
// Current channel args from the resolver.
const grpc_channel_args* args_ = nullptr;
@ -739,24 +725,12 @@ void XdsLb::ResetBackoffLocked() {
}
}
void XdsLb::ParseLbConfig(const ParsedXdsConfig* xds_config) {
if (xds_config == nullptr || xds_config->balancer_name() == nullptr) return;
// TODO(yashykt) : does this need to be a gpr_strdup
// TODO(juanlishen): Read balancer name from bootstrap file.
balancer_name_ = UniquePtr<char>(gpr_strdup(xds_config->balancer_name()));
child_policy_config_ = xds_config->child_policy();
fallback_policy_config_ = xds_config->fallback_policy();
}
void XdsLb::UpdateLocked(UpdateArgs args) {
const bool is_initial_update = xds_client_ == nullptr;
ParseLbConfig(static_cast<const ParsedXdsConfig*>(args.config.get()));
// TODO(roth): This check should go away once we are getting the xds
// server from the bootstrap file.
if (balancer_name_ == nullptr) {
gpr_log(GPR_ERROR, "[xdslb %p] LB config parsing fails.", this);
return;
}
// Update config.
auto* xds_config = static_cast<const ParsedXdsConfig*>(args.config.get());
child_policy_config_ = xds_config->child_policy();
fallback_policy_config_ = xds_config->fallback_policy();
// Update fallback address list.
fallback_backend_addresses_ = std::move(args.addresses);
// Update args.
@ -765,9 +739,13 @@ void XdsLb::UpdateLocked(UpdateArgs args) {
args.args = nullptr;
// Create an xds client if we don't have one yet.
if (xds_client_ == nullptr) {
grpc_error* error = GRPC_ERROR_NONE;
xds_client_ = MakeOrphanable<XdsClient>(
combiner(), interested_parties(), balancer_name_.get(),
StringView(server_name_), nullptr /* service config watcher */, *args_);
combiner(), interested_parties(), StringView(server_name_),
nullptr /* service config watcher */, *args_, &error);
// TODO(roth): When we move instantiation of the XdsClient into the
// xds resolver, add proper error handling there.
GPR_ASSERT(error == GRPC_ERROR_NONE);
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_trace)) {
gpr_log(GPR_INFO, "[xdslb %p] Created xds client %p", this,
xds_client_.get());
@ -1715,32 +1693,18 @@ class XdsFactory : public LoadBalancingPolicyFactory {
// xds was mentioned as a policy in the deprecated loadBalancingPolicy
// field or in the client API.
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:loadBalancingPolicy error:Xds Parser has required field - "
"balancerName. Please use loadBalancingConfig field of service "
"config instead.");
"field:loadBalancingPolicy error:xds policy requires configuration. "
"Please use loadBalancingConfig field of service config instead.");
return nullptr;
}
GPR_DEBUG_ASSERT(strcmp(json->key, name()) == 0);
InlinedVector<grpc_error*, 3> error_list;
const char* balancer_name = nullptr;
RefCountedPtr<LoadBalancingPolicy::Config> child_policy;
RefCountedPtr<LoadBalancingPolicy::Config> fallback_policy;
for (const grpc_json* field = json->child; field != nullptr;
field = field->next) {
if (field->key == nullptr) continue;
if (strcmp(field->key, "balancerName") == 0) {
if (balancer_name != nullptr) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:balancerName error:Duplicate entry"));
}
if (field->type != GRPC_JSON_STRING) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:balancerName error:type should be string"));
continue;
}
balancer_name = field->value;
} else if (strcmp(field->key, "childPolicy") == 0) {
if (strcmp(field->key, "childPolicy") == 0) {
if (child_policy != nullptr) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:childPolicy error:Duplicate entry"));
@ -1768,7 +1732,7 @@ class XdsFactory : public LoadBalancingPolicyFactory {
}
if (error_list.empty()) {
return RefCountedPtr<LoadBalancingPolicy::Config>(New<ParsedXdsConfig>(
balancer_name, std::move(child_policy), std::move(fallback_policy)));
std::move(child_policy), std::move(fallback_policy)));
} else {
*error = GRPC_ERROR_CREATE_FROM_VECTOR("Xds Parser", &error_list);
return nullptr;

@ -49,7 +49,6 @@ namespace {
constexpr char kEdsTypeUrl[] =
"type.googleapis.com/envoy.api.v2.ClusterLoadAssignment";
constexpr char kEndpointRequired[] = "endpointRequired";
} // namespace
@ -102,22 +101,113 @@ bool XdsDropConfig::ShouldDrop(const UniquePtr<char>** category_name) const {
return false;
}
grpc_slice XdsEdsRequestCreateAndEncode(const char* server_name) {
namespace {
void PopulateMetadataValue(upb_arena* arena, google_protobuf_Value* value_pb,
const XdsBootstrap::MetadataValue& value);
void PopulateListValue(upb_arena* arena, google_protobuf_ListValue* list_value,
const std::vector<XdsBootstrap::MetadataValue>& values) {
for (const auto& value : values) {
auto* value_pb = google_protobuf_ListValue_add_values(list_value, arena);
PopulateMetadataValue(arena, value_pb, value);
}
}
void PopulateMetadata(
upb_arena* arena, google_protobuf_Struct* metadata_pb,
const Map<const char*, XdsBootstrap::MetadataValue, StringLess>& metadata) {
for (const auto& p : metadata) {
google_protobuf_Struct_FieldsEntry* field =
google_protobuf_Struct_add_fields(metadata_pb, arena);
google_protobuf_Struct_FieldsEntry_set_key(field,
upb_strview_makez(p.first));
google_protobuf_Value* value =
google_protobuf_Struct_FieldsEntry_mutable_value(field, arena);
PopulateMetadataValue(arena, value, p.second);
}
}
void PopulateMetadataValue(upb_arena* arena, google_protobuf_Value* value_pb,
const XdsBootstrap::MetadataValue& value) {
switch (value.type) {
case XdsBootstrap::MetadataValue::Type::MD_NULL:
google_protobuf_Value_set_null_value(value_pb, 0);
break;
case XdsBootstrap::MetadataValue::Type::DOUBLE:
google_protobuf_Value_set_number_value(value_pb, value.double_value);
break;
case XdsBootstrap::MetadataValue::Type::STRING:
google_protobuf_Value_set_string_value(
value_pb, upb_strview_makez(value.string_value));
break;
case XdsBootstrap::MetadataValue::Type::BOOL:
google_protobuf_Value_set_bool_value(value_pb, value.bool_value);
break;
case XdsBootstrap::MetadataValue::Type::STRUCT: {
google_protobuf_Struct* struct_value =
google_protobuf_Value_mutable_struct_value(value_pb, arena);
PopulateMetadata(arena, struct_value, value.struct_value);
break;
}
case XdsBootstrap::MetadataValue::Type::LIST: {
google_protobuf_ListValue* list_value =
google_protobuf_Value_mutable_list_value(value_pb, arena);
PopulateListValue(arena, list_value, value.list_value);
break;
}
}
}
void PopulateNode(upb_arena* arena, const XdsBootstrap::Node* node,
const char* build_version, envoy_api_v2_core_Node* node_msg) {
if (node != nullptr) {
if (node->id != nullptr) {
envoy_api_v2_core_Node_set_id(node_msg, upb_strview_makez(node->id));
}
if (node->cluster != nullptr) {
envoy_api_v2_core_Node_set_cluster(node_msg,
upb_strview_makez(node->cluster));
}
if (!node->metadata.empty()) {
google_protobuf_Struct* metadata =
envoy_api_v2_core_Node_mutable_metadata(node_msg, arena);
PopulateMetadata(arena, metadata, node->metadata);
}
if (node->locality_region != nullptr || node->locality_zone != nullptr ||
node->locality_subzone != nullptr) {
envoy_api_v2_core_Locality* locality =
envoy_api_v2_core_Node_mutable_locality(node_msg, arena);
if (node->locality_region != nullptr) {
envoy_api_v2_core_Locality_set_region(
locality, upb_strview_makez(node->locality_region));
}
if (node->locality_zone != nullptr) {
envoy_api_v2_core_Locality_set_zone(
locality, upb_strview_makez(node->locality_zone));
}
if (node->locality_subzone != nullptr) {
envoy_api_v2_core_Locality_set_sub_zone(
locality, upb_strview_makez(node->locality_subzone));
}
}
}
envoy_api_v2_core_Node_set_build_version(node_msg,
upb_strview_makez(build_version));
}
} // namespace
grpc_slice XdsEdsRequestCreateAndEncode(const char* server_name,
const XdsBootstrap::Node* node,
const char* build_version) {
upb::Arena arena;
// Create a request.
envoy_api_v2_DiscoveryRequest* request =
envoy_api_v2_DiscoveryRequest_new(arena.ptr());
envoy_api_v2_core_Node* node =
envoy_api_v2_core_Node* node_msg =
envoy_api_v2_DiscoveryRequest_mutable_node(request, arena.ptr());
google_protobuf_Struct* metadata =
envoy_api_v2_core_Node_mutable_metadata(node, arena.ptr());
google_protobuf_Struct_FieldsEntry* field =
google_protobuf_Struct_add_fields(metadata, arena.ptr());
google_protobuf_Struct_FieldsEntry_set_key(
field, upb_strview_makez(kEndpointRequired));
google_protobuf_Value* value =
google_protobuf_Struct_FieldsEntry_mutable_value(field, arena.ptr());
google_protobuf_Value_set_bool_value(value, true);
PopulateNode(arena.ptr(), node, build_version, node_msg);
envoy_api_v2_DiscoveryRequest_add_resource_names(
request, upb_strview_makez(server_name), arena.ptr());
envoy_api_v2_DiscoveryRequest_set_type_url(request,
@ -327,11 +417,18 @@ grpc_slice LrsRequestEncode(
} // namespace
grpc_slice XdsLrsRequestCreateAndEncode(const char* server_name) {
grpc_slice XdsLrsRequestCreateAndEncode(const char* server_name,
const XdsBootstrap::Node* node,
const char* build_version) {
upb::Arena arena;
// Create a request.
envoy_service_load_stats_v2_LoadStatsRequest* request =
envoy_service_load_stats_v2_LoadStatsRequest_new(arena.ptr());
// Populate node.
envoy_api_v2_core_Node* node_msg =
envoy_service_load_stats_v2_LoadStatsRequest_mutable_node(request,
arena.ptr());
PopulateNode(arena.ptr(), node, build_version, node_msg);
// Add cluster stats. There is only one because we only use one server name in
// one channel.
envoy_api_v2_endpoint_ClusterStats* cluster_stats =

@ -26,6 +26,7 @@
#include <grpc/slice_buffer.h>
#include "src/core/ext/filters/client_channel/server_address.h"
#include "src/core/ext/filters/client_channel/xds/xds_bootstrap.h"
#include "src/core/ext/filters/client_channel/xds/xds_client_stats.h"
namespace grpc_core {
@ -139,7 +140,9 @@ struct EdsUpdate {
struct CdsUpdate {};
// Creates an EDS request querying \a service_name.
grpc_slice XdsEdsRequestCreateAndEncode(const char* server_name);
grpc_slice XdsEdsRequestCreateAndEncode(const char* server_name,
const XdsBootstrap::Node* node,
const char* build_version);
// Parses the EDS response and returns the args to update locality map. If there
// is any error, the output update is invalid.
@ -147,7 +150,9 @@ grpc_error* XdsEdsResponseDecodeAndParse(const grpc_slice& encoded_response,
EdsUpdate* update);
// Creates an LRS request querying \a server_name.
grpc_slice XdsLrsRequestCreateAndEncode(const char* server_name);
grpc_slice XdsLrsRequestCreateAndEncode(const char* server_name,
const XdsBootstrap::Node* node,
const char* build_version);
// Creates an LRS request sending client-side load reports. If all the counters
// in \a client_stats are zero, returns empty slice.

@ -0,0 +1,452 @@
//
// Copyright 2019 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include <grpc/support/port_platform.h>
#include "src/core/ext/filters/client_channel/xds/xds_bootstrap.h"
#include <errno.h>
#include <stdlib.h>
#include <grpc/support/string_util.h>
#include "src/core/lib/gpr/env.h"
#include "src/core/lib/iomgr/load_file.h"
#include "src/core/lib/slice/slice_internal.h"
namespace grpc_core {
UniquePtr<XdsBootstrap> XdsBootstrap::ReadFromFile(grpc_error** error) {
UniquePtr<char> path(gpr_getenv("GRPC_XDS_BOOTSTRAP"));
if (path == nullptr) {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"GRPC_XDS_BOOTSTRAP env var not set");
return nullptr;
}
grpc_slice contents;
*error = grpc_load_file(path.get(), /*add_null_terminator=*/true, &contents);
if (*error != GRPC_ERROR_NONE) return nullptr;
return MakeUnique<XdsBootstrap>(contents, error);
}
XdsBootstrap::XdsBootstrap(grpc_slice contents, grpc_error** error)
: contents_(contents) {
tree_ = grpc_json_parse_string_with_len(
reinterpret_cast<char*>(GPR_SLICE_START_PTR(contents_)),
GPR_SLICE_LENGTH(contents_));
if (tree_ == nullptr) {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"failed to parse bootstrap file JSON");
return;
}
if (tree_->type != GRPC_JSON_OBJECT || tree_->key != nullptr) {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"malformed JSON in bootstrap file");
return;
}
InlinedVector<grpc_error*, 1> error_list;
bool seen_xds_server = false;
bool seen_node = false;
for (grpc_json* child = tree_->child; child != nullptr; child = child->next) {
if (child->key == nullptr) {
error_list.push_back(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("JSON key is null"));
} else if (strcmp(child->key, "xds_server") == 0) {
if (child->type != GRPC_JSON_OBJECT) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"xds_server\" field is not an object"));
}
if (seen_xds_server) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"duplicate \"xds_server\" field"));
}
seen_xds_server = true;
grpc_error* parse_error = ParseXdsServer(child);
if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
} else if (strcmp(child->key, "node") == 0) {
if (child->type != GRPC_JSON_OBJECT) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"node\" field is not an object"));
}
if (seen_node) {
error_list.push_back(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("duplicate \"node\" field"));
}
seen_node = true;
grpc_error* parse_error = ParseNode(child);
if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
}
}
if (!seen_xds_server) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"xds_server\" field not present"));
}
*error = GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing xds bootstrap file",
&error_list);
}
XdsBootstrap::~XdsBootstrap() {
grpc_json_destroy(tree_);
grpc_slice_unref_internal(contents_);
}
grpc_error* XdsBootstrap::ParseXdsServer(grpc_json* json) {
InlinedVector<grpc_error*, 1> error_list;
server_uri_ = nullptr;
bool seen_channel_creds = false;
for (grpc_json* child = json->child; child != nullptr; child = child->next) {
if (child->key == nullptr) {
error_list.push_back(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("JSON key is null"));
} else if (strcmp(child->key, "server_uri") == 0) {
if (child->type != GRPC_JSON_STRING) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"server_uri\" field is not a string"));
}
if (server_uri_ != nullptr) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"duplicate \"server_uri\" field"));
}
server_uri_ = child->value;
} else if (strcmp(child->key, "channel_creds") == 0) {
if (child->type != GRPC_JSON_ARRAY) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"channel_creds\" field is not a array"));
}
if (seen_channel_creds) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"duplicate \"channel_creds\" field"));
}
seen_channel_creds = true;
grpc_error* parse_error = ParseChannelCredsArray(child);
if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
}
}
if (server_uri_ == nullptr) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"server_uri\" field not present"));
}
return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"xds_server\" object",
&error_list);
}
grpc_error* XdsBootstrap::ParseChannelCredsArray(grpc_json* json) {
InlinedVector<grpc_error*, 1> error_list;
size_t idx = 0;
for (grpc_json *child = json->child; child != nullptr;
child = child->next, ++idx) {
if (child->key != nullptr) {
char* msg;
gpr_asprintf(&msg, "array element %" PRIuPTR " key is not null", idx);
error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg));
}
if (child->type != GRPC_JSON_OBJECT) {
char* msg;
gpr_asprintf(&msg, "array element %" PRIuPTR " is not an object", idx);
error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg));
} else {
grpc_error* parse_error = ParseChannelCreds(child, idx);
if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
}
}
return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"channel_creds\" array",
&error_list);
}
grpc_error* XdsBootstrap::ParseChannelCreds(grpc_json* json, size_t idx) {
InlinedVector<grpc_error*, 1> error_list;
ChannelCreds channel_creds;
for (grpc_json* child = json->child; child != nullptr; child = child->next) {
if (child->key == nullptr) {
error_list.push_back(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("JSON key is null"));
} else if (strcmp(child->key, "type") == 0) {
if (child->type != GRPC_JSON_STRING) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"type\" field is not a string"));
}
if (channel_creds.type != nullptr) {
error_list.push_back(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("duplicate \"type\" field"));
}
channel_creds.type = child->value;
} else if (strcmp(child->key, "config") == 0) {
if (child->type != GRPC_JSON_OBJECT) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"config\" field is not an object"));
}
if (channel_creds.config != nullptr) {
error_list.push_back(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("duplicate \"config\" field"));
}
channel_creds.config = child;
}
}
if (channel_creds.type != nullptr) channel_creds_.push_back(channel_creds);
// Can't use GRPC_ERROR_CREATE_FROM_VECTOR() here, because the error
// string is not static in this case.
if (error_list.empty()) return GRPC_ERROR_NONE;
char* msg;
gpr_asprintf(&msg, "errors parsing index %" PRIuPTR, idx);
grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
gpr_free(msg);
for (size_t i = 0; i < error_list.size(); ++i) {
error = grpc_error_add_child(error, error_list[i]);
GRPC_ERROR_UNREF(error_list[i]);
}
return error;
}
grpc_error* XdsBootstrap::ParseNode(grpc_json* json) {
InlinedVector<grpc_error*, 1> error_list;
node_ = MakeUnique<Node>();
bool seen_metadata = false;
bool seen_locality = false;
for (grpc_json* child = json->child; child != nullptr; child = child->next) {
if (child->key == nullptr) {
error_list.push_back(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("JSON key is null"));
} else if (strcmp(child->key, "id") == 0) {
if (child->type != GRPC_JSON_STRING) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"id\" field is not a string"));
}
if (node_->id != nullptr) {
error_list.push_back(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("duplicate \"id\" field"));
}
node_->id = child->value;
} else if (strcmp(child->key, "cluster") == 0) {
if (child->type != GRPC_JSON_STRING) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"cluster\" field is not a string"));
}
if (node_->cluster != nullptr) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"duplicate \"cluster\" field"));
}
node_->cluster = child->value;
} else if (strcmp(child->key, "locality") == 0) {
if (child->type != GRPC_JSON_OBJECT) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"locality\" field is not an object"));
}
if (seen_locality) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"duplicate \"locality\" field"));
}
seen_locality = true;
grpc_error* parse_error = ParseLocality(child);
if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
} else if (strcmp(child->key, "metadata") == 0) {
if (child->type != GRPC_JSON_OBJECT) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"metadata\" field is not an object"));
}
if (seen_metadata) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"duplicate \"metadata\" field"));
}
seen_metadata = true;
InlinedVector<grpc_error*, 1> parse_errors =
ParseMetadataStruct(child, &node_->metadata);
if (!parse_errors.empty()) {
grpc_error* parse_error = GRPC_ERROR_CREATE_FROM_VECTOR(
"errors parsing \"metadata\" object", &parse_errors);
error_list.push_back(parse_error);
}
}
}
return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"node\" object",
&error_list);
}
grpc_error* XdsBootstrap::ParseLocality(grpc_json* json) {
InlinedVector<grpc_error*, 1> error_list;
node_->locality_region = nullptr;
node_->locality_zone = nullptr;
node_->locality_subzone = nullptr;
for (grpc_json* child = json->child; child != nullptr; child = child->next) {
if (child->key == nullptr) {
error_list.push_back(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("JSON key is null"));
} else if (strcmp(child->key, "region") == 0) {
if (child->type != GRPC_JSON_STRING) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"region\" field is not a string"));
}
if (node_->locality_region != nullptr) {
error_list.push_back(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("duplicate \"region\" field"));
}
node_->locality_region = child->value;
} else if (strcmp(child->key, "zone") == 0) {
if (child->type != GRPC_JSON_STRING) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"zone\" field is not a string"));
}
if (node_->locality_zone != nullptr) {
error_list.push_back(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("duplicate \"zone\" field"));
}
node_->locality_zone = child->value;
} else if (strcmp(child->key, "subzone") == 0) {
if (child->type != GRPC_JSON_STRING) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"\"subzone\" field is not a string"));
}
if (node_->locality_subzone != nullptr) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"duplicate \"subzone\" field"));
}
node_->locality_subzone = child->value;
}
}
return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing \"locality\" object",
&error_list);
}
InlinedVector<grpc_error*, 1> XdsBootstrap::ParseMetadataStruct(
grpc_json* json,
Map<const char*, XdsBootstrap::MetadataValue, StringLess>* result) {
InlinedVector<grpc_error*, 1> error_list;
for (grpc_json* child = json->child; child != nullptr; child = child->next) {
if (child->key == nullptr) {
error_list.push_back(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("JSON key is null"));
continue;
}
if (result->find(child->key) != result->end()) {
char* msg;
gpr_asprintf(&msg, "duplicate metadata key \"%s\"", child->key);
error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg));
gpr_free(msg);
}
MetadataValue& value = (*result)[child->key];
grpc_error* parse_error = ParseMetadataValue(child, 0, &value);
if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
}
return error_list;
}
InlinedVector<grpc_error*, 1> XdsBootstrap::ParseMetadataList(
grpc_json* json, std::vector<MetadataValue>* result) {
InlinedVector<grpc_error*, 1> error_list;
size_t idx = 0;
for (grpc_json *child = json->child; child != nullptr;
child = child->next, ++idx) {
if (child->key != nullptr) {
char* msg;
gpr_asprintf(&msg, "JSON key is non-null for index %" PRIuPTR, idx);
error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg));
gpr_free(msg);
}
result->emplace_back();
grpc_error* parse_error = ParseMetadataValue(child, idx, &result->back());
if (parse_error != GRPC_ERROR_NONE) error_list.push_back(parse_error);
}
return error_list;
}
grpc_error* XdsBootstrap::ParseMetadataValue(grpc_json* json, size_t idx,
MetadataValue* result) {
grpc_error* error = GRPC_ERROR_NONE;
auto context_func = [json, idx]() {
char* context;
if (json->key != nullptr) {
gpr_asprintf(&context, "key \"%s\"", json->key);
} else {
gpr_asprintf(&context, "index %" PRIuPTR, idx);
}
return context;
};
switch (json->type) {
case GRPC_JSON_STRING:
result->type = MetadataValue::Type::STRING;
result->string_value = json->value;
break;
case GRPC_JSON_NUMBER:
result->type = MetadataValue::Type::DOUBLE;
errno = 0; // To distinguish error.
result->double_value = strtod(json->value, nullptr);
if (errno != 0) {
char* context = context_func();
char* msg;
gpr_asprintf(&msg, "error parsing numeric value for %s: \"%s\"",
context, json->value);
error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
gpr_free(context);
gpr_free(msg);
}
break;
case GRPC_JSON_TRUE:
result->type = MetadataValue::Type::BOOL;
result->bool_value = true;
break;
case GRPC_JSON_FALSE:
result->type = MetadataValue::Type::BOOL;
result->bool_value = false;
break;
case GRPC_JSON_NULL:
result->type = MetadataValue::Type::MD_NULL;
break;
case GRPC_JSON_ARRAY: {
result->type = MetadataValue::Type::LIST;
InlinedVector<grpc_error*, 1> error_list =
ParseMetadataList(json, &result->list_value);
if (!error_list.empty()) {
// Can't use GRPC_ERROR_CREATE_FROM_VECTOR() here, because the error
// string is not static in this case.
char* context = context_func();
char* msg;
gpr_asprintf(&msg, "errors parsing struct for %s", context);
error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
gpr_free(context);
gpr_free(msg);
for (size_t i = 0; i < error_list.size(); ++i) {
error = grpc_error_add_child(error, error_list[i]);
GRPC_ERROR_UNREF(error_list[i]);
}
}
break;
}
case GRPC_JSON_OBJECT: {
result->type = MetadataValue::Type::STRUCT;
InlinedVector<grpc_error*, 1> error_list =
ParseMetadataStruct(json, &result->struct_value);
if (!error_list.empty()) {
// Can't use GRPC_ERROR_CREATE_FROM_VECTOR() here, because the error
// string is not static in this case.
char* context = context_func();
char* msg;
gpr_asprintf(&msg, "errors parsing struct for %s", context);
error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
gpr_free(context);
gpr_free(msg);
for (size_t i = 0; i < error_list.size(); ++i) {
error = grpc_error_add_child(error, error_list[i]);
GRPC_ERROR_UNREF(error_list[i]);
}
}
break;
}
default:
break;
}
return error;
}
} // namespace grpc_core

@ -0,0 +1,99 @@
//
// Copyright 2019 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_XDS_XDS_BOOTSTRAP_H
#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_XDS_XDS_BOOTSTRAP_H
#include <grpc/support/port_platform.h>
#include <vector>
#include <grpc/impl/codegen/slice.h>
#include "src/core/lib/gprpp/inlined_vector.h"
#include "src/core/lib/gprpp/map.h"
#include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/json/json.h"
namespace grpc_core {
class XdsBootstrap {
public:
struct MetadataValue {
enum class Type { MD_NULL, DOUBLE, STRING, BOOL, STRUCT, LIST };
Type type = Type::MD_NULL;
// TODO(roth): Once we can use C++17, these can be in a std::variant.
double double_value;
const char* string_value;
bool bool_value;
Map<const char*, MetadataValue, StringLess> struct_value;
std::vector<MetadataValue> list_value;
};
struct Node {
const char* id = nullptr;
const char* cluster = nullptr;
const char* locality_region = nullptr;
const char* locality_zone = nullptr;
const char* locality_subzone = nullptr;
Map<const char*, MetadataValue, StringLess> metadata;
};
struct ChannelCreds {
const char* type = nullptr;
grpc_json* config = nullptr;
};
// If *error is not GRPC_ERROR_NONE after returning, then there was an
// error reading the file.
static UniquePtr<XdsBootstrap> ReadFromFile(grpc_error** error);
// Do not instantiate directly -- use ReadFromFile() above instead.
XdsBootstrap(grpc_slice contents, grpc_error** error);
~XdsBootstrap();
const char* server_uri() const { return server_uri_; }
const InlinedVector<ChannelCreds, 1>& channel_creds() const {
return channel_creds_;
}
const Node* node() const { return node_.get(); }
private:
grpc_error* ParseXdsServer(grpc_json* json);
grpc_error* ParseChannelCredsArray(grpc_json* json);
grpc_error* ParseChannelCreds(grpc_json* json, size_t idx);
grpc_error* ParseNode(grpc_json* json);
grpc_error* ParseLocality(grpc_json* json);
InlinedVector<grpc_error*, 1> ParseMetadataStruct(
grpc_json* json, Map<const char*, MetadataValue, StringLess>* result);
InlinedVector<grpc_error*, 1> ParseMetadataList(
grpc_json* json, std::vector<MetadataValue>* result);
grpc_error* ParseMetadataValue(grpc_json* json, size_t idx,
MetadataValue* result);
grpc_slice contents_;
grpc_json* tree_ = nullptr;
const char* server_uri_ = nullptr;
InlinedVector<ChannelCreds, 1> channel_creds_;
UniquePtr<Node> node_;
};
} // namespace grpc_core
#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_XDS_XDS_BOOTSTRAP_H */

@ -28,9 +28,10 @@ grpc_channel_args* ModifyXdsChannelArgs(grpc_channel_args* args) {
return args;
}
grpc_channel* CreateXdsChannel(const char* target_uri,
grpc_channel* CreateXdsChannel(const XdsBootstrap& bootstrap,
const grpc_channel_args& args) {
return grpc_insecure_channel_create(target_uri, &args, nullptr);
if (!bootstrap.channel_creds().empty()) return nullptr;
return grpc_insecure_channel_create(bootstrap.server_uri(), &args, nullptr);
}
} // namespace grpc_core

@ -23,6 +23,8 @@
#include <grpc/impl/codegen/grpc_types.h>
#include "src/core/ext/filters/client_channel/xds/xds_bootstrap.h"
namespace grpc_core {
/// Makes any necessary modifications to \a args for use in the xds
@ -33,7 +35,7 @@ namespace grpc_core {
/// Caller takes ownership of the returned args.
grpc_channel_args* ModifyXdsChannelArgs(grpc_channel_args* args);
grpc_channel* CreateXdsChannel(const char* target_uri,
grpc_channel* CreateXdsChannel(const XdsBootstrap& bootstrap,
const grpc_channel_args& args);
} // namespace grpc_core

@ -32,6 +32,7 @@
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/iomgr/sockaddr_utils.h"
#include "src/core/lib/security/credentials/credentials.h"
#include "src/core/lib/security/credentials/fake/fake_credentials.h"
#include "src/core/lib/security/transport/target_authority_table.h"
#include "src/core/lib/slice/slice_internal.h"
@ -62,19 +63,35 @@ grpc_channel_args* ModifyXdsChannelArgs(grpc_channel_args* args) {
return result;
}
grpc_channel* CreateXdsChannel(const char* target_uri,
grpc_channel* CreateXdsChannel(const XdsBootstrap& bootstrap,
const grpc_channel_args& args) {
grpc_channel_credentials* creds =
grpc_channel_credentials_find_in_args(&args);
grpc_channel_credentials* creds = nullptr;
RefCountedPtr<grpc_channel_credentials> creds_to_unref;
if (!bootstrap.channel_creds().empty()) {
for (size_t i = 0; i < bootstrap.channel_creds().size(); ++i) {
if (strcmp(bootstrap.channel_creds()[i].type, "google_default") == 0) {
creds = grpc_google_default_credentials_create();
break;
} else if (strcmp(bootstrap.channel_creds()[i].type, "fake") == 0) {
creds = grpc_fake_transport_security_credentials_create();
break;
}
}
if (creds == nullptr) return nullptr;
creds_to_unref.reset(creds);
} else {
creds = grpc_channel_credentials_find_in_args(&args);
if (creds == nullptr) {
// Built with security but parent channel is insecure.
return grpc_insecure_channel_create(target_uri, &args, nullptr);
return grpc_insecure_channel_create(bootstrap.server_uri(), &args,
nullptr);
}
}
const char* arg_to_remove = GRPC_ARG_CHANNEL_CREDENTIALS;
grpc_channel_args* new_args =
grpc_channel_args_copy_and_remove(&args, &arg_to_remove, 1);
grpc_channel* channel =
grpc_secure_channel_create(creds, target_uri, new_args, nullptr);
grpc_channel* channel = grpc_secure_channel_create(
creds, bootstrap.server_uri(), new_args, nullptr);
grpc_channel_args_destroy(new_args);
return channel;
}

@ -248,7 +248,7 @@ class XdsClient::ChannelState : public InternallyRefCounted<ChannelState> {
OrphanablePtr<Reporter> reporter_;
};
ChannelState(RefCountedPtr<XdsClient> xds_client, const char* balancer_name,
ChannelState(RefCountedPtr<XdsClient> xds_client,
const grpc_channel_args& args);
~ChannelState();
@ -374,12 +374,11 @@ grpc_channel_args* BuildXdsChannelArgs(const grpc_channel_args& args) {
} // namespace
XdsClient::ChannelState::ChannelState(RefCountedPtr<XdsClient> xds_client,
const char* balancer_name,
const grpc_channel_args& args)
: InternallyRefCounted<ChannelState>(&grpc_xds_client_trace),
xds_client_(std::move(xds_client)) {
grpc_channel_args* new_args = BuildXdsChannelArgs(args);
channel_ = CreateXdsChannel(balancer_name, *new_args);
channel_ = CreateXdsChannel(*xds_client_->bootstrap_, *new_args);
grpc_channel_args_destroy(new_args);
GPR_ASSERT(channel_ != nullptr);
StartConnectivityWatchLocked();
@ -547,8 +546,9 @@ XdsClient::ChannelState::AdsCallState::AdsCallState(
nullptr, GRPC_MILLIS_INF_FUTURE, nullptr);
GPR_ASSERT(call_ != nullptr);
// Init the request payload.
grpc_slice request_payload_slice =
XdsEdsRequestCreateAndEncode(xds_client()->server_name_.get());
grpc_slice request_payload_slice = XdsEdsRequestCreateAndEncode(
xds_client()->server_name_.get(), xds_client()->bootstrap_->node(),
xds_client()->build_version_.get());
send_message_payload_ =
grpc_raw_byte_buffer_create(&request_payload_slice, 1);
grpc_slice_unref_internal(request_payload_slice);
@ -923,8 +923,9 @@ XdsClient::ChannelState::LrsCallState::LrsCallState(
nullptr, GRPC_MILLIS_INF_FUTURE, nullptr);
GPR_ASSERT(call_ != nullptr);
// Init the request payload.
grpc_slice request_payload_slice =
XdsLrsRequestCreateAndEncode(xds_client()->server_name_.get());
grpc_slice request_payload_slice = XdsLrsRequestCreateAndEncode(
xds_client()->server_name_.get(), xds_client()->bootstrap_->node(),
xds_client()->build_version_.get());
send_message_payload_ =
grpc_raw_byte_buffer_create(&request_payload_slice, 1);
grpc_slice_unref_internal(request_payload_slice);
@ -1177,18 +1178,41 @@ bool XdsClient::ChannelState::LrsCallState::IsCurrentCallOnChannel() const {
// XdsClient
//
namespace {
UniquePtr<char> GenerateBuildVersionString() {
char* build_version_str;
gpr_asprintf(&build_version_str, "gRPC C-core %s %s", grpc_version_string(),
GPR_PLATFORM_STRING);
return UniquePtr<char>(build_version_str);
}
} // namespace
XdsClient::XdsClient(grpc_combiner* combiner,
grpc_pollset_set* interested_parties,
const char* balancer_name, StringView server_name,
StringView server_name,
UniquePtr<ServiceConfigWatcherInterface> watcher,
const grpc_channel_args& channel_args)
: combiner_(GRPC_COMBINER_REF(combiner, "xds_client")),
const grpc_channel_args& channel_args, grpc_error** error)
: build_version_(GenerateBuildVersionString()),
combiner_(GRPC_COMBINER_REF(combiner, "xds_client")),
interested_parties_(interested_parties),
bootstrap_(XdsBootstrap::ReadFromFile(error)),
server_name_(server_name.dup()),
service_config_watcher_(std::move(watcher)),
chand_(MakeOrphanable<ChannelState>(
Ref(DEBUG_LOCATION, "XdsClient+ChannelState"), balancer_name,
channel_args)) {
service_config_watcher_(std::move(watcher)) {
if (*error != GRPC_ERROR_NONE) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, "[xds_client %p: failed to read bootstrap file: %s",
this, grpc_error_string(*error));
}
return;
}
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, "[xds_client %p: creating channel to %s", this,
bootstrap_->server_uri());
}
chand_ = MakeOrphanable<ChannelState>(
Ref(DEBUG_LOCATION, "XdsClient+ChannelState"), channel_args);
// TODO(roth): Start LDS call.
}

@ -21,6 +21,7 @@
#include "src/core/ext/filters/client_channel/service_config.h"
#include "src/core/ext/filters/client_channel/xds/xds_api.h"
#include "src/core/ext/filters/client_channel/xds/xds_bootstrap.h"
#include "src/core/ext/filters/client_channel/xds/xds_client_stats.h"
#include "src/core/lib/gprpp/map.h"
#include "src/core/lib/gprpp/memory.h"
@ -68,10 +69,12 @@ class XdsClient : public InternallyRefCounted<XdsClient> {
virtual void OnError(grpc_error* error) = 0;
};
// If *error is not GRPC_ERROR_NONE after construction, then there was
// an error initializing the client.
XdsClient(grpc_combiner* combiner, grpc_pollset_set* interested_parties,
const char* balancer_name, StringView server_name,
StringView server_name,
UniquePtr<ServiceConfigWatcherInterface> watcher,
const grpc_channel_args& channel_args);
const grpc_channel_args& channel_args, grpc_error** error);
~XdsClient();
void Orphan() override;
@ -131,9 +134,13 @@ class XdsClient : public InternallyRefCounted<XdsClient> {
static const grpc_arg_pointer_vtable kXdsClientVtable;
UniquePtr<char> build_version_;
grpc_combiner* combiner_;
grpc_pollset_set* interested_parties_;
UniquePtr<XdsBootstrap> bootstrap_;
UniquePtr<char> server_name_;
UniquePtr<ServiceConfigWatcherInterface> service_config_watcher_;

@ -67,7 +67,7 @@ grpc_json* grpc_json_parse_string(char* input);
* If indent is 0, then newlines will be suppressed as well, and the
* output will be condensed at its maximum.
*/
char* grpc_json_dump_to_string(grpc_json* json, int indent);
char* grpc_json_dump_to_string(const grpc_json* json, int indent);
/* Use these to create or delete a grpc_json object.
* Deletion is recursive. We will not attempt to free any of the strings

@ -311,7 +311,7 @@ grpc_json* grpc_json_parse_string(char* input) {
return grpc_json_parse_string_with_len(input, UNBOUND_JSON_STRING_LENGTH);
}
static void json_dump_recursive(grpc_json_writer* writer, grpc_json* json,
static void json_dump_recursive(grpc_json_writer* writer, const grpc_json* json,
int in_object) {
while (json) {
if (in_object) grpc_json_writer_object_key(writer, json->key);
@ -351,7 +351,7 @@ static grpc_json_writer_vtable writer_vtable = {
json_writer_output_char, json_writer_output_string,
json_writer_output_string_with_len};
char* grpc_json_dump_to_string(grpc_json* json, int indent) {
char* grpc_json_dump_to_string(const grpc_json* json, int indent) {
grpc_json_writer writer;
json_writer_userdata state;

@ -387,6 +387,7 @@ CORE_SOURCE_FILES = [
'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc',
'src/core/ext/filters/client_channel/lb_policy/xds/xds.cc',
'src/core/ext/filters/client_channel/xds/xds_api.cc',
'src/core/ext/filters/client_channel/xds/xds_bootstrap.cc',
'src/core/ext/filters/client_channel/xds/xds_channel_secure.cc',
'src/core/ext/filters/client_channel/xds/xds_client.cc',
'src/core/ext/filters/client_channel/xds/xds_client_stats.cc',

@ -96,3 +96,20 @@ grpc_cc_test(
"//test/core/util:grpc_test_util",
],
)
grpc_cc_test(
name = "xds_bootstrap_test",
srcs = ["xds_bootstrap_test.cc"],
external_deps = [
"gtest",
],
language = "C++",
deps = [
"//:gpr",
"//:grpc",
"//test/core/util:grpc_test_util",
],
# TODO(nnoble): Remove this once https://github.com/grpc/grpc/issues/20541
# is resolved.
tags = ["no_windows"],
)

@ -0,0 +1,338 @@
//
// Copyright 2019 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include <regex>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <grpc/grpc.h>
#include <grpc/slice.h>
#include "src/core/ext/filters/client_channel/xds/xds_bootstrap.h"
#include "test/core/util/test_config.h"
namespace grpc_core {
namespace testing {
void VerifyRegexMatch(grpc_error* error, const std::regex& e) {
std::smatch match;
std::string s(grpc_error_string(error));
EXPECT_TRUE(std::regex_search(s, match, e));
GRPC_ERROR_UNREF(error);
}
TEST(XdsBootstrapTest, Basic) {
const char* json =
"{"
" \"xds_server\": {"
" \"server_uri\": \"fake:///lb\","
" \"channel_creds\": ["
" {"
" \"type\": \"fake\","
" \"ignore\": 0"
" }"
" ],"
" \"ignore\": 0"
" },"
" \"node\": {"
" \"id\": \"foo\","
" \"cluster\": \"bar\","
" \"locality\": {"
" \"region\": \"milky_way\","
" \"zone\": \"sol_system\","
" \"subzone\": \"earth\","
" \"ignore\": {}"
" },"
" \"metadata\": {"
" \"null\": null,"
" \"string\": \"quux\","
" \"double\": 123.4,"
" \"bool\": true,"
" \"struct\": {"
" \"whee\": 0"
" },"
" \"list\": [1, 2, 3]"
" },"
" \"ignore\": \"whee\""
" },"
" \"ignore\": {}"
"}";
grpc_slice slice = grpc_slice_from_copied_string(json);
grpc_error* error = GRPC_ERROR_NONE;
grpc_core::XdsBootstrap bootstrap(slice, &error);
EXPECT_EQ(error, GRPC_ERROR_NONE);
EXPECT_STREQ(bootstrap.server_uri(), "fake:///lb");
ASSERT_EQ(bootstrap.channel_creds().size(), 1);
EXPECT_STREQ(bootstrap.channel_creds()[0].type, "fake");
EXPECT_EQ(bootstrap.channel_creds()[0].config, nullptr);
ASSERT_NE(bootstrap.node(), nullptr);
EXPECT_STREQ(bootstrap.node()->id, "foo");
EXPECT_STREQ(bootstrap.node()->cluster, "bar");
EXPECT_STREQ(bootstrap.node()->locality_region, "milky_way");
EXPECT_STREQ(bootstrap.node()->locality_zone, "sol_system");
EXPECT_STREQ(bootstrap.node()->locality_subzone, "earth");
EXPECT_THAT(
bootstrap.node()->metadata,
::testing::ElementsAre(
::testing::Pair(::testing::StrEq("null"),
::testing::AllOf(::testing::Field(
&XdsBootstrap::MetadataValue::type,
XdsBootstrap::MetadataValue::Type::MD_NULL))),
::testing::Pair(
::testing::StrEq("string"),
::testing::AllOf(
::testing::Field(&XdsBootstrap::MetadataValue::type,
XdsBootstrap::MetadataValue::Type::STRING),
::testing::Field(&XdsBootstrap::MetadataValue::string_value,
::testing::StrEq("quux")))),
::testing::Pair(
::testing::StrEq("double"),
::testing::AllOf(
::testing::Field(&XdsBootstrap::MetadataValue::type,
XdsBootstrap::MetadataValue::Type::DOUBLE),
::testing::Field(&XdsBootstrap::MetadataValue::double_value,
123.4))),
::testing::Pair(
::testing::StrEq("bool"),
::testing::AllOf(
::testing::Field(&XdsBootstrap::MetadataValue::type,
XdsBootstrap::MetadataValue::Type::BOOL),
::testing::Field(&XdsBootstrap::MetadataValue::bool_value,
true))),
::testing::Pair(
::testing::StrEq("struct"),
::testing::AllOf(
::testing::Field(&XdsBootstrap::MetadataValue::type,
XdsBootstrap::MetadataValue::Type::STRUCT),
::testing::Field(
&XdsBootstrap::MetadataValue::struct_value,
::testing::ElementsAre(::testing::Pair(
::testing::StrEq("whee"),
::testing::AllOf(
::testing::Field(
&XdsBootstrap::MetadataValue::type,
XdsBootstrap::MetadataValue::Type::DOUBLE),
::testing::Field(
&XdsBootstrap::MetadataValue::double_value,
0))))))),
::testing::Pair(
::testing::StrEq("list"),
::testing::Field(&XdsBootstrap::MetadataValue::type,
XdsBootstrap::MetadataValue::Type::LIST))));
// TODO(roth): Once our InlinedVector<> implementation supports
// iteration, replace this by using ElementsAre() in the statement above.
auto it = bootstrap.node()->metadata.find("list");
ASSERT_TRUE(it != bootstrap.node()->metadata.end());
ASSERT_EQ(it->second.list_value.size(), 3);
EXPECT_EQ(it->second.list_value[0].type,
XdsBootstrap::MetadataValue::Type::DOUBLE);
EXPECT_EQ(it->second.list_value[0].double_value, 1);
EXPECT_EQ(it->second.list_value[1].type,
XdsBootstrap::MetadataValue::Type::DOUBLE);
EXPECT_EQ(it->second.list_value[1].double_value, 2);
EXPECT_EQ(it->second.list_value[2].type,
XdsBootstrap::MetadataValue::Type::DOUBLE);
EXPECT_EQ(it->second.list_value[2].double_value, 3);
}
TEST(XdsBootstrapTest, ValidWithoutChannelCredsAndNode) {
const char* json =
"{"
" \"xds_server\": {"
" \"server_uri\": \"fake:///lb\""
" }"
"}";
grpc_slice slice = grpc_slice_from_copied_string(json);
grpc_error* error = GRPC_ERROR_NONE;
grpc_core::XdsBootstrap bootstrap(slice, &error);
EXPECT_EQ(error, GRPC_ERROR_NONE);
EXPECT_STREQ(bootstrap.server_uri(), "fake:///lb");
EXPECT_EQ(bootstrap.channel_creds().size(), 0);
EXPECT_EQ(bootstrap.node(), nullptr);
}
TEST(XdsBootstrapTest, InvalidJson) {
grpc_slice slice = grpc_slice_from_copied_string("");
grpc_error* error = GRPC_ERROR_NONE;
grpc_core::XdsBootstrap bootstrap(slice, &error);
gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
ASSERT_TRUE(error != GRPC_ERROR_NONE);
std::regex e(std::string("failed to parse bootstrap file JSON"));
VerifyRegexMatch(error, e);
}
TEST(XdsBootstrapTest, MalformedJson) {
grpc_slice slice = grpc_slice_from_copied_string("\"foo\"");
grpc_error* error = GRPC_ERROR_NONE;
grpc_core::XdsBootstrap bootstrap(slice, &error);
gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
ASSERT_TRUE(error != GRPC_ERROR_NONE);
std::regex e(std::string("malformed JSON in bootstrap file"));
VerifyRegexMatch(error, e);
}
TEST(XdsBootstrapTest, MissingXdsServer) {
grpc_slice slice = grpc_slice_from_copied_string("{}");
grpc_error* error = GRPC_ERROR_NONE;
grpc_core::XdsBootstrap bootstrap(slice, &error);
gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
ASSERT_TRUE(error != GRPC_ERROR_NONE);
std::regex e(std::string("\"xds_server\" field not present"));
VerifyRegexMatch(error, e);
}
TEST(XdsBootstrapTest, BadXdsServer) {
grpc_slice slice = grpc_slice_from_copied_string(
"{"
" \"xds_server\":1,"
" \"xds_server\":{}"
"}");
grpc_error* error = GRPC_ERROR_NONE;
grpc_core::XdsBootstrap bootstrap(slice, &error);
gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
ASSERT_TRUE(error != GRPC_ERROR_NONE);
std::regex e(
std::string("\"xds_server\" field is not an object(.*)"
"duplicate \"xds_server\" field(.*)"
"errors parsing \"xds_server\" object(.*)"
"\"server_uri\" field not present"));
VerifyRegexMatch(error, e);
}
TEST(XdsBootstrapTest, BadXdsServerContents) {
grpc_slice slice = grpc_slice_from_copied_string(
"{"
" \"xds_server\":{"
" \"server_uri\":1,"
" \"server_uri\":\"foo\","
" \"channel_creds\":1,"
" \"channel_creds\":{}"
" }"
"}");
grpc_error* error = GRPC_ERROR_NONE;
grpc_core::XdsBootstrap bootstrap(slice, &error);
gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
ASSERT_TRUE(error != GRPC_ERROR_NONE);
std::regex e(
std::string("errors parsing \"xds_server\" object(.*)"
"\"server_uri\" field is not a string(.*)"
"duplicate \"server_uri\" field(.*)"
"\"channel_creds\" field is not an array(.*)"
"duplicate \"channel_creds\" field(.*)"
"\"channel_creds\" field is not an array"));
VerifyRegexMatch(error, e);
}
TEST(XdsBootstrapTest, BadChannelCredsContents) {
grpc_slice slice = grpc_slice_from_copied_string(
"{"
" \"xds_server\":{"
" \"server_uri\":\"foo\","
" \"channel_creds\":["
" {"
" \"type\":0,"
" \"type\":\"fake\","
" \"config\":1,"
" \"config\":{}"
" }"
" ]"
" }"
"}");
grpc_error* error = GRPC_ERROR_NONE;
grpc_core::XdsBootstrap bootstrap(slice, &error);
gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
ASSERT_TRUE(error != GRPC_ERROR_NONE);
std::regex e(
std::string("errors parsing \"xds_server\" object(.*)"
"errors parsing \"channel_creds\" object(.*)"
"\"type\" field is not a string(.*)"
"duplicate \"type\" field(.*)"
"\"config\" field is not an object(.*)"
"duplicate \"config\" field"));
VerifyRegexMatch(error, e);
}
TEST(XdsBootstrapTest, BadNode) {
grpc_slice slice = grpc_slice_from_copied_string(
"{"
" \"node\":1,"
" \"node\":{"
" \"id\":0,"
" \"id\":\"foo\","
" \"cluster\":0,"
" \"cluster\":\"foo\","
" \"locality\":0,"
" \"locality\":{"
" \"region\":0,"
" \"region\":\"foo\","
" \"zone\":0,"
" \"zone\":\"foo\","
" \"subzone\":0,"
" \"subzone\":\"foo\""
" },"
" \"metadata\":0,"
" \"metadata\":{"
" \"foo\":0,"
" \"foo\":\"whee\","
" \"foo\":\"whee2\""
" }"
" }"
"}");
grpc_error* error = GRPC_ERROR_NONE;
grpc_core::XdsBootstrap bootstrap(slice, &error);
gpr_log(GPR_ERROR, "%s", grpc_error_string(error));
ASSERT_TRUE(error != GRPC_ERROR_NONE);
std::regex e(
std::string("\"node\" field is not an object(.*)"
"duplicate \"node\" field(.*)"
"errors parsing \"node\" object(.*)"
"\"id\" field is not a string(.*)"
"duplicate \"id\" field(.*)"
"\"cluster\" field is not a string(.*)"
"duplicate \"cluster\" field(.*)"
"\"locality\" field is not an object(.*)"
"duplicate \"locality\" field(.*)"
"errors parsing \"locality\" object(.*)"
"\"region\" field is not a string(.*)"
"duplicate \"region\" field(.*)"
"\"zone\" field is not a string(.*)"
"duplicate \"zone\" field(.*)"
"\"subzone\" field is not a string(.*)"
"duplicate \"subzone\" field(.*)"
"\"metadata\" field is not an object(.*)"
"duplicate \"metadata\" field(.*)"
"errors parsing \"metadata\" object(.*)"
"duplicate metadata key \"foo\""));
VerifyRegexMatch(error, e);
}
} // namespace testing
} // namespace grpc_core
int main(int argc, char** argv) {
// Regexes don't work in gcc4.8 and below, so just skip testing in those cases
#if defined(__GNUC__) && \
((__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__) <= 8))
return 0;
#endif
grpc::testing::TestEnvironment env(argc, argv);
grpc_init();
::testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
grpc_shutdown();
return ret;
}

@ -508,6 +508,10 @@ grpc_cc_test(
"//test/core/util:grpc_test_util",
"//test/cpp/util:test_util",
],
data = [
"xds_bootstrap.json",
"xds_bootstrap_bad.json",
],
tags = ["no_windows"], # TODO(jtattermusch): fix test on windows
)

@ -0,0 +1,22 @@
{
"xds_server": {
"server_uri": "fake:///lb",
"channel_creds": [
{
"type": "fake"
}
]
},
"node": {
"id": "xds_end2end_test",
"cluster": "test",
"metadata": {
"foo": "bar"
},
"locality": {
"region": "corp",
"zone": "svl",
"subzone": "mp3"
}
}
}

@ -0,0 +1,12 @@
{
"xds_server": {
"server_uri": "fake:///wrong_lb",
"channel_creds": [
{
"type": "fake"
}
]
},
"node": {
}
}

@ -536,6 +536,7 @@ class XdsEnd2endTest : public ::testing::Test {
static void TearDownTestCase() { grpc_shutdown(); }
void SetUp() override {
gpr_setenv("GRPC_XDS_BOOTSTRAP", "test/cpp/end2end/xds_bootstrap.json");
response_generator_ =
grpc_core::MakeRefCounted<grpc_core::FakeResolverResponseGenerator>();
lb_channel_response_generator_ =
@ -664,7 +665,7 @@ class XdsEnd2endTest : public ::testing::Test {
gpr_log(GPR_INFO, "========= BACKEND %lu READY ==========", backend_idx);
}
grpc_core::ServerAddressList CreateLbAddressesFromPortList(
grpc_core::ServerAddressList CreateAddressListFromPortList(
const std::vector<int>& ports) {
grpc_core::ServerAddressList addresses;
for (int port : ports) {
@ -674,10 +675,7 @@ class XdsEnd2endTest : public ::testing::Test {
GPR_ASSERT(lb_uri != nullptr);
grpc_resolved_address address;
GPR_ASSERT(grpc_parse_uri(lb_uri, &address));
std::vector<grpc_arg> args_to_add;
grpc_channel_args* args = grpc_channel_args_copy_and_add(
nullptr, args_to_add.data(), args_to_add.size());
addresses.emplace_back(address.addr, address.len, args);
addresses.emplace_back(address.addr, address.len, nullptr);
grpc_uri_destroy(lb_uri);
gpr_free(lb_uri_str);
}
@ -690,7 +688,7 @@ class XdsEnd2endTest : public ::testing::Test {
lb_channel_response_generator = nullptr) {
grpc_core::ExecCtx exec_ctx;
grpc_core::Resolver::Result result;
result.addresses = CreateLbAddressesFromPortList(ports);
result.addresses = CreateAddressListFromPortList(ports);
if (service_config_json != nullptr) {
grpc_error* error = GRPC_ERROR_NONE;
result.service_config =
@ -723,7 +721,7 @@ class XdsEnd2endTest : public ::testing::Test {
nullptr) {
grpc_core::ExecCtx exec_ctx;
grpc_core::Resolver::Result result;
result.addresses = CreateLbAddressesFromPortList(ports);
result.addresses = CreateAddressListFromPortList(ports);
if (service_config_json != nullptr) {
grpc_error* error = GRPC_ERROR_NONE;
result.service_config =
@ -739,7 +737,7 @@ class XdsEnd2endTest : public ::testing::Test {
void SetNextReresolutionResponse(const std::vector<int>& ports) {
grpc_core::ExecCtx exec_ctx;
grpc_core::Resolver::Result result;
result.addresses = CreateLbAddressesFromPortList(ports);
result.addresses = CreateAddressListFromPortList(ports);
response_generator_->SetReresolutionResponse(std::move(result));
}
@ -916,7 +914,7 @@ class XdsEnd2endTest : public ::testing::Test {
"{\n"
" \"loadBalancingConfig\":[\n"
" { \"does_not_exist\":{} },\n"
" { \"xds_experimental\":{ \"balancerName\": \"fake:///lb\" } }\n"
" { \"xds_experimental\":{} }\n"
" ]\n"
"}";
};
@ -1101,20 +1099,14 @@ TEST_F(SecureNamingTest, TargetNameIsExpected) {
// Tests that secure naming check fails if target name is unexpected.
TEST_F(SecureNamingTest, TargetNameIsUnexpected) {
gpr_setenv("GRPC_XDS_BOOTSTRAP", "test/cpp/end2end/xds_bootstrap_bad.json");
::testing::FLAGS_gtest_death_test_style = "threadsafe";
// Make sure that we blow up (via abort() from the security connector) when
// the name from the balancer doesn't match expectations.
ASSERT_DEATH_IF_SUPPORTED(
{
ResetStub(0, 0, kApplicationTargetName_ + ";lb");
SetNextResolution({},
"{\n"
" \"loadBalancingConfig\":[\n"
" { \"does_not_exist\":{} },\n"
" { \"xds_experimental\":{ \"balancerName\": "
"\"fake:///wrong_lb\" } }\n"
" ]\n"
"}");
SetNextResolution({}, kDefaultServiceConfig_.c_str());
SetNextResolutionForLbChannel({balancers_[0]->port()});
channel_->WaitForConnected(grpc_timeout_seconds_to_deadline(1));
},

@ -969,6 +969,8 @@ src/core/ext/filters/client_channel/subchannel_pool_interface.cc \
src/core/ext/filters/client_channel/subchannel_pool_interface.h \
src/core/ext/filters/client_channel/xds/xds_api.cc \
src/core/ext/filters/client_channel/xds/xds_api.h \
src/core/ext/filters/client_channel/xds/xds_bootstrap.cc \
src/core/ext/filters/client_channel/xds/xds_bootstrap.h \
src/core/ext/filters/client_channel/xds/xds_channel.h \
src/core/ext/filters/client_channel/xds/xds_channel_args.h \
src/core/ext/filters/client_channel/xds/xds_channel_secure.cc \

@ -6040,6 +6040,30 @@
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "xds_bootstrap_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save