mirror of https://github.com/grpc/grpc.git
Merge pull request #24711 from yashykt/filewatcherconfig
File watcher certificate provider config parsingreviewable/pr24618/r8
commit
0b4c258354
24 changed files with 728 additions and 165 deletions
@ -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 <grpc/support/port_platform.h> |
||||
|
||||
#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<std::string> 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> |
||||
FileWatcherCertificateProviderFactory::Config::Parse(const Json& config_json, |
||||
grpc_error** error) { |
||||
auto config = MakeRefCounted<FileWatcherCertificateProviderFactory::Config>(); |
||||
if (config_json.type() != Json::Type::OBJECT) { |
||||
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
||||
"error:config type should be OBJECT."); |
||||
return nullptr; |
||||
} |
||||
std::vector<grpc_error*> 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<CertificateProviderFactory::Config> |
||||
FileWatcherCertificateProviderFactory::CreateCertificateProviderConfig( |
||||
const Json& config_json, grpc_error** error) { |
||||
return FileWatcherCertificateProviderFactory::Config::Parse(config_json, |
||||
error); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -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 <grpc/support/port_platform.h> |
||||
|
||||
#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<Config> 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<CertificateProviderFactory::Config> |
||||
CreateCertificateProviderConfig(const Json& config_json, |
||||
grpc_error** error) override; |
||||
|
||||
RefCountedPtr<grpc_tls_certificate_provider> CreateCertificateProvider( |
||||
RefCountedPtr<CertificateProviderFactory::Config> config) override { |
||||
// TODO(yashykt) : To be implemented
|
||||
return nullptr; |
||||
} |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_CORE_EXT_XDS_FILE_WATCHER_CERTIFICATE_PROVIDER_FACTORY_H
|
@ -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 <gmock/gmock.h> |
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <grpc/grpc.h> |
||||
|
||||
#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; |
||||
} |
Loading…
Reference in new issue