diff --git a/BUILD b/BUILD index 34bd146cef3..90502289f66 100644 --- a/BUILD +++ b/BUILD @@ -1384,12 +1384,28 @@ grpc_cc_library( "envoy_ads_upbdefs", "grpc_base", "grpc_client_channel", + "grpc_file_watcher_certificate_provider_factory", "grpc_google_mesh_ca_certificate_provider_factory", "grpc_transport_chttp2_client_secure", "grpc_xds_credentials", ], ) +grpc_cc_library( + name = "grpc_file_watcher_certificate_provider_factory", + srcs = [ + "src/core/ext/xds/file_watcher_certificate_provider_factory.cc", + ], + hdrs = [ + "src/core/ext/xds/file_watcher_certificate_provider_factory.h", + ], + language = "c++", + deps = [ + "grpc_base", + "grpc_xds_credentials", + ], +) + grpc_cc_library( name = "grpc_google_mesh_ca_certificate_provider_factory", srcs = [ diff --git a/BUILD.gn b/BUILD.gn index 0acbd1b9048..6c9be4cd0b2 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -728,6 +728,8 @@ config("grpc_config") { "src/core/ext/xds/certificate_provider_registry.h", "src/core/ext/xds/certificate_provider_store.cc", "src/core/ext/xds/certificate_provider_store.h", + "src/core/ext/xds/file_watcher_certificate_provider_factory.cc", + "src/core/ext/xds/file_watcher_certificate_provider_factory.h", "src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc", "src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h", "src/core/ext/xds/xds_api.cc", diff --git a/CMakeLists.txt b/CMakeLists.txt index 9339b3f725e..91229935083 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -823,6 +823,7 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_cxx examine_stack_test) endif() add_dependencies(buildtests_cxx exception_test) + add_dependencies(buildtests_cxx file_watcher_certificate_provider_factory_test) add_dependencies(buildtests_cxx filter_end2end_test) add_dependencies(buildtests_cxx flaky_network_test) add_dependencies(buildtests_cxx generic_end2end_test) @@ -1708,6 +1709,7 @@ add_library(grpc src/core/ext/upbdefs-generated/validate/validate.upbdefs.c src/core/ext/xds/certificate_provider_registry.cc src/core/ext/xds/certificate_provider_store.cc + src/core/ext/xds/file_watcher_certificate_provider_factory.cc src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc src/core/ext/xds/xds_api.cc src/core/ext/xds/xds_bootstrap.cc @@ -11088,6 +11090,45 @@ target_link_libraries(exception_test ) +endif() +if(gRPC_BUILD_TESTS) + +add_executable(file_watcher_certificate_provider_factory_test + test/core/xds/file_watcher_certificate_provider_factory_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) + +target_include_directories(file_watcher_certificate_provider_factory_test + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} + ${_gRPC_RE2_INCLUDE_DIR} + ${_gRPC_SSL_INCLUDE_DIR} + ${_gRPC_UPB_GENERATED_DIR} + ${_gRPC_UPB_GRPC_GENERATED_DIR} + ${_gRPC_UPB_INCLUDE_DIR} + ${_gRPC_ZLIB_INCLUDE_DIR} + third_party/googletest/googletest/include + third_party/googletest/googletest + third_party/googletest/googlemock/include + third_party/googletest/googlemock + ${_gRPC_PROTO_GENS_DIR} +) + +target_link_libraries(file_watcher_certificate_provider_factory_test + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util + grpc + gpr + address_sorting + upb + ${_gRPC_GFLAGS_LIBRARIES} +) + + endif() if(gRPC_BUILD_TESTS) diff --git a/Makefile b/Makefile index 7438b4a44d9..d35cda2f758 100644 --- a/Makefile +++ b/Makefile @@ -2109,6 +2109,7 @@ LIBGRPC_SRC = \ src/core/ext/upbdefs-generated/validate/validate.upbdefs.c \ src/core/ext/xds/certificate_provider_registry.cc \ src/core/ext/xds/certificate_provider_store.cc \ + src/core/ext/xds/file_watcher_certificate_provider_factory.cc \ src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc \ src/core/ext/xds/xds_api.cc \ src/core/ext/xds/xds_bootstrap.cc \ @@ -4776,6 +4777,7 @@ src/core/ext/upbdefs-generated/udpa/core/v1/resource_name.upbdefs.c: $(OPENSSL_D src/core/ext/upbdefs-generated/validate/validate.upbdefs.c: $(OPENSSL_DEP) src/core/ext/xds/certificate_provider_registry.cc: $(OPENSSL_DEP) src/core/ext/xds/certificate_provider_store.cc: $(OPENSSL_DEP) +src/core/ext/xds/file_watcher_certificate_provider_factory.cc: $(OPENSSL_DEP) src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc: $(OPENSSL_DEP) src/core/ext/xds/xds_api.cc: $(OPENSSL_DEP) src/core/ext/xds/xds_bootstrap.cc: $(OPENSSL_DEP) diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index 576c84ae732..a819c8015dc 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -632,6 +632,7 @@ libs: - src/core/ext/xds/certificate_provider_factory.h - src/core/ext/xds/certificate_provider_registry.h - src/core/ext/xds/certificate_provider_store.h + - src/core/ext/xds/file_watcher_certificate_provider_factory.h - src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h - src/core/ext/xds/xds_api.h - src/core/ext/xds/xds_bootstrap.h @@ -1134,6 +1135,7 @@ libs: - src/core/ext/upbdefs-generated/validate/validate.upbdefs.c - src/core/ext/xds/certificate_provider_registry.cc - src/core/ext/xds/certificate_provider_store.cc + - src/core/ext/xds/file_watcher_certificate_provider_factory.cc - src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc - src/core/ext/xds/xds_api.cc - src/core/ext/xds/xds_bootstrap.cc @@ -5979,6 +5981,19 @@ targets: - gpr - address_sorting - upb +- name: file_watcher_certificate_provider_factory_test + gtest: true + build: test + language: c++ + headers: [] + src: + - test/core/xds/file_watcher_certificate_provider_factory_test.cc + deps: + - grpc_test_util + - grpc + - gpr + - address_sorting + - upb - name: filter_end2end_test gtest: true build: test diff --git a/config.m4 b/config.m4 index 9fc7ee10271..2b440d9a9cf 100644 --- a/config.m4 +++ b/config.m4 @@ -312,6 +312,7 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/upbdefs-generated/validate/validate.upbdefs.c \ src/core/ext/xds/certificate_provider_registry.cc \ src/core/ext/xds/certificate_provider_store.cc \ + src/core/ext/xds/file_watcher_certificate_provider_factory.cc \ src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc \ src/core/ext/xds/xds_api.cc \ src/core/ext/xds/xds_bootstrap.cc \ diff --git a/config.w32 b/config.w32 index 914dceba5f0..b337409cade 100644 --- a/config.w32 +++ b/config.w32 @@ -279,6 +279,7 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\upbdefs-generated\\validate\\validate.upbdefs.c " + "src\\core\\ext\\xds\\certificate_provider_registry.cc " + "src\\core\\ext\\xds\\certificate_provider_store.cc " + + "src\\core\\ext\\xds\\file_watcher_certificate_provider_factory.cc " + "src\\core\\ext\\xds\\google_mesh_ca_certificate_provider_factory.cc " + "src\\core\\ext\\xds\\xds_api.cc " + "src\\core\\ext\\xds\\xds_bootstrap.cc " + diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index 2d2843a8809..c99f16cdda0 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -446,6 +446,7 @@ Pod::Spec.new do |s| 'src/core/ext/xds/certificate_provider_factory.h', 'src/core/ext/xds/certificate_provider_registry.h', 'src/core/ext/xds/certificate_provider_store.h', + 'src/core/ext/xds/file_watcher_certificate_provider_factory.h', 'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h', 'src/core/ext/xds/xds_api.h', 'src/core/ext/xds/xds_bootstrap.h', @@ -1054,6 +1055,7 @@ Pod::Spec.new do |s| 'src/core/ext/xds/certificate_provider_factory.h', 'src/core/ext/xds/certificate_provider_registry.h', 'src/core/ext/xds/certificate_provider_store.h', + 'src/core/ext/xds/file_watcher_certificate_provider_factory.h', 'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h', 'src/core/ext/xds/xds_api.h', 'src/core/ext/xds/xds_bootstrap.h', diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index c99259b3577..06ccfca9e2f 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -709,6 +709,8 @@ Pod::Spec.new do |s| 'src/core/ext/xds/certificate_provider_registry.h', 'src/core/ext/xds/certificate_provider_store.cc', 'src/core/ext/xds/certificate_provider_store.h', + 'src/core/ext/xds/file_watcher_certificate_provider_factory.cc', + 'src/core/ext/xds/file_watcher_certificate_provider_factory.h', 'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc', 'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h', 'src/core/ext/xds/xds_api.cc', @@ -1583,6 +1585,7 @@ Pod::Spec.new do |s| 'src/core/ext/xds/certificate_provider_factory.h', 'src/core/ext/xds/certificate_provider_registry.h', 'src/core/ext/xds/certificate_provider_store.h', + 'src/core/ext/xds/file_watcher_certificate_provider_factory.h', 'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h', 'src/core/ext/xds/xds_api.h', 'src/core/ext/xds/xds_bootstrap.h', diff --git a/grpc.gemspec b/grpc.gemspec index df687b72ec7..c99857566f9 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -626,6 +626,8 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/xds/certificate_provider_registry.h ) s.files += %w( src/core/ext/xds/certificate_provider_store.cc ) s.files += %w( src/core/ext/xds/certificate_provider_store.h ) + s.files += %w( src/core/ext/xds/file_watcher_certificate_provider_factory.cc ) + s.files += %w( src/core/ext/xds/file_watcher_certificate_provider_factory.h ) s.files += %w( src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc ) s.files += %w( src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h ) s.files += %w( src/core/ext/xds/xds_api.cc ) diff --git a/grpc.gyp b/grpc.gyp index dd7fa56df7e..964aa4857e3 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -722,6 +722,7 @@ 'src/core/ext/upbdefs-generated/validate/validate.upbdefs.c', 'src/core/ext/xds/certificate_provider_registry.cc', 'src/core/ext/xds/certificate_provider_store.cc', + 'src/core/ext/xds/file_watcher_certificate_provider_factory.cc', 'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc', 'src/core/ext/xds/xds_api.cc', 'src/core/ext/xds/xds_bootstrap.cc', diff --git a/package.xml b/package.xml index 40997492ed6..9860a395924 100644 --- a/package.xml +++ b/package.xml @@ -606,6 +606,8 @@ + + diff --git a/src/core/ext/filters/client_channel/resolver_result_parsing.cc b/src/core/ext/filters/client_channel/resolver_result_parsing.cc index 6642b423021..c716bc5720d 100644 --- a/src/core/ext/filters/client_channel/resolver_result_parsing.cc +++ b/src/core/ext/filters/client_channel/resolver_result_parsing.cc @@ -95,26 +95,19 @@ std::unique_ptr ParseRetryPolicy( } } // Parse initialBackoff. - it = json.object_value().find("initialBackoff"); - if (it != json.object_value().end()) { - if (!ParseDurationFromJson(it->second, &retry_policy->initial_backoff)) { - error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "field:initialBackoff error:Failed to parse")); - } else if (retry_policy->initial_backoff == 0) { - error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "field:initialBackoff error:must be greater than 0")); - } + if (ParseJsonObjectFieldAsDuration(json.object_value(), "initialBackoff", + &retry_policy->initial_backoff, + &error_list) && + retry_policy->initial_backoff == 0) { + error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "field:initialBackoff error:must be greater than 0")); } // Parse maxBackoff. - it = json.object_value().find("maxBackoff"); - if (it != json.object_value().end()) { - if (!ParseDurationFromJson(it->second, &retry_policy->max_backoff)) { - error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "field:maxBackoff error:failed to parse")); - } else if (retry_policy->max_backoff == 0) { - error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "field:maxBackoff error:should be greater than 0")); - } + if (ParseJsonObjectFieldAsDuration(json.object_value(), "maxBackoff", + &retry_policy->max_backoff, &error_list) && + retry_policy->max_backoff == 0) { + error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "field:maxBackoff error:should be greater than 0")); } // Parse backoffMultiplier. it = json.object_value().find("backoffMultiplier"); @@ -383,13 +376,8 @@ ClientChannelServiceConfigParser::ParsePerMethodParams( } } // Parse timeout. - it = json.object_value().find("timeout"); - if (it != json.object_value().end()) { - if (!ParseDurationFromJson(it->second, &timeout)) { - error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "field:timeout error:Failed parsing")); - }; - } + ParseJsonObjectFieldAsDuration(json.object_value(), "timeout", &timeout, + &error_list, false); // Parse retry policy. it = json.object_value().find("retryPolicy"); if (it != json.object_value().end()) { diff --git a/src/core/ext/xds/file_watcher_certificate_provider_factory.cc b/src/core/ext/xds/file_watcher_certificate_provider_factory.cc new file mode 100644 index 00000000000..fab8dac52ef --- /dev/null +++ b/src/core/ext/xds/file_watcher_certificate_provider_factory.cc @@ -0,0 +1,119 @@ +// +// +// Copyright 2020 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 + +#include "src/core/ext/xds/file_watcher_certificate_provider_factory.h" + +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" + +#include "src/core/lib/json/json_util.h" + +namespace grpc_core { + +namespace { + +const char* kFileWatcherPlugin = "file_watcher"; + +} // namespace + +// +// FileWatcherCertificateProviderFactory::Config +// + +const char* FileWatcherCertificateProviderFactory::Config::name() const { + return kFileWatcherPlugin; +} + +std::string FileWatcherCertificateProviderFactory::Config::ToString() const { + std::vector parts; + parts.push_back("{"); + if (!identity_cert_file_.empty()) { + parts.push_back( + absl::StrFormat("certificate_file=\"%s\", ", identity_cert_file_)); + } + if (!identity_cert_file_.empty()) { + parts.push_back( + absl::StrFormat("private_key_file=\"%s\", ", private_key_file_)); + } + if (!identity_cert_file_.empty()) { + parts.push_back( + absl::StrFormat("ca_certificate_file=\"%s\", ", root_cert_file_)); + } + parts.push_back( + absl::StrFormat("refresh_interval=%ldms}", refresh_interval_ms_)); + return absl::StrJoin(parts, ""); +} + +RefCountedPtr +FileWatcherCertificateProviderFactory::Config::Parse(const Json& config_json, + grpc_error** error) { + auto config = MakeRefCounted(); + if (config_json.type() != Json::Type::OBJECT) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "error:config type should be OBJECT."); + return nullptr; + } + std::vector error_list; + ParseJsonObjectField(config_json.object_value(), "certificate_file", + &config->identity_cert_file_, &error_list, false); + ParseJsonObjectField(config_json.object_value(), "private_key_file", + &config->private_key_file_, &error_list, false); + if (config->identity_cert_file_.empty() != + config->private_key_file_.empty()) { + error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "fields \"certificate_file\" and \"private_key_file\" must be both set " + "or both unset.")); + } + ParseJsonObjectField(config_json.object_value(), "ca_certificate_file", + &config->root_cert_file_, &error_list, false); + if (config->identity_cert_file_.empty() && config->root_cert_file_.empty()) { + error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "At least one of \"certificate_file\" and \"ca_certificate_file\" must " + "be specified.")); + } + if (!ParseJsonObjectFieldAsDuration( + config_json.object_value(), "refresh_interval", + &config->refresh_interval_ms_, &error_list, false)) { + config->refresh_interval_ms_ = 10 * 60 * 1000; // 10 minutes default + } + if (!error_list.empty()) { + *error = GRPC_ERROR_CREATE_FROM_VECTOR( + "Error parsing file watcher certificate provider config", &error_list); + return nullptr; + } + return config; +} + +// +// FileWatcherCertificateProviderFactory +// + +const char* FileWatcherCertificateProviderFactory::name() const { + return kFileWatcherPlugin; +} + +RefCountedPtr +FileWatcherCertificateProviderFactory::CreateCertificateProviderConfig( + const Json& config_json, grpc_error** error) { + return FileWatcherCertificateProviderFactory::Config::Parse(config_json, + error); +} + +} // namespace grpc_core diff --git a/src/core/ext/xds/file_watcher_certificate_provider_factory.h b/src/core/ext/xds/file_watcher_certificate_provider_factory.h new file mode 100644 index 00000000000..96b61e1869b --- /dev/null +++ b/src/core/ext/xds/file_watcher_certificate_provider_factory.h @@ -0,0 +1,72 @@ +// +// +// Copyright 2020 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_XDS_FILE_WATCHER_CERTIFICATE_PROVIDER_FACTORY_H +#define GRPC_CORE_EXT_XDS_FILE_WATCHER_CERTIFICATE_PROVIDER_FACTORY_H + +#include + +#include "src/core/ext/xds/certificate_provider_factory.h" + +namespace grpc_core { + +class FileWatcherCertificateProviderFactory + : public CertificateProviderFactory { + public: + class Config : public CertificateProviderFactory::Config { + public: + static RefCountedPtr Parse(const Json& config_json, + grpc_error** error); + + const char* name() const override; + + std::string ToString() const override; + + const std::string& identity_cert_file() const { + return identity_cert_file_; + } + + const std::string& private_key_file() const { return private_key_file_; } + + const std::string& root_cert_file() const { return root_cert_file_; } + + grpc_millis refresh_interval_ms() const { return refresh_interval_ms_; } + + private: + std::string identity_cert_file_; + std::string private_key_file_; + std::string root_cert_file_; + grpc_millis refresh_interval_ms_; + }; + + const char* name() const override; + + RefCountedPtr + CreateCertificateProviderConfig(const Json& config_json, + grpc_error** error) override; + + RefCountedPtr CreateCertificateProvider( + RefCountedPtr config) override { + // TODO(yashykt) : To be implemented + return nullptr; + } +}; + +} // namespace grpc_core + +#endif // GRPC_CORE_EXT_XDS_FILE_WATCHER_CERTIFICATE_PROVIDER_FACTORY_H diff --git a/src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc b/src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc index ef59b894d60..c1b7b84a826 100644 --- a/src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc +++ b/src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc @@ -37,123 +37,6 @@ namespace { const char* kMeshCaPlugin = "meshCA"; -// -// Helper functions for extracting types from JSON -// -template -bool ExtractJsonType(const Json& json, const std::string& field_name, - NumericType* output, ErrorVectorType* error_list) { - static_assert(std::is_integral::value, "Integral required"); - if (json.type() != Json::Type::NUMBER) { - error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( - absl::StrCat("field:", field_name, " error:type should be NUMBER") - .c_str())); - return false; - } - std::istringstream ss(json.string_value()); - ss >> *output; - // The JSON parsing API should have dealt with parsing errors, but check - // anyway - if (GPR_UNLIKELY(ss.bad())) { - error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( - absl::StrCat("field:", field_name, " error:failed to parse.").c_str())); - return false; - } - return true; -} - -template -bool ExtractJsonType(const Json& json, const std::string& field_name, - bool* output, ErrorVectorType* error_list) { - switch (json.type()) { - case Json::Type::JSON_TRUE: - *output = true; - return true; - case Json::Type::JSON_FALSE: - *output = false; - return true; - default: - error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( - absl::StrCat("field:", field_name, " error:type should be BOOLEAN") - .c_str())); - return false; - } -} - -template -bool ExtractJsonType(const Json& json, const std::string& field_name, - std::string* output, ErrorVectorType* error_list) { - if (json.type() != Json::Type::STRING) { - *output = ""; - error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( - absl::StrCat("field:", field_name, " error:type should be STRING") - .c_str())); - return false; - } - *output = json.string_value(); - return true; -} - -template -bool ExtractJsonType(const Json& json, const std::string& field_name, - const Json::Array** output, ErrorVectorType* error_list) { - if (json.type() != Json::Type::ARRAY) { - *output = nullptr; - error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( - absl::StrCat("field:", field_name, " error:type should be ARRAY") - .c_str())); - return false; - } - *output = &json.array_value(); - return true; -} - -template -bool ExtractJsonType(const Json& json, const std::string& field_name, - const Json::Object** output, ErrorVectorType* error_list) { - if (json.type() != Json::Type::OBJECT) { - *output = nullptr; - error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( - absl::StrCat("field:", field_name, " error:type should be OBJECT") - .c_str())); - return false; - } - *output = &json.object_value(); - return true; -} - -template -bool ExtractJsonType(const Json& json, const std::string& field_name, - grpc_millis* output, ErrorVectorType* error_list) { - if (!ParseDurationFromJson(json, output)) { - *output = GRPC_MILLIS_INF_PAST; - error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( - absl::StrCat("field:", field_name, - " error:type should be STRING of the form given by " - "google.proto.Duration.") - .c_str())); - return false; - } - return true; -} - -template -bool ParseJsonObjectField(const Json::Object& object, - const std::string& field_name, T* output, - ErrorVectorType* error_list, bool optional = false) { - auto it = object.find(field_name); - if (it == object.end()) { - if (!optional) { - error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( - absl::StrCat("field:", field_name, " error:does not exist.") - .c_str())); - } - return false; - } - auto& child_object_json = it->second; - return ExtractJsonType(child_object_json, field_name, output, error_list); -} - } // namespace // @@ -175,22 +58,22 @@ GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectStsService( std::vector error_list_sts_service; if (!ParseJsonObjectField(sts_service, "token_exchange_service_uri", &sts_config_.token_exchange_service_uri, - &error_list_sts_service, true)) { + &error_list_sts_service, false)) { sts_config_.token_exchange_service_uri = "securetoken.googleapis.com"; // default } ParseJsonObjectField(sts_service, "resource", &sts_config_.resource, - &error_list_sts_service, true); + &error_list_sts_service, false); ParseJsonObjectField(sts_service, "audience", &sts_config_.audience, - &error_list_sts_service, true); + &error_list_sts_service, false); if (!ParseJsonObjectField(sts_service, "scope", &sts_config_.scope, - &error_list_sts_service, true)) { + &error_list_sts_service, false)) { sts_config_.scope = "https://www.googleapis.com/auth/cloud-platform"; // default } ParseJsonObjectField(sts_service, "requested_token_type", &sts_config_.requested_token_type, - &error_list_sts_service, true); + &error_list_sts_service, false); ParseJsonObjectField(sts_service, "subject_token_path", &sts_config_.subject_token_path, &error_list_sts_service); @@ -199,10 +82,10 @@ GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectStsService( &error_list_sts_service); ParseJsonObjectField(sts_service, "actor_token_path", &sts_config_.actor_token_path, &error_list_sts_service, - true); + false); ParseJsonObjectField(sts_service, "actor_token_type", &sts_config_.actor_token_type, &error_list_sts_service, - true); + false); return error_list_sts_service; } @@ -228,7 +111,7 @@ GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectGoogleGrpc( const Json::Object& google_grpc) { std::vector error_list_google_grpc; if (!ParseJsonObjectField(google_grpc, "target_uri", &endpoint_, - &error_list_google_grpc, true)) { + &error_list_google_grpc, false)) { endpoint_ = "meshca.googleapis.com"; // Default target } const Json::Array* call_credentials_array = nullptr; @@ -268,8 +151,8 @@ GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectGrpcServices( "field:google_grpc", &error_list_google_grpc)); } } - if (!ParseJsonObjectField(grpc_service, "timeout", &timeout_, - &error_list_grpc_services, true)) { + if (!ParseJsonObjectFieldAsDuration(grpc_service, "timeout", &timeout_, + &error_list_grpc_services, false)) { timeout_ = 10 * 1000; // 10sec default } return error_list_grpc_services; @@ -281,7 +164,7 @@ GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectServer( std::vector error_list_server; std::string api_type; if (ParseJsonObjectField(server, "api_type", &api_type, &error_list_server, - true)) { + false)) { if (api_type != "GRPC") { error_list_server.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( "field:api_type error:Only GRPC is supported")); @@ -330,30 +213,30 @@ GoogleMeshCaCertificateProviderFactory::Config::Parse(const Json& config_json, GRPC_ERROR_CREATE_FROM_VECTOR("field:server", &error_list_server)); } } - if (!ParseJsonObjectField(config_json.object_value(), "certificate_lifetime", - &config->certificate_lifetime_, &error_list, - true)) { + if (!ParseJsonObjectFieldAsDuration( + config_json.object_value(), "certificate_lifetime", + &config->certificate_lifetime_, &error_list, false)) { config->certificate_lifetime_ = 24 * 60 * 60 * 1000; // 24hrs default } - if (!ParseJsonObjectField(config_json.object_value(), "renewal_grace_period", - &config->renewal_grace_period_, &error_list, - true)) { + if (!ParseJsonObjectFieldAsDuration( + config_json.object_value(), "renewal_grace_period", + &config->renewal_grace_period_, &error_list, false)) { config->renewal_grace_period_ = 12 * 60 * 60 * 1000; // 12hrs default } std::string key_type; if (ParseJsonObjectField(config_json.object_value(), "key_type", &key_type, - &error_list, true)) { + &error_list, false)) { if (key_type != "RSA") { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( "field:key_type error:Only RSA is supported.")); } } if (!ParseJsonObjectField(config_json.object_value(), "key_size", - &config->key_size_, &error_list, true)) { + &config->key_size_, &error_list, false)) { config->key_size_ = 2048; // default 2048 bit key size } if (!ParseJsonObjectField(config_json.object_value(), "location", - &config->location_, &error_list, true)) { + &config->location_, &error_list, false)) { // GCE/GKE Metadata server needs to be contacted to get the value. } if (!error_list.empty()) { diff --git a/src/core/lib/json/json_util.h b/src/core/lib/json/json_util.h index 071087f7658..00a96eb761e 100644 --- a/src/core/lib/json/json_util.h +++ b/src/core/lib/json/json_util.h @@ -21,6 +21,9 @@ #include +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" + #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/json/json.h" @@ -32,6 +35,170 @@ namespace grpc_core { // Returns true on success, false otherwise. bool ParseDurationFromJson(const Json& field, grpc_millis* duration); +// +// Helper functions for extracting types from JSON. +// Return true on success, false otherwise. If an error is encountered during +// parsing, a descriptive error is appended to \a error_list. +// +template +inline bool ExtractJsonNumber(const Json& json, const std::string& field_name, + NumericType* output, + ErrorVectorType* error_list) { + static_assert(std::is_integral::value, "Integral required"); + if (json.type() != Json::Type::NUMBER) { + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, " error:type should be NUMBER") + .c_str())); + return false; + } + if (!absl::SimpleAtoi(json.string_value(), output)) { + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, " error:failed to parse.").c_str())); + return false; + } + return true; +} + +template +inline bool ExtractJsonBool(const Json& json, const std::string& field_name, + bool* output, ErrorVectorType* error_list) { + switch (json.type()) { + case Json::Type::JSON_TRUE: + *output = true; + return true; + case Json::Type::JSON_FALSE: + *output = false; + return true; + default: + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, " error:type should be BOOLEAN") + .c_str())); + return false; + } +} + +template +inline bool ExtractJsonString(const Json& json, const std::string& field_name, + std::string* output, + ErrorVectorType* error_list) { + if (json.type() != Json::Type::STRING) { + *output = ""; + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, " error:type should be STRING") + .c_str())); + return false; + } + *output = json.string_value(); + return true; +} + +template +inline bool ExtractJsonArray(const Json& json, const std::string& field_name, + const Json::Array** output, + ErrorVectorType* error_list) { + if (json.type() != Json::Type::ARRAY) { + *output = nullptr; + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, " error:type should be ARRAY") + .c_str())); + return false; + } + *output = &json.array_value(); + return true; +} + +template +inline bool ExtractJsonObject(const Json& json, const std::string& field_name, + const Json::Object** output, + ErrorVectorType* error_list) { + if (json.type() != Json::Type::OBJECT) { + *output = nullptr; + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, " error:type should be OBJECT") + .c_str())); + return false; + } + *output = &json.object_value(); + return true; +} + +template +inline bool ExtractJsonType(const Json& json, const std::string& field_name, + NumericType* output, ErrorVectorType* error_list) { + return ExtractJsonNumber(json, field_name, output, error_list); +} + +template +inline bool ExtractJsonType(const Json& json, const std::string& field_name, + bool* output, ErrorVectorType* error_list) { + return ExtractJsonBool(json, field_name, output, error_list); +} + +template +inline bool ExtractJsonType(const Json& json, const std::string& field_name, + std::string* output, ErrorVectorType* error_list) { + return ExtractJsonString(json, field_name, output, error_list); +} + +template +inline bool ExtractJsonType(const Json& json, const std::string& field_name, + const Json::Array** output, + ErrorVectorType* error_list) { + return ExtractJsonArray(json, field_name, output, error_list); +} + +template +inline bool ExtractJsonType(const Json& json, const std::string& field_name, + const Json::Object** output, + ErrorVectorType* error_list) { + return ExtractJsonObject(json, field_name, output, error_list); +} + +template +inline bool ParseJsonObjectField(const Json::Object& object, + const std::string& field_name, T* output, + ErrorVectorType* error_list, + bool required = true) { + auto it = object.find(field_name); + if (it == object.end()) { + if (required) { + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, " error:does not exist.") + .c_str())); + } + return false; + } + auto& child_object_json = it->second; + return ExtractJsonType(child_object_json, field_name, output, error_list); +} + +template +inline bool ParseJsonObjectFieldAsDuration(const Json::Object& object, + const std::string& field_name, + grpc_millis* output, + ErrorVectorType* error_list, + bool required = true) { + auto it = object.find(field_name); + if (it == object.end()) { + if (required) { + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, " error:does not exist.") + .c_str())); + } + return false; + } + if (!ParseDurationFromJson(it->second, output)) { + *output = GRPC_MILLIS_INF_PAST; + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, + " error:type should be STRING of the form given by " + "google.proto.Duration.") + .c_str())); + return false; + } + return true; +} + } // namespace grpc_core #endif // GRPC_CORE_LIB_JSON_JSON_UTIL_H diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index db2683376ef..2ee93303c37 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -288,6 +288,7 @@ CORE_SOURCE_FILES = [ 'src/core/ext/upbdefs-generated/validate/validate.upbdefs.c', 'src/core/ext/xds/certificate_provider_registry.cc', 'src/core/ext/xds/certificate_provider_store.cc', + 'src/core/ext/xds/file_watcher_certificate_provider_factory.cc', 'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc', 'src/core/ext/xds/xds_api.cc', 'src/core/ext/xds/xds_bootstrap.cc', diff --git a/test/core/client_channel/service_config_test.cc b/test/core/client_channel/service_config_test.cc index abdb43fefa9..97749cfb15f 100644 --- a/test/core/client_channel/service_config_test.cc +++ b/test/core/client_channel/service_config_test.cc @@ -733,7 +733,8 @@ TEST_F(ClientChannelParserTest, InvalidTimeout) { "Method Params.*referenced_errors.*" "methodConfig.*referenced_errors.*" "Client channel parser.*referenced_errors.*" - "field:timeout error:Failed parsing")); + "field:timeout error:type should be STRING of the form given " + "by google.proto.Duration")); GRPC_ERROR_UNREF(error); } @@ -876,7 +877,8 @@ TEST_F(ClientChannelParserTest, InvalidRetryPolicyInitialBackoff) { "methodConfig.*referenced_errors.*" "Client channel parser.*referenced_errors.*" "retryPolicy.*referenced_errors.*" - "field:initialBackoff error:Failed to parse")); + "field:initialBackoff error:type should be STRING of the " + "form given by google.proto.Duration")); GRPC_ERROR_UNREF(error); } @@ -905,7 +907,8 @@ TEST_F(ClientChannelParserTest, InvalidRetryPolicyMaxBackoff) { "methodConfig.*referenced_errors.*" "Client channel parser.*referenced_errors.*" "retryPolicy.*referenced_errors.*" - "field:maxBackoff error:failed to parse")); + "field:maxBackoff error:type should be STRING of the form " + "given by google.proto.Duration")); GRPC_ERROR_UNREF(error); } diff --git a/test/core/xds/BUILD b/test/core/xds/BUILD index 6ddd10d2b3f..ceca797f9c0 100644 --- a/test/core/xds/BUILD +++ b/test/core/xds/BUILD @@ -44,6 +44,18 @@ grpc_cc_test( ], ) +grpc_cc_test( + name = "file_watcher_certificate_provider_factory_test", + srcs = ["file_watcher_certificate_provider_factory_test.cc"], + external_deps = ["gtest"], + language = "C++", + deps = [ + "//:gpr", + "//:grpc", + "//test/core/util:grpc_test_util", + ], +) + grpc_cc_test( name = "google_mesh_ca_certificate_provider_factory_test", srcs = ["google_mesh_ca_certificate_provider_factory_test.cc"], diff --git a/test/core/xds/file_watcher_certificate_provider_factory_test.cc b/test/core/xds/file_watcher_certificate_provider_factory_test.cc new file mode 100644 index 00000000000..0ecb0123cae --- /dev/null +++ b/test/core/xds/file_watcher_certificate_provider_factory_test.cc @@ -0,0 +1,202 @@ +// +// +// Copyright 2020 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 "src/core/ext/xds/file_watcher_certificate_provider_factory.h" + +#include "absl/strings/str_format.h" + +#include +#include + +#include + +#include "test/core/util/test_config.h" + +namespace grpc_core { +namespace testing { +namespace { + +const char* kIdentityCertFile = "/path/to/identity_cert_file"; +const char* kPrivateKeyFile = "/path/to/private_key_file"; +const char* kRootCertFile = "/path/to/root_cert_file"; +const int kRefreshInterval = 400; + +TEST(FileWatcherConfigTest, Basic) { + std::string json_str = absl::StrFormat( + "{" + " \"certificate_file\": \"%s\"," + " \"private_key_file\": \"%s\"," + " \"ca_certificate_file\": \"%s\"," + " \"refresh_interval\": \"%ds\"" + "}", + kIdentityCertFile, kPrivateKeyFile, kRootCertFile, kRefreshInterval); + grpc_error* error = GRPC_ERROR_NONE; + Json json = Json::Parse(json_str, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + auto config = + FileWatcherCertificateProviderFactory::Config::Parse(json, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + EXPECT_EQ(config->identity_cert_file(), kIdentityCertFile); + EXPECT_EQ(config->private_key_file(), kPrivateKeyFile); + EXPECT_EQ(config->root_cert_file(), kRootCertFile); + EXPECT_EQ(config->refresh_interval_ms(), kRefreshInterval * 1000); +} + +TEST(FileWatcherConfigTest, DefaultRefreshInterval) { + std::string json_str = absl::StrFormat( + "{" + " \"certificate_file\": \"%s\"," + " \"private_key_file\": \"%s\"," + " \"ca_certificate_file\": \"%s\"" + "}", + kIdentityCertFile, kPrivateKeyFile, kRootCertFile); + grpc_error* error = GRPC_ERROR_NONE; + Json json = Json::Parse(json_str, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + auto config = + FileWatcherCertificateProviderFactory::Config::Parse(json, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + EXPECT_EQ(config->identity_cert_file(), kIdentityCertFile); + EXPECT_EQ(config->private_key_file(), kPrivateKeyFile); + EXPECT_EQ(config->root_cert_file(), kRootCertFile); + EXPECT_EQ(config->refresh_interval_ms(), 600 * 1000); +} + +TEST(FileWatcherConfigTest, OnlyRootCertificatesFileProvided) { + std::string json_str = absl::StrFormat( + "{" + " \"ca_certificate_file\": \"%s\"" + "}", + kRootCertFile); + grpc_error* error = GRPC_ERROR_NONE; + Json json = Json::Parse(json_str, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + auto config = + FileWatcherCertificateProviderFactory::Config::Parse(json, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + EXPECT_TRUE(config->identity_cert_file().empty()); + EXPECT_TRUE(config->private_key_file().empty()); + EXPECT_EQ(config->root_cert_file(), kRootCertFile); + EXPECT_EQ(config->refresh_interval_ms(), 600 * 1000); +} + +TEST(FileWatcherConfigTest, OnlyIdenityCertificatesAndPrivateKeyProvided) { + std::string json_str = absl::StrFormat( + "{" + " \"certificate_file\": \"%s\"," + " \"private_key_file\": \"%s\"" + "}", + kIdentityCertFile, kPrivateKeyFile); + grpc_error* error = GRPC_ERROR_NONE; + Json json = Json::Parse(json_str, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + auto config = + FileWatcherCertificateProviderFactory::Config::Parse(json, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + EXPECT_EQ(config->identity_cert_file(), kIdentityCertFile); + EXPECT_EQ(config->private_key_file(), kPrivateKeyFile); + EXPECT_TRUE(config->root_cert_file().empty()); + EXPECT_EQ(config->refresh_interval_ms(), 600 * 1000); +} + +TEST(FileWatcherConfigTest, WrongTypes) { + const char* json_str = + "{" + " \"certificate_file\": 123," + " \"private_key_file\": 123," + " \"ca_certificate_file\": 123," + " \"refresh_interval\": 123" + "}"; + grpc_error* error = GRPC_ERROR_NONE; + Json json = Json::Parse(json_str, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + auto config = + FileWatcherCertificateProviderFactory::Config::Parse(json, &error); + EXPECT_THAT(grpc_error_string(error), + ::testing::ContainsRegex( + "field:certificate_file error:type should be STRING.*" + "field:private_key_file error:type should be STRING.*" + "field:ca_certificate_file error:type should be STRING.*" + "field:refresh_interval error:type should be STRING of the " + "form given by " + "google.proto.Duration.*")); + GRPC_ERROR_UNREF(error); +} + +TEST(FileWatcherConfigTest, IdentityCertProvidedButPrivateKeyMissing) { + std::string json_str = absl::StrFormat( + "{" + " \"certificate_file\": \"%s\"" + "}", + kIdentityCertFile); + grpc_error* error = GRPC_ERROR_NONE; + Json json = Json::Parse(json_str, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + auto config = + FileWatcherCertificateProviderFactory::Config::Parse(json, &error); + EXPECT_THAT(grpc_error_string(error), + ::testing::ContainsRegex( + "fields \"certificate_file\" and \"private_key_file\" must " + "be both set or both unset.")); + GRPC_ERROR_UNREF(error); +} + +TEST(FileWatcherConfigTest, PrivateKeyProvidedButIdentityCertMissing) { + std::string json_str = absl::StrFormat( + "{" + " \"private_key_file\": \"%s\"" + "}", + kPrivateKeyFile); + grpc_error* error = GRPC_ERROR_NONE; + Json json = Json::Parse(json_str, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + auto config = + FileWatcherCertificateProviderFactory::Config::Parse(json, &error); + EXPECT_THAT(grpc_error_string(error), + ::testing::ContainsRegex( + "fields \"certificate_file\" and \"private_key_file\" must " + "be both set or both unset.")); + GRPC_ERROR_UNREF(error); +} + +TEST(FileWatcherConfigTest, EmptyJsonObject) { + std::string json_str = absl::StrFormat("{}"); + grpc_error* error = GRPC_ERROR_NONE; + Json json = Json::Parse(json_str, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + auto config = + FileWatcherCertificateProviderFactory::Config::Parse(json, &error); + EXPECT_THAT( + grpc_error_string(error), + ::testing::ContainsRegex("At least one of \"certificate_file\" and " + "\"ca_certificate_file\" must be specified.")); + GRPC_ERROR_UNREF(error); +} + +} // namespace +} // namespace testing +} // namespace grpc_core + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + grpc::testing::TestEnvironment env(argc, argv); + grpc_init(); + auto result = RUN_ALL_TESTS(); + grpc_shutdown(); + return result; +} diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index 791e2298921..7b8f2aaab8b 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -1558,6 +1558,8 @@ src/core/ext/xds/certificate_provider_registry.cc \ src/core/ext/xds/certificate_provider_registry.h \ src/core/ext/xds/certificate_provider_store.cc \ src/core/ext/xds/certificate_provider_store.h \ +src/core/ext/xds/file_watcher_certificate_provider_factory.cc \ +src/core/ext/xds/file_watcher_certificate_provider_factory.h \ src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc \ src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h \ src/core/ext/xds/xds_api.cc \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 8abfd99d44a..aede4d9a3d3 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -1395,6 +1395,8 @@ src/core/ext/xds/certificate_provider_registry.cc \ src/core/ext/xds/certificate_provider_registry.h \ src/core/ext/xds/certificate_provider_store.cc \ src/core/ext/xds/certificate_provider_store.h \ +src/core/ext/xds/file_watcher_certificate_provider_factory.cc \ +src/core/ext/xds/file_watcher_certificate_provider_factory.h \ src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc \ src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h \ src/core/ext/xds/xds_api.cc \ diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 1a19005d128..198ab5978a9 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -4455,6 +4455,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": "file_watcher_certificate_provider_factory_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false,