Skip to content

Commit eaab311

Browse files
agrawrohwbpcode
andauthored
datasource: refactor to use Config::WatchedDirectory directly as it's already exception-free (#41818)
## Description This PR resolves an old TODO around using `Config::WatchedDirectory` instead of directly creating a watcher as `Config::WatchedDirectory` is already exception-free now. --- **Commit Message:** datasource: refactor to make watched directory exception-free **Additional Description:** Use `Config::WatchedDirectory` instead of directly creating a watcher as `Config::WatchedDirectory` is already exception-free. **Risk Level:** Low **Testing:** Added Tests **Docs Changes:** N/A **Release Notes:** N/A --------- Signed-off-by: Rohit Agrawal <[email protected]> Co-authored-by: code <[email protected]>
1 parent 033da06 commit eaab311

File tree

4 files changed

+55
-13
lines changed

4 files changed

+55
-13
lines changed

source/common/config/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ envoy_cc_library(
5151
deps = [
5252
":remote_data_fetcher_lib",
5353
":utility_lib",
54+
":watched_directory_lib",
5455
"//envoy/api:api_interface",
5556
"//envoy/init:manager_interface",
5657
"//envoy/thread_local:thread_local_interface",

source/common/config/datasource.cc

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ absl::optional<std::string> getPath(const envoy::config::core::v3::DataSource& s
8989

9090
DynamicData::DynamicData(Event::Dispatcher& main_dispatcher,
9191
ThreadLocal::TypedSlotPtr<ThreadLocalData> slot,
92-
Filesystem::WatcherPtr watcher)
92+
WatchedDirectoryPtr watcher)
9393
: dispatcher_(main_dispatcher), slot_(std::move(slot)), watcher_(std::move(watcher)) {}
9494

9595
DynamicData::~DynamicData() {
@@ -140,13 +140,12 @@ absl::StatusOr<DataSourceProviderPtr> DataSourceProvider::create(const ProtoData
140140
});
141141

142142
const auto& filename = source.filename();
143-
auto watcher = main_dispatcher.createFilesystemWatcher();
144-
// DynamicData will ensure that the watcher is destroyed before the slot is destroyed.
145-
// TODO(wbpcode): use Config::WatchedDirectory instead of directly creating a watcher
146-
// if the Config::WatchedDirectory is exception-free in the future.
147-
auto watcher_status = watcher->addWatch(
148-
absl::StrCat(source.watched_directory().path(), "/"), Filesystem::Watcher::Events::MovedTo,
149-
[slot_ptr = slot.get(), &api, filename, allow_empty, max_size](uint32_t) -> absl::Status {
143+
auto directory_watcher_or_error =
144+
WatchedDirectory::create(source.watched_directory(), main_dispatcher);
145+
RETURN_IF_NOT_OK_REF(directory_watcher_or_error.status());
146+
147+
directory_watcher_or_error.value()->setCallback(
148+
[slot_ptr = slot.get(), &api, filename, allow_empty, max_size]() -> absl::Status {
150149
auto new_data_or_error = readFile(filename, api, allow_empty, max_size);
151150
if (!new_data_or_error.ok()) {
152151
// Log an error but don't fail the watch to avoid throwing EnvoyException at runtime.
@@ -163,10 +162,9 @@ absl::StatusOr<DataSourceProviderPtr> DataSourceProvider::create(const ProtoData
163162
});
164163
return absl::OkStatus();
165164
});
166-
RETURN_IF_NOT_OK(watcher_status);
167165

168-
return std::unique_ptr<DataSourceProvider>(
169-
new DataSourceProvider(DynamicData(main_dispatcher, std::move(slot), std::move(watcher))));
166+
return std::unique_ptr<DataSourceProvider>(new DataSourceProvider(DynamicData(
167+
main_dispatcher, std::move(slot), std::move(directory_watcher_or_error.value()))));
170168
}
171169

172170
} // namespace DataSource

source/common/config/datasource.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "source/common/common/empty_string.h"
1313
#include "source/common/common/enum_to_int.h"
1414
#include "source/common/config/remote_data_fetcher.h"
15+
#include "source/common/config/watched_directory.h"
1516
#include "source/common/init/target_impl.h"
1617

1718
#include "absl/types/optional.h"
@@ -67,15 +68,15 @@ class DynamicData {
6768

6869
DynamicData(DynamicData&&) = default;
6970
DynamicData(Event::Dispatcher& main_dispatcher, ThreadLocal::TypedSlotPtr<ThreadLocalData> slot,
70-
Filesystem::WatcherPtr watcher);
71+
WatchedDirectoryPtr watcher);
7172
~DynamicData();
7273

7374
const std::string& data() const;
7475

7576
private:
7677
Event::Dispatcher& dispatcher_;
7778
ThreadLocal::TypedSlotPtr<ThreadLocalData> slot_;
78-
Filesystem::WatcherPtr watcher_;
79+
WatchedDirectoryPtr watcher_;
7980
};
8081

8182
/**

test/common/config/datasource_test.cc

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,48 @@ TEST(DataSourceProviderTest, FileDataSourceAndWithWatchButUpdateError) {
403403
unlink(TestEnvironment::temporaryPath("envoy_test/watcher_new_link").c_str());
404404
}
405405

406+
TEST(DataSourceProviderTest, FileDataSourceAndWatchDirectoryCreationFailure) {
407+
unlink(TestEnvironment::temporaryPath("envoy_test/watcher_target").c_str());
408+
unlink(TestEnvironment::temporaryPath("envoy_test/watcher_link").c_str());
409+
410+
envoy::config::core::v3::DataSource config;
411+
TestEnvironment::createPath(TestEnvironment::temporaryPath("envoy_test"));
412+
413+
// Use a non-existent directory path that will cause WatchedDirectory::create() to fail.
414+
const std::string yaml = fmt::format(R"EOF(
415+
filename: "{}"
416+
watched_directory:
417+
path: "/non/existent/directory/path"
418+
)EOF",
419+
TestEnvironment::temporaryPath("envoy_test/watcher_link"));
420+
TestUtility::loadFromYamlAndValidate(yaml, config);
421+
422+
{
423+
std::ofstream file(TestEnvironment::temporaryPath("envoy_test/watcher_target"));
424+
file << "Hello, world!";
425+
file.close();
426+
}
427+
TestEnvironment::createSymlink(TestEnvironment::temporaryPath("envoy_test/watcher_target"),
428+
TestEnvironment::temporaryPath("envoy_test/watcher_link"));
429+
430+
EXPECT_EQ(envoy::config::core::v3::DataSource::SpecifierCase::kFilename, config.specifier_case());
431+
432+
Api::ApiPtr api = Api::createApiForTest();
433+
Event::DispatcherPtr dispatcher = api->allocateDispatcher("test_thread");
434+
NiceMock<ThreadLocal::MockInstance> tls;
435+
436+
// Creating a provider with an invalid watched directory path should return an error.
437+
auto provider_or_error =
438+
DataSource::DataSourceProvider::create(config, *dispatcher, tls, *api, false, 0);
439+
EXPECT_FALSE(provider_or_error.ok());
440+
EXPECT_THAT(provider_or_error.status().message(),
441+
testing::HasSubstr("/non/existent/directory/path"));
442+
443+
// Remove the file.
444+
unlink(TestEnvironment::temporaryPath("envoy_test/watcher_target").c_str());
445+
unlink(TestEnvironment::temporaryPath("envoy_test/watcher_link").c_str());
446+
}
447+
406448
} // namespace
407449
} // namespace Config
408450
} // namespace Envoy

0 commit comments

Comments
 (0)