Skip to content
This repository was archived by the owner on Dec 16, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions test/integration/integration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -294,10 +294,9 @@ void BaseIntegrationTest::createUpstreams() {
}

void BaseIntegrationTest::createEnvoy() {
std::vector<uint32_t> ports;
for (auto& upstream : fake_upstreams_) {
if (upstream->localAddress()->ip()) {
ports.push_back(upstream->localAddress()->ip()->port());
ports_.push_back(upstream->localAddress()->ip()->port());
}
}

Expand All @@ -314,7 +313,7 @@ void BaseIntegrationTest::createEnvoy() {
// Note that finalize assumes that every fake_upstream_ must correspond to a bootstrap config
// static entry. So, if you want to manually create a fake upstream without specifying it in the
// config, you will need to do so *after* initialize() (which calls this function) is done.
config_helper_.finalize(ports);
config_helper_.finalize(ports_);

envoy::config::bootstrap::v2::Bootstrap bootstrap = config_helper_.bootstrap();
if (use_lds_) {
Expand Down
3 changes: 2 additions & 1 deletion test/integration/integration.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ struct ApiFilesystemConfig {
/**
* Test fixture for all integration tests.
*/
class BaseIntegrationTest : Logger::Loggable<Logger::Id::testing> {
class BaseIntegrationTest : protected Logger::Loggable<Logger::Id::testing> {
public:
using TestTimeSystemPtr = std::unique_ptr<Event::TestTimeSystem>;
using InstanceConstSharedPtrFn = std::function<Network::Address::InstanceConstSharedPtr(int)>;
Expand Down Expand Up @@ -363,6 +363,7 @@ class BaseIntegrationTest : Logger::Loggable<Logger::Id::testing> {
bool tls_xds_upstream_{false};
bool use_lds_{true}; // Use the integration framework's LDS set up.
Grpc::SotwOrDelta sotw_or_delta_{Grpc::SotwOrDelta::Sotw};
std::vector<uint32_t> ports_;

private:
// The type for the Envoy-to-backend connection
Expand Down
40 changes: 40 additions & 0 deletions test/stress/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
licenses(["notice"]) # Apache 2

load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_test",
"envoy_cc_test_library",
"envoy_package",
)

envoy_package()

envoy_cc_test_library(
name = "stress_test_lib",
srcs = [
"stress_test.cc",
"stress_test_common.cc",
"stress_test_downstream.cc",
"stress_test_upstream.cc",
],
hdrs = [
"stress_test.h",
"stress_test_common.h",
"stress_test_downstream.h",
"stress_test_upstream.h",
],
deps = [
"//source/server:server_lib",
"//test/integration:http_protocol_integration_lib",
],
)

envoy_cc_test(
name = "stress_test_self_test",
srcs = [
"stress_test_self_test.cc",
],
deps = [
":stress_test_lib",
],
)
196 changes: 196 additions & 0 deletions test/stress/stress_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
#include "stress_test.h"

namespace Envoy {
namespace Stress {

const std::string StressTest::ORIGIN_CLUSTER_NAME{"origin_cluster"};

static const std::string BOOTSTRAP_CONFIG = R"EOF(
admin:
access_log_path: /dev/null
address:
socket_address:
address: {}
port_value: 0
dynamic_resources:
lds_config:
path: /dev/null
static_resources:
listeners:
name: listener_0
address:
socket_address:
address: {}
port_value: 0
filter_chains:
filters:
name: envoy.http_connection_manager
config:
stat_prefix: config_test
http_filters:
name: envoy.router
codec_type: auto
access_log:
name: envoy.file_access_log
filter:
not_health_check_filter: {{}}
config:
path: /dev/null
route_config:
virtual_hosts:
name: integration
routes:
route:
cluster: {}
match:
prefix: "/"
domains: "*"
name: route_config_0
)EOF";

std::string StressTest::baseBootstrap(Network::Address::IpVersion ip_version) {
return fmt::format(BOOTSTRAP_CONFIG, Network::Test::getLoopbackAddressString(ip_version),
Network::Test::getLoopbackAddressString(ip_version), ORIGIN_CLUSTER_NAME);
}

ClusterHelper& StressTest::addCluster(ClusterHelperPtr&& cluster_helper) {
const std::string& name = cluster_helper->name();
auto it = clusters_.emplace(std::make_pair(
name, std::make_unique<Cluster>(std::move(cluster_helper), transport_socket_factory_,
ip_version_, http_type_)));

if (!it.second) {
throw EnvoyException(fmt::format("Duplicate cluster named '{}'", name));
}

return it.first->second->clusterHelper();
}

void StressTest::bind() {
for (auto& it : clusters_) {
it.second->bind();
}
}

LoadGeneratorPtr StressTest::start() {
{
const auto& it = clusters_.find(ORIGIN_CLUSTER_NAME);
if (it == clusters_.end()) {
throw EnvoyException(fmt::format("One cluster must be named '{}'", ORIGIN_CLUSTER_NAME));
}
}

for (auto& it : clusters_) {
it.second->start();
it.second->addClusterToBootstrap(config_helper_, ports_);
}

setUpstreamProtocol(Http::CodecClient::Type::HTTP2 == http_type_
? FakeHttpConnection::Type::HTTP2
: FakeHttpConnection::Type::HTTP1);
// Start envoy
HttpIntegrationTest::initialize();

ENVOY_LOG(debug, "Bootstrap Config:\n{}",
MessageUtil::getYamlStringFromMessage(config_helper_.bootstrap(), true));

Network::Address::InstanceConstSharedPtr address{
loopbackAddress(ip_version_, lookupPort("http"))};
return std::make_unique<LoadGenerator>(client_, transport_socket_factory_, http_type_, address);
}

uint16_t StressTest::firstPortInCluster(const std::string& cluster_name) const {
const auto& it = clusters_.find(cluster_name);
return it == clusters_.end() ? 0 : it->second->firstPort();
}

const ClusterHelper& StressTest::findCluster(const std::string& cluster_name) const {
const auto& it = clusters_.find(cluster_name);
if (it == clusters_.end()) {
throw EnvoyException(fmt::format("Cannot find cluster '{}'", cluster_name));
}
return it->second->clusterHelper();
}

void StressTest::stopServers() {
// Stop envoy by destroying it.
test_server_ = nullptr;

// Wait until all clusters have no more active connections
for (auto& it : clusters_) {
it.second->wait();
}
}

// Must be called before Envoy is stopped
void StressTest::extractCounters(StressTest::CounterMap& counters, const std::string& prefix) {
for (const auto& it : test_server_->stat_store().counters()) {
if (!absl::StartsWith(it->name(), prefix)) {
continue;
}
counters[it->name()] = it->value();
}
}

void StressTest::dumpCounters(StressTest::CounterMap& counters) {
for (const auto& it : counters) {
ENVOY_LOG(info, "{} = {}", it.first, it.second);
}
}

void StressTest::Cluster::bind() {
if (bound_) {
return;
}
for (size_t i = 0; i < cluster_helper_->servers().size(); ++i) {
listeners_.emplace_back(new LocalListenSocket(ip_version_));
ENVOY_LOG(debug, "{} bound port {}", cluster_helper_->name(),
listeners_.back()->localAddress()->ip()->port());
}
bound_ = true;
}

void StressTest::Cluster::addClusterToBootstrap(ConfigHelper& config_helper,
std::vector<uint32_t>& ports) const {
config_helper.addConfigModifier([this](envoy::config::bootstrap::v2::Bootstrap& bootstrap) {
auto cluster = bootstrap.mutable_static_resources()->add_clusters();

cluster->set_name(cluster_helper_->name());
cluster->set_type(envoy::api::v2::Cluster_DiscoveryType::Cluster_DiscoveryType_STATIC);
cluster->set_lb_policy(envoy::api::v2::Cluster_LbPolicy::Cluster_LbPolicy_ROUND_ROBIN);

if (http_type_ == Http::CodecClient::Type::HTTP1) {
auto opts = cluster->mutable_http_protocol_options();
opts->set_accept_http_10(false);
} else {
auto opts = cluster->mutable_http2_protocol_options();
auto value = opts->mutable_max_concurrent_streams();
value->set_value(2147483647U);
}

for (const auto& listener : listeners_) {
auto hosts = cluster->add_hosts();
auto address = hosts->mutable_socket_address();
address->set_address(Network::Test::getLoopbackAddressString(ip_version_));
address->set_port_value(listener->localAddress()->ip()->port());
}
});

// This avoids "assert failure: ports.size() > port_idx" complaints from
// ConfigHelper::finalize()
for (const auto& listener : listeners_) {
ports.push_back(listener->localAddress()->ip()->port());
}
}

void StressTest::Cluster::start() {
bind();
for (size_t i = 0; i < cluster_helper_->servers().size(); ++i) {
servers_.emplace_back(new Server(fmt::format("{}-{}", cluster_helper_->name(), i),
*listeners_[i], transport_socket_factory_, http_type_));
servers_.back()->start(*cluster_helper_->servers()[i]);
}
}

} // namespace Stress
} // namespace Envoy
94 changes: 94 additions & 0 deletions test/stress/stress_test.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#pragma once

#include "test/integration/http_integration.h"
#include "test/stress/stress_test_downstream.h"
#include "test/stress/stress_test_upstream.h"

namespace Envoy {
namespace Stress {

class StressTest : public HttpIntegrationTest {
public:
static const std::string ORIGIN_CLUSTER_NAME;

StressTest(Network::Address::IpVersion ip_protocol, Http::CodecClient::Type http_type)
: HttpIntegrationTest(http_type, ip_protocol, baseBootstrap(ip_protocol)),
ip_version_(ip_protocol), http_type_{http_type},
transport_socket_factory_{}, client_{"client"} {
// Tell the base class that we will create our own upstream origin server
fake_upstreams_count_ = 0;
}

protected:
Network::Address::IpVersion ipVersion() const { return ip_version_; }

Http::CodecClient::Type httpType() const { return http_type_; }

ClusterHelper& addCluster(ClusterHelperPtr&& cluster_helper);

void bind();

LoadGeneratorPtr start();

uint16_t envoyPort() { return static_cast<uint16_t>(lookupPort("http")); }

uint16_t firstPortInCluster(const std::string& cluster_name) const;

const ClusterHelper& findCluster(const std::string& cluster_name) const;

void stopServers();

using CounterMap = std::unordered_map<std::string, double>;

// Must be called before Envoy is stopped
void extractCounters(CounterMap& counters, const std::string& prefix = "");

void dumpCounters(CounterMap& counters);

private:
static std::string baseBootstrap(Network::Address::IpVersion ip_protocol);

class Cluster {
public:
Cluster(ClusterHelperPtr&& cluster_helper,
Network::TransportSocketFactory& transport_socket_factory,
Network::Address::IpVersion ip_version, Http::CodecClient::Type http_type)
: transport_socket_factory_{transport_socket_factory}, ip_version_{ip_version},
http_type_{http_type}, cluster_helper_{std::move(cluster_helper)} {}

void wait() { cluster_helper_->wait(); }

void bind();

uint16_t firstPort() const {
return static_cast<uint16_t>(listeners_[0]->localAddress()->ip()->port());
}

const ClusterHelper& clusterHelper() const { return *cluster_helper_; }
ClusterHelper& clusterHelper() { return *cluster_helper_; }

void addClusterToBootstrap(ConfigHelper& config_helper, std::vector<uint32_t>& ports) const;

void start();

private:
bool bound_{false};
Network::TransportSocketFactory& transport_socket_factory_;
Network::Address::IpVersion ip_version_;
Http::CodecClient::Type http_type_;
ClusterHelperPtr cluster_helper_;
std::vector<Network::TcpListenSocketPtr> listeners_;
std::vector<ServerPtr> servers_;
};

typedef std::unique_ptr<Cluster> ClusterPtr;

Network::Address::IpVersion ip_version_;
Http::CodecClient::Type http_type_;
Network::RawBufferSocketFactory transport_socket_factory_;
Client client_;
std::unordered_map<std::string, ClusterPtr> clusters_;
};

} // namespace Stress
} // namespace Envoy
Loading