Skip to content

Commit

Permalink
api: add bootstrap extensions (envoyproxy#11413)
Browse files Browse the repository at this point in the history
Commit Message:
Adds a configurable server wide extension hook point. Allow extensions to instantiate singletons / context managers with configs in bootstrap.

Additional Description:
Risk Level: Low (not used API)
Testing: unittest, mock
Docs Changes: protodoc
Release Notes: N/A as no real extension lives today.
Fixes envoyproxy#11219 
Signed-off-by: Lizan Zhou <[email protected]>
  • Loading branch information
lizan authored Jun 4, 2020
1 parent ed30094 commit 8cb2958
Show file tree
Hide file tree
Showing 15 changed files with 166 additions and 4 deletions.
7 changes: 6 additions & 1 deletion api/envoy/config/bootstrap/v3/bootstrap.proto
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "envoy/config/core/v3/address.proto";
import "envoy/config/core/v3/base.proto";
import "envoy/config/core/v3/config_source.proto";
import "envoy/config/core/v3/event_service_config.proto";
import "envoy/config/core/v3/extension.proto";
import "envoy/config/core/v3/socket_option.proto";
import "envoy/config/listener/v3/listener.proto";
import "envoy/config/metrics/v3/stats.proto";
Expand Down Expand Up @@ -35,7 +36,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
// <config_overview_bootstrap>` for more detail.

// Bootstrap :ref:`configuration overview <config_overview_bootstrap>`.
// [#next-free-field: 21]
// [#next-free-field: 22]
message Bootstrap {
option (udpa.annotations.versioning).previous_message_type =
"envoy.config.bootstrap.v2.Bootstrap";
Expand Down Expand Up @@ -181,6 +182,10 @@ message Bootstrap {
// :ref:`use_tcp_for_dns_lookups <envoy_api_field_config.cluster.v3.Cluster.use_tcp_for_dns_lookups>` are
// specified.
bool use_tcp_for_dns_lookups = 20;

// Specifies optional bootstrap extensions to be instantiated at startup time.
// Each item contains extension specific configuration.
repeated core.v3.TypedExtensionConfig bootstrap_extensions = 21;
}

// Administration interface :ref:`operations documentation
Expand Down
7 changes: 6 additions & 1 deletion api/envoy/config/bootstrap/v4alpha/bootstrap.proto
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "envoy/config/core/v4alpha/address.proto";
import "envoy/config/core/v4alpha/base.proto";
import "envoy/config/core/v4alpha/config_source.proto";
import "envoy/config/core/v4alpha/event_service_config.proto";
import "envoy/config/core/v4alpha/extension.proto";
import "envoy/config/core/v4alpha/socket_option.proto";
import "envoy/config/listener/v4alpha/listener.proto";
import "envoy/config/metrics/v4alpha/stats.proto";
Expand Down Expand Up @@ -34,7 +35,7 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO
// <config_overview_bootstrap>` for more detail.

// Bootstrap :ref:`configuration overview <config_overview_bootstrap>`.
// [#next-free-field: 21]
// [#next-free-field: 22]
message Bootstrap {
option (udpa.annotations.versioning).previous_message_type =
"envoy.config.bootstrap.v3.Bootstrap";
Expand Down Expand Up @@ -173,6 +174,10 @@ message Bootstrap {
// :ref:`use_tcp_for_dns_lookups <envoy_api_field_config.cluster.v4alpha.Cluster.use_tcp_for_dns_lookups>` are
// specified.
bool use_tcp_for_dns_lookups = 20;

// Specifies optional bootstrap extensions to be instantiated at startup time.
// Each item contains extension specific configuration.
repeated core.v4alpha.TypedExtensionConfig bootstrap_extensions = 21;
}

// Administration interface :ref:`operations documentation
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions include/envoy/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -293,3 +293,12 @@ envoy_cc_library(
"//include/envoy/network:connection_handler_interface",
],
)

envoy_cc_library(
name = "bootstrap_extension_config_interface",
hdrs = ["bootstrap_extension_config.h"],
deps = [
":filter_config_interface",
"//include/envoy/config:typed_config_interface",
],
)
47 changes: 47 additions & 0 deletions include/envoy/server/bootstrap_extension_config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#pragma once

#include <memory>

#include "envoy/server/filter_config.h"

#include "common/protobuf/protobuf.h"

namespace Envoy {
namespace Server {

/**
* Parent class for bootstrap extensions.
*/
class BootstrapExtension {
public:
virtual ~BootstrapExtension() = default;
};

using BootstrapExtensionPtr = std::unique_ptr<BootstrapExtension>;

namespace Configuration {

/**
* Implemented for each bootstrap extension and registered via Registry::registerFactory or the
* convenience class RegisterFactory.
*/
class BootstrapExtensionFactory : public Config::TypedFactory {
public:
~BootstrapExtensionFactory() override = default;

/**
* Create a particular bootstrap extension implementation from a config proto. If the
* implementation is unable to produce a factory with the provided parameters, it should throw an
* EnvoyException. The returned pointer should never be nullptr.
* @param config the custom configuration for this bootstrap extension type.
* @param context general filter context through which persistent resources can be accessed.
*/
virtual BootstrapExtensionPtr createBootstrapExtension(const Protobuf::Message& config,
ServerFactoryContext& context) PURE;

std::string category() const override { return "envoy.bootstrap"; }
};

} // namespace Configuration
} // namespace Server
} // namespace Envoy
1 change: 1 addition & 0 deletions source/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ envoy_cc_library(
"//include/envoy/event:signal_interface",
"//include/envoy/event:timer_interface",
"//include/envoy/network:dns_interface",
"//include/envoy/server:bootstrap_extension_config_interface",
"//include/envoy/server:drain_manager_interface",
"//include/envoy/server:instance_interface",
"//include/envoy/server:listener_manager_interface",
Expand Down
11 changes: 11 additions & 0 deletions source/server/server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "envoy/event/timer.h"
#include "envoy/network/dns.h"
#include "envoy/registry/registry.h"
#include "envoy/server/bootstrap_extension_config.h"
#include "envoy/server/options.h"
#include "envoy/upstream/cluster_manager.h"

Expand Down Expand Up @@ -481,6 +482,16 @@ void InstanceImpl::initialize(const Options& options,
// GuardDog (deadlock detection) object and thread setup before workers are
// started and before our own run() loop runs.
guard_dog_ = std::make_unique<Server::GuardDogImpl>(stats_store_, config_, *api_);

for (const auto& bootstrap_extension : bootstrap_.bootstrap_extensions()) {
auto& factory = Config::Utility::getAndCheckFactory<Configuration::BootstrapExtensionFactory>(
bootstrap_extension);
auto config = Config::Utility::translateAnyToFactoryConfig(
bootstrap_extension.typed_config(), messageValidationContext().staticValidationVisitor(),
factory);
bootstrap_extensions_.push_back(
factory.createBootstrapExtension(*config, serverFactoryContext()));
}
}

void InstanceImpl::onClusterManagerPrimaryInitializationComplete() {
Expand Down
2 changes: 2 additions & 0 deletions source/server/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "envoy/config/bootstrap/v3/bootstrap.pb.h"
#include "envoy/event/timer.h"
#include "envoy/server/bootstrap_extension_config.h"
#include "envoy/server/drain_manager.h"
#include "envoy/server/guarddog.h"
#include "envoy/server/instance.h"
Expand Down Expand Up @@ -346,6 +347,7 @@ class InstanceImpl final : Logger::Loggable<Logger::Id::main>,
Upstream::ProdClusterInfoFactory info_factory_;
Upstream::HdsDelegatePtr hds_delegate_;
std::unique_ptr<OverloadManagerImpl> overload_manager_;
std::vector<BootstrapExtensionPtr> bootstrap_extensions_;
Envoy::MutexTracer* mutex_tracer_;
Grpc::ContextImpl grpc_context_;
Http::ContextImpl http_context_;
Expand Down
1 change: 1 addition & 0 deletions test/mocks/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ envoy_cc_mock(
deps = [
"//include/envoy/secret:secret_manager_interface",
"//include/envoy/server:admin_interface",
"//include/envoy/server:bootstrap_extension_config_interface",
"//include/envoy/server:configuration_interface",
"//include/envoy/server:drain_manager_interface",
"//include/envoy/server:filter_config_interface",
Expand Down
3 changes: 3 additions & 0 deletions test/mocks/server/mocks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,9 @@ MockTracerFactoryContext::MockTracerFactoryContext() {
}

MockTracerFactoryContext::~MockTracerFactoryContext() = default;

MockBootstrapExtensionFactory::MockBootstrapExtensionFactory() = default;
MockBootstrapExtensionFactory::~MockBootstrapExtensionFactory() = default;
} // namespace Configuration
} // namespace Server
} // namespace Envoy
12 changes: 12 additions & 0 deletions test/mocks/server/mocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "envoy/config/listener/v3/listener_components.pb.h"
#include "envoy/protobuf/message_validator.h"
#include "envoy/server/admin.h"
#include "envoy/server/bootstrap_extension_config.h"
#include "envoy/server/configuration.h"
#include "envoy/server/drain_manager.h"
#include "envoy/server/filter_config.h"
Expand Down Expand Up @@ -679,6 +680,17 @@ class MockTracerFactoryContext : public TracerFactoryContext {
testing::NiceMock<Configuration::MockServerFactoryContext> server_factory_context_;
};

class MockBootstrapExtensionFactory : public BootstrapExtensionFactory {
public:
MockBootstrapExtensionFactory();
~MockBootstrapExtensionFactory() override;

MOCK_METHOD(BootstrapExtensionPtr, createBootstrapExtension,
(const Protobuf::Message&, Configuration::ServerFactoryContext&), (override));
MOCK_METHOD(ProtobufTypes::MessagePtr, createEmptyConfigProto, (), (override));
MOCK_METHOD(std::string, name, (), (const override));
};

} // namespace Configuration
} // namespace Server
} // namespace Envoy
1 change: 1 addition & 0 deletions test/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ envoy_cc_test(
"//source/extensions/tracers/zipkin:config",
"//source/server:process_context_lib",
"//source/server:server_lib",
"//test/common/config:dummy_config_proto_cc_proto",
"//test/common/stats:stat_test_utility_lib",
"//test/integration:integration_lib",
"//test/mocks/server:server_mocks",
Expand Down
50 changes: 50 additions & 0 deletions test/server/server_test.cc
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
#include <memory>

#include "envoy/config/core/v3/base.pb.h"
#include "envoy/server/bootstrap_extension_config.h"

#include "common/common/assert.h"
#include "common/common/version.h"
#include "common/network/address_impl.h"
#include "common/network/listen_socket_impl.h"
#include "common/network/socket_option_impl.h"
#include "common/protobuf/protobuf.h"
#include "common/thread_local/thread_local_impl.h"

#include "server/process_context_impl.h"
#include "server/server.h"

#include "test/common/config/dummy_config.pb.h"
#include "test/common/stats/stat_test_utility.h"
#include "test/integration/server.h"
#include "test/mocks/server/mocks.h"
Expand Down Expand Up @@ -1051,6 +1054,53 @@ TEST_P(ServerInstanceImplTest, WithProcessContext) {
EXPECT_FALSE(object_from_context.boolean_flag_);
}

class FooBootstrapExtension : public BootstrapExtension {};

TEST_P(ServerInstanceImplTest, WithBootstrapExtensions) {
NiceMock<Configuration::MockBootstrapExtensionFactory> mock_factory;
EXPECT_CALL(mock_factory, createEmptyConfigProto()).WillRepeatedly(Invoke([]() {
return std::make_unique<test::common::config::DummyConfig>();
}));
EXPECT_CALL(mock_factory, name()).WillRepeatedly(Return("envoy_test.bootstrap.foo"));
EXPECT_CALL(mock_factory, createBootstrapExtension(_, _))
.WillOnce(Invoke([](const Protobuf::Message& config, Configuration::ServerFactoryContext&) {
const auto* proto = dynamic_cast<const test::common::config::DummyConfig*>(&config);
EXPECT_NE(nullptr, proto);
EXPECT_EQ(proto->a(), "foo");
return std::make_unique<FooBootstrapExtension>();
}));

Registry::InjectFactory<Configuration::BootstrapExtensionFactory> registered_factory(
mock_factory);

EXPECT_NO_THROW(initialize("test/server/test_data/server/bootstrap_extensions.yaml"));
}

TEST_P(ServerInstanceImplTest, WithBootstrapExtensionsThrowingError) {
NiceMock<Configuration::MockBootstrapExtensionFactory> mock_factory;
EXPECT_CALL(mock_factory, createEmptyConfigProto()).WillRepeatedly(Invoke([]() {
return std::make_unique<test::common::config::DummyConfig>();
}));
EXPECT_CALL(mock_factory, name()).WillRepeatedly(Return("envoy_test.bootstrap.foo"));
EXPECT_CALL(mock_factory, createBootstrapExtension(_, _))
.WillOnce(Invoke([](const Protobuf::Message&,
Configuration::ServerFactoryContext&) -> BootstrapExtensionPtr {
throw EnvoyException("Unable to initiate mock_bootstrap_extension.");
}));

Registry::InjectFactory<Configuration::BootstrapExtensionFactory> registered_factory(
mock_factory);

EXPECT_THROW_WITH_REGEX(initialize("test/server/test_data/server/bootstrap_extensions.yaml"),
EnvoyException, "Unable to initiate mock_bootstrap_extension.");
}

TEST_P(ServerInstanceImplTest, WithUnknownBootstrapExtensions) {
EXPECT_THROW_WITH_REGEX(
initialize("test/server/test_data/server/bootstrap_extensions.yaml"), EnvoyException,
"Didn't find a registered implementation for name: 'envoy_test.bootstrap.foo'");
}

// Static configuration validation. We test with both allow/reject settings various aspects of
// configuration from YAML.
class StaticValidationTest
Expand Down
5 changes: 5 additions & 0 deletions test/server/test_data/server/bootstrap_extensions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
bootstrap_extensions:
- name: envoy_test.bootstrap.foo
typed_config:
"@type": type.googleapis.com/test.common.config.DummyConfig
a: foo

0 comments on commit 8cb2958

Please sign in to comment.