diff --git a/docs/operations/cli.rst b/docs/operations/cli.rst index fd88f987826b..24139f828ac2 100644 --- a/docs/operations/cli.rst +++ b/docs/operations/cli.rst @@ -10,6 +10,17 @@ following are the command line options that Envoy supports. *(required)* The path to the :ref:`JSON configuration file `. +.. option:: --mode + + *(optional)* One of the operating modes for Envoy: + + * ``serve``: *(default)* Validate the JSON configuration and then serve traffic normally. + + * ``validate``: Validate the JSON configuration and then exit, printing either an "OK" message (in + which case the exit code is 0) or any errors generated by the configuration file (exit code 1). + No network traffic is generated, and the hot restart process is not performed, so no other Envoy + process on the machine will be disturbed. + .. option:: --admin-address-path *(optional)* The output file path where the admin address and port will be written. diff --git a/include/envoy/server/options.h b/include/envoy/server/options.h index 3ab17abd76e8..9d4eac89a28c 100644 --- a/include/envoy/server/options.h +++ b/include/envoy/server/options.h @@ -11,6 +11,28 @@ namespace Envoy { namespace Server { +/** + * Whether to run Envoy in serving mode, or in config validation mode at one of two levels (in which + * case we'll verify the configuration file is valid, print any errors, and exit without serving.) + */ +enum class Mode { + /** + * Default mode: Regular Envoy serving process. Configs are validated in the normal course of + * initialization, but if all is well we proceed to serve traffic. + */ + Serve, + + /** + * Validate as much as possible without opening network connections upstream or downstream. + */ + Validate, + + // TODO(rlazarus): Add a third option for "light validation": Mock out access to the filesystem. + // Perform no validation of files referenced in the config, such as runtime configs, SSL certs, + // etc. Validation will pass even if those files are malformed or don't exist, allowing the config + // to be validated in a non-prod environment. +}; + /** * General options for the server. */ @@ -62,6 +84,12 @@ class Options { */ virtual uint64_t restartEpoch() PURE; + /** + * @return whether to verify the configuration file is valid, print any errors, and exit + * without serving. + */ + virtual Mode mode() const PURE; + /** * @return std::chrono::milliseconds the duration in msec between log flushes. */ diff --git a/include/envoy/upstream/BUILD b/include/envoy/upstream/BUILD index 77dd3fe7fef3..c63deb669e4f 100644 --- a/include/envoy/upstream/BUILD +++ b/include/envoy/upstream/BUILD @@ -15,9 +15,12 @@ envoy_cc_library( ":load_balancer_interface", ":thread_local_cluster_interface", ":upstream_interface", + "//include/envoy/access_log:access_log_interface", "//include/envoy/http:async_client_interface", "//include/envoy/http:conn_pool_interface", "//include/envoy/json:json_object_interface", + "//include/envoy/local_info:local_info_interface", + "//include/envoy/runtime:runtime_interface", ], ) diff --git a/include/envoy/upstream/cluster_manager.h b/include/envoy/upstream/cluster_manager.h index 18f2d90fa49f..f0dabaac6518 100644 --- a/include/envoy/upstream/cluster_manager.h +++ b/include/envoy/upstream/cluster_manager.h @@ -6,9 +6,12 @@ #include #include +#include "envoy/access_log/access_log.h" #include "envoy/http/async_client.h" #include "envoy/http/conn_pool.h" #include "envoy/json/json_object.h" +#include "envoy/local_info/local_info.h" +#include "envoy/runtime/runtime.h" #include "envoy/upstream/load_balancer.h" #include "envoy/upstream/thread_local_cluster.h" #include "envoy/upstream/upstream.h" @@ -103,6 +106,8 @@ class ClusterManager { virtual void shutdown() PURE; }; +typedef std::unique_ptr ClusterManagerPtr; + /** * Global configuration for any SDS clusters. */ @@ -139,6 +144,16 @@ class ClusterManagerFactory { public: virtual ~ClusterManagerFactory() {} + /** + * Allocate a cluster manager from configuration JSON. + */ + virtual ClusterManagerPtr clusterManagerFromJson(const Json::Object& config, Stats::Store& stats, + ThreadLocal::Instance& tls, + Runtime::Loader& runtime, + Runtime::RandomGenerator& random, + const LocalInfo::LocalInfo& local_info, + AccessLog::AccessLogManager& log_manager) PURE; + /** * Allocate an HTTP connection pool. */ diff --git a/source/common/common/assert.h b/source/common/common/assert.h index 7569dd194ff0..cede5823bd7f 100644 --- a/source/common/common/assert.h +++ b/source/common/common/assert.h @@ -10,7 +10,7 @@ namespace Envoy { #define RELEASE_ASSERT(X) \ { \ if (!(X)) { \ - Logger::Registry::getLog(Logger::Id::assert) \ + Envoy::Logger::Registry::getLog(Envoy::Logger::Id::assert) \ .critical("assert failure: {}: {}:{}", #X, __FILE__, __LINE__); \ abort(); \ } \ @@ -26,7 +26,7 @@ namespace Envoy { * Indicate a panic situation and exit. */ #define PANIC(X) \ - Logger::Registry::getLog(Logger::Id::assert) \ + Envoy::Logger::Registry::getLog(Envoy::Logger::Id::assert) \ .critical("panic: {}: {}:{}", X, __FILE__, __LINE__); \ abort(); diff --git a/source/common/http/async_client_impl.cc b/source/common/http/async_client_impl.cc index 1764dfdecb26..c38c93d524bc 100644 --- a/source/common/http/async_client_impl.cc +++ b/source/common/http/async_client_impl.cc @@ -40,6 +40,7 @@ AsyncClient::Request* AsyncClientImpl::send(MessagePtr&& request, AsyncClient::C const Optional& timeout) { AsyncRequestImpl* async_request = new AsyncRequestImpl(std::move(request), *this, callbacks, timeout); + async_request->initialize(); std::unique_ptr new_request{async_request}; // The request may get immediately failed. If so, we will return nullptr. @@ -155,7 +156,9 @@ AsyncRequestImpl::AsyncRequestImpl(MessagePtr&& request, AsyncClientImpl& parent AsyncClient::Callbacks& callbacks, const Optional& timeout) : AsyncStreamImpl(parent, *this, timeout), request_(std::move(request)), callbacks_(callbacks) { +} +void AsyncRequestImpl::initialize() { sendHeaders(request_->headers(), !request_->body()); if (!remoteClosed() && request_->body()) { sendData(*request_->body(), true); diff --git a/source/common/http/async_client_impl.h b/source/common/http/async_client_impl.h index 4547e7f53016..a4a84418948f 100644 --- a/source/common/http/async_client_impl.h +++ b/source/common/http/async_client_impl.h @@ -217,6 +217,7 @@ class AsyncRequestImpl final : public AsyncClient::Request, virtual void cancel() override; private: + void initialize(); void onComplete(); // AsyncClient::StreamCallbacks diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index b1dcadb603cd..e5aa52a9a16b 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -552,6 +552,14 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::connPool( return container.pools_[enumToInt(priority)].get(); } +ClusterManagerPtr ProdClusterManagerFactory::clusterManagerFromJson( + const Json::Object& config, Stats::Store& stats, ThreadLocal::Instance& tls, + Runtime::Loader& runtime, Runtime::RandomGenerator& random, + const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager) { + return ClusterManagerPtr{ + new ClusterManagerImpl(config, *this, stats, tls, runtime, random, local_info, log_manager)}; +} + Http::ConnectionPool::InstancePtr ProdClusterManagerFactory::allocateConnPool(Event::Dispatcher& dispatcher, HostConstSharedPtr host, ResourcePriority priority) { diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index 4f636e4ece06..f2f5fbd4d877 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -38,6 +38,11 @@ class ProdClusterManagerFactory : public ClusterManagerFactory { local_info_(local_info) {} // Upstream::ClusterManagerFactory + ClusterManagerPtr clusterManagerFromJson(const Json::Object& config, Stats::Store& stats, + ThreadLocal::Instance& tls, Runtime::Loader& runtime, + Runtime::RandomGenerator& random, + const LocalInfo::LocalInfo& local_info, + AccessLog::AccessLogManager& log_manager) override; Http::ConnectionPool::InstancePtr allocateConnPool(Event::Dispatcher& dispatcher, HostConstSharedPtr host, ResourcePriority priority) override; diff --git a/source/exe/BUILD b/source/exe/BUILD index c7184cd019fb..1a40fd609d6c 100644 --- a/source/exe/BUILD +++ b/source/exe/BUILD @@ -26,6 +26,7 @@ envoy_cc_library( "//source/common/event:libevent_lib", "//source/common/local_info:local_info_lib", "//source/common/network:utility_lib", + "//source/common/stats:stats_lib", "//source/common/stats:thread_local_store_lib", "//source/server:drain_manager_lib", "//source/server:options_lib", @@ -58,6 +59,7 @@ envoy_cc_library( "//source/common/common:compiler_requirements_lib", ":envoy_common_lib", ":hot_restart_lib", + "//source/server/config_validation:server_lib", ] + select({ "//bazel:disable_signal_trace": [], "//conditions:default": [":sigaction_lib"], diff --git a/source/exe/main.cc b/source/exe/main.cc index 4c37a55bfa49..92bb941472c5 100644 --- a/source/exe/main.cc +++ b/source/exe/main.cc @@ -5,6 +5,7 @@ #include "common/event/libevent.h" #include "common/local_info/local_info_impl.h" #include "common/network/utility.h" +#include "common/stats/stats_impl.h" #include "common/stats/thread_local_store.h" #include "exe/hot_restart.h" @@ -13,6 +14,7 @@ #include "exe/signal_action.h" #endif +#include "server/config_validation/server.h" #include "server/drain_manager_impl.h" #include "server/options_impl.h" #include "server/server.h" @@ -45,10 +47,24 @@ int main(int argc, char** argv) { // Enabled by default. Control with "bazel --define=signal_trace=disabled" Envoy::SignalAction handle_sigs; #endif - ares_library_init(ARES_LIB_INIT_ALL); Envoy::Event::Libevent::Global::initialize(); Envoy::OptionsImpl options(argc, argv, Envoy::Server::SharedMemory::version(), spdlog::level::warn); + Envoy::Server::ProdComponentFactory component_factory; + Envoy::LocalInfo::LocalInfoImpl local_info( + Envoy::Network::Utility::getLocalAddress(Envoy::Network::Address::IpVersion::v4), + options.serviceZone(), options.serviceClusterName(), options.serviceNodeName()); + + switch (options.mode()) { + case Envoy::Server::Mode::Serve: + break; + case Envoy::Server::Mode::Validate: + Envoy::Thread::MutexBasicLockable log_lock; + Envoy::Logger::Registry::initialize(options.logLevel(), log_lock); + return Envoy::Server::validateConfig(options, component_factory, local_info) ? 0 : 1; + } + + ares_library_init(ARES_LIB_INIT_ALL); std::unique_ptr restarter; try { @@ -61,11 +77,7 @@ int main(int argc, char** argv) { Envoy::Logger::Registry::initialize(options.logLevel(), restarter->logLock()); Envoy::DefaultTestHooks default_test_hooks; Envoy::Stats::ThreadLocalStoreImpl stats_store(*restarter); - Envoy::Server::ProdComponentFactory component_factory; // TODO(henna): Add CLI option for local address IP version. - Envoy::LocalInfo::LocalInfoImpl local_info( - Envoy::Network::Utility::getLocalAddress(Envoy::Network::Address::IpVersion::v4), - options.serviceZone(), options.serviceClusterName(), options.serviceNodeName()); Envoy::Server::InstanceImpl server(options, default_test_hooks, *restarter, stats_store, restarter->accessLogLock(), component_factory, local_info); server.run(); diff --git a/source/server/BUILD b/source/server/BUILD index ee2d3b634349..e4847912f39d 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -136,13 +136,13 @@ envoy_cc_library( "//source/common/api:api_lib", "//source/common/common:utility_lib", "//source/common/common:version_lib", - "//source/common/json:config_schemas_lib", "//source/common/memory:stats_lib", "//source/common/network:utility_lib", "//source/common/runtime:runtime_lib", "//source/common/ssl:context_lib", "//source/common/stats:statsd_lib", "//source/common/thread_local:thread_local_lib", + "//source/common/upstream:cluster_manager_lib", "//source/server/http:admin_lib", ], ) diff --git a/source/server/config_validation/BUILD b/source/server/config_validation/BUILD new file mode 100644 index 000000000000..9068fd16563a --- /dev/null +++ b/source/server/config_validation/BUILD @@ -0,0 +1,98 @@ +licenses(["notice"]) # Apache 2 + +load("//bazel:envoy_build_system.bzl", "envoy_cc_library", "envoy_package") + +envoy_package() + +envoy_cc_library( + name = "api_lib", + srcs = ["api.cc"], + hdrs = ["api.h"], + deps = [ + ":dispatcher_lib", + "//include/envoy/api:api_interface", + "//include/envoy/filesystem:filesystem_interface", + "//source/common/api:api_lib", + ], +) + +envoy_cc_library( + name = "async_client_lib", + srcs = ["async_client.cc"], + hdrs = ["async_client.h"], + deps = [ + "//include/envoy/http:async_client_interface", + "//include/envoy/http:message_interface", + ], +) + +envoy_cc_library( + name = "cluster_manager_lib", + srcs = ["cluster_manager.cc"], + hdrs = ["cluster_manager.h"], + deps = [ + ":async_client_lib", + "//include/envoy/upstream:cluster_manager_interface", + "//source/common/upstream:cluster_manager_lib", + ], +) + +envoy_cc_library( + name = "connection_handler_lib", + srcs = ["connection_handler.cc"], + hdrs = ["connection_handler.h"], + deps = [ + "//include/envoy/api:api_interface", + "//include/envoy/network:connection_handler_interface", + "//include/envoy/network:filter_interface", + "//include/envoy/network:listen_socket_interface", + "//source/common/common:assert_lib", + ], +) + +envoy_cc_library( + name = "dispatcher_lib", + srcs = ["dispatcher.cc"], + hdrs = ["dispatcher.h"], + deps = [ + "//include/envoy/event:dispatcher_interface", + "//source/common/common:assert_lib", + "//source/common/event:dispatcher_lib", + ], +) + +envoy_cc_library( + name = "dns_lib", + srcs = ["dns.cc"], + hdrs = ["dns.h"], + deps = [ + "//include/envoy/event:dispatcher_interface", + "//include/envoy/network:dns_interface", + ], +) + +envoy_cc_library( + name = "server_lib", + srcs = ["server.cc"], + hdrs = ["server.h"], + deps = [ + ":api_lib", + ":cluster_manager_lib", + ":connection_handler_lib", + ":dns_lib", + "//include/envoy/common:optional", + "//include/envoy/server:drain_manager_interface", + "//include/envoy/server:instance_interface", + "//include/envoy/ssl:context_manager_interface", + "//include/envoy/tracing:http_tracer_interface", + "//source/common/access_log:access_log_manager_lib", + "//source/common/common:assert_lib", + "//source/common/runtime:runtime_lib", + "//source/common/ssl:context_lib", + "//source/common/stats:stats_lib", + "//source/common/thread_local:thread_local_lib", + "//source/server:configuration_lib", + "//source/server:server_lib", + "//source/server/http:admin_lib", + ], +) diff --git a/source/server/config_validation/api.cc b/source/server/config_validation/api.cc new file mode 100644 index 000000000000..b85969408031 --- /dev/null +++ b/source/server/config_validation/api.cc @@ -0,0 +1,16 @@ +#include "server/config_validation/api.h" + +#include "server/config_validation/dispatcher.h" + +namespace Envoy { +namespace Api { + +ValidationImpl::ValidationImpl(std::chrono::milliseconds file_flush_interval_msec) + : Impl(file_flush_interval_msec) {} + +Event::DispatcherPtr ValidationImpl::allocateDispatcher() { + return Event::DispatcherPtr{new Event::ValidationDispatcher()}; +} + +} // Api +} // Envoy diff --git a/source/server/config_validation/api.h b/source/server/config_validation/api.h new file mode 100644 index 000000000000..969541a2a0f2 --- /dev/null +++ b/source/server/config_validation/api.h @@ -0,0 +1,23 @@ +#pragma once + +#include "envoy/api/api.h" +#include "envoy/filesystem/filesystem.h" + +#include "common/api/api_impl.h" + +namespace Envoy { +namespace Api { + +/** + * Config-validation-only implementation of Api::Api. Delegates to Api::Impl, + * except for allocateDispatcher() which sets up a ValidationDispatcher. + */ +class ValidationImpl : public Impl { +public: + ValidationImpl(std::chrono::milliseconds file_flush_interval_msec); + + Event::DispatcherPtr allocateDispatcher() override; +}; + +} // Api +} // Envoy diff --git a/source/server/config_validation/async_client.cc b/source/server/config_validation/async_client.cc new file mode 100644 index 000000000000..71f4f6433084 --- /dev/null +++ b/source/server/config_validation/async_client.cc @@ -0,0 +1,17 @@ +#include "server/config_validation/async_client.h" + +namespace Envoy { +namespace Http { + +AsyncClient::Request* ValidationAsyncClient::send(MessagePtr&&, Callbacks&, + const Optional&) { + return nullptr; +} + +AsyncClient::Stream* ValidationAsyncClient::start(StreamCallbacks&, + const Optional&) { + return nullptr; +} + +} // Http +} // Envoy diff --git a/source/server/config_validation/async_client.h b/source/server/config_validation/async_client.h new file mode 100644 index 000000000000..4f1267eb4ad9 --- /dev/null +++ b/source/server/config_validation/async_client.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include "envoy/http/async_client.h" +#include "envoy/http/message.h" + +namespace Envoy { +namespace Http { + +/** + * Config-validation-only implementation of AsyncClient. Both methods on AsyncClient are allowed to + * return nullptr if the request can't be created, so that's what the ValidationAsyncClient does in + * all cases. + */ +class ValidationAsyncClient : public AsyncClient { +public: + // Http::AsyncClient + AsyncClient::Request* send(MessagePtr&&, Callbacks&, + const Optional&) override; + AsyncClient::Stream* start(StreamCallbacks&, const Optional&) override; +}; + +} // Http +} // Envoy diff --git a/source/server/config_validation/cluster_manager.cc b/source/server/config_validation/cluster_manager.cc new file mode 100644 index 000000000000..9fd60b011cb8 --- /dev/null +++ b/source/server/config_validation/cluster_manager.cc @@ -0,0 +1,51 @@ +#include "server/config_validation/cluster_manager.h" + +namespace Envoy { +namespace Upstream { + +ValidationClusterManagerFactory::ValidationClusterManagerFactory( + Runtime::Loader& runtime, Stats::Store& stats, ThreadLocal::Instance& tls, + Runtime::RandomGenerator& random, Network::DnsResolver& dns_resolver, + Ssl::ContextManager& ssl_context_manager, Event::Dispatcher& primary_dispatcher, + const LocalInfo::LocalInfo& local_info) + : ProdClusterManagerFactory(runtime, stats, tls, random, dns_resolver, ssl_context_manager, + primary_dispatcher, local_info) {} + +ClusterManagerPtr ValidationClusterManagerFactory::clusterManagerFromJson( + const Json::Object& config, Stats::Store& stats, ThreadLocal::Instance& tls, + Runtime::Loader& runtime, Runtime::RandomGenerator& random, + const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager) { + return ClusterManagerPtr{new ValidationClusterManager(config, *this, stats, tls, runtime, random, + local_info, log_manager)}; +} + +CdsApiPtr ValidationClusterManagerFactory::createCds(const Json::Object& config, + ClusterManager& cm) { + // Create the CdsApiImpl... + ProdClusterManagerFactory::createCds(config, cm); + // ... and then throw it away, so that we don't actually connect to it. + return nullptr; +} + +ValidationClusterManager::ValidationClusterManager( + const Json::Object& config, ClusterManagerFactory& factory, Stats::Store& stats, + ThreadLocal::Instance& tls, Runtime::Loader& runtime, Runtime::RandomGenerator& random, + const LocalInfo::LocalInfo& local_info, AccessLog::AccessLogManager& log_manager) + : ClusterManagerImpl(config, factory, stats, tls, runtime, random, local_info, log_manager) {} + +Http::ConnectionPool::Instance* +ValidationClusterManager::httpConnPoolForCluster(const std::string&, ResourcePriority, + LoadBalancerContext*) { + return nullptr; +} + +Host::CreateConnectionData ValidationClusterManager::tcpConnForCluster(const std::string&) { + return Host::CreateConnectionData{nullptr, nullptr}; +} + +Http::AsyncClient& ValidationClusterManager::httpAsyncClientForCluster(const std::string&) { + return async_client_; +} + +} // Upstream +} // Envoy diff --git a/source/server/config_validation/cluster_manager.h b/source/server/config_validation/cluster_manager.h new file mode 100644 index 000000000000..dbf7a4dfd285 --- /dev/null +++ b/source/server/config_validation/cluster_manager.h @@ -0,0 +1,60 @@ +#pragma once + +#include "envoy/upstream/cluster_manager.h" + +#include "common/upstream/cluster_manager_impl.h" + +#include "server/config_validation/async_client.h" + +namespace Envoy { +namespace Upstream { + +/** + * Config-validation-only implementation of ClusterManagerFactory, which creates + * ValidationClusterManagers. It also creates, but never returns, CdsApiImpls. + */ +class ValidationClusterManagerFactory : public ProdClusterManagerFactory { +public: + ValidationClusterManagerFactory(Runtime::Loader& runtime, Stats::Store& stats, + ThreadLocal::Instance& tls, Runtime::RandomGenerator& random, + Network::DnsResolver& dns_resolver, + Ssl::ContextManager& ssl_context_manager, + Event::Dispatcher& primary_dispatcher, + const LocalInfo::LocalInfo& local_info); + + ClusterManagerPtr clusterManagerFromJson(const Json::Object& config, Stats::Store& stats, + ThreadLocal::Instance& tls, Runtime::Loader& runtime, + Runtime::RandomGenerator& random, + const LocalInfo::LocalInfo& local_info, + AccessLog::AccessLogManager& log_manager) override; + + // Delegates to ProdClusterManagerFactory::createCds, but discards the result and returns nullptr + // unconditionally. + CdsApiPtr createCds(const Json::Object& config, ClusterManager& cm) override; +}; + +/** + * Config-validation-only implementation of ClusterManager, which opens no upstream connections. + */ +class ValidationClusterManager : public ClusterManagerImpl { +public: + ValidationClusterManager(const Json::Object& config, ClusterManagerFactory& factory, + Stats::Store& stats, ThreadLocal::Instance& tls, + Runtime::Loader& runtime, Runtime::RandomGenerator& random, + const LocalInfo::LocalInfo& local_info, + AccessLog::AccessLogManager& log_manager); + + Http::ConnectionPool::Instance* httpConnPoolForCluster(const std::string&, ResourcePriority, + LoadBalancerContext*) override; + Host::CreateConnectionData tcpConnForCluster(const std::string&) override; + Http::AsyncClient& httpAsyncClientForCluster(const std::string&) override; + +private: + // ValidationAsyncClient always returns null on send() and start(), so it has + // no internal state -- we might as well just keep one and hand out references + // to it. + Http::ValidationAsyncClient async_client_; +}; + +} // Upstream +} // Envoy diff --git a/source/server/config_validation/connection_handler.cc b/source/server/config_validation/connection_handler.cc new file mode 100644 index 000000000000..97a124e20757 --- /dev/null +++ b/source/server/config_validation/connection_handler.cc @@ -0,0 +1,27 @@ +#include "server/config_validation/connection_handler.h" + +#include "common/common/assert.h" + +namespace Envoy { +namespace Server { + +ValidationConnectionHandler::ValidationConnectionHandler(Api::ApiPtr&& api) + : api_(std::move(api)), dispatcher_(api_->allocateDispatcher()) {} + +ValidationConnectionHandler::~ValidationConnectionHandler() { + dispatcher_->clearDeferredDeleteList(); +} + +void ValidationConnectionHandler::addListener(Network::FilterChainFactory&, Network::ListenSocket&, + Stats::Scope&, const Network::ListenerOptions&) { + NOT_IMPLEMENTED; +} + +void ValidationConnectionHandler::addSslListener(Network::FilterChainFactory&, Ssl::ServerContext&, + Network::ListenSocket&, Stats::Scope&, + const Network::ListenerOptions&) { + NOT_IMPLEMENTED; +} + +} // Server +} // Envoy diff --git a/source/server/config_validation/connection_handler.h b/source/server/config_validation/connection_handler.h new file mode 100644 index 000000000000..4e88c1305323 --- /dev/null +++ b/source/server/config_validation/connection_handler.h @@ -0,0 +1,40 @@ +#pragma once + +#include "envoy/api/api.h" +#include "envoy/network/connection_handler.h" +#include "envoy/network/filter.h" +#include "envoy/network/listen_socket.h" + +namespace Envoy { +namespace Server { + +/** + * Config-validation implementation of ConnectionHandler. Calls to add*Listener() will fail, since + * no listeners can be added at validation time. + */ +class ValidationConnectionHandler : public Network::ConnectionHandler { +public: + ValidationConnectionHandler(Api::ApiPtr&& api); + ~ValidationConnectionHandler(); + + Api::Api& api() { return *api_; } + Event::Dispatcher& dispatcher() { return *dispatcher_; } + + // Network::ConnectionHandler + uint64_t numConnections() override { return 0; } + void addListener(Network::FilterChainFactory&, Network::ListenSocket&, Stats::Scope&, + const Network::ListenerOptions&) override; + void addSslListener(Network::FilterChainFactory&, Ssl::ServerContext&, Network::ListenSocket&, + Stats::Scope&, const Network::ListenerOptions&) override; + Network::Listener* findListenerByAddress(const Network::Address::Instance&) override { + return nullptr; + } + void closeListeners() override{}; + +private: + Api::ApiPtr api_; + Event::DispatcherPtr dispatcher_; +}; + +} // Server +} // Envoy diff --git a/source/server/config_validation/dispatcher.cc b/source/server/config_validation/dispatcher.cc new file mode 100644 index 000000000000..dda8d9291366 --- /dev/null +++ b/source/server/config_validation/dispatcher.cc @@ -0,0 +1,37 @@ +#include "server/config_validation/dispatcher.h" + +#include "common/common/assert.h" + +namespace Envoy { +namespace Event { + +Network::ClientConnectionPtr + ValidationDispatcher::createClientConnection(Network::Address::InstanceConstSharedPtr) { + NOT_IMPLEMENTED; +} + +Network::ClientConnectionPtr +ValidationDispatcher::createSslClientConnection(Ssl::ClientContext&, + Network::Address::InstanceConstSharedPtr) { + NOT_IMPLEMENTED; +} + +Network::DnsResolverPtr ValidationDispatcher::createDnsResolver() { NOT_IMPLEMENTED; } + +Network::ListenerPtr ValidationDispatcher::createListener(Network::ConnectionHandler&, + Network::ListenSocket&, + Network::ListenerCallbacks&, + Stats::Scope&, + const Network::ListenerOptions&) { + NOT_IMPLEMENTED; +} + +Network::ListenerPtr +ValidationDispatcher::createSslListener(Network::ConnectionHandler&, Ssl::ServerContext&, + Network::ListenSocket&, Network::ListenerCallbacks&, + Stats::Scope&, const Network::ListenerOptions&) { + NOT_IMPLEMENTED; +} + +} // Event +} // Envoy diff --git a/source/server/config_validation/dispatcher.h b/source/server/config_validation/dispatcher.h new file mode 100644 index 000000000000..5649a9ffa722 --- /dev/null +++ b/source/server/config_validation/dispatcher.h @@ -0,0 +1,31 @@ +#pragma once + +#include "envoy/event/dispatcher.h" + +#include "common/event/dispatcher_impl.h" + +namespace Envoy { +namespace Event { + +/** + * Config-validation-only implementation of Event::Dispatcher. This class delegates all calls to + * Event::DispatcherImpl, except for the methods involved with network events. Those methods are + * disallowed at validation time. + */ +class ValidationDispatcher : public DispatcherImpl { +public: + Network::ClientConnectionPtr + createClientConnection(Network::Address::InstanceConstSharedPtr) override; + Network::ClientConnectionPtr + createSslClientConnection(Ssl::ClientContext&, Network::Address::InstanceConstSharedPtr) override; + Network::DnsResolverPtr createDnsResolver() override; + Network::ListenerPtr createListener(Network::ConnectionHandler&, Network::ListenSocket&, + Network::ListenerCallbacks&, Stats::Scope&, + const Network::ListenerOptions&) override; + Network::ListenerPtr createSslListener(Network::ConnectionHandler&, Ssl::ServerContext&, + Network::ListenSocket&, Network::ListenerCallbacks&, + Stats::Scope&, const Network::ListenerOptions&) override; +}; + +} // Event +} // Envoy diff --git a/source/server/config_validation/dns.cc b/source/server/config_validation/dns.cc new file mode 100644 index 000000000000..222f9c19d6bd --- /dev/null +++ b/source/server/config_validation/dns.cc @@ -0,0 +1,13 @@ +#include "server/config_validation/dns.h" + +namespace Envoy { +namespace Network { + +ActiveDnsQuery* ValidationDnsResolver::resolve(const std::string&, DnsLookupFamily, + ResolveCb callback) { + callback({}); + return nullptr; +} + +} // Network +} // Envoy diff --git a/source/server/config_validation/dns.h b/source/server/config_validation/dns.h new file mode 100644 index 000000000000..c15bb3a725d5 --- /dev/null +++ b/source/server/config_validation/dns.h @@ -0,0 +1,23 @@ +#pragma once + +#include "envoy/event/dispatcher.h" +#include "envoy/network/dns.h" + +namespace Envoy { +namespace Network { + +/** + * DnsResolver to be used in config validation runs. Every DNS query immediately fails to resolve, + * since we never need DNS information to validate a config. (If a config contains an unresolveable + * name, it still passes validation -- for example, we might be running validation in a test + * environment, while the name resolves fine in prod.) + */ +class ValidationDnsResolver : public DnsResolver { +public: + // Network::DnsResolver + ActiveDnsQuery* resolve(const std::string& dns_name, DnsLookupFamily dns_lookup_family, + ResolveCb callback) override; +}; + +} // Network +} // Envoy diff --git a/source/server/config_validation/server.cc b/source/server/config_validation/server.cc new file mode 100644 index 000000000000..1d6154cd66a5 --- /dev/null +++ b/source/server/config_validation/server.cc @@ -0,0 +1,77 @@ +#include "server/config_validation/server.h" + +#include "server/configuration_impl.h" + +namespace Envoy { +namespace Server { + +bool validateConfig(Options& options, ComponentFactory& component_factory, + const LocalInfo::LocalInfo& local_info) { + Thread::MutexBasicLockable access_log_lock; + Stats::IsolatedStoreImpl stats_store; + + try { + ValidationInstance server(options, stats_store, access_log_lock, component_factory, local_info); + std::cout << "configuration '" << options.configPath() << "' OK" << std::endl; + server.shutdown(); + return true; + } catch (const EnvoyException& e) { + return false; + } +} + +ValidationInstance::ValidationInstance(Options& options, Stats::IsolatedStoreImpl& store, + Thread::BasicLockable& access_log_lock, + ComponentFactory& component_factory, + const LocalInfo::LocalInfo& local_info) + : options_(options), stats_store_(store), + handler_(Api::ApiPtr{new Api::ValidationImpl(options.fileFlushIntervalMsec())}), + local_info_(local_info), + access_log_manager_(handler_.api(), handler_.dispatcher(), access_log_lock, store) { + try { + initialize(options, component_factory); + } catch (const EnvoyException& e) { + log().critical("error initializing configuration '{}': {}", options.configPath(), e.what()); + thread_local_.shutdownThread(); + throw; + } +} + +void ValidationInstance::initialize(Options& options, ComponentFactory& component_factory) { + // See comments on InstanceImpl::initialize() for the overall flow here. + // + // For validation, we only do a subset of normal server initialization: everything that could fail + // on a malformed config (e.g. JSON parsing and all the object construction that follows), but + // more importantly nothing with observable effects (e.g. binding to ports or shutting down any + // other Envoy process). + // + // If we get all the way through that stripped-down initialization flow, to the point where we'd + // be ready to serve, then the config has passed validation. + Json::ObjectSharedPtr config_json = Json::Factory::loadFromFile(options.configPath()); + Configuration::InitialImpl initial_config(*config_json); + thread_local_.registerThread(handler_.dispatcher(), true); + runtime_loader_ = component_factory.createRuntime(*this, initial_config); + ssl_context_manager_.reset(new Ssl::ContextManagerImpl(*runtime_loader_)); + cluster_manager_factory_.reset(new Upstream::ValidationClusterManagerFactory( + runtime(), stats(), threadLocal(), random(), dnsResolver(), sslContextManager(), dispatcher(), + localInfo())); + + Configuration::MainImpl* main_config = + new Configuration::MainImpl(*this, *cluster_manager_factory_); + config_.reset(main_config); + main_config->initialize(*config_json); + + clusterManager().setInitializedCb([this]() + -> void { init_manager_.initialize([]() -> void {}); }); +} + +void ValidationInstance::shutdown() { + // This normally happens at the bottom of InstanceImpl::run(), but we don't have a run(). We can + // do an abbreviated shutdown here since there's less to clean up -- for example, no workers to + // exit. + config_->clusterManager().shutdown(); + thread_local_.shutdownThread(); +} + +} // Server +} // Envoy diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h new file mode 100644 index 000000000000..149943f89f72 --- /dev/null +++ b/source/server/config_validation/server.h @@ -0,0 +1,107 @@ +#pragma once + +#include + +#include "envoy/common/optional.h" +#include "envoy/server/drain_manager.h" +#include "envoy/server/instance.h" +#include "envoy/ssl/context_manager.h" +#include "envoy/tracing/http_tracer.h" + +#include "common/access_log/access_log_manager_impl.h" +#include "common/common/assert.h" +#include "common/runtime/runtime_impl.h" +#include "common/ssl/context_manager_impl.h" +#include "common/stats/stats_impl.h" +#include "common/thread_local/thread_local_impl.h" + +#include "server/config_validation/api.h" +#include "server/config_validation/cluster_manager.h" +#include "server/config_validation/connection_handler.h" +#include "server/config_validation/dns.h" +#include "server/http/admin.h" +#include "server/server.h" +#include "server/worker.h" + +namespace Envoy { +namespace Server { + +/** + * validateConfig() takes over from main() for a config-validation run of Envoy. It returns true if + * the config is valid, false if invalid. + */ +bool validateConfig(Options& options, ComponentFactory& component_factory, + const LocalInfo::LocalInfo& local_info); + +/** + * ValidationInstance does the bulk of the work for config-validation runs of Envoy. It implements + * Server::Instance, but some functionality not needed until serving time, such as updating + * health-check status, is not implemented. Everything else is written in terms of other + * validation-specific interface implementations, with the end result that we can load and + * initialize a configuration, skipping any steps that affect the outside world (such as + * hot-restarting or connecting to upstream clusters) but otherwise exercising the entire startup + * flow. + * + * If we finish initialization, and reach the point where an ordinary Envoy run would begin serving + * requests, the validation is considered successful. + */ +class ValidationInstance : Logger::Loggable, public Instance { +public: + ValidationInstance(Options& options, Stats::IsolatedStoreImpl& store, + Thread::BasicLockable& access_log_lock, ComponentFactory& component_factory, + const LocalInfo::LocalInfo& local_info); + + // Server::Instance + Admin& admin() override { NOT_IMPLEMENTED; } + Api::Api& api() override { return handler_.api(); } + Upstream::ClusterManager& clusterManager() override { return config_->clusterManager(); } + Ssl::ContextManager& sslContextManager() override { return *ssl_context_manager_; } + Event::Dispatcher& dispatcher() override { return handler_.dispatcher(); } + Network::DnsResolver& dnsResolver() override { return dns_resolver_; } + bool draining() override { NOT_IMPLEMENTED; } + void drainListeners() override { NOT_IMPLEMENTED; } + DrainManager& drainManager() override { NOT_IMPLEMENTED; } + AccessLog::AccessLogManager& accessLogManager() override { return access_log_manager_; } + void failHealthcheck(bool) override { NOT_IMPLEMENTED; } + int getListenSocketFd(const std::string&) override { NOT_IMPLEMENTED; } + Network::ListenSocket* getListenSocketByIndex(uint32_t) override { NOT_IMPLEMENTED; } + void getParentStats(HotRestart::GetParentStatsInfo&) override { NOT_IMPLEMENTED; } + HotRestart& hotRestart() override { NOT_IMPLEMENTED; } + Init::Manager& initManager() override { return init_manager_; } + Runtime::RandomGenerator& random() override { return random_generator_; } + RateLimit::ClientPtr + rateLimitClient(const Optional& timeout) override { + return config_->rateLimitClientFactory().create(timeout); + } + Runtime::Loader& runtime() override { return *runtime_loader_; } + void shutdown() override; + void shutdownAdmin() override { NOT_IMPLEMENTED; } + bool healthCheckFailed() override { NOT_IMPLEMENTED; } + Options& options() override { return options_; } + time_t startTimeCurrentEpoch() override { NOT_IMPLEMENTED; } + time_t startTimeFirstEpoch() override { NOT_IMPLEMENTED; } + Stats::Store& stats() override { return stats_store_; } + Tracing::HttpTracer& httpTracer() override { return config_->httpTracer(); } + ThreadLocal::Instance& threadLocal() override { return thread_local_; } + const LocalInfo::LocalInfo& localInfo() override { return local_info_; } + +private: + void initialize(Options& options, ComponentFactory& component_factory); + + Options& options_; + Stats::IsolatedStoreImpl& stats_store_; + ThreadLocal::InstanceImpl thread_local_; + ValidationConnectionHandler handler_; + Runtime::LoaderPtr runtime_loader_; + Runtime::RandomGeneratorImpl random_generator_; + std::unique_ptr ssl_context_manager_; + std::unique_ptr config_; + Network::ValidationDnsResolver dns_resolver_; + const LocalInfo::LocalInfo& local_info_; + AccessLog::AccessLogManagerImpl access_log_manager_; + std::unique_ptr cluster_manager_factory_; + InitManagerImpl init_manager_; +}; + +} // Server +} // Envoy diff --git a/source/server/configuration_impl.cc b/source/server/configuration_impl.cc index ecce1aa5424f..9999a4657533 100644 --- a/source/server/configuration_impl.cc +++ b/source/server/configuration_impl.cc @@ -36,17 +36,14 @@ bool FilterChainUtility::buildFilterChain(Network::FilterManager& filter_manager return filter_manager.initializeReadFilters(); } -MainImpl::MainImpl(Server::Instance& server) : server_(server) {} +MainImpl::MainImpl(Server::Instance& server, + Upstream::ClusterManagerFactory& cluster_manager_factory) + : server_(server), cluster_manager_factory_(cluster_manager_factory) {} void MainImpl::initialize(const Json::Object& json) { - cluster_manager_factory_.reset(new Upstream::ProdClusterManagerFactory( - server_.runtime(), server_.stats(), server_.threadLocal(), server_.random(), - server_.dnsResolver(), server_.sslContextManager(), server_.dispatcher(), - server_.localInfo())); - cluster_manager_.reset(new Upstream::ClusterManagerImpl( - *json.getObject("cluster_manager"), *cluster_manager_factory_, server_.stats(), - server_.threadLocal(), server_.runtime(), server_.random(), server_.localInfo(), - server_.accessLogManager())); + cluster_manager_ = cluster_manager_factory_.clusterManagerFromJson( + *json.getObject("cluster_manager"), server_.stats(), server_.threadLocal(), server_.runtime(), + server_.random(), server_.localInfo(), server_.accessLogManager()); std::vector listeners = json.getObjectArray("listeners"); log().info("loading {} listener(s)", listeners.size()); @@ -207,6 +204,7 @@ bool MainImpl::ListenerConfig::createFilterChain(Network::Connection& connection } InitialImpl::InitialImpl(const Json::Object& json) { + json.validateSchema(Json::Schema::TOP_LEVEL_CONFIG_SCHEMA); Json::ObjectSharedPtr admin = json.getObject("admin"); admin_.access_log_path_ = admin->getString("access_log_path"); admin_.profile_path_ = admin->getString("profile_path", "/var/log/envoy/envoy.prof"); diff --git a/source/server/configuration_impl.h b/source/server/configuration_impl.h index 9ae0bf38444c..b2b6bc61d35d 100644 --- a/source/server/configuration_impl.h +++ b/source/server/configuration_impl.h @@ -120,7 +120,7 @@ class FilterChainUtility { */ class MainImpl : Logger::Loggable, public Main { public: - MainImpl(Server::Instance& server); + MainImpl(Server::Instance& server, Upstream::ClusterManagerFactory& cluster_manager_factory_); /** * DEPRECATED - Register an NetworkFilterConfigFactory implementation as an option to create @@ -257,7 +257,7 @@ class MainImpl : Logger::Loggable, public Main { } Server::Instance& server_; - std::unique_ptr cluster_manager_factory_; + Upstream::ClusterManagerFactory& cluster_manager_factory_; std::unique_ptr cluster_manager_; Tracing::HttpTracerPtr http_tracer_; std::list listeners_; diff --git a/source/server/options_impl.cc b/source/server/options_impl.cc index daceefb2f0ba..49d0ffb92350 100644 --- a/source/server/options_impl.cc +++ b/source/server/options_impl.cc @@ -53,6 +53,10 @@ OptionsImpl::OptionsImpl(int argc, char** argv, const std::string& hot_restart_v TCLAP::ValueArg parent_shutdown_time_s("", "parent-shutdown-time-s", "Hot restart parent shutdown time in seconds", false, 900, "uint64_t", cmd); + TCLAP::ValueArg mode("", "mode", + "One of 'serve' (default; validate configs and then serve " + "traffic normally) or 'validate' (validate configs and exit).", + false, "serve", "string", cmd); try { cmd.parse(argc, argv); @@ -73,6 +77,15 @@ OptionsImpl::OptionsImpl(int argc, char** argv, const std::string& hot_restart_v } } + if (mode.getValue() == "serve") { + mode_ = Server::Mode::Serve; + } else if (mode.getValue() == "validate") { + mode_ = Server::Mode::Validate; + } else { + std::cerr << "error: unknown mode '" << mode.getValue() << "'" << std::endl; + exit(1); + } + // For base ID, scale what the user inputs by 10 so that we have spread for domain sockets. base_id_ = base_id.getValue() * 10; concurrency_ = concurrency.getValue(); diff --git a/source/server/options_impl.h b/source/server/options_impl.h index 7a0762dae627..a5b736f4144e 100644 --- a/source/server/options_impl.h +++ b/source/server/options_impl.h @@ -30,6 +30,7 @@ class OptionsImpl : public Server::Options { spdlog::level::level_enum logLevel() override { return log_level_; } std::chrono::seconds parentShutdownTime() override { return parent_shutdown_time_; } uint64_t restartEpoch() override { return restart_epoch_; } + Server::Mode mode() const override { return mode_; } std::chrono::milliseconds fileFlushIntervalMsec() override { return file_flush_interval_msec_; } private: @@ -45,5 +46,6 @@ class OptionsImpl : public Server::Options { std::chrono::milliseconds file_flush_interval_msec_; std::chrono::seconds drain_time_; std::chrono::seconds parent_shutdown_time_; + Server::Mode mode_; }; } // Envoy diff --git a/source/server/server.cc b/source/server/server.cc index c6ec2352b060..ad30bba74fc3 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -17,7 +17,6 @@ #include "common/api/api_impl.h" #include "common/common/utility.h" #include "common/common/version.h" -#include "common/json/config_schemas.h" #include "common/memory/stats.h" #include "common/network/utility.h" #include "common/runtime/runtime_impl.h" @@ -177,7 +176,6 @@ void InstanceImpl::initialize(Options& options, TestHooks& hooks, // Handle configuration that needs to take place prior to the main configuration load. Json::ObjectSharedPtr config_json = Json::Factory::loadFromFile(options.configPath()); - config_json->validateSchema(Json::Schema::TOP_LEVEL_CONFIG_SCHEMA); Configuration::InitialImpl initial_config(*config_json); log().info("admin address: {}", initial_config.admin().address()->asString()); @@ -214,9 +212,14 @@ void InstanceImpl::initialize(Options& options, TestHooks& hooks, // Once we have runtime we can initialize the SSL context manager. ssl_context_manager_.reset(new Ssl::ContextManagerImpl(*runtime_loader_)); + cluster_manager_factory_.reset(new Upstream::ProdClusterManagerFactory( + runtime(), stats(), threadLocal(), random(), dnsResolver(), sslContextManager(), dispatcher(), + localInfo())); + // Now the configuration gets parsed. The configuration may start setting thread local data // per above. See MainImpl::initialize() for why we do this pointer dance. - Configuration::MainImpl* main_config = new Configuration::MainImpl(*this); + Configuration::MainImpl* main_config = + new Configuration::MainImpl(*this, *cluster_manager_factory_); config_.reset(main_config); main_config->initialize(*config_json); diff --git a/source/server/server.h b/source/server/server.h index 7a9c7aff8303..df9862e65691 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -18,6 +18,7 @@ #include "common/runtime/runtime_impl.h" #include "common/ssl/context_manager_impl.h" #include "common/thread_local/thread_local_impl.h" +#include "common/upstream/cluster_manager_impl.h" #include "server/connection_handler_impl.h" #include "server/http/admin.h" @@ -174,6 +175,7 @@ class InstanceImpl : Logger::Loggable, public Instance { const LocalInfo::LocalInfo& local_info_; DrainManagerPtr drain_manager_; AccessLog::AccessLogManagerImpl access_log_manager_; + std::unique_ptr cluster_manager_factory_; InitManagerImpl init_manager_; std::unique_ptr guard_dog_; }; diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index 63dbd2f1d927..efae2dacd721 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -64,6 +64,21 @@ class TestClusterManagerFactory : public ClusterManagerFactory { return CdsApiPtr{createCds_()}; } + ClusterManagerPtr clusterManagerFromJson(const Json::Object& config, Stats::Store& stats, + ThreadLocal::Instance& tls, Runtime::Loader& runtime, + Runtime::RandomGenerator& random, + const LocalInfo::LocalInfo& local_info, + AccessLog::AccessLogManager& log_manager) override { + return ClusterManagerPtr{ + clusterManagerFromJson_(config, stats, tls, runtime, random, local_info, log_manager)}; + } + + MOCK_METHOD7(clusterManagerFromJson_, + ClusterManager*(const Json::Object& config, Stats::Store& stats, + ThreadLocal::Instance& tls, Runtime::Loader& runtime, + Runtime::RandomGenerator& random, + const LocalInfo::LocalInfo& local_info, + AccessLog::AccessLogManager& log_manager)); MOCK_METHOD1(allocateConnPool_, Http::ConnectionPool::Instance*(HostConstSharedPtr host)); MOCK_METHOD4(clusterFromJson_, Cluster*(const Json::Object& cluster, ClusterManager& cm, const Optional& sds_config, diff --git a/test/config_test/config_test.cc b/test/config_test/config_test.cc index 5cc75e1bb26b..bc99e6435441 100644 --- a/test/config_test/config_test.cc +++ b/test/config_test/config_test.cc @@ -23,7 +23,11 @@ namespace ConfigTest { class ConfigTest { public: - ConfigTest(const std::string& file_path) : options_(file_path) { + ConfigTest(const std::string& file_path) + : options_(file_path), + cluster_manager_factory_(server_.runtime(), server_.stats(), server_.threadLocal(), + server_.random(), server_.dnsResolver(), ssl_context_manager_, + server_.dispatcher(), server_.localInfo()) { ON_CALL(server_, options()).WillByDefault(ReturnRef(options_)); ON_CALL(server_, sslContextManager()).WillByDefault(ReturnRef(ssl_context_manager_)); ON_CALL(server_.api_, fileReadToEnd("lightstep_access_token")) @@ -31,7 +35,7 @@ class ConfigTest { Json::ObjectSharedPtr config_json = Json::Factory::loadFromFile(file_path); Server::Configuration::InitialImpl initial_config(*config_json); - Server::Configuration::MainImpl main_config(server_); + Server::Configuration::MainImpl main_config(server_, cluster_manager_factory_); ON_CALL(server_, clusterManager()) .WillByDefault( @@ -49,6 +53,7 @@ class ConfigTest { NiceMock server_; NiceMock ssl_context_manager_; Server::TestOptionsImpl options_; + Upstream::ProdClusterManagerFactory cluster_manager_factory_; }; uint32_t run(const std::string& directory) { diff --git a/test/integration/server.h b/test/integration/server.h index bd9ab98de84f..566e19709d67 100644 --- a/test/integration/server.h +++ b/test/integration/server.h @@ -43,6 +43,7 @@ class TestOptionsImpl : public Options { std::chrono::milliseconds fileFlushIntervalMsec() override { return std::chrono::milliseconds(10000); } + Mode mode() const override { return Mode::Serve; } private: const std::string config_path_; @@ -60,6 +61,17 @@ class TestDrainManager : public DrainManager { bool draining_{}; }; +class TestComponentFactory : public ComponentFactory { +public: + Server::DrainManagerPtr createDrainManager(Server::Instance&) override { + return Server::DrainManagerPtr{new Server::TestDrainManager()}; + } + Runtime::LoaderPtr createRuntime(Server::Instance& server, + Server::Configuration::Initial& config) override { + return Server::InstanceUtil::createRuntime(server, config); + } +}; + } // Server namespace Stats { diff --git a/test/mocks/server/BUILD b/test/mocks/server/BUILD index d5fe953a19ff..cf01bd18e491 100644 --- a/test/mocks/server/BUILD +++ b/test/mocks/server/BUILD @@ -21,6 +21,8 @@ envoy_cc_mock( "//include/envoy/ssl:context_manager_interface", "//source/common/ssl:context_lib", "//source/common/stats:stats_lib", + "//source/common/tracing:http_tracer_lib", + "//source/common/upstream:cluster_manager_lib", "//test/mocks/access_log:access_log_mocks", "//test/mocks/api:api_mocks", "//test/mocks/http:http_mocks", diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index 7995ae5e9522..44dee4e78e8e 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -14,6 +14,8 @@ #include "common/ssl/context_manager_impl.h" #include "common/stats/stats_impl.h" +#include "common/tracing/http_tracer_impl.h" +#include "common/upstream/cluster_manager_impl.h" #include "test/mocks/access_log/mocks.h" #include "test/mocks/api/mocks.h" @@ -51,6 +53,7 @@ class MockOptions : public Options { MOCK_METHOD0(parentShutdownTime, std::chrono::seconds()); MOCK_METHOD0(restartEpoch, uint64_t()); MOCK_METHOD0(fileFlushIntervalMsec, std::chrono::milliseconds()); + MOCK_CONST_METHOD0(mode, Mode()); std::string config_path_; std::string admin_address_path_; diff --git a/test/run_envoy_bazel_coverage.sh b/test/run_envoy_bazel_coverage.sh index 0726ddbca553..3e9fa1d71d5c 100755 --- a/test/run_envoy_bazel_coverage.sh +++ b/test/run_envoy_bazel_coverage.sh @@ -38,7 +38,7 @@ echo "Cleanup completed." # stats. The #foo# pattern is because gcov produces files such as # bazel-out#local-fastbuild#bin#external#spdlog_git#_virtual_includes#spdlog#spdlog#details#pattern_formatter_impl.h.gcov. # To find these while modifying this regex, perform a gcov run with -k set. -[[ -z "${GCOVR_EXCLUDE_REGEX}" ]] && GCOVR_EXCLUDE_REGEX=".*pb.h.gcov|.*#genfiles#.*|test#.*|external#.*|.*#external#.*|.*#prebuilt#.*" +[[ -z "${GCOVR_EXCLUDE_REGEX}" ]] && GCOVR_EXCLUDE_REGEX=".*pb.h.gcov|.*#genfiles#.*|test#.*|external#.*|.*#external#.*|.*#prebuilt#.*|.*#config_validation#.*" [[ -z "${GCOVR_EXCLUDE_DIR}" ]] && GCOVR_EXCLUDE_DIR=".*/external/.*" COVERAGE_DIR="${SRCDIR}"/generated/coverage diff --git a/test/server/config_validation/BUILD b/test/server/config_validation/BUILD new file mode 100644 index 000000000000..0147bb8fcb4d --- /dev/null +++ b/test/server/config_validation/BUILD @@ -0,0 +1,68 @@ +licenses(["notice"]) # Apache 2 + +load("//bazel:envoy_build_system.bzl", "envoy_cc_test", "envoy_package") + +envoy_package() + +envoy_cc_test( + name = "async_client_test", + srcs = ["async_client_test.cc"], + deps = [ + "//include/envoy/http:message_interface", + "//source/common/http:message_lib", + "//source/server/config_validation:async_client_lib", + "//test/mocks/http:http_mocks", + "//test/mocks/upstream:upstream_mocks", + ], +) + +envoy_cc_test( + name = "cluster_manager_test", + srcs = ["cluster_manager_test.cc"], + deps = [ + "//include/envoy/json:json_object_interface", + "//include/envoy/upstream:resource_manager_interface", + "//include/envoy/upstream:upstream_interface", + "//source/common/json:json_loader_lib", + "//source/common/ssl:context_lib", + "//source/common/stats:stats_lib", + "//source/server/config_validation:cluster_manager_lib", + "//test/mocks/access_log:access_log_mocks", + "//test/mocks/event:event_mocks", + "//test/mocks/http:http_mocks", + "//test/mocks/local_info:local_info_mocks", + "//test/mocks/network:network_mocks", + "//test/mocks/runtime:runtime_mocks", + "//test/mocks/thread_local:thread_local_mocks", + "//test/mocks/upstream:upstream_mocks", + ], +) + +envoy_cc_test( + name = "connection_handler_test", + srcs = ["connection_handler_test.cc"], + deps = [ + "//include/envoy/api:api_interface", + "//source/common/network:address_lib", + "//source/server/config_validation:connection_handler_lib", + "//test/mocks/api:api_mocks", + "//test/mocks/event:event_mocks", + ], +) + +envoy_cc_test( + name = "server_test", + srcs = ["server_test.cc"], + data = [ + "//configs:example_configs", + "//test/config_test:example_configs_test_setup.sh", + ], + deps = [ + "//source/server/config_validation:server_lib", + "//test/integration:integration_lib", + "//test/mocks/server:server_mocks", + "//test/mocks/stats:stats_mocks", + "//test/test_common:environment_lib", + "//test/test_common:utility_lib", + ], +) diff --git a/test/server/config_validation/async_client_test.cc b/test/server/config_validation/async_client_test.cc new file mode 100644 index 000000000000..fcaa91bd273a --- /dev/null +++ b/test/server/config_validation/async_client_test.cc @@ -0,0 +1,25 @@ +#include "envoy/http/message.h" + +#include "common/http/message_impl.h" + +#include "server/config_validation/async_client.h" + +#include "test/mocks/http/mocks.h" +#include "test/mocks/upstream/mocks.h" + +namespace Envoy { +namespace Http { + +TEST(ValidationAsyncClientTest, MockedMethods) { + MessagePtr message{new RequestMessageImpl()}; + MockAsyncClientCallbacks callbacks; + MockAsyncClientStreamCallbacks stream_callbacks; + + ValidationAsyncClient client; + EXPECT_EQ(nullptr, + client.send(std::move(message), callbacks, Optional())); + EXPECT_EQ(nullptr, client.start(stream_callbacks, Optional())); +} + +} // Http +} // Envoy diff --git a/test/server/config_validation/cluster_manager_test.cc b/test/server/config_validation/cluster_manager_test.cc new file mode 100644 index 000000000000..c7fbddee5ad4 --- /dev/null +++ b/test/server/config_validation/cluster_manager_test.cc @@ -0,0 +1,58 @@ +#include "envoy/json/json_object.h" +#include "envoy/upstream/resource_manager.h" +#include "envoy/upstream/upstream.h" + +#include "common/json/json_loader.h" +#include "common/ssl/context_manager_impl.h" +#include "common/stats/stats_impl.h" + +#include "server/config_validation/cluster_manager.h" + +#include "test/mocks/access_log/mocks.h" +#include "test/mocks/event/mocks.h" +#include "test/mocks/http/mocks.h" +#include "test/mocks/local_info/mocks.h" +#include "test/mocks/network/mocks.h" +#include "test/mocks/runtime/mocks.h" +#include "test/mocks/thread_local/mocks.h" +#include "test/mocks/upstream/mocks.h" + +namespace Envoy { +namespace Upstream { + +TEST(ValidationClusterManagerTest, MockedMethods) { + NiceMock runtime; + Stats::IsolatedStoreImpl stats; + NiceMock tls; + NiceMock random; + NiceMock dns_resolver; + Ssl::ContextManagerImpl ssl_context_manager{runtime}; + NiceMock dispatcher; + LocalInfo::MockLocalInfo local_info; + + ValidationClusterManagerFactory factory(runtime, stats, tls, random, dns_resolver, + ssl_context_manager, dispatcher, local_info); + + std::string json = R"EOF( + { + "clusters": [] + } + )EOF"; + Json::ObjectSharedPtr config = Json::Factory::loadFromString(json); + AccessLog::MockAccessLogManager log_manager; + + ClusterManagerPtr cluster_manager = + factory.clusterManagerFromJson(*config, stats, tls, runtime, random, local_info, log_manager); + EXPECT_EQ(nullptr, + cluster_manager->httpConnPoolForCluster("cluster", ResourcePriority::Default, nullptr)); + Host::CreateConnectionData data = cluster_manager->tcpConnForCluster("cluster"); + EXPECT_EQ(nullptr, data.connection_); + EXPECT_EQ(nullptr, data.host_description_); + + Http::AsyncClient& client = cluster_manager->httpAsyncClientForCluster("cluster"); + Http::MockAsyncClientStreamCallbacks stream_callbacks; + EXPECT_EQ(nullptr, client.start(stream_callbacks, Optional())); +} + +} // Upstream +} // Envoy diff --git a/test/server/config_validation/connection_handler_test.cc b/test/server/config_validation/connection_handler_test.cc new file mode 100644 index 000000000000..6772c40b103b --- /dev/null +++ b/test/server/config_validation/connection_handler_test.cc @@ -0,0 +1,29 @@ +#include "envoy/api/api.h" + +#include "common/network/address_impl.h" + +#include "server/config_validation/connection_handler.h" + +#include "test/mocks/api/mocks.h" +#include "test/mocks/event/mocks.h" + +namespace Envoy { +namespace Server { + +using testing::NiceMock; +using testing::Return; + +TEST(ValidationConnectionHandlerTest, MockedMethods) { + Api::MockApi* api = new Api::MockApi(); + Event::MockDispatcher* dispatcher = new NiceMock(); + EXPECT_CALL(*api, allocateDispatcher_()).WillOnce(Return(dispatcher)); + + ValidationConnectionHandler handler(Api::ApiPtr{api}); + EXPECT_EQ(0, handler.numConnections()); + Network::Address::Ipv4Instance address("0.0.0.0", 0); + EXPECT_EQ(nullptr, handler.findListenerByAddress(address)); + EXPECT_NO_THROW(handler.closeListeners()); +} + +} // Server +} // Envoy diff --git a/test/server/config_validation/server_test.cc b/test/server/config_validation/server_test.cc new file mode 100644 index 000000000000..4523fbf082cc --- /dev/null +++ b/test/server/config_validation/server_test.cc @@ -0,0 +1,45 @@ +#include "server/config_validation/server.h" + +#include "test/integration/server.h" +#include "test/mocks/server/mocks.h" +#include "test/mocks/stats/mocks.h" +#include "test/test_common/environment.h" + +namespace Envoy { +namespace Server { + +// Test param is the path to the config file to validate. +class ValidationServerTest : public testing::TestWithParam { +public: + static void SetUpTestCase() { + TestEnvironment::exec( + {TestEnvironment::runfilesPath("test/config_test/example_configs_test_setup.sh")}); + directory_ = TestEnvironment::temporaryDirectory() + "/test/config_test/"; + } + +protected: + ValidationServerTest() : options_(directory_ + GetParam()) {} + + static std::string directory_; + + testing::NiceMock options_; + TestComponentFactory component_factory_; + testing::NiceMock local_info_; +}; + +std::string ValidationServerTest::directory_ = ""; + +TEST_P(ValidationServerTest, Validate) { + EXPECT_TRUE(validateConfig(options_, component_factory_, local_info_)); +} + +// TODO(rlazarus): We'd like use this setup to replace //test/config_test (that is, run it against +// all the example configs) but can't until light validation is implemented, mocking out access to +// the filesystem for TLS certs, etc. In the meantime, these are the example configs that work +// as-is. +INSTANTIATE_TEST_CASE_P(ValidConfigs, ValidationServerTest, + ::testing::Values("front-envoy.json", "google_com_proxy.json", + "s2s-grpc-envoy.json", "service-envoy.json")); + +} // Server +} // Envoy diff --git a/test/server/configuration_impl_test.cc b/test/server/configuration_impl_test.cc index 62a168bb638b..be5f29cea459 100644 --- a/test/server/configuration_impl_test.cc +++ b/test/server/configuration_impl_test.cc @@ -43,7 +43,19 @@ TEST(FilterChainUtility, buildFilterChainFailWithBadFilters) { EXPECT_EQ(FilterChainUtility::buildFilterChain(connection, factories), false); } -TEST(ConfigurationImplTest, DefaultStatsFlushInterval) { +class ConfigurationImplTest : public testing::Test { +protected: + ConfigurationImplTest() + : cluster_manager_factory_(server_.runtime(), server_.stats(), server_.threadLocal(), + server_.random(), server_.dnsResolver(), + server_.sslContextManager(), server_.dispatcher(), + server_.localInfo()) {} + + NiceMock server_; + Upstream::ProdClusterManagerFactory cluster_manager_factory_; +}; + +TEST_F(ConfigurationImplTest, DefaultStatsFlushInterval) { std::string json = R"EOF( { "listeners": [], @@ -56,14 +68,13 @@ TEST(ConfigurationImplTest, DefaultStatsFlushInterval) { Json::ObjectSharedPtr loader = Json::Factory::loadFromString(json); - NiceMock server; - MainImpl config(server); + MainImpl config(server_, cluster_manager_factory_); config.initialize(*loader); EXPECT_EQ(std::chrono::milliseconds(5000), config.statsFlushInterval()); } -TEST(ConfigurationImplTest, CustomStatsFlushInterval) { +TEST_F(ConfigurationImplTest, CustomStatsFlushInterval) { std::string json = R"EOF( { "listeners": [], @@ -78,14 +89,13 @@ TEST(ConfigurationImplTest, CustomStatsFlushInterval) { Json::ObjectSharedPtr loader = Json::Factory::loadFromString(json); - NiceMock server; - MainImpl config(server); + MainImpl config(server_, cluster_manager_factory_); config.initialize(*loader); EXPECT_EQ(std::chrono::milliseconds(500), config.statsFlushInterval()); } -TEST(ConfigurationImplTest, EmptyFilter) { +TEST_F(ConfigurationImplTest, EmptyFilter) { std::string json = R"EOF( { "listeners" : [ @@ -102,14 +112,13 @@ TEST(ConfigurationImplTest, EmptyFilter) { Json::ObjectSharedPtr loader = Json::Factory::loadFromString(json); - NiceMock server; - MainImpl config(server); + MainImpl config(server_, cluster_manager_factory_); config.initialize(*loader); EXPECT_EQ(1U, config.listeners().size()); } -TEST(ConfigurationImplTest, DefaultListenerPerConnectionBufferLimit) { +TEST_F(ConfigurationImplTest, DefaultListenerPerConnectionBufferLimit) { std::string json = R"EOF( { "listeners" : [ @@ -126,14 +135,13 @@ TEST(ConfigurationImplTest, DefaultListenerPerConnectionBufferLimit) { Json::ObjectSharedPtr loader = Json::Factory::loadFromString(json); - NiceMock server; - MainImpl config(server); + MainImpl config(server_, cluster_manager_factory_); config.initialize(*loader); EXPECT_EQ(1024 * 1024U, config.listeners().back()->perConnectionBufferLimitBytes()); } -TEST(ConfigurationImplTest, SetListenerPerConnectionBufferLimit) { +TEST_F(ConfigurationImplTest, SetListenerPerConnectionBufferLimit) { std::string json = R"EOF( { "listeners" : [ @@ -151,14 +159,13 @@ TEST(ConfigurationImplTest, SetListenerPerConnectionBufferLimit) { Json::ObjectSharedPtr loader = Json::Factory::loadFromString(json); - NiceMock server; - MainImpl config(server); + MainImpl config(server_, cluster_manager_factory_); config.initialize(*loader); EXPECT_EQ(8192U, config.listeners().back()->perConnectionBufferLimitBytes()); } -TEST(ConfigurationImplTest, VerifySubjectAltNameConfig) { +TEST_F(ConfigurationImplTest, VerifySubjectAltNameConfig) { std::string json = R"EOF( { "listeners" : [ @@ -183,14 +190,13 @@ TEST(ConfigurationImplTest, VerifySubjectAltNameConfig) { Json::ObjectSharedPtr loader = TestEnvironment::jsonLoadFromString(json); - NiceMock server; - MainImpl config(server); + MainImpl config(server_, cluster_manager_factory_); config.initialize(*loader); EXPECT_TRUE(config.listeners().back()->sslContext() != nullptr); } -TEST(ConfigurationImplTest, SetUpstreamClusterPerConnectionBufferLimit) { +TEST_F(ConfigurationImplTest, SetUpstreamClusterPerConnectionBufferLimit) { std::string json = R"EOF( { "listeners" : [], @@ -213,8 +219,7 @@ TEST(ConfigurationImplTest, SetUpstreamClusterPerConnectionBufferLimit) { Json::ObjectSharedPtr loader = Json::Factory::loadFromString(json); - NiceMock server; - MainImpl config(server); + MainImpl config(server_, cluster_manager_factory_); config.initialize(*loader); ASSERT_EQ(1U, config.clusterManager().clusters().count("test_cluster")); @@ -224,10 +229,10 @@ TEST(ConfigurationImplTest, SetUpstreamClusterPerConnectionBufferLimit) { ->second.get() .info() ->perConnectionBufferLimitBytes()); - server.thread_local_.shutdownThread(); + server_.thread_local_.shutdownThread(); } -TEST(ConfigurationImplTest, BadListenerConfig) { +TEST_F(ConfigurationImplTest, BadListenerConfig) { std::string json = R"EOF( { "listeners" : [ @@ -245,12 +250,11 @@ TEST(ConfigurationImplTest, BadListenerConfig) { Json::ObjectSharedPtr loader = Json::Factory::loadFromString(json); - NiceMock server; - MainImpl config(server); + MainImpl config(server_, cluster_manager_factory_); EXPECT_THROW(config.initialize(*loader), Json::Exception); } -TEST(ConfigurationImplTest, BadFilterConfig) { +TEST_F(ConfigurationImplTest, BadFilterConfig) { std::string json = R"EOF( { "listeners" : [ @@ -273,12 +277,11 @@ TEST(ConfigurationImplTest, BadFilterConfig) { Json::ObjectSharedPtr loader = Json::Factory::loadFromString(json); - NiceMock server; - MainImpl config(server); + MainImpl config(server_, cluster_manager_factory_); EXPECT_THROW(config.initialize(*loader), Json::Exception); } -TEST(ConfigurationImplTest, BadFilterName) { +TEST_F(ConfigurationImplTest, BadFilterName) { std::string json = R"EOF( { "listeners" : [ @@ -301,13 +304,12 @@ TEST(ConfigurationImplTest, BadFilterName) { Json::ObjectSharedPtr loader = Json::Factory::loadFromString(json); - NiceMock server; - MainImpl config(server); + MainImpl config(server_, cluster_manager_factory_); EXPECT_THROW_WITH_MESSAGE(config.initialize(*loader), EnvoyException, "unable to create filter factory for 'invalid'/'read'"); } -TEST(ConfigurationImplTest, ServiceClusterNotSetWhenLSTracing) { +TEST_F(ConfigurationImplTest, ServiceClusterNotSetWhenLSTracing) { std::string json = R"EOF( { "listeners" : [ @@ -334,13 +336,12 @@ TEST(ConfigurationImplTest, ServiceClusterNotSetWhenLSTracing) { Json::ObjectSharedPtr loader = Json::Factory::loadFromString(json); - NiceMock server; - server.local_info_.cluster_name_ = ""; - MainImpl config(server); + server_.local_info_.cluster_name_ = ""; + MainImpl config(server_, cluster_manager_factory_); EXPECT_THROW(config.initialize(*loader), EnvoyException); } -TEST(ConfigurationImplTest, NullTracerSetWhenTracingConfigurationAbsent) { +TEST_F(ConfigurationImplTest, NullTracerSetWhenTracingConfigurationAbsent) { std::string json = R"EOF( { "listeners" : [ @@ -357,15 +358,14 @@ TEST(ConfigurationImplTest, NullTracerSetWhenTracingConfigurationAbsent) { Json::ObjectSharedPtr loader = Json::Factory::loadFromString(json); - NiceMock server; - server.local_info_.cluster_name_ = ""; - MainImpl config(server); + server_.local_info_.cluster_name_ = ""; + MainImpl config(server_, cluster_manager_factory_); config.initialize(*loader); EXPECT_NE(nullptr, dynamic_cast(&config.httpTracer())); } -TEST(ConfigurationImplTest, NullTracerSetWhenHttpKeyAbsentFromTracerConfiguration) { +TEST_F(ConfigurationImplTest, NullTracerSetWhenHttpKeyAbsentFromTracerConfiguration) { std::string json = R"EOF( { "listeners" : [ @@ -392,15 +392,14 @@ TEST(ConfigurationImplTest, NullTracerSetWhenHttpKeyAbsentFromTracerConfiguratio Json::ObjectSharedPtr loader = Json::Factory::loadFromString(json); - NiceMock server; - server.local_info_.cluster_name_ = ""; - MainImpl config(server); + server_.local_info_.cluster_name_ = ""; + MainImpl config(server_, cluster_manager_factory_); config.initialize(*loader); EXPECT_NE(nullptr, dynamic_cast(&config.httpTracer())); } -TEST(ConfigurationImplTest, ConfigurationFailsWhenInvalidTracerSpecified) { +TEST_F(ConfigurationImplTest, ConfigurationFailsWhenInvalidTracerSpecified) { std::string json = R"EOF( { "listeners" : [ @@ -427,8 +426,7 @@ TEST(ConfigurationImplTest, ConfigurationFailsWhenInvalidTracerSpecified) { Json::ObjectSharedPtr loader = Json::Factory::loadFromString(json); - NiceMock server; - MainImpl config(server); + MainImpl config(server_, cluster_manager_factory_); EXPECT_THROW_WITH_MESSAGE(config.initialize(*loader), EnvoyException, "No HttpTracerFactory found for type: invalid"); } @@ -450,7 +448,7 @@ class TestDeprecatedEchoConfigFactory : public NetworkFilterConfigFactory { } }; -TEST(NetworkFilterConfigTest, DeprecatedFilterConfigFactoryRegistrationTest) { +TEST_F(ConfigurationImplTest, DeprecatedFilterConfigFactoryRegistrationTest) { // Test ensures that the deprecated network filter registration still works without error. // Register the config factory @@ -478,8 +476,7 @@ TEST(NetworkFilterConfigTest, DeprecatedFilterConfigFactoryRegistrationTest) { Json::ObjectSharedPtr loader = Json::Factory::loadFromString(json); - NiceMock server; - MainImpl config(server); + MainImpl config(server_, cluster_manager_factory_); config.initialize(*loader); } } // Configuration diff --git a/test/server/options_impl_test.cc b/test/server/options_impl_test.cc index 6af55e0aa88a..28d11b02f4a7 100644 --- a/test/server/options_impl_test.cc +++ b/test/server/options_impl_test.cc @@ -27,11 +27,16 @@ TEST(OptionsImplDeathTest, HotRestartVersion) { EXPECT_EXIT(createOptionsImpl("envoy --hot-restart-version"), testing::ExitedWithCode(0), ""); } +TEST(OptionsImplDeathTest, InvalidMode) { + EXPECT_EXIT(createOptionsImpl("envoy --mode bogus"), testing::ExitedWithCode(1), "bogus"); +} + TEST(OptionsImplTest, All) { std::unique_ptr options = createOptionsImpl( - "envoy --concurrency 2 -c hello --admin-address-path path --restart-epoch 1 -l info " - "--service-cluster cluster --service-node node --service-zone zone " + "envoy --mode validate --concurrency 2 -c hello --admin-address-path path --restart-epoch 1 " + "-l info --service-cluster cluster --service-node node --service-zone zone " "--file-flush-interval-msec 9000 --drain-time-s 60 --parent-shutdown-time-s 90"); + EXPECT_EQ(Server::Mode::Validate, options->mode()); EXPECT_EQ(2U, options->concurrency()); EXPECT_EQ("hello", options->configPath()); EXPECT_EQ("path", options->adminAddressPath()); @@ -50,5 +55,6 @@ TEST(OptionsImplTest, DefaultParams) { EXPECT_EQ(std::chrono::seconds(600), options->drainTime()); EXPECT_EQ(std::chrono::seconds(900), options->parentShutdownTime()); EXPECT_EQ("", options->adminAddressPath()); + EXPECT_EQ(Server::Mode::Serve, options->mode()); } } // Envoy diff --git a/test/server/server_test.cc b/test/server/server_test.cc index efccfca86a0f..70db4b7a4140 100644 --- a/test/server/server_test.cc +++ b/test/server/server_test.cc @@ -37,17 +37,6 @@ TEST(InitManagerImplTest, Targets) { target.callback_(); } -class TestComponentFactory : public ComponentFactory { -public: - Server::DrainManagerPtr createDrainManager(Server::Instance&) override { - return Server::DrainManagerPtr{new Server::TestDrainManager()}; - } - Runtime::LoaderPtr createRuntime(Server::Instance& server, - Server::Configuration::Initial& config) override { - return Server::InstanceUtil::createRuntime(server, config); - } -}; - // Class creates minimally viable server instance for testing. class ServerInstanceImplTest : public testing::Test { protected: