diff --git a/include/envoy/server/BUILD b/include/envoy/server/BUILD index 8175022dff32f..4a8c79cb9c12f 100644 --- a/include/envoy/server/BUILD +++ b/include/envoy/server/BUILD @@ -147,6 +147,7 @@ envoy_cc_library( deps = [ ":admin_interface", ":lifecycle_notifier_interface", + ":process_context_interface", "//include/envoy/access_log:access_log_interface", "//include/envoy/api:api_interface", "//include/envoy/http:codes_interface", @@ -192,6 +193,11 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "process_context_interface", + hdrs = ["process_context.h"], +) + envoy_cc_library( name = "transport_socket_config_interface", hdrs = ["transport_socket_config.h"], diff --git a/include/envoy/server/filter_config.h b/include/envoy/server/filter_config.h index 2f433b1e6b656..1e7fff9dfd6d0 100644 --- a/include/envoy/server/filter_config.h +++ b/include/envoy/server/filter_config.h @@ -15,6 +15,7 @@ #include "envoy/server/admin.h" #include "envoy/server/lifecycle_notifier.h" #include "envoy/server/overload_manager.h" +#include "envoy/server/process_context.h" #include "envoy/singleton/manager.h" #include "envoy/stats/scope.h" #include "envoy/thread_local/thread_local.h" @@ -147,6 +148,11 @@ class FactoryContext { */ virtual Http::Context& httpContext() PURE; + /** + * @return ProcessContext& a reference to the process context. + */ + virtual ProcessContext& processContext() PURE; + /** * @return Api::Api& a reference to the api object. */ diff --git a/include/envoy/server/instance.h b/include/envoy/server/instance.h index 223aa410522c0..6c3d148dea12c 100644 --- a/include/envoy/server/instance.h +++ b/include/envoy/server/instance.h @@ -187,6 +187,11 @@ class Instance { */ virtual Http::Context& httpContext() PURE; + /** + * @return the server-wide process context. + */ + virtual ProcessContext& processContext() PURE; + /** * @return ThreadLocal::Instance& the thread local storage engine for the server. This is used to * allow runtime lockless updates to configuration, etc. across multiple threads. diff --git a/include/envoy/server/process_context.h b/include/envoy/server/process_context.h new file mode 100644 index 0000000000000..7a4ceeddb7824 --- /dev/null +++ b/include/envoy/server/process_context.h @@ -0,0 +1,29 @@ +#pragma once + +#include "envoy/common/pure.h" + +namespace Envoy { + +/** + * Represents some other part of the process. + */ +class ProcessObject { +public: + virtual ~ProcessObject() {} +}; + +/** + * Context passed to filters to access resources from non-Envoy parts of the + * process. + */ +class ProcessContext { +public: + virtual ~ProcessContext() {} + + /** + * @return the ProcessObject for this context. + */ + virtual ProcessObject& get() const PURE; +}; + +} // namespace Envoy diff --git a/source/exe/main_common.cc b/source/exe/main_common.cc index 200433db3ab98..84d57abfc5acf 100644 --- a/source/exe/main_common.cc +++ b/source/exe/main_common.cc @@ -42,7 +42,8 @@ MainCommonBase::MainCommonBase(const OptionsImpl& options, Event::TimeSystem& ti Server::ComponentFactory& component_factory, std::unique_ptr&& random_generator, Thread::ThreadFactory& thread_factory, - Filesystem::Instance& file_system) + Filesystem::Instance& file_system, + std::unique_ptr process_context) : options_(options), component_factory_(component_factory), thread_factory_(thread_factory), file_system_(file_system), stats_allocator_(symbol_table_) { switch (options_.mode()) { @@ -75,7 +76,7 @@ MainCommonBase::MainCommonBase(const OptionsImpl& options, Event::TimeSystem& ti server_ = std::make_unique( options_, time_system, local_address, listener_hooks, *restarter_, *stats_store_, access_log_lock, component_factory, std::move(random_generator), *tls_, thread_factory_, - file_system_); + file_system_, std::move(process_context)); break; } @@ -128,7 +129,7 @@ MainCommon::MainCommon(int argc, const char* const* argv) : options_(argc, argv, &MainCommon::hotRestartVersion, spdlog::level::info), base_(options_, real_time_system_, default_listener_hooks_, prod_component_factory_, std::make_unique(), platform_impl_.threadFactory(), - platform_impl_.fileSystem()) {} + platform_impl_.fileSystem(), nullptr) {} std::string MainCommon::hotRestartVersion(bool hot_restart_enabled) { #ifdef ENVOY_HOT_RESTART diff --git a/source/exe/main_common.h b/source/exe/main_common.h index 24b737432e1a6..3a49d8979099d 100644 --- a/source/exe/main_common.h +++ b/source/exe/main_common.h @@ -38,7 +38,8 @@ class MainCommonBase { MainCommonBase(const OptionsImpl& options, Event::TimeSystem& time_system, ListenerHooks& listener_hooks, Server::ComponentFactory& component_factory, std::unique_ptr&& random_generator, - Thread::ThreadFactory& thread_factory, Filesystem::Instance& file_system); + Thread::ThreadFactory& thread_factory, Filesystem::Instance& file_system, + std::unique_ptr process_context); bool run(); diff --git a/source/server/BUILD b/source/server/BUILD index 28b136d9d2805..7973979f2ac18 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -280,6 +280,12 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "process_context_lib", + hdrs = ["process_context_impl.h"], + deps = ["//include/envoy/server:process_context_interface"], +) + envoy_cc_library( name = "proto_descriptors_lib", srcs = ["proto_descriptors.cc"], @@ -326,6 +332,7 @@ envoy_cc_library( "//include/envoy/server:instance_interface", "//include/envoy/server:listener_manager_interface", "//include/envoy/server:options_interface", + "//include/envoy/server:process_context_interface", "//include/envoy/stats:stats_macros", "//include/envoy/tracing:http_tracer_interface", "//include/envoy/upstream:cluster_manager_interface", diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index a63cbfd0dea6d..589ec0b76d124 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -93,6 +93,7 @@ class ValidationInstance : Logger::Loggable, time_t startTimeFirstEpoch() override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } Stats::Store& stats() override { return stats_store_; } Http::Context& httpContext() override { return http_context_; } + ProcessContext& processContext() override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; } ThreadLocal::Instance& threadLocal() override { return thread_local_; } const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } TimeSource& timeSource() override { return api_->timeSource(); } diff --git a/source/server/listener_manager_impl.h b/source/server/listener_manager_impl.h index 767369578c995..f90641e6ea10b 100644 --- a/source/server/listener_manager_impl.h +++ b/source/server/listener_manager_impl.h @@ -302,6 +302,7 @@ class ListenerImpl : public Network::ListenerConfig, ServerLifecycleNotifier& lifecycleNotifier() override { return parent_.server_.lifecycleNotifier(); } + ProcessContext& processContext() override { return parent_.server_.processContext(); } // Network::DrainDecision bool drainClose() const override; diff --git a/source/server/process_context_impl.h b/source/server/process_context_impl.h new file mode 100644 index 0000000000000..352d40b49f5a6 --- /dev/null +++ b/source/server/process_context_impl.h @@ -0,0 +1,17 @@ +#pragma once + +#include "envoy/server/process_context.h" + +namespace Envoy { + +class ProcessContextImpl : public ProcessContext { +public: + ProcessContextImpl(ProcessObject& process_object) : process_object_(process_object) {} + // ProcessContext + ProcessObject& get() const override { return process_object_; } + +private: + ProcessObject& process_object_; +}; + +} // namespace Envoy diff --git a/source/server/server.cc b/source/server/server.cc index 805d6bfa5bd44..7aeedb19989c3 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -54,7 +54,8 @@ InstanceImpl::InstanceImpl(const Options& options, Event::TimeSystem& time_syste ComponentFactory& component_factory, Runtime::RandomGeneratorPtr&& random_generator, ThreadLocal::Instance& tls, Thread::ThreadFactory& thread_factory, - Filesystem::Instance& file_system) + Filesystem::Instance& file_system, + std::unique_ptr process_context) : secret_manager_(std::make_unique()), shutdown_(false), options_(options), time_source_(time_system), restarter_(restarter), start_time_(time(nullptr)), original_start_time_(start_time_), stats_store_(store), @@ -70,7 +71,8 @@ InstanceImpl::InstanceImpl(const Options& options, Event::TimeSystem& time_syste terminated_(false), mutex_tracer_(options.mutexTracingEnabled() ? &Envoy::MutexTracerImpl::getOrCreateTracer() : nullptr), - http_context_(store.symbolTable()), main_thread_id_(std::this_thread::get_id()) { + http_context_(store.symbolTable()), process_context_(std::move(process_context)), + main_thread_id_(std::this_thread::get_id()) { try { if (!options.logPath().empty()) { try { diff --git a/source/server/server.h b/source/server/server.h index aa31d22d27d1b..010937f4555ec 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -11,6 +11,7 @@ #include "envoy/server/drain_manager.h" #include "envoy/server/guarddog.h" #include "envoy/server/instance.h" +#include "envoy/server/process_context.h" #include "envoy/server/tracer_config.h" #include "envoy/ssl/context_manager.h" #include "envoy/stats/stats_macros.h" @@ -149,7 +150,8 @@ class InstanceImpl : Logger::Loggable, HotRestart& restarter, Stats::StoreRoot& store, Thread::BasicLockable& access_log_lock, ComponentFactory& component_factory, Runtime::RandomGeneratorPtr&& random_generator, ThreadLocal::Instance& tls, - Thread::ThreadFactory& thread_factory, Filesystem::Instance& file_system); + Thread::ThreadFactory& thread_factory, Filesystem::Instance& file_system, + std::unique_ptr process_context); ~InstanceImpl() override; @@ -185,6 +187,7 @@ class InstanceImpl : Logger::Loggable, time_t startTimeFirstEpoch() override { return original_start_time_; } Stats::Store& stats() override { return stats_store_; } Http::Context& httpContext() override { return http_context_; } + ProcessContext& processContext() override { return *process_context_; } ThreadLocal::Instance& threadLocal() override { return thread_local_; } const LocalInfo::LocalInfo& localInfo() override { return *local_info_; } TimeSource& timeSource() override { return time_source_; } @@ -259,6 +262,7 @@ class InstanceImpl : Logger::Loggable, std::unique_ptr overload_manager_; Envoy::MutexTracer* mutex_tracer_; Http::ContextImpl http_context_; + std::unique_ptr process_context_; std::unique_ptr heap_shrinker_; const std::thread::id main_thread_id_; diff --git a/test/integration/BUILD b/test/integration/BUILD index d0021f6719d16..d7dea75c31d27 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -411,6 +411,7 @@ envoy_cc_test_library( "//source/server:connection_handler_lib", "//source/server:hot_restart_nop_lib", "//source/server:listener_hooks_lib", + "//source/server:process_context_lib", "//source/server:server_lib", "//test/common/grpc:grpc_client_integration_lib", "//test/common/runtime:utility_lib", diff --git a/test/integration/server.cc b/test/integration/server.cc index 5cfa3d21f26e1..4224cb36a23f3 100644 --- a/test/integration/server.cc +++ b/test/integration/server.cc @@ -13,6 +13,7 @@ #include "server/hot_restart_nop_impl.h" #include "server/options_impl.h" +#include "server/process_context_impl.h" #include "test/common/runtime/utility.h" #include "test/integration/integration.h" @@ -176,7 +177,8 @@ void IntegrationTestServerImpl::createAndRunEnvoyServer( Server::InstanceImpl server(options, time_system, local_address, hooks, restarter, stat_store, access_log_lock, component_factory, std::move(random_generator), tls, - Thread::threadFactoryForTest(), Filesystem::fileSystemForTest()); + Thread::threadFactoryForTest(), Filesystem::fileSystemForTest(), + nullptr); // This is technically thread unsafe (assigning to a shared_ptr accessed // across threads), but because we synchronize below through serverReady(), the only // consumer on the main test thread in ~IntegrationTestServerImpl will not race. diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index 16c229b18e172..0a048f26c5a7a 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -365,6 +365,7 @@ class MockInstance : public Instance { MOCK_METHOD0(startTimeFirstEpoch, time_t()); MOCK_METHOD0(stats, Stats::Store&()); MOCK_METHOD0(httpContext, Http::Context&()); + MOCK_METHOD0(processContext, ProcessContext&()); MOCK_METHOD0(threadLocal, ThreadLocal::Instance&()); MOCK_METHOD0(localInfo, const LocalInfo::LocalInfo&()); MOCK_CONST_METHOD0(statsFlushInterval, std::chrono::milliseconds()); @@ -447,6 +448,7 @@ class MockFactoryContext : public virtual FactoryContext { MOCK_METHOD0(timeSource, TimeSource&()); Event::TestTimeSystem& timeSystem() { return time_system_; } Http::Context& httpContext() override { return http_context_; } + MOCK_METHOD0(processContext, ProcessContext&()); MOCK_METHOD0(api, Api::Api&()); testing::NiceMock access_log_manager_; diff --git a/test/server/server_fuzz_test.cc b/test/server/server_fuzz_test.cc index 7bee758d4f440..53db522ebbb08 100644 --- a/test/server/server_fuzz_test.cc +++ b/test/server/server_fuzz_test.cc @@ -82,7 +82,8 @@ DEFINE_PROTO_FUZZER(const envoy::config::bootstrap::v2::Bootstrap& input) { options, test_time.timeSystem(), std::make_shared("127.0.0.1"), hooks, restart, stats_store, fakelock, component_factory, std::make_unique(), - thread_local_instance, Thread::threadFactoryForTest(), Filesystem::fileSystemForTest()); + thread_local_instance, Thread::threadFactoryForTest(), Filesystem::fileSystemForTest(), + nullptr); } catch (const EnvoyException& ex) { ENVOY_LOG_MISC(debug, "Controlled EnvoyException exit: {}", ex.what()); return; diff --git a/test/server/server_test.cc b/test/server/server_test.cc index 303755bac2d8a..86347c7a3df04 100644 --- a/test/server/server_test.cc +++ b/test/server/server_test.cc @@ -5,6 +5,7 @@ #include "common/network/address_impl.h" #include "common/thread_local/thread_local_impl.h" +#include "server/process_context_impl.h" #include "server/server.h" #include "test/integration/server.h" @@ -147,12 +148,16 @@ class ServerInstanceImplTest : public testing::TestWithParam(); + if (process_object_ != nullptr) { + process_context_ = std::make_unique(*process_object_); + } server_ = std::make_unique( options_, test_time_.timeSystem(), Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("127.0.0.1")), hooks_, restart_, stats_store_, fakelock_, component_factory_, std::make_unique>(), *thread_local_, - Thread::threadFactoryForTest(), Filesystem::fileSystemForTest()); + Thread::threadFactoryForTest(), Filesystem::fileSystemForTest(), + std::move(process_context_)); EXPECT_TRUE(server_->api().fileSystem().fileExists("/dev/null")); } @@ -170,7 +175,7 @@ class ServerInstanceImplTest : public testing::TestWithParam>(), *thread_local_, - Thread::threadFactoryForTest(), Filesystem::fileSystemForTest()); + Thread::threadFactoryForTest(), Filesystem::fileSystemForTest(), nullptr); EXPECT_TRUE(server_->api().fileSystem().fileExists("/dev/null")); } @@ -187,6 +192,8 @@ class ServerInstanceImplTest : public testing::TestWithParam process_context_; std::unique_ptr server_; }; @@ -432,7 +439,7 @@ TEST_P(ServerInstanceImplTest, NoOptionsPassed) { Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("127.0.0.1")), hooks_, restart_, stats_store_, fakelock_, component_factory_, std::make_unique>(), *thread_local_, - Thread::threadFactoryForTest(), Filesystem::fileSystemForTest())), + Thread::threadFactoryForTest(), Filesystem::fileSystemForTest(), nullptr)), EnvoyException, "At least one of --config-path and --config-yaml should be non-empty"); } @@ -493,6 +500,28 @@ TEST_P(ServerInstanceImplTest, ZipkinHttpTracingEnabled) { EXPECT_NE(nullptr, dynamic_cast(tracer())); } +class TestObject : public ProcessObject { +public: + void setFlag(bool value) { boolean_flag_ = value; } + + bool boolean_flag_ = true; +}; + +TEST_P(ServerInstanceImplTest, WithProcessContext) { + TestObject object; + process_object_ = &object; + + EXPECT_NO_THROW(initialize("test/server/empty_bootstrap.yaml")); + + ProcessContext& context = server_->processContext(); + auto& object_from_context = dynamic_cast(context.get()); + EXPECT_EQ(&object_from_context, &object); + EXPECT_TRUE(object_from_context.boolean_flag_); + + object.boolean_flag_ = false; + EXPECT_FALSE(object_from_context.boolean_flag_); +} + } // namespace } // namespace Server } // namespace Envoy