#include "common/common/fmt.h" #include "extensions/common/aws/utility.h" #include "test/integration/integration.h" #include "test/integration/utility.h" #include "test/server/utility.h" #include "test/test_common/utility.h" namespace Envoy { using Envoy::Extensions::Common::Aws::Utility; class AwsMetadataIntegrationTestBase : public ::testing::Test, public BaseIntegrationTest { public: AwsMetadataIntegrationTestBase(int status_code, int delay_s) : BaseIntegrationTest(Network::Address::IpVersion::v4, renderConfig(status_code, delay_s)) {} static std::string renderConfig(int status_code, int delay_s) { return absl::StrCat(ConfigHelper::baseConfig(), fmt::format(R"EOF( filter_chains: filters: name: http typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: metadata_test http_filters: - name: fault typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault delay: fixed_delay: seconds: {} nanos: {} percentage: numerator: 100 denominator: HUNDRED - name: envoy.filters.http.router codec_type: HTTP1 route_config: virtual_hosts: name: metadata_endpoint routes: - name: redirect_route redirect: prefix_rewrite: "/" match: prefix: "/redirect" - name: auth_route direct_response: status: {} body: inline_string: METADATA_VALUE_WITH_AUTH match: prefix: "/" headers: - name: Authorization exact_match: AUTH_TOKEN - name: no_auth_route direct_response: status: {} body: inline_string: METADATA_VALUE match: prefix: "/" domains: "*" name: route_config_0 )EOF", delay_s, delay_s > 0 ? 0 : 1000, status_code, status_code)); } void SetUp() override { BaseIntegrationTest::initialize(); } }; class AwsMetadataIntegrationTestSuccess : public AwsMetadataIntegrationTestBase { public: AwsMetadataIntegrationTestSuccess() : AwsMetadataIntegrationTestBase(200, 0) {} }; TEST_F(AwsMetadataIntegrationTestSuccess, Success) { const auto endpoint = fmt::format("{}:{}", Network::Test::getLoopbackAddressUrlString(version_), lookupPort("listener_0")); const auto response = Utility::metadataFetcher(endpoint, "", ""); ASSERT_TRUE(response.has_value()); EXPECT_EQ("METADATA_VALUE", *response); ASSERT_NE(nullptr, test_server_->counter("http.metadata_test.downstream_rq_completed")); EXPECT_EQ(1, test_server_->counter("http.metadata_test.downstream_rq_completed")->value()); } TEST_F(AwsMetadataIntegrationTestSuccess, AuthToken) { const auto endpoint = fmt::format("{}:{}", Network::Test::getLoopbackAddressUrlString(version_), lookupPort("listener_0")); const auto response = Utility::metadataFetcher(endpoint, "", "AUTH_TOKEN"); ASSERT_TRUE(response.has_value()); EXPECT_EQ("METADATA_VALUE_WITH_AUTH", *response); ASSERT_NE(nullptr, test_server_->counter("http.metadata_test.downstream_rq_completed")); EXPECT_EQ(1, test_server_->counter("http.metadata_test.downstream_rq_completed")->value()); } TEST_F(AwsMetadataIntegrationTestSuccess, Redirect) { const auto endpoint = fmt::format("{}:{}", Network::Test::getLoopbackAddressUrlString(version_), lookupPort("listener_0")); const auto response = Utility::metadataFetcher(endpoint, "redirect", "AUTH_TOKEN"); ASSERT_TRUE(response.has_value()); EXPECT_EQ("METADATA_VALUE_WITH_AUTH", *response); // We should make 2 requests, 1 that results in a redirect, and a final successful one ASSERT_NE(nullptr, test_server_->counter("http.metadata_test.downstream_rq_completed")); EXPECT_EQ(2, test_server_->counter("http.metadata_test.downstream_rq_completed")->value()); ASSERT_NE(nullptr, test_server_->counter("http.metadata_test.downstream_rq_3xx")); EXPECT_EQ(1, test_server_->counter("http.metadata_test.downstream_rq_3xx")->value()); } class AwsMetadataIntegrationTestFailure : public AwsMetadataIntegrationTestBase { public: AwsMetadataIntegrationTestFailure() : AwsMetadataIntegrationTestBase(503, 0) {} }; TEST_F(AwsMetadataIntegrationTestFailure, Failure) { const auto endpoint = fmt::format("{}:{}", Network::Test::getLoopbackAddressUrlString(version_), lookupPort("listener_0")); const auto start_time = timeSystem().monotonicTime(); const auto response = Utility::metadataFetcher(endpoint, "", ""); const auto end_time = timeSystem().monotonicTime(); EXPECT_FALSE(response.has_value()); // Verify correct number of retries ASSERT_NE(nullptr, test_server_->counter("http.metadata_test.downstream_rq_completed")); EXPECT_EQ(4, test_server_->counter("http.metadata_test.downstream_rq_completed")->value()); // Verify correct sleep time between retries: 4 * 1000 = 4000 EXPECT_LE(4000, std::chrono::duration_cast(end_time - start_time).count()); } class AwsMetadataIntegrationTestTimeout : public AwsMetadataIntegrationTestBase { public: AwsMetadataIntegrationTestTimeout() : AwsMetadataIntegrationTestBase(200, 10) {} }; TEST_F(AwsMetadataIntegrationTestTimeout, Timeout) { const auto endpoint = fmt::format("{}:{}", Network::Test::getLoopbackAddressUrlString(version_), lookupPort("listener_0")); const auto start_time = timeSystem().monotonicTime(); const auto response = Utility::metadataFetcher(endpoint, "", ""); const auto end_time = timeSystem().monotonicTime(); EXPECT_FALSE(response.has_value()); // We do now check http.metadata_test.downstream_rq_completed value here because it's // behavior is different between Linux and Mac when Curl disconnects on timeout. On Mac it is // incremented, while on Linux it is not. // Verify correct sleep time between retries: 4 * 5000 = 20000 EXPECT_LE(20000, std::chrono::duration_cast(end_time - start_time).count()); EXPECT_GT(40000, std::chrono::duration_cast(end_time - start_time).count()); } } // namespace Envoy