| // |
| // 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/lib/security/credentials/tls/grpc_tls_certificate_distributor.h" |
| |
| #include <deque> |
| #include <list> |
| #include <string> |
| #include <thread> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include <grpc/support/alloc.h> |
| #include <grpc/support/log.h> |
| #include <grpc/support/string_util.h> |
| |
| #include "src/core/lib/slice/slice_internal.h" |
| #include "test/core/util/test_config.h" |
| #include "test/core/util/tls_utils.h" |
| |
| namespace grpc_core { |
| |
| namespace testing { |
| |
| constexpr const char* kCertName1 = "cert_1_name"; |
| constexpr const char* kCertName2 = "cert_2_name"; |
| constexpr const char* kRootCert1Name = "root_cert_1_name"; |
| constexpr const char* kRootCert1Contents = "root_cert_1_contents"; |
| constexpr const char* kRootCert2Name = "root_cert_2_name"; |
| constexpr const char* kRootCert2Contents = "root_cert_2_contents"; |
| constexpr const char* kIdentityCert1Name = "identity_cert_1_name"; |
| constexpr const char* kIdentityCert1PrivateKey = "identity_private_key_1"; |
| constexpr const char* kIdentityCert1Contents = "identity_cert_1_contents"; |
| constexpr const char* kIdentityCert2Name = "identity_cert_2_name"; |
| constexpr const char* kIdentityCert2PrivateKey = "identity_private_key_2"; |
| constexpr const char* kIdentityCert2Contents = "identity_cert_2_contents"; |
| constexpr const char* kErrorMessage = "error_message"; |
| constexpr const char* kRootErrorMessage = "root_error_message"; |
| constexpr const char* kIdentityErrorMessage = "identity_error_message"; |
| |
| class GrpcTlsCertificateDistributorTest : public ::testing::Test { |
| protected: |
| // Forward declaration. |
| class TlsCertificatesTestWatcher; |
| |
| // CredentialInfo contains the parameters when calling OnCertificatesChanged |
| // of a watcher. When OnCertificatesChanged is invoked, we will push a |
| // CredentialInfo to the cert_update_queue of state_, and check in each test |
| // if the status updates are correct. |
| struct CredentialInfo { |
| std::string root_certs; |
| PemKeyCertPairList key_cert_pairs; |
| CredentialInfo(std::string root, PemKeyCertPairList key_cert) |
| : root_certs(std::move(root)), key_cert_pairs(std::move(key_cert)) {} |
| bool operator==(const CredentialInfo& other) const { |
| return root_certs == other.root_certs && |
| key_cert_pairs == other.key_cert_pairs; |
| } |
| }; |
| |
| // ErrorInfo contains the parameters when calling OnError of a watcher. When |
| // OnError is invoked, we will push a ErrorInfo to the error_queue of state_, |
| // and check in each test if the status updates are correct. |
| struct ErrorInfo { |
| std::string root_cert_str; |
| std::string identity_cert_str; |
| ErrorInfo(std::string root, std::string identity) |
| : root_cert_str(std::move(root)), |
| identity_cert_str(std::move(identity)) {} |
| bool operator==(const ErrorInfo& other) const { |
| return root_cert_str == other.root_cert_str && |
| identity_cert_str == other.identity_cert_str; |
| } |
| }; |
| |
| struct WatcherState { |
| TlsCertificatesTestWatcher* watcher = nullptr; |
| std::deque<CredentialInfo> cert_update_queue; |
| std::deque<ErrorInfo> error_queue; |
| |
| std::deque<CredentialInfo> GetCredentialQueue() { |
| // We move the data member value so the data member will be re-initiated |
| // with size 0, and ready for the next check. |
| return std::move(cert_update_queue); |
| } |
| std::deque<ErrorInfo> GetErrorQueue() { |
| // We move the data member value so the data member will be re-initiated |
| // with size 0, and ready for the next check. |
| return std::move(error_queue); |
| } |
| }; |
| |
| class TlsCertificatesTestWatcher : public grpc_tls_certificate_distributor:: |
| TlsCertificatesWatcherInterface { |
| public: |
| // ctor sets state->watcher to this. |
| explicit TlsCertificatesTestWatcher(WatcherState* state) : state_(state) { |
| state_->watcher = this; |
| } |
| |
| // dtor sets state->watcher to nullptr. |
| ~TlsCertificatesTestWatcher() override { state_->watcher = nullptr; } |
| |
| void OnCertificatesChanged( |
| absl::optional<absl::string_view> root_certs, |
| absl::optional<PemKeyCertPairList> key_cert_pairs) override { |
| std::string updated_root; |
| if (root_certs.has_value()) { |
| updated_root = std::string(*root_certs); |
| } |
| PemKeyCertPairList updated_identity; |
| if (key_cert_pairs.has_value()) { |
| updated_identity = std::move(*key_cert_pairs); |
| } |
| state_->cert_update_queue.emplace_back(std::move(updated_root), |
| std::move(updated_identity)); |
| } |
| |
| void OnError(grpc_error_handle root_cert_error, |
| grpc_error_handle identity_cert_error) override { |
| GPR_ASSERT(!GRPC_ERROR_IS_NONE(root_cert_error) || |
| !GRPC_ERROR_IS_NONE(identity_cert_error)); |
| std::string root_error_str; |
| std::string identity_error_str; |
| if (!GRPC_ERROR_IS_NONE(root_cert_error)) { |
| GPR_ASSERT(grpc_error_get_str( |
| root_cert_error, GRPC_ERROR_STR_DESCRIPTION, &root_error_str)); |
| } |
| if (!GRPC_ERROR_IS_NONE(identity_cert_error)) { |
| GPR_ASSERT(grpc_error_get_str(identity_cert_error, |
| GRPC_ERROR_STR_DESCRIPTION, |
| &identity_error_str)); |
| } |
| state_->error_queue.emplace_back(std::move(root_error_str), |
| std::move(identity_error_str)); |
| GRPC_ERROR_UNREF(root_cert_error); |
| GRPC_ERROR_UNREF(identity_cert_error); |
| } |
| |
| private: |
| WatcherState* state_; |
| }; |
| |
| // CallbackStatus contains the parameters when calling watch_status_callback_ |
| // of the distributor. When a particular callback is invoked, we will push a |
| // CallbackStatus to a callback_queue_, and check in each test if the status |
| // updates are correct. |
| struct CallbackStatus { |
| std::string cert_name; |
| bool root_being_watched; |
| bool identity_being_watched; |
| CallbackStatus(std::string name, bool root_watched, bool identity_watched) |
| : cert_name(std::move(name)), |
| root_being_watched(root_watched), |
| identity_being_watched(identity_watched) {} |
| bool operator==(const CallbackStatus& other) const { |
| return cert_name == other.cert_name && |
| root_being_watched == other.root_being_watched && |
| identity_being_watched == other.identity_being_watched; |
| } |
| }; |
| |
| void SetUp() override { |
| distributor_.SetWatchStatusCallback([this](std::string cert_name, |
| bool root_being_watched, |
| bool identity_being_watched) { |
| callback_queue_.emplace_back(std::move(cert_name), root_being_watched, |
| identity_being_watched); |
| }); |
| } |
| |
| WatcherState* MakeWatcher(absl::optional<std::string> root_cert_name, |
| absl::optional<std::string> identity_cert_name) { |
| MutexLock lock(&mu_); |
| watchers_.emplace_back(); |
| // TlsCertificatesTestWatcher ctor takes a pointer to the WatcherState. |
| // It sets WatcherState::watcher to point to itself. |
| // The TlsCertificatesTestWatcher dtor will set WatcherState::watcher back |
| // to nullptr to indicate that it's been destroyed. |
| auto watcher = |
| absl::make_unique<TlsCertificatesTestWatcher>(&watchers_.back()); |
| distributor_.WatchTlsCertificates(std::move(watcher), |
| std::move(root_cert_name), |
| std::move(identity_cert_name)); |
| return &watchers_.back(); |
| } |
| |
| void CancelWatch(WatcherState* state) { |
| MutexLock lock(&mu_); |
| distributor_.CancelTlsCertificatesWatch(state->watcher); |
| EXPECT_EQ(state->watcher, nullptr); |
| } |
| |
| std::deque<CallbackStatus> GetCallbackQueue() { |
| // We move the data member value so the data member will be re-initiated |
| // with size 0, and ready for the next check. |
| return std::move(callback_queue_); |
| } |
| |
| grpc_tls_certificate_distributor distributor_; |
| // Use a std::list<> here to avoid the address invalidation caused by internal |
| // reallocation of std::vector<>. |
| std::list<WatcherState> watchers_; |
| std::deque<CallbackStatus> callback_queue_; |
| // This is to make watchers_ and callback_queue_ thread-safe. |
| Mutex mu_; |
| }; |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, BasicCredentialBehaviors) { |
| EXPECT_FALSE(distributor_.HasRootCerts(kRootCert1Name)); |
| EXPECT_FALSE(distributor_.HasKeyCertPairs(kIdentityCert1Name)); |
| // After setting the certificates to the corresponding cert names, the |
| // distributor should possess the corresponding certs. |
| distributor_.SetKeyMaterials(kRootCert1Name, kRootCert1Contents, |
| absl::nullopt); |
| EXPECT_TRUE(distributor_.HasRootCerts(kRootCert1Name)); |
| distributor_.SetKeyMaterials( |
| kIdentityCert1Name, absl::nullopt, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)); |
| EXPECT_TRUE(distributor_.HasKeyCertPairs(kIdentityCert1Name)); |
| // Querying a non-existing cert name should return false. |
| EXPECT_FALSE(distributor_.HasRootCerts(kRootCert2Name)); |
| EXPECT_FALSE(distributor_.HasKeyCertPairs(kIdentityCert2Name)); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, UpdateCredentialsOnAnySide) { |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1); |
| EXPECT_THAT(GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kCertName1, true, true))); |
| // SetKeyMaterials should trigger watcher's OnCertificatesChanged method. |
| distributor_.SetKeyMaterials( |
| kCertName1, kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)); |
| EXPECT_THAT( |
| watcher_state_1->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)))); |
| // Set root certs should trigger watcher's OnCertificatesChanged again. |
| distributor_.SetKeyMaterials(kCertName1, kRootCert2Contents, absl::nullopt); |
| EXPECT_THAT( |
| watcher_state_1->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| kRootCert2Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)))); |
| // Set identity certs should trigger watcher's OnCertificatesChanged again. |
| distributor_.SetKeyMaterials( |
| kCertName1, absl::nullopt, |
| MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents)); |
| EXPECT_THAT( |
| watcher_state_1->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| kRootCert2Contents, |
| MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents)))); |
| CancelWatch(watcher_state_1); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, SameIdentityNameDiffRootName) { |
| // Register watcher 1. |
| WatcherState* watcher_state_1 = |
| MakeWatcher(kRootCert1Name, kIdentityCert1Name); |
| EXPECT_THAT( |
| GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kRootCert1Name, true, false), |
| CallbackStatus(kIdentityCert1Name, false, true))); |
| // Register watcher 2. |
| WatcherState* watcher_state_2 = |
| MakeWatcher(kRootCert2Name, kIdentityCert1Name); |
| EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre(CallbackStatus( |
| kRootCert2Name, true, false))); |
| // Push credential updates to kRootCert1Name and check if the status works as |
| // expected. |
| distributor_.SetKeyMaterials(kRootCert1Name, kRootCert1Contents, |
| absl::nullopt); |
| // Check the updates are delivered to watcher 1. |
| EXPECT_THAT(watcher_state_1->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo(kRootCert1Contents, {}))); |
| // Push credential updates to kRootCert2Name. |
| distributor_.SetKeyMaterials(kRootCert2Name, kRootCert2Contents, |
| absl::nullopt); |
| // Check the updates are delivered to watcher 2. |
| EXPECT_THAT(watcher_state_2->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo(kRootCert2Contents, {}))); |
| // Push credential updates to kIdentityCert1Name and check if the status works |
| // as expected. |
| distributor_.SetKeyMaterials( |
| kIdentityCert1Name, absl::nullopt, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)); |
| // Check the updates are delivered to watcher 1 and watcher 2. |
| EXPECT_THAT( |
| watcher_state_1->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)))); |
| EXPECT_THAT( |
| watcher_state_2->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| kRootCert2Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)))); |
| // Cancel watcher 1. |
| CancelWatch(watcher_state_1); |
| EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre(CallbackStatus( |
| kRootCert1Name, false, false))); |
| // Cancel watcher 2. |
| CancelWatch(watcher_state_2); |
| EXPECT_THAT( |
| GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kRootCert2Name, false, false), |
| CallbackStatus(kIdentityCert1Name, false, false))); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, SameRootNameDiffIdentityName) { |
| // Register watcher 1. |
| WatcherState* watcher_state_1 = |
| MakeWatcher(kRootCert1Name, kIdentityCert1Name); |
| EXPECT_THAT( |
| GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kRootCert1Name, true, false), |
| CallbackStatus(kIdentityCert1Name, false, true))); |
| // Register watcher 2. |
| WatcherState* watcher_state_2 = |
| MakeWatcher(kRootCert1Name, kIdentityCert2Name); |
| EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre(CallbackStatus( |
| kIdentityCert2Name, false, true))); |
| // Push credential updates to kRootCert1Name and check if the status works as |
| // expected. |
| distributor_.SetKeyMaterials(kRootCert1Name, kRootCert1Contents, |
| absl::nullopt); |
| // Check the updates are delivered to watcher 1. |
| EXPECT_THAT(watcher_state_1->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo(kRootCert1Contents, {}))); |
| // Check the updates are delivered to watcher 2. |
| EXPECT_THAT(watcher_state_2->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo(kRootCert1Contents, {}))); |
| // Push credential updates to SetKeyMaterials. |
| distributor_.SetKeyMaterials( |
| kIdentityCert1Name, absl::nullopt, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)); |
| // Check the updates are delivered to watcher 1. |
| EXPECT_THAT( |
| watcher_state_1->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)))); |
| // Push credential updates to kIdentityCert2Name. |
| distributor_.SetKeyMaterials( |
| kIdentityCert2Name, absl::nullopt, |
| MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents)); |
| // Check the updates are delivered to watcher 2. |
| EXPECT_THAT( |
| watcher_state_2->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents)))); |
| // Cancel watcher 1. |
| CancelWatch(watcher_state_1); |
| EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre(CallbackStatus( |
| kIdentityCert1Name, false, false))); |
| // Cancel watcher 2. |
| CancelWatch(watcher_state_2); |
| EXPECT_THAT( |
| GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kRootCert1Name, false, false), |
| CallbackStatus(kIdentityCert2Name, false, false))); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, |
| AddAndCancelFirstWatcherForSameRootAndIdentityCertName) { |
| // Register watcher 1 watching kCertName1 for both root and identity certs. |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1); |
| EXPECT_THAT(GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kCertName1, true, true))); |
| // Push credential updates to kCertName1 and check if the status works as |
| // expected. |
| distributor_.SetKeyMaterials( |
| kCertName1, kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)); |
| // Check the updates are delivered to watcher 1. |
| EXPECT_THAT( |
| watcher_state_1->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)))); |
| // Cancel watcher 1. |
| CancelWatch(watcher_state_1); |
| EXPECT_THAT(GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kCertName1, false, false))); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, |
| AddAndCancelFirstWatcherForIdentityCertNameWithRootBeingWatched) { |
| // Register watcher 1 watching kCertName1 for root certs. |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName1, absl::nullopt); |
| EXPECT_THAT(GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kCertName1, true, false))); |
| // Register watcher 2 watching kCertName1 for identity certs. |
| WatcherState* watcher_state_2 = MakeWatcher(absl::nullopt, kCertName1); |
| EXPECT_THAT(GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kCertName1, true, true))); |
| // Push credential updates to kCertName1 and check if the status works as |
| // expected. |
| distributor_.SetKeyMaterials( |
| kCertName1, kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)); |
| // Check the updates are delivered to watcher 1. |
| EXPECT_THAT(watcher_state_1->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo(kRootCert1Contents, {}))); |
| // Check the updates are delivered to watcher 2. |
| EXPECT_THAT(watcher_state_2->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| "", MakeCertKeyPairs(kIdentityCert1PrivateKey, |
| kIdentityCert1Contents)))); |
| // Push root cert updates to kCertName1. |
| distributor_.SetKeyMaterials(kCertName1, kRootCert2Contents, absl::nullopt); |
| // Check the updates are delivered to watcher 1. |
| EXPECT_THAT(watcher_state_1->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo(kRootCert2Contents, {}))); |
| // Check the updates are not delivered to watcher 2. |
| EXPECT_THAT(watcher_state_2->GetCredentialQueue(), ::testing::ElementsAre()); |
| // Push identity cert updates to kCertName1. |
| distributor_.SetKeyMaterials( |
| kCertName1, absl::nullopt, |
| MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents)); |
| // Check the updates are not delivered to watcher 1. |
| EXPECT_THAT(watcher_state_1->GetCredentialQueue(), ::testing::ElementsAre()); |
| // Check the updates are delivered to watcher 2. |
| EXPECT_THAT(watcher_state_2->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| "", MakeCertKeyPairs(kIdentityCert2PrivateKey, |
| kIdentityCert2Contents)))); |
| watcher_state_2->cert_update_queue.clear(); |
| // Cancel watcher 2. |
| CancelWatch(watcher_state_2); |
| EXPECT_THAT(GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kCertName1, true, false))); |
| // Cancel watcher 1. |
| CancelWatch(watcher_state_1); |
| EXPECT_THAT(GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kCertName1, false, false))); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, |
| AddAndCancelFirstWatcherForRootCertNameWithIdentityBeingWatched) { |
| // Register watcher 1 watching kCertName1 for identity certs. |
| WatcherState* watcher_state_1 = MakeWatcher(absl::nullopt, kCertName1); |
| EXPECT_THAT(GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kCertName1, false, true))); |
| // Register watcher 2 watching kCertName1 for root certs. |
| WatcherState* watcher_state_2 = MakeWatcher(kCertName1, absl::nullopt); |
| EXPECT_THAT(GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kCertName1, true, true))); |
| // Push credential updates to kCertName1 and check if the status works as |
| // expected. |
| distributor_.SetKeyMaterials( |
| kCertName1, kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)); |
| // Check the updates are delivered to watcher 1. |
| EXPECT_THAT(watcher_state_1->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| "", MakeCertKeyPairs(kIdentityCert1PrivateKey, |
| kIdentityCert1Contents)))); |
| // Check the updates are delivered to watcher 2. |
| EXPECT_THAT(watcher_state_2->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo(kRootCert1Contents, {}))); |
| // Push root cert updates to kCertName1. |
| distributor_.SetKeyMaterials(kCertName1, kRootCert2Contents, absl::nullopt); |
| // Check the updates are delivered to watcher 2. |
| EXPECT_THAT(watcher_state_2->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo(kRootCert2Contents, {}))); |
| // Check the updates are not delivered to watcher 1. |
| EXPECT_THAT(watcher_state_1->GetCredentialQueue(), ::testing::ElementsAre()); |
| // Push identity cert updates to kCertName1. |
| distributor_.SetKeyMaterials( |
| kCertName1, absl::nullopt, |
| MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents)); |
| // Check the updates are not delivered to watcher 2. |
| EXPECT_THAT(watcher_state_2->GetCredentialQueue(), ::testing::ElementsAre()); |
| // Check the updates are delivered to watcher 1. |
| EXPECT_THAT(watcher_state_1->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| "", MakeCertKeyPairs(kIdentityCert2PrivateKey, |
| kIdentityCert2Contents)))); |
| // Cancel watcher 2. |
| CancelWatch(watcher_state_2); |
| EXPECT_THAT(GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kCertName1, false, true))); |
| // Cancel watcher 1. |
| CancelWatch(watcher_state_1); |
| EXPECT_THAT(GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kCertName1, false, false))); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, |
| RemoveAllWatchersForCertNameAndAddAgain) { |
| // Register watcher 1 and watcher 2 watching kCertName1 for root and identity |
| // certs. |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1); |
| EXPECT_THAT(GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kCertName1, true, true))); |
| WatcherState* watcher_state_2 = MakeWatcher(kCertName1, kCertName1); |
| EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre()); |
| // Push credential updates to kCertName1. |
| distributor_.SetKeyMaterials( |
| kCertName1, kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)); |
| // Cancel watcher 2. |
| CancelWatch(watcher_state_2); |
| EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre()); |
| // Cancel watcher 1. |
| CancelWatch(watcher_state_1); |
| EXPECT_THAT(GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kCertName1, false, false))); |
| // Register watcher 3 watching kCertName for root and identity certs. |
| WatcherState* watcher_state_3 = MakeWatcher(kCertName1, kCertName1); |
| EXPECT_THAT(GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kCertName1, true, true))); |
| // Push credential updates to kCertName1. |
| distributor_.SetKeyMaterials( |
| kCertName1, kRootCert2Contents, |
| MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents)); |
| // Check the updates are delivered to watcher 3. |
| EXPECT_THAT( |
| watcher_state_3->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| kRootCert2Contents, |
| MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents)))); |
| // Cancel watcher 3. |
| CancelWatch(watcher_state_3); |
| EXPECT_THAT(GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kCertName1, false, false))); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, ResetCallbackToNull) { |
| // Register watcher 1 watching kCertName1 for root and identity certs. |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1); |
| EXPECT_THAT(GetCallbackQueue(), |
| ::testing::ElementsAre(CallbackStatus(kCertName1, true, true))); |
| // Reset callback to nullptr. |
| distributor_.SetWatchStatusCallback(nullptr); |
| // Cancel watcher 1 shouldn't trigger any callback. |
| CancelWatch(watcher_state_1); |
| EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre()); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, SetKeyMaterialsInCallback) { |
| distributor_.SetWatchStatusCallback([this](std::string cert_name, |
| bool /*root_being_watched*/, |
| bool /*identity_being_watched*/) { |
| distributor_.SetKeyMaterials( |
| cert_name, kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)); |
| }); |
| auto verify_function = [this](std::string cert_name) { |
| WatcherState* watcher_state_1 = MakeWatcher(cert_name, cert_name); |
| // Check the updates are delivered to watcher 1. |
| EXPECT_THAT( |
| watcher_state_1->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| kRootCert1Contents, MakeCertKeyPairs(kIdentityCert1PrivateKey, |
| kIdentityCert1Contents)))); |
| CancelWatch(watcher_state_1); |
| }; |
| // Start 10 threads that will register a watcher to a new cert name, verify |
| // the key materials being set, and then cancel the watcher, to make sure the |
| // lock mechanism in the distributor is safe. |
| std::vector<std::thread> threads; |
| threads.reserve(10); |
| for (int i = 0; i < 10; ++i) { |
| threads.emplace_back(verify_function, std::to_string(i)); |
| } |
| for (auto& th : threads) { |
| th.join(); |
| } |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, WatchACertInfoWithValidCredentials) { |
| // Push credential updates to kCertName1. |
| distributor_.SetKeyMaterials( |
| kCertName1, kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)); |
| // Push root credential updates to kCertName2. |
| distributor_.SetKeyMaterials(kRootCert2Name, kRootCert2Contents, |
| absl::nullopt); |
| // Push identity credential updates to kCertName2. |
| distributor_.SetKeyMaterials( |
| kIdentityCert2Name, absl::nullopt, |
| MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents)); |
| // Register watcher 1. |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1); |
| // watcher 1 should receive the credentials right away. |
| EXPECT_THAT( |
| watcher_state_1->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)))); |
| CancelWatch(watcher_state_1); |
| // Register watcher 2. |
| WatcherState* watcher_state_2 = MakeWatcher(kRootCert2Name, absl::nullopt); |
| // watcher 2 should receive the root credentials right away. |
| EXPECT_THAT(watcher_state_2->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo(kRootCert2Contents, {}))); |
| // Register watcher 3. |
| WatcherState* watcher_state_3 = |
| MakeWatcher(absl::nullopt, kIdentityCert2Name); |
| // watcher 3 should received the identity credentials right away. |
| EXPECT_THAT(watcher_state_3->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| "", MakeCertKeyPairs(kIdentityCert2PrivateKey, |
| kIdentityCert2Contents)))); |
| CancelWatch(watcher_state_2); |
| CancelWatch(watcher_state_3); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, |
| SetErrorForCertForBothRootAndIdentity) { |
| // Register watcher 1. |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1); |
| // Calling SetErrorForCert on both cert names should only call one OnError |
| // on watcher 1. |
| distributor_.SetErrorForCert( |
| kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage), |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage)); |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), |
| ::testing::ElementsAre( |
| ErrorInfo(kRootErrorMessage, kIdentityErrorMessage))); |
| // Calling SetErrorForCert on root cert name should call OnError |
| // on watcher 1 again. |
| distributor_.SetErrorForCert( |
| kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kErrorMessage), |
| absl::nullopt); |
| EXPECT_THAT( |
| watcher_state_1->GetErrorQueue(), |
| ::testing::ElementsAre(ErrorInfo(kErrorMessage, kIdentityErrorMessage))); |
| // Calling SetErrorForCert on identity cert name should call OnError |
| // on watcher 1 again. |
| distributor_.SetErrorForCert( |
| kCertName1, absl::nullopt, |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING(kErrorMessage)); |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), |
| ::testing::ElementsAre(ErrorInfo(kErrorMessage, kErrorMessage))); |
| distributor_.CancelTlsCertificatesWatch(watcher_state_1->watcher); |
| EXPECT_EQ(watcher_state_1->watcher, nullptr); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, SetErrorForCertForRootOrIdentity) { |
| // Register watcher 1. |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName1, absl::nullopt); |
| // Calling SetErrorForCert on root name should only call one OnError |
| // on watcher 1. |
| distributor_.SetErrorForCert( |
| kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage), |
| absl::nullopt); |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), |
| ::testing::ElementsAre(ErrorInfo(kRootErrorMessage, ""))); |
| // Calling SetErrorForCert on identity name should do nothing. |
| distributor_.SetErrorForCert( |
| kCertName1, absl::nullopt, |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage)); |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), ::testing::ElementsAre()); |
| // Calling SetErrorForCert on both names should still get one OnError call. |
| distributor_.SetErrorForCert( |
| kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage), |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage)); |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), |
| ::testing::ElementsAre(ErrorInfo(kRootErrorMessage, ""))); |
| CancelWatch(watcher_state_1); |
| // Register watcher 2. |
| WatcherState* watcher_state_2 = MakeWatcher(absl::nullopt, kCertName1); |
| // Calling SetErrorForCert on identity name should only call one OnError |
| // on watcher 2. |
| distributor_.SetErrorForCert( |
| kCertName1, absl::nullopt, |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage)); |
| EXPECT_THAT(watcher_state_2->GetErrorQueue(), |
| ::testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage))); |
| // Calling SetErrorForCert on root name should do nothing. |
| distributor_.SetErrorForCert( |
| kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage), |
| absl::nullopt); |
| EXPECT_THAT(watcher_state_2->GetErrorQueue(), ::testing::ElementsAre()); |
| // Calling SetErrorForCert on both names should still get one OnError call. |
| distributor_.SetErrorForCert( |
| kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage), |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage)); |
| EXPECT_THAT(watcher_state_2->GetErrorQueue(), |
| ::testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage))); |
| CancelWatch(watcher_state_2); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, |
| SetErrorForIdentityNameWithPreexistingErrorForRootName) { |
| // SetErrorForCert for kCertName1. |
| distributor_.SetErrorForCert( |
| kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage), |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage)); |
| // Register watcher 1 for kCertName1 as root and kCertName2 as identity. |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName2); |
| // Should trigger OnError call right away since kCertName1 has error. |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), |
| ::testing::ElementsAre(ErrorInfo(kRootErrorMessage, ""))); |
| // Calling SetErrorForCert on kCertName2 should trigger OnError with both |
| // errors, because kCertName1 also has error. |
| distributor_.SetErrorForCert( |
| kCertName2, absl::nullopt, |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage)); |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), |
| ::testing::ElementsAre( |
| ErrorInfo(kRootErrorMessage, kIdentityErrorMessage))); |
| CancelWatch(watcher_state_1); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, |
| SetErrorForCertForRootNameWithSameNameForIdentityErrored) { |
| // SetErrorForCert for kCertName1. |
| distributor_.SetErrorForCert( |
| kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage), |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage)); |
| // Register watcher 1 for kCertName2 as root and kCertName1 as identity. |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName2, kCertName1); |
| // Should trigger OnError call right away since kCertName2 has error. |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), |
| ::testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage))); |
| // Calling SetErrorForCert on kCertName2 should trigger OnError with both |
| // errors, because kCertName1 also has error. |
| distributor_.SetErrorForCert( |
| kCertName2, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage), |
| absl::nullopt); |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), |
| ::testing::ElementsAre( |
| ErrorInfo(kRootErrorMessage, kIdentityErrorMessage))); |
| CancelWatch(watcher_state_1); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, |
| SetErrorForIdentityNameWithoutErrorForRootName) { |
| // Register watcher 1 for kCertName1 as root and kCertName2 as identity. |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName2); |
| // Should not trigger OnError. |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), ::testing::ElementsAre()); |
| // Calling SetErrorForCert on kCertName2 should trigger OnError. |
| distributor_.SetErrorForCert( |
| kCertName2, absl::nullopt, |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage)); |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), |
| ::testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage))); |
| CancelWatch(watcher_state_1); |
| // Register watcher 2 for kCertName2 as identity and a non-existing name |
| // kRootCert1Name as root. |
| WatcherState* watcher_state_2 = MakeWatcher(kRootCert1Name, kCertName2); |
| // Should not trigger OnError. |
| EXPECT_THAT(watcher_state_2->GetErrorQueue(), ::testing::ElementsAre()); |
| // Calling SetErrorForCert on kCertName2 should trigger OnError. |
| distributor_.SetErrorForCert( |
| kCertName2, absl::nullopt, |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage)); |
| EXPECT_THAT(watcher_state_2->error_queue, |
| ::testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage))); |
| CancelWatch(watcher_state_2); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, |
| SetErrorForRootNameWithPreexistingErrorForIdentityName) { |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName2, kCertName1); |
| // Should not trigger OnError. |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), ::testing::ElementsAre()); |
| // Calling SetErrorForCert on kCertName2 should trigger OnError. |
| distributor_.SetErrorForCert( |
| kCertName2, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage), |
| absl::nullopt); |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), |
| ::testing::ElementsAre(ErrorInfo(kRootErrorMessage, ""))); |
| CancelWatch(watcher_state_1); |
| // Register watcher 2 for kCertName2 as root and a non-existing name |
| // kIdentityCert1Name as identity. |
| WatcherState* watcher_state_2 = MakeWatcher(kCertName2, kIdentityCert1Name); |
| // Should not trigger OnError. |
| EXPECT_THAT(watcher_state_2->GetErrorQueue(), ::testing::ElementsAre()); |
| // Calling SetErrorForCert on kCertName2 should trigger OnError. |
| distributor_.SetErrorForCert( |
| kCertName2, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage), |
| absl::nullopt); |
| EXPECT_THAT(watcher_state_2->GetErrorQueue(), |
| ::testing::ElementsAre(ErrorInfo(kRootErrorMessage, ""))); |
| CancelWatch(watcher_state_2); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, |
| CancelTheLastWatcherOnAnErroredCertInfo) { |
| // Register watcher 1. |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1); |
| // Calling SetErrorForCert on both cert names should only call one OnError |
| // on watcher 1. |
| distributor_.SetErrorForCert( |
| kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage), |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage)); |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), |
| ::testing::ElementsAre( |
| ErrorInfo(kRootErrorMessage, kIdentityErrorMessage))); |
| // When watcher 1 is removed, the cert info entry should be removed. |
| CancelWatch(watcher_state_1); |
| // Register watcher 2 on the same cert name. |
| WatcherState* watcher_state_2 = MakeWatcher(kCertName1, kCertName1); |
| // Should not trigger OnError call on watcher 2 right away. |
| EXPECT_THAT(watcher_state_2->GetErrorQueue(), ::testing::ElementsAre()); |
| CancelWatch(watcher_state_2); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, |
| WatchErroredCertInfoWithValidCredentialData) { |
| // Push credential updates to kCertName1. |
| distributor_.SetKeyMaterials( |
| kCertName1, kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)); |
| // Calling SetErrorForCert on both cert names. |
| distributor_.SetErrorForCert( |
| kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage), |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage)); |
| // Register watcher 1. |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1); |
| // watcher 1 should receive both the old credentials and the error right away. |
| EXPECT_THAT( |
| watcher_state_1->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)))); |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), |
| ::testing::ElementsAre( |
| ErrorInfo(kRootErrorMessage, kIdentityErrorMessage))); |
| CancelWatch(watcher_state_1); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, |
| SetErrorForCertThenSuccessfulCredentialUpdates) { |
| // Calling SetErrorForCert on both cert names. |
| distributor_.SetErrorForCert( |
| kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage), |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage)); |
| // Push credential updates to kCertName1. |
| distributor_.SetKeyMaterials( |
| kCertName1, kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)); |
| // Register watcher 1. |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1); |
| // watcher 1 should only receive credential updates without any error, because |
| // the previous error is wiped out by a successful update. |
| EXPECT_THAT( |
| watcher_state_1->GetCredentialQueue(), |
| ::testing::ElementsAre(CredentialInfo( |
| kRootCert1Contents, |
| MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents)))); |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), ::testing::ElementsAre()); |
| CancelWatch(watcher_state_1); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, WatchCertInfoThenInvokeSetError) { |
| // Register watcher 1. |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1); |
| // Register watcher 2. |
| WatcherState* watcher_state_2 = MakeWatcher(kRootCert1Name, absl::nullopt); |
| // Register watcher 3. |
| WatcherState* watcher_state_3 = |
| MakeWatcher(absl::nullopt, kIdentityCert1Name); |
| distributor_.SetError(GRPC_ERROR_CREATE_FROM_STATIC_STRING(kErrorMessage)); |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), |
| ::testing::ElementsAre(ErrorInfo(kErrorMessage, kErrorMessage))); |
| EXPECT_THAT(watcher_state_2->GetErrorQueue(), |
| ::testing::ElementsAre(ErrorInfo(kErrorMessage, ""))); |
| EXPECT_THAT(watcher_state_3->GetErrorQueue(), |
| ::testing::ElementsAre(ErrorInfo("", kErrorMessage))); |
| CancelWatch(watcher_state_1); |
| CancelWatch(watcher_state_2); |
| CancelWatch(watcher_state_3); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, WatchErroredCertInfoBySetError) { |
| // Register watcher 1 watching kCertName1 as root. |
| WatcherState* watcher_state_1 = MakeWatcher(kCertName1, absl::nullopt); |
| // Register watcher 2 watching kCertName2 as identity. |
| WatcherState* watcher_state_2 = MakeWatcher(absl::nullopt, kCertName2); |
| // Call SetError and then cancel all watchers. |
| distributor_.SetError(GRPC_ERROR_CREATE_FROM_STATIC_STRING(kErrorMessage)); |
| CancelWatch(watcher_state_1); |
| CancelWatch(watcher_state_2); |
| // Register watcher 3 watching kCertName1 as root and kCertName2 as identity |
| // should not get the error updates. |
| WatcherState* watcher_state_3 = MakeWatcher(kCertName1, kCertName2); |
| EXPECT_THAT(watcher_state_3->GetErrorQueue(), ::testing::ElementsAre()); |
| CancelWatch(watcher_state_3); |
| // Register watcher 4 watching kCertName2 as root and kCertName1 as identity |
| // should not get the error updates. |
| WatcherState* watcher_state_4 = MakeWatcher(kCertName2, kCertName1); |
| EXPECT_THAT(watcher_state_4->GetErrorQueue(), ::testing::ElementsAre()); |
| CancelWatch(watcher_state_4); |
| } |
| |
| TEST_F(GrpcTlsCertificateDistributorTest, SetErrorForCertInCallback) { |
| distributor_.SetWatchStatusCallback([this](std::string cert_name, |
| bool /*root_being_watched*/, |
| bool /*identity_being_watched*/) { |
| this->distributor_.SetErrorForCert( |
| cert_name, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage), |
| GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage)); |
| }); |
| auto verify_function = [this](std::string cert_name) { |
| WatcherState* watcher_state_1 = MakeWatcher(cert_name, cert_name); |
| // Check the errors are delivered to watcher 1. |
| EXPECT_THAT(watcher_state_1->GetErrorQueue(), |
| ::testing::ElementsAre( |
| ErrorInfo(kRootErrorMessage, kIdentityErrorMessage))); |
| CancelWatch(watcher_state_1); |
| }; |
| // Start 1000 threads that will register a watcher to a new cert name, verify |
| // the key materials being set, and then cancel the watcher, to make sure the |
| // lock mechanism in the distributor is safe. |
| std::vector<std::thread> threads; |
| threads.reserve(1000); |
| for (int i = 0; i < 1000; ++i) { |
| threads.emplace_back(verify_function, std::to_string(i)); |
| } |
| for (auto& th : threads) { |
| th.join(); |
| } |
| } |
| |
| } // namespace testing |
| |
| } // namespace grpc_core |
| |
| int main(int argc, char** argv) { |
| grpc::testing::TestEnvironment env(&argc, argv); |
| ::testing::InitGoogleTest(&argc, argv); |
| grpc_init(); |
| int ret = RUN_ALL_TESTS(); |
| grpc_shutdown(); |
| return ret; |
| } |