diff --git a/api/envoy/admin/v3/init_dump.proto b/api/envoy/admin/v3/init_dump.proto new file mode 100644 index 0000000000000..3df9bfb51da68 --- /dev/null +++ b/api/envoy/admin/v3/init_dump.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +package envoy.admin.v3; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; + +option java_package = "io.envoyproxy.envoy.admin.v3"; +option java_outer_classname = "InitDumpProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: InitDump] + +// Dumps of unready targets of envoy init managers. Envoy's admin fills this message with init managers, +// which provides the information of their unready targets. +// The :ref:`/init_dump ` will dump all unready targets information. +message UnreadyTargetsDumps { + // Message of unready targets information of an init manager. + message UnreadyTargetsDump { + // Name of the init manager. Example: "init_manager_xxx". + string name = 1; + + // Names of unready targets of the init manager. Example: "target_xxx". + repeated string target_names = 2; + } + + // You can choose specific component to dump unready targets with mask query parameter. + // See :ref:`/init_dump?mask={} ` for more information. + // The dumps of unready targets of all init managers. + repeated UnreadyTargetsDump unready_targets_dumps = 1; +} diff --git a/api/envoy/admin/v4alpha/init_dump.proto b/api/envoy/admin/v4alpha/init_dump.proto new file mode 100644 index 0000000000000..81c423e52024d --- /dev/null +++ b/api/envoy/admin/v4alpha/init_dump.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; + +package envoy.admin.v4alpha; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; + +option java_package = "io.envoyproxy.envoy.admin.v4alpha"; +option java_outer_classname = "InitDumpProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: InitDump] + +// Dumps of unready targets of envoy init managers. Envoy's admin fills this message with init managers, +// which provides the information of their unready targets. +// The :ref:`/init_dump ` will dump all unready targets information. +message UnreadyTargetsDumps { + option (udpa.annotations.versioning).previous_message_type = "envoy.admin.v3.UnreadyTargetsDumps"; + + // Message of unready targets information of an init manager. + message UnreadyTargetsDump { + option (udpa.annotations.versioning).previous_message_type = + "envoy.admin.v3.UnreadyTargetsDumps.UnreadyTargetsDump"; + + // Name of the init manager. Example: "init_manager_xxx". + string name = 1; + + // Names of unready targets of the init manager. Example: "target_xxx". + repeated string target_names = 2; + } + + // You can choose specific component to dump unready targets with mask query parameter. + // See :ref:`/init_dump?mask={} ` for more information. + // The dumps of unready targets of all init managers. + repeated UnreadyTargetsDump unready_targets_dumps = 1; +} diff --git a/docs/root/operations/admin.rst b/docs/root/operations/admin.rst index d649197cc910b..15d518f4418f0 100644 --- a/docs/root/operations/admin.rst +++ b/docs/root/operations/admin.rst @@ -212,6 +212,24 @@ modify different aspects of the server: See :option:`--hot-restart-version`. +.. _operations_admin_interface_init_dump: + +.. http:get:: /init_dump + + Dump currently information of unready targets of various Envoy components as JSON-serialized proto + messages. See the :ref:`response definition ` for more + information. + +.. _operations_admin_interface_init_dump_by_mask: + +.. http:get:: /init_dump?mask={} + + When mask query parameters is specified, the mask value is the desired component to dump unready targets. + The mask is parsed as a ``ProtobufWkt::FieldMask``. + + For example, get the unready targets of all listeners with + ``/init_dump?mask=listener`` + .. _operations_admin_interface_listeners: .. http:get:: /listeners diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 9d1c099e70d4a..33e326ecc7f2f 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -65,6 +65,7 @@ New Features * access log: added support for :ref:`%DOWNSTREAM_PEER_FINGERPRINT_1% ` as a response flag. * access log: added support for nested objects in :ref:`JSON logging mode `. * access log: added :ref:`omit_empty_values` option to omit unset value from formatted log. +* admin: added the ability to dump init manager unready targets information :ref:`/init_dump ` and :ref:`/init_dump?mask={} `. * build: enable building envoy :ref:`arm64 images ` by buildx tool in x86 CI platform. * cluster: added new :ref:`connection_pool_per_downstream_connection ` flag, which enable creation of a new connection pool for each downstream connection. * decompressor filter: reports compressed and uncompressed bytes in trailers. diff --git a/generated_api_shadow/envoy/admin/v3/init_dump.proto b/generated_api_shadow/envoy/admin/v3/init_dump.proto new file mode 100644 index 0000000000000..3df9bfb51da68 --- /dev/null +++ b/generated_api_shadow/envoy/admin/v3/init_dump.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +package envoy.admin.v3; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; + +option java_package = "io.envoyproxy.envoy.admin.v3"; +option java_outer_classname = "InitDumpProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: InitDump] + +// Dumps of unready targets of envoy init managers. Envoy's admin fills this message with init managers, +// which provides the information of their unready targets. +// The :ref:`/init_dump ` will dump all unready targets information. +message UnreadyTargetsDumps { + // Message of unready targets information of an init manager. + message UnreadyTargetsDump { + // Name of the init manager. Example: "init_manager_xxx". + string name = 1; + + // Names of unready targets of the init manager. Example: "target_xxx". + repeated string target_names = 2; + } + + // You can choose specific component to dump unready targets with mask query parameter. + // See :ref:`/init_dump?mask={} ` for more information. + // The dumps of unready targets of all init managers. + repeated UnreadyTargetsDump unready_targets_dumps = 1; +} diff --git a/generated_api_shadow/envoy/admin/v4alpha/init_dump.proto b/generated_api_shadow/envoy/admin/v4alpha/init_dump.proto new file mode 100644 index 0000000000000..81c423e52024d --- /dev/null +++ b/generated_api_shadow/envoy/admin/v4alpha/init_dump.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; + +package envoy.admin.v4alpha; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; + +option java_package = "io.envoyproxy.envoy.admin.v4alpha"; +option java_outer_classname = "InitDumpProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: InitDump] + +// Dumps of unready targets of envoy init managers. Envoy's admin fills this message with init managers, +// which provides the information of their unready targets. +// The :ref:`/init_dump ` will dump all unready targets information. +message UnreadyTargetsDumps { + option (udpa.annotations.versioning).previous_message_type = "envoy.admin.v3.UnreadyTargetsDumps"; + + // Message of unready targets information of an init manager. + message UnreadyTargetsDump { + option (udpa.annotations.versioning).previous_message_type = + "envoy.admin.v3.UnreadyTargetsDumps.UnreadyTargetsDump"; + + // Name of the init manager. Example: "init_manager_xxx". + string name = 1; + + // Names of unready targets of the init manager. Example: "target_xxx". + repeated string target_names = 2; + } + + // You can choose specific component to dump unready targets with mask query parameter. + // See :ref:`/init_dump?mask={} ` for more information. + // The dumps of unready targets of all init managers. + repeated UnreadyTargetsDump unready_targets_dumps = 1; +} diff --git a/include/envoy/init/BUILD b/include/envoy/init/BUILD index 4bbc0d18f6826..1ece83ee55352 100644 --- a/include/envoy/init/BUILD +++ b/include/envoy/init/BUILD @@ -27,5 +27,6 @@ envoy_cc_library( deps = [ ":target_interface", ":watcher_interface", + "@envoy_api//envoy/admin/v3:pkg_cc_proto", ], ) diff --git a/include/envoy/init/manager.h b/include/envoy/init/manager.h index 94cf0dbb25e1a..06b0f7b29989d 100644 --- a/include/envoy/init/manager.h +++ b/include/envoy/init/manager.h @@ -1,9 +1,12 @@ #pragma once +#include "envoy/admin/v3/init_dump.pb.h" #include "envoy/common/pure.h" #include "envoy/init/target.h" #include "envoy/init/watcher.h" +#include "absl/container/flat_hash_map.h" + namespace Envoy { namespace Init { @@ -73,6 +76,16 @@ struct Manager { * @param watcher the watcher to notify when initialization is complete. */ virtual void initialize(const Watcher& watcher) PURE; + + /** + * @return the unready targets of the manager. + */ + virtual const absl::flat_hash_map& unreadyTargets() const PURE; + + /** + * Add unready targets information into the config dump. + */ + virtual void dumpUnreadyTargets(envoy::admin::v3::UnreadyTargetsDumps& dumps) PURE; }; } // namespace Init diff --git a/include/envoy/network/BUILD b/include/envoy/network/BUILD index 0427cc307df44..b845c4d34d941 100644 --- a/include/envoy/network/BUILD +++ b/include/envoy/network/BUILD @@ -170,6 +170,7 @@ envoy_cc_library( ":udp_packet_writer_handler_interface", "//include/envoy/access_log:access_log_interface", "//include/envoy/common:resource_interface", + "//include/envoy/init:manager_interface", "//include/envoy/stats:stats_interface", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], diff --git a/include/envoy/network/listener.h b/include/envoy/network/listener.h index 4a1ff1d01060d..da708aa95d35b 100644 --- a/include/envoy/network/listener.h +++ b/include/envoy/network/listener.h @@ -9,6 +9,7 @@ #include "envoy/common/exception.h" #include "envoy/common/resource.h" #include "envoy/config/core/v3/base.pb.h" +#include "envoy/init/manager.h" #include "envoy/network/connection.h" #include "envoy/network/connection_balancer.h" #include "envoy/network/listen_socket.h" @@ -166,6 +167,11 @@ class ListenerConfig { * @return pending connection backlog for TCP listeners. */ virtual uint32_t tcpBacklogSize() const PURE; + + /** + * @return init manager of the listener. + */ + virtual Init::Manager& initManager() PURE; }; /** diff --git a/include/envoy/server/listener_manager.h b/include/envoy/server/listener_manager.h index e01551414def6..ea7062313bde4 100644 --- a/include/envoy/server/listener_manager.h +++ b/include/envoy/server/listener_manager.h @@ -235,6 +235,11 @@ class ListenerManager { * @return the server's API Listener if it exists, nullopt if it does not. */ virtual ApiListenerOptRef apiListener() PURE; + + /* + * @return TRUE if the worker has started or FALSE if not. + */ + virtual bool isWorkerStarted() PURE; }; // overload operator| to allow ListenerManager::listeners(ListenerState) to be called using a diff --git a/source/common/init/manager_impl.cc b/source/common/init/manager_impl.cc index 95cb37e4cc3b6..650203fabbea5 100644 --- a/source/common/init/manager_impl.cc +++ b/source/common/init/manager_impl.cc @@ -67,6 +67,14 @@ const absl::flat_hash_map& ManagerImpl::unreadyTargets() return target_names_count_; } +void ManagerImpl::dumpUnreadyTargets(envoy::admin::v3::UnreadyTargetsDumps& unready_targets_dumps) { + auto& message = *unready_targets_dumps.mutable_unready_targets_dumps()->Add(); + message.set_name(name_); + for (const auto& [target_name, count] : target_names_count_) { + message.add_target_names(target_name); + } +} + void ManagerImpl::onTargetReady(absl::string_view target_name) { // If there are no remaining targets and one mysteriously calls us back, this manager is haunted. ASSERT(count_ != 0, diff --git a/source/common/init/manager_impl.h b/source/common/init/manager_impl.h index 026014245ccdb..49189ac120a77 100644 --- a/source/common/init/manager_impl.h +++ b/source/common/init/manager_impl.h @@ -36,9 +36,8 @@ class ManagerImpl : public Manager, Logger::Loggable { State state() const override; void add(const Target& target) override; void initialize(const Watcher& watcher) override; - - // Expose the const reference of target_names_count_ hash map to public. - const absl::flat_hash_map& unreadyTargets() const; + const absl::flat_hash_map& unreadyTargets() const override; + void dumpUnreadyTargets(envoy::admin::v3::UnreadyTargetsDumps& dumps) override; private: // Callback function with an additional target_name parameter, decrease unready targets count by diff --git a/source/server/admin/BUILD b/source/server/admin/BUILD index 0124f59e457f7..8451d4b805887 100644 --- a/source/server/admin/BUILD +++ b/source/server/admin/BUILD @@ -17,6 +17,7 @@ envoy_cc_library( ":clusters_handler_lib", ":config_dump_handler_lib", ":config_tracker_lib", + ":init_dump_handler_lib", ":listeners_handler_lib", ":logs_handler_lib", ":profiling_handler_lib", @@ -63,6 +64,7 @@ envoy_cc_library( "//source/common/router:scoped_config_lib", "//source/common/stats:isolated_store_lib", "//source/extensions/access_loggers/file:file_access_log_lib", + "//source/server:listener_manager_lib", ], ) @@ -259,6 +261,21 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "init_dump_handler_lib", + srcs = ["init_dump_handler.cc"], + hdrs = ["init_dump_handler.h"], + deps = [ + ":handler_ctx_lib", + ":utils_lib", + "//include/envoy/server:admin_interface", + "//include/envoy/server:instance_interface", + "//source/common/http:codes_lib", + "//source/common/http:header_map_lib", + "@envoy_api//envoy/admin/v3:pkg_cc_proto", + ], +) + envoy_cc_library( name = "utils_lib", srcs = ["utils.cc"], diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index 2f7d1e58b108d..c92b56c5c0329 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -35,6 +35,7 @@ #include "common/router/config_impl.h" #include "server/admin/utils.h" +#include "server/listener_impl.h" #include "extensions/access_loggers/file/file_access_log_impl.h" @@ -154,9 +155,10 @@ AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server) Http::ConnectionManagerImpl::generateTracingStats("http.admin.", no_op_store_)), route_config_provider_(server.timeSource()), scoped_route_config_provider_(server.timeSource()), clusters_handler_(server), - config_dump_handler_(config_tracker_, server), stats_handler_(server), logs_handler_(server), - profiling_handler_(profile_path), runtime_handler_(server), listeners_handler_(server), - server_cmd_handler_(server), server_info_handler_(server), + config_dump_handler_(config_tracker_, server), init_dump_handler_(server), + stats_handler_(server), logs_handler_(server), profiling_handler_(profile_path), + runtime_handler_(server), listeners_handler_(server), server_cmd_handler_(server), + server_info_handler_(server), // TODO(jsedgwick) add /runtime_reset endpoint that removes all admin-set values handlers_{ {"/", "Admin home page", MAKE_ADMIN_HANDLER(handlerAdminHome), false, false}, @@ -166,6 +168,8 @@ AdminImpl::AdminImpl(const std::string& profile_path, Server::Instance& server) MAKE_ADMIN_HANDLER(clusters_handler_.handlerClusters), false, false}, {"/config_dump", "dump current Envoy configs (experimental)", MAKE_ADMIN_HANDLER(config_dump_handler_.handlerConfigDump), false, false}, + {"/init_dump", "dump current Envoy init manager information (experimental)", + MAKE_ADMIN_HANDLER(init_dump_handler_.handlerInitDump), false, false}, {"/contention", "dump current Envoy mutex contention stats (if enabled)", MAKE_ADMIN_HANDLER(stats_handler_.handlerContention), false, false}, {"/cpuprofiler", "enable/disable the CPU profiler", diff --git a/source/server/admin/admin.h b/source/server/admin/admin.h index c1f89cfcbe910..8ed948da85b13 100644 --- a/source/server/admin/admin.h +++ b/source/server/admin/admin.h @@ -39,6 +39,7 @@ #include "server/admin/clusters_handler.h" #include "server/admin/config_dump_handler.h" #include "server/admin/config_tracker_impl.h" +#include "server/admin/init_dump_handler.h" #include "server/admin/listeners_handler.h" #include "server/admin/logs_handler.h" #include "server/admin/profiling_handler.h" @@ -287,6 +288,7 @@ class AdminImpl : public Admin, Http::Code handlerAdminHome(absl::string_view path_and_query, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream&); + Http::Code handlerHelp(absl::string_view path_and_query, Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, AdminStream&); @@ -320,7 +322,8 @@ class AdminImpl : public Admin, public: AdminListener(AdminImpl& parent, Stats::ScopePtr&& listener_scope) : parent_(parent), name_("admin"), scope_(std::move(listener_scope)), - stats_(Http::ConnectionManagerImpl::generateListenerStats("http.admin.", *scope_)) {} + stats_(Http::ConnectionManagerImpl::generateListenerStats("http.admin.", *scope_)), + init_manager_(nullptr) {} // Network::ListenerConfig Network::FilterChainManager& filterChainManager() override { return parent_; } @@ -351,6 +354,7 @@ class AdminImpl : public Admin, return empty_access_logs_; } uint32_t tcpBacklogSize() const override { return ENVOY_TCP_BACKLOG_SIZE; } + Init::Manager& initManager() override { return *init_manager_; } AdminImpl& parent_; const std::string name_; @@ -361,6 +365,7 @@ class AdminImpl : public Admin, private: const std::vector empty_access_logs_; + std::unique_ptr init_manager_; }; using AdminListenerPtr = std::unique_ptr; @@ -398,6 +403,7 @@ class AdminImpl : public Admin, NullScopedRouteConfigProvider scoped_route_config_provider_; Server::ClustersHandler clusters_handler_; Server::ConfigDumpHandler config_dump_handler_; + Server::InitDumpHandler init_dump_handler_; Server::StatsHandler stats_handler_; Server::LogsHandler logs_handler_; Server::ProfilingHandler profiling_handler_; diff --git a/source/server/admin/init_dump_handler.cc b/source/server/admin/init_dump_handler.cc new file mode 100644 index 0000000000000..8828b64e34e34 --- /dev/null +++ b/source/server/admin/init_dump_handler.cc @@ -0,0 +1,69 @@ +#include "server/admin/init_dump_handler.h" + +#include "common/http/headers.h" +#include "common/http/utility.h" +#include "common/network/utility.h" + +#include "server/admin/utils.h" + +namespace Envoy { +namespace Server { + +namespace { +// Helper method to get the mask parameter. +absl::optional maskParam(const Http::Utility::QueryParams& params) { + return Utility::queryParam(params, "mask"); +} + +} // namespace + +InitDumpHandler::InitDumpHandler(Server::Instance& server) : HandlerContextBase(server) {} + +Http::Code InitDumpHandler::handlerInitDump(absl::string_view url, + Http::ResponseHeaderMap& response_headers, + Buffer::Instance& response, AdminStream&) const { + Http::Utility::QueryParams query_params = Http::Utility::parseAndDecodeQueryString(url); + const auto mask = maskParam(query_params); + + envoy::admin::v3::UnreadyTargetsDumps dump = *dumpUnreadyTargets(mask); + MessageUtil::redact(dump); + + response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json); + response.add(MessageUtil::getJsonStringFromMessage(dump, true)); // pretty-print + return Http::Code::OK; +} + +std::unique_ptr +InitDumpHandler::dumpUnreadyTargets(const absl::optional& component) const { + auto unready_targets_dumps = std::make_unique(); + + if (component.has_value()) { + if (component.value() == "listener") { + dumpListenerUnreadyTargets(*unready_targets_dumps); + } + // More options for unready targets config dump. + } else { + // Dump all possible information of unready targets. + dumpListenerUnreadyTargets(*unready_targets_dumps); + // More unready targets to add into config dump. + } + return unready_targets_dumps; +} + +void InitDumpHandler::dumpListenerUnreadyTargets( + envoy::admin::v3::UnreadyTargetsDumps& unready_targets_dumps) const { + std::vector> listeners; + if (server_.listenerManager().isWorkerStarted()) { + listeners = server_.listenerManager().listeners(ListenerManager::WARMING); + } else { + listeners = server_.listenerManager().listeners(ListenerManager::ACTIVE); + } + + for (const auto& listener_config : listeners) { + auto& listener = listener_config.get(); + listener.initManager().dumpUnreadyTargets(unready_targets_dumps); + } +} + +} // namespace Server +} // namespace Envoy diff --git a/source/server/admin/init_dump_handler.h b/source/server/admin/init_dump_handler.h new file mode 100644 index 0000000000000..bb3683f74ca7a --- /dev/null +++ b/source/server/admin/init_dump_handler.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "envoy/admin/v3/init_dump.pb.h" +#include "envoy/http/codes.h" +#include "envoy/http/header_map.h" +#include "envoy/server/admin.h" +#include "envoy/server/instance.h" + +#include "server/admin/handler_ctx.h" + +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Server { + +class InitDumpHandler : public HandlerContextBase { + +public: + InitDumpHandler(Server::Instance& server); + + Http::Code handlerInitDump(absl::string_view path_and_query, + Http::ResponseHeaderMap& response_headers, Buffer::Instance& response, + AdminStream&) const; + +private: + /** + * Helper methods for the /init_dump url handler to add unready targets information. + */ + std::unique_ptr + dumpUnreadyTargets(const absl::optional& target) const; + + /** + * Helper methods for the /init_dump url handler to add unready targets config of listeners. + */ + void + dumpListenerUnreadyTargets(envoy::admin::v3::UnreadyTargetsDumps& unready_targets_dumps) const; +}; + +} // namespace Server +} // namespace Envoy diff --git a/source/server/listener_impl.h b/source/server/listener_impl.h index 818056dfdbecc..99c7a333b29f5 100644 --- a/source/server/listener_impl.h +++ b/source/server/listener_impl.h @@ -312,7 +312,7 @@ class ListenerImpl final : public Network::ListenerConfig, return access_logs_; } uint32_t tcpBacklogSize() const override { return tcp_backlog_size_; } - Init::Manager& initManager(); + Init::Manager& initManager() override; envoy::config::core::v3::TrafficDirection direction() const override { return config().traffic_direction(); } diff --git a/source/server/listener_manager_impl.h b/source/server/listener_manager_impl.h index 106e1d629dc90..ab839373829e4 100644 --- a/source/server/listener_manager_impl.h +++ b/source/server/listener_manager_impl.h @@ -195,6 +195,7 @@ class ListenerManagerImpl : public ListenerManager, Logger::Loggable( Network::Test::getCanonicalLoopbackAddress(GetParam()), nullptr, true)), connection_handler_(new Server::ConnectionHandlerImpl(*dispatcher_)), name_("proxy"), - filter_chain_(Network::Test::createEmptyFilterChainWithRawBufferSockets()) { + filter_chain_(Network::Test::createEmptyFilterChainWithRawBufferSockets()), + init_manager_(nullptr) { EXPECT_CALL(socket_factory_, socketType()).WillOnce(Return(Network::Socket::Type::Stream)); EXPECT_CALL(socket_factory_, localAddress()).WillOnce(ReturnRef(socket_->localAddress())); EXPECT_CALL(socket_factory_, getListenSocket()).WillOnce(Return(socket_)); @@ -80,6 +81,7 @@ class ProxyProtocolRegressionTest : public testing::TestWithParam empty_access_logs_; + std::unique_ptr init_manager_; }; // Parameterize the listener socket address version. diff --git a/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc b/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc index 03093cdd50c13..9cf36ab76c837 100644 --- a/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc +++ b/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc @@ -61,7 +61,8 @@ class ProxyProtocolTest : public testing::TestWithParam( Network::Test::getCanonicalLoopbackAddress(GetParam()), nullptr, true)), connection_handler_(new Server::ConnectionHandlerImpl(*dispatcher_)), name_("proxy"), - filter_chain_(Network::Test::createEmptyFilterChainWithRawBufferSockets()) { + filter_chain_(Network::Test::createEmptyFilterChainWithRawBufferSockets()), + init_manager_(nullptr) { EXPECT_CALL(socket_factory_, socketType()).WillOnce(Return(Network::Socket::Type::Stream)); EXPECT_CALL(socket_factory_, localAddress()).WillOnce(ReturnRef(socket_->localAddress())); EXPECT_CALL(socket_factory_, getListenSocket()).WillOnce(Return(socket_)); @@ -95,6 +96,7 @@ class ProxyProtocolTest : public testing::TestWithParam empty_access_logs_; + std::unique_ptr init_manager_; }; // Parameterize the listener socket address version. @@ -1246,7 +1249,8 @@ class WildcardProxyProtocolTest : public testing::TestWithParamlocalAddress()->ip()->port())), connection_handler_(new Server::ConnectionHandlerImpl(*dispatcher_)), name_("proxy"), - filter_chain_(Network::Test::createEmptyFilterChainWithRawBufferSockets()) { + filter_chain_(Network::Test::createEmptyFilterChainWithRawBufferSockets()), + init_manager_(nullptr) { EXPECT_CALL(socket_factory_, socketType()).WillOnce(Return(Network::Socket::Type::Stream)); EXPECT_CALL(socket_factory_, localAddress()).WillOnce(ReturnRef(socket_->localAddress())); EXPECT_CALL(socket_factory_, getListenSocket()).WillOnce(Return(socket_)); @@ -1290,6 +1294,7 @@ class WildcardProxyProtocolTest : public testing::TestWithParam empty_access_logs_; + std::unique_ptr init_manager_; }; // Parameterize the listener socket address version. diff --git a/test/integration/fake_upstream.h b/test/integration/fake_upstream.h index 84c59fcf1d5ab..4d405ec129a05 100644 --- a/test/integration/fake_upstream.h +++ b/test/integration/fake_upstream.h @@ -656,7 +656,8 @@ class FakeUpstream : Logger::Loggable, FakeListener(FakeUpstream& parent) : parent_(parent), name_("fake_upstream"), udp_listener_factory_(std::make_unique()), - udp_writer_factory_(std::make_unique()) {} + udp_writer_factory_(std::make_unique()), + init_manager_(nullptr) {} private: // Network::ListenerConfig @@ -688,6 +689,7 @@ class FakeUpstream : Logger::Loggable, } ResourceLimit& openConnections() override { return connection_resource_; } uint32_t tcpBacklogSize() const override { return ENVOY_TCP_BACKLOG_SIZE; } + Init::Manager& initManager() override { return *init_manager_; } void setMaxConnections(const uint32_t num_connections) { connection_resource_.setMax(num_connections); @@ -701,6 +703,7 @@ class FakeUpstream : Logger::Loggable, const Network::UdpPacketWriterFactoryPtr udp_writer_factory_; BasicResourceLimitImpl connection_resource_; const std::vector empty_access_logs_; + std::unique_ptr init_manager_; }; void threadRoutine(); diff --git a/test/mocks/init/mocks.h b/test/mocks/init/mocks.h index 252a9846aefd0..fc997e4030c49 100644 --- a/test/mocks/init/mocks.h +++ b/test/mocks/init/mocks.h @@ -5,6 +5,7 @@ #include "common/init/target_impl.h" #include "common/init/watcher_impl.h" +#include "absl/container/flat_hash_map.h" #include "gmock/gmock.h" namespace Envoy { @@ -72,6 +73,8 @@ struct MockManager : Manager { MOCK_METHOD(Manager::State, state, (), (const)); MOCK_METHOD(void, add, (const Target&)); MOCK_METHOD(void, initialize, (const Watcher&)); + MOCK_METHOD((const absl::flat_hash_map&), unreadyTargets, (), (const)); + MOCK_METHOD(void, dumpUnreadyTargets, (envoy::admin::v3::UnreadyTargetsDumps&)); }; } // namespace Init diff --git a/test/mocks/network/mocks.h b/test/mocks/network/mocks.h index 10bf76fb78726..e061c118e6dcb 100644 --- a/test/mocks/network/mocks.h +++ b/test/mocks/network/mocks.h @@ -359,6 +359,7 @@ class MockListenerConfig : public ListenerConfig { MOCK_METHOD(ConnectionBalancer&, connectionBalancer, ()); MOCK_METHOD(ResourceLimit&, openConnections, ()); MOCK_METHOD(uint32_t, tcpBacklogSize, (), (const)); + MOCK_METHOD(Init::Manager&, initManager, ()); envoy::config::core::v3::TrafficDirection direction() const override { return envoy::config::core::v3::UNSPECIFIED; diff --git a/test/mocks/server/listener_manager.h b/test/mocks/server/listener_manager.h index a91a9acb1764e..a08a60cf8f8e1 100644 --- a/test/mocks/server/listener_manager.h +++ b/test/mocks/server/listener_manager.h @@ -25,6 +25,7 @@ class MockListenerManager : public ListenerManager { MOCK_METHOD(void, beginListenerUpdate, ()); MOCK_METHOD(void, endListenerUpdate, (ListenerManager::FailureStates &&)); MOCK_METHOD(ApiListenerOptRef, apiListener, ()); + MOCK_METHOD(bool, isWorkerStarted, ()); }; } // namespace Server } // namespace Envoy diff --git a/test/server/admin/BUILD b/test/server/admin/BUILD index e2e036bbc833f..e395ccb176c89 100644 --- a/test/server/admin/BUILD +++ b/test/server/admin/BUILD @@ -128,6 +128,14 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "init_dump_handler_test", + srcs = ["init_dump_handler_test.cc"], + deps = [ + ":admin_instance_lib", + ], +) + envoy_cc_test( name = "config_tracker_impl_test", srcs = ["config_tracker_impl_test.cc"], diff --git a/test/server/admin/init_dump_handler_test.cc b/test/server/admin/init_dump_handler_test.cc new file mode 100644 index 0000000000000..608725c073f68 --- /dev/null +++ b/test/server/admin/init_dump_handler_test.cc @@ -0,0 +1,111 @@ +#include "test/server/admin/admin_instance.h" + +using testing::Return; +using testing::ReturnRef; + +namespace Envoy { +namespace Server { + +INSTANTIATE_TEST_SUITE_P(IpVersions, AdminInstanceTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); + +// Test Using /init_dump to dump information of all unready targets. +TEST_P(AdminInstanceTest, UnreadyTargetsDump) { + Buffer::OwnedImpl response; + Http::TestResponseHeaderMapImpl header_map; + + Network::MockListenerConfig listener_1; + Init::ManagerImpl init_manager_1{"test_init_manager_1"}; + Init::TargetImpl target_1("test_target_1", nullptr); + init_manager_1.add(target_1); + EXPECT_CALL(listener_1, initManager()).WillOnce(ReturnRef(init_manager_1)); + + Network::MockListenerConfig listener_2; + Init::ManagerImpl init_manager_2{"test_init_manager_2"}; + Init::TargetImpl target_2("test_target_2", nullptr); + init_manager_2.add(target_2); + EXPECT_CALL(listener_2, initManager()).WillOnce(ReturnRef(init_manager_2)); + + MockListenerManager listener_manager; + EXPECT_CALL(server_, listenerManager()).WillRepeatedly(ReturnRef(listener_manager)); + + std::vector> listeners; + listeners.push_back(listener_1); + listeners.push_back(listener_2); + EXPECT_CALL(listener_manager, isWorkerStarted()).WillRepeatedly(Return(true)); + EXPECT_CALL(listener_manager, listeners(_)).WillOnce(Return(listeners)); + + EXPECT_EQ(Http::Code::OK, getCallback("/init_dump", header_map, response)); + std::string output = response.toString(); + // The expected value should be updated when ProtobufTypes::MessagePtr + // AdminImpl::dumpListenerUnreadyTargets function includes more dump when mask has no value. + const std::string expected_json = R"EOF({ + "unready_targets_dumps": [ + { + "name": "init manager test_init_manager_1", + "target_names": [ + "target test_target_1" + ] + }, + { + "name": "init manager test_init_manager_2", + "target_names": [ + "target test_target_2" + ] + } + ] +} +)EOF"; + EXPECT_EQ(output, expected_json); +} + +// Test Using /init_dump?listener to dump unready targets of listeners. +TEST_P(AdminInstanceTest, ListenerUnreadyTargetsDump) { + Buffer::OwnedImpl response; + Http::TestResponseHeaderMapImpl header_map; + + Network::MockListenerConfig listener_1; + Init::ManagerImpl init_manager_1{"test_init_manager_1"}; + Init::TargetImpl target_1("test_target_1", nullptr); + init_manager_1.add(target_1); + EXPECT_CALL(listener_1, initManager()).WillOnce(ReturnRef(init_manager_1)); + + Network::MockListenerConfig listener_2; + Init::ManagerImpl init_manager_2{"test_init_manager_2"}; + Init::TargetImpl target_2("test_target_2", nullptr); + init_manager_2.add(target_2); + EXPECT_CALL(listener_2, initManager()).WillOnce(ReturnRef(init_manager_2)); + + MockListenerManager listener_manager; + EXPECT_CALL(server_, listenerManager()).WillRepeatedly(ReturnRef(listener_manager)); + + std::vector> listeners; + listeners.push_back(listener_1); + listeners.push_back(listener_2); + EXPECT_CALL(listener_manager, isWorkerStarted()).WillRepeatedly(Return(true)); + EXPECT_CALL(listener_manager, listeners(_)).WillOnce(Return(listeners)); + + EXPECT_EQ(Http::Code::OK, getCallback("/init_dump?mask=listener", header_map, response)); + std::string output = response.toString(); + const std::string expected_json = R"EOF({ + "unready_targets_dumps": [ + { + "name": "init manager test_init_manager_1", + "target_names": [ + "target test_target_1" + ] + }, + { + "name": "init manager test_init_manager_2", + "target_names": [ + "target test_target_2" + ] + } + ] +} +)EOF"; + EXPECT_EQ(output, expected_json); +} +} // namespace Server +} // namespace Envoy diff --git a/test/server/connection_handler_test.cc b/test/server/connection_handler_test.cc index b200d56d1be01..56357156bb8b3 100644 --- a/test/server/connection_handler_test.cc +++ b/test/server/connection_handler_test.cc @@ -68,7 +68,8 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable()), - access_logs_({access_log}), inline_filter_chain_manager_(filter_chain_manager) { + access_logs_({access_log}), inline_filter_chain_manager_(filter_chain_manager), + init_manager_(nullptr) { envoy::config::listener::v3::UdpListenerConfig dummy; std::string listener_name("raw_udp_listener"); dummy.set_udp_listener_name(listener_name); @@ -115,6 +116,7 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable access_logs_; std::shared_ptr> inline_filter_chain_manager_; + std::unique_ptr init_manager_; }; using TestListenerPtr = std::unique_ptr;