Skip to content

Commit

Permalink
Envoy test changes for reverse connections
Browse files Browse the repository at this point in the history
Signed-off-by: Basundhara Chakrabarty <[email protected]>
  • Loading branch information
basundhara-c committed Nov 26, 2024
1 parent 55ea871 commit 589d672
Show file tree
Hide file tree
Showing 17 changed files with 679 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable<L
Stats::Scope& listenerScope() override { return *parent_.stats_store_.rootScope(); }
uint64_t listenerTag() const override { return tag_; }
const std::string& name() const override { return name_; }
const std::string& versionInfo() const override { PANIC("not implemented"); }
Network::UdpListenerConfigOptRef udpListenerConfig() override { return {}; }
Network::InternalListenerConfigOptRef internalListenerConfig() override {
if (internal_listener_config_ == nullptr) {
Expand Down
26 changes: 26 additions & 0 deletions test/extensions/bootstrap/reverse_connection/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
load(
"//bazel:envoy_build_system.bzl",
"envoy_package",
)
load(
"//test/extensions:extensions_build_system.bzl",
"envoy_extension_cc_test",
)

licenses(["notice"]) # Apache 2

envoy_package()

envoy_extension_cc_test(
name = "reverse_connection_manager_test",
srcs = ["reverse_connection_manager_test.cc"],
extension_names = ["envoy.bootstrap.internal_listener"],
deps = [
"//source/extensions/bootstrap/reverse_connection:reverse_connection_includes",
"//source/extensions/bootstrap/reverse_connection:reverse_connection_initiator_lib",
"//test/mocks/event:event_mocks",
"//test/mocks/network:network_mocks",
"//test/mocks/stats:stats_mocks",
"//test/test_common:test_runtime_lib",
],
)
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#include <memory>

#include "source/extensions/bootstrap/reverse_connection/reverse_connection_initiator.h"
#include "source/extensions/bootstrap/reverse_connection/reverse_connection_manager_impl.h"

#include "test/mocks/event/mocks.h"
#include "test/mocks/network/mocks.h"
#include "test/mocks/stats/mocks.h"
#include "test/test_common/test_runtime.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"

using testing::_;
using testing::Invoke;
using testing::NiceMock;
using testing::Return;
using testing::ReturnRef;

namespace Envoy {
namespace Extensions {
namespace Bootstrap {
namespace ReverseConnection {
namespace {

class ReverseConnectionManagerImplTest : public testing::Test {
public:
ReverseConnectionManagerImplTest()
: dispatcher_(std::make_unique<NiceMock<Event::MockDispatcher>>()),
rc_manager_(std::make_shared<
Envoy::Extensions::Bootstrap::ReverseConnection::ReverseConnectionManagerImpl>(
&dispatcher_)) {}

std::unique_ptr<NiceMock<Event::MockDispatcher>> dispatcher_;
std::shared_ptr<Envoy::Extensions::Bootstrap::ReverseConnection::ReverseConnectionManagerImpl>
rc_manager_;
NiceMock<Stats::MockScope> scope_;
NiceMock<Network::MockListenerConfig> listener_config_;
std::unique_ptr<ReverseConnectionInitiator> rc_initiator_;
};

TEST_F(ReverseConnectionManagerImplTest, InitializeStats) {
EXPECT_CALL(scope_, createScope_("reverse_conn_manager."))
.WillOnce(Return(std::make_shared<Stats::MockScope>()));
rc_manager_->initializeStats(scope_);
}

// TEST_F(ReverseConnectionManagerImplTest, RegisterRCInitiators) {
// auto reverse_conn_params =
// std::make_unique<Network::ReverseConnectionListenerConfig::ReverseConnParams>();
// reverse_conn_params->src_node_id_ = "node_id";
// reverse_conn_params->src_cluster_id_ = "cluster_id";
// reverse_conn_params->src_tenant_id_ = "tenant_id";
// reverse_conn_params->remote_cluster_to_conn_count_map_ = {{"remote_cluster", 1}};

// EXPECT_CALL(listener_config_, reverseConnectionListenerConfig())
// .WillRepeatedly(Return(&reverse_connection_listener_config_));
// EXPECT_CALL(reverse_connection_listener_config_, getReverseConnParams())
// .WillRepeatedly(ReturnRef(reverse_conn_params));

// // Expectations for the debug log statement
// EXPECT_CALL(listener_config_, name()).WillRepeatedly(Return("test_listener"));
// EXPECT_CALL(listener_config_, listenerTag()).WillRepeatedly(Return(123));
// EXPECT_CALL(listener_config_, versionInfo()).WillRepeatedly(Return("test_version"));
// EXPECT_CALL(*dispatcher_, name()).WillRepeatedly(Return("test_dispatcher"));

// // Expectation for findOrCreateRCInitiator
// EXPECT_CALL(rc_manager_, findOrCreateRCInitiator(_, _, _, _, _))
// .WillOnce(Invoke([&](const Network::ListenerConfig& listener_ref, const std::string&
// node_id, const std::string& cluster_id,
// const std::string& tenant_id, const std::string& remote_cluster)
// {
// return std::make_unique<ReverseConnectionInitiator>(node_id, cluster_id, tenant_id,
// remote_cluster);
// }));

// manager_.registerRCInitiators(listener_config_);
// }

TEST_F(ReverseConnectionManagerImplTest, RegisterRCInitiators) {
// Setup mocks for ListenerConfig and ReverseConnectionListenerConfig
NiceMock<Network::MockReverseConnectionListenerConfig> reverse_connection_listener_config;
Network::ReverseConnectionListenerConfig::ReverseConnParams reverse_conn_params = {
.src_node_id_ = "node-1",
.src_cluster_id_ = "cluster-1",
.src_tenant_id_ = "tenant-1",
.remote_cluster_to_conn_count_map_ = {{"remote-cluster-1", 10}, {"remote-cluster-2", 5}}};

// Setup ListenerConfig to return ReverseConnectionListenerConfig
EXPECT_CALL(listener_config_, reverseConnectionListenerConfig())
.WillRepeatedly(Return(&reverse_connection_listener_config));

// Setup ReverseConnectionListenerConfig to return ReverseConnectionParams
EXPECT_CALL(reverse_connection_listener_config, getReverseConnParams())
.WillRepeatedly(Return(&reverse_conn_params));

// Set up listener properties
const std::string listener_name = "test-listener";
const uint64_t listener_tag = 1234;
const std::string version_info = "v1";

EXPECT_CALL(listener_config_, name()).WillRepeatedly(ReturnRef(listener_name));
EXPECT_CALL(listener_config_, listenerTag()).WillRepeatedly(Return(listener_tag));
EXPECT_CALL(listener_config_, versionInfo()).WillRepeatedly(ReturnRef(version_info));

// Mock Dispatcher to validate post actions
EXPECT_CALL(*dispatcher_, post(_)).WillOnce(Invoke([](std::function<void()> cb) {
cb();
})); // Execute posted callback immediately

// Expectations for creating a new ReverseConnectionInitiator
EXPECT_CALL(*this, createRCInitiatorDone(_))
.WillOnce(Invoke([](ReverseConnectionInitiator* initiator) {
ASSERT_NE(initiator, nullptr);
ENVOY_LOG(debug, "RC Initiator successfully created");
}));

// Call the method under test
rc_manager_->registerRCInitiators(listener_config_);

// Verify that the ReverseConnectionInitiator was created and added to the map
auto it = rc_manager_->available_rc_initiators_.find(listener_tag);
EXPECT_NE(it, rc_manager_->available_rc_initiators_.end());
// EXPECT_EQ(it->second->getOptions().src_node_id, src_node_id);
// EXPECT_EQ(it->second->getOptions().src_cluster_id, src_cluster_id);
// EXPECT_EQ(it->second->getOptions().src_tenant_id, src_tenant_id);
// EXPECT_EQ(it->second->getOptions().remote_cluster_to_conn_count,
// remote_cluster_to_conn_count_map);
}

} // namespace
} // namespace ReverseConnection
} // namespace Bootstrap
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class ListenerFilterWithDataFuzzer : public Network::ListenerConfig,
uint64_t listenerTag() const override { return 1; }
ResourceLimit& openConnections() override { return open_connections_; }
const std::string& name() const override { return name_; }
const std::string& versionInfo() const override { PANIC("not implemented"); }
Network::UdpListenerConfigOptRef udpListenerConfig() override { return {}; }
Network::InternalListenerConfigOptRef internalListenerConfig() override { return {}; }
const Network::ListenerInfoConstSharedPtr& listenerInfo() const override {
Expand Down
1 change: 1 addition & 0 deletions test/integration/fake_upstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,7 @@ class FakeUpstream : Logger::Loggable<Logger::Id::testing>,
return parent_.socket_factories_;
}
bool bindToPort() const override { return true; }
const std::string& versionInfo() const override { PANIC("not implemented"); }
bool handOffRestoredDestinationConnections() const override { return false; }
uint32_t perConnectionBufferLimitBytes() const override { return 0; }
std::chrono::milliseconds listenerFiltersTimeout() const override { return {}; }
Expand Down
158 changes: 158 additions & 0 deletions test/integration/reverse_conn_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#include "test/integration/reverse_conn_test.h"

#include <string>

#include "source/common/http/header_map_impl.h"

#include "gtest/gtest.h"

namespace Envoy {

INSTANTIATE_TEST_SUITE_P(IpVersions, ReverseConnTest,
testing::ValuesIn(TestEnvironment::getIpVersionsForTest()),
TestUtility::ipTestParamsToString);

TEST_P(ReverseConnTest, TestReverseConn) {
initialize();

codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http")));

// create reverse connection
std::string path =
"/reverse_connections" +
Http::Utility::queryParamsToString(Http::Utility::QueryParams(
{{"node_id", "host_1"}, {"remote_cluster", "cloud_envoy"}, {"cluster_id", "cluster_1"}}));

auto response = codec_client_->makeHeaderOnlyRequest(Http::TestRequestHeaderMapImpl{
{":method", "POST"},
{":path", path},
{":scheme", "http"},
{":authority", "no-headers.com"},
});

waitForNextUpstreamRequest(0, TestUtility::DefaultTimeout);
// Send response headers, and end_stream if there is no response body.
upstream_request_->encodeHeaders(
Http::TestResponseHeaderMapImpl{
{":status", "200"},
{"content-length", "27"},
{"content-type", "text/plain"},
{"server", "envoy"},
{"custom-header-to-increase-response-size", "custom-header-value"},
},
false);
// Send any response data, with end_stream true.
Buffer::OwnedImpl response_data("reverse connection accepted");
upstream_request_->encodeData(response_data, false);

// Wait for the response to be read by the codec client.
ASSERT_TRUE(response->waitForEndStream());

EXPECT_TRUE(response->complete());
EXPECT_STREQ("reversed", response->body().c_str());
EXPECT_EQ("200", response->headers().Status()->value().getStringView());

// get reverse connection info
response = codec_client_->makeHeaderOnlyRequest(
Http::TestRequestHeaderMapImpl{{":method", "GET"},
{":path", "/reverse_connections"},
{":scheme", "http"},
{":authority", "host"}});
ASSERT_TRUE(response->waitForEndStream());
EXPECT_TRUE(response->complete());
EXPECT_EQ("200", response->headers().Status()->value().getStringView());
EXPECT_STREQ("{\"accepted\":[],\"connected\":[\"cloud_envoy\"]}", response->body().c_str());

response = codec_client_->makeHeaderOnlyRequest(
Http::TestRequestHeaderMapImpl{{":method", "GET"},
{":path", "/reverse_connections?remote_cluster=cloud_envoy"},
{":scheme", "http"},
{":authority", "host"}});
ASSERT_TRUE(response->waitForEndStream());
EXPECT_TRUE(response->complete());
EXPECT_EQ("200", response->headers().Status()->value().getStringView());
EXPECT_STREQ("{\"available_connections\":1}", response->body().c_str());

// delete reverse connections
response = codec_client_->makeHeaderOnlyRequest(
Http::TestRequestHeaderMapImpl{{":method", "DELETE"},
{":path", "/reverse_connections?remote_cluster=cloud_envoy"},
{":scheme", "http"},
{":authority", "host"}});
ASSERT_TRUE(response->waitForEndStream());
EXPECT_TRUE(response->complete());
EXPECT_EQ("200", response->headers().Status()->value().getStringView());

// get again
response = codec_client_->makeHeaderOnlyRequest(
Http::TestRequestHeaderMapImpl{{":method", "GET"},
{":path", "/reverse_connections"},
{":scheme", "http"},
{":authority", "host"}});
ASSERT_TRUE(response->waitForEndStream());
EXPECT_TRUE(response->complete());
EXPECT_EQ("200", response->headers().Status()->value().getStringView());
EXPECT_STREQ("{\"accepted\":[],\"connected\":[]}", response->body().c_str());

response = codec_client_->makeHeaderOnlyRequest(
Http::TestRequestHeaderMapImpl{{":method", "GET"},
{":path", "/reverse_connections?remote_cluster=cloud_envoy"},
{":scheme", "http"},
{":authority", "host"}});
ASSERT_TRUE(response->waitForEndStream());
EXPECT_TRUE(response->complete());
EXPECT_EQ("200", response->headers().Status()->value().getStringView());
EXPECT_STREQ("{\"available_connections\":0}", response->body().c_str());
}

TEST_P(ReverseConnTest, TestReverseConnUnknownCluster) {
initialize();

codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http")));
std::string path = "/reverse_connections" +
Http::Utility::queryParamsToString(
Http::Utility::QueryParams({{"node_id", "host_1"},
{"remote_cluster", "unknown_cluster"},
{"cluster_id", "cluster_1"}}));
auto response = codec_client_->makeHeaderOnlyRequest(Http::TestRequestHeaderMapImpl{
{":method", "POST"},
{":path", path},
{":scheme", "http"},
{":authority", "no-headers.com"},
});
ASSERT_TRUE(response->waitForEndStream());
EXPECT_TRUE(response->complete());
EXPECT_EQ("500", response->headers().Status()->value().getStringView());
EXPECT_STREQ("cluster unknown_cluster not found", response->body().c_str());
}

TEST_P(ReverseConnTest, GetUnknownCluster) {
initialize();

codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http")));
auto response = codec_client_->makeHeaderOnlyRequest(Http::TestRequestHeaderMapImpl{
{":method", "GET"},
{":path", "/reverse_connections?remote_cluster=unknown_cluster"},
{":scheme", "http"},
{":authority", "host"}});
ASSERT_TRUE(response->waitForEndStream());
EXPECT_TRUE(response->complete());
EXPECT_EQ("200", response->headers().Status()->value().getStringView());
EXPECT_STREQ("{\"available_connections\":0}", response->body().c_str());
}

TEST_P(ReverseConnTest, DeleteUnknownCluster) {
initialize();

codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http")));
auto response = codec_client_->makeHeaderOnlyRequest(Http::TestRequestHeaderMapImpl{
{":method", "DELETE"},
{":path", "/reverse_connections?remote_cluster=unknown_cluster"},
{":scheme", "http"},
{":authority", "host"}});
ASSERT_TRUE(response->waitForEndStream());
EXPECT_TRUE(response->complete());
EXPECT_EQ("200", response->headers().Status()->value().getStringView());
}

} // namespace Envoy
Loading

0 comments on commit 589d672

Please sign in to comment.