LCOV - code coverage report
Current view: top level - source/extensions/common/aws - metadata_fetcher.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 0 119 0.0 %
Date: 2024-01-05 06:35:25 Functions: 0 11 0.0 %

          Line data    Source code
       1             : #include "source/extensions/common/aws/metadata_fetcher.h"
       2             : 
       3             : #include "envoy/config/core/v3/base.pb.h"
       4             : #include "envoy/config/core/v3/http_uri.pb.h"
       5             : 
       6             : #include "source/common/common/enum_to_int.h"
       7             : #include "source/common/http/headers.h"
       8             : #include "source/common/http/utility.h"
       9             : #include "source/common/protobuf/utility.h"
      10             : 
      11             : namespace Envoy {
      12             : namespace Extensions {
      13             : namespace Common {
      14             : namespace Aws {
      15             : 
      16             : namespace {
      17             : 
      18             : class MetadataFetcherImpl : public MetadataFetcher,
      19             :                             public Logger::Loggable<Logger::Id::aws>,
      20             :                             public Http::AsyncClient::Callbacks {
      21             : 
      22             : public:
      23             :   MetadataFetcherImpl(Upstream::ClusterManager& cm, absl::string_view cluster_name)
      24           0 :       : cm_(cm), cluster_name_(std::string(cluster_name)) {}
      25             : 
      26           0 :   ~MetadataFetcherImpl() override { cancel(); }
      27             : 
      28           0 :   void cancel() override {
      29           0 :     if (request_ && !complete_) {
      30           0 :       request_->cancel();
      31           0 :       ENVOY_LOG(debug, "fetch AWS Metadata [cluster = {}]: cancelled", cluster_name_);
      32           0 :     }
      33           0 :     reset();
      34           0 :   }
      35             : 
      36           0 :   absl::string_view failureToString(MetadataFetcher::MetadataReceiver::Failure reason) override {
      37           0 :     switch (reason) {
      38           0 :     case MetadataFetcher::MetadataReceiver::Failure::Network:
      39           0 :       return "Network";
      40           0 :     case MetadataFetcher::MetadataReceiver::Failure::InvalidMetadata:
      41           0 :       return "InvalidMetadata";
      42           0 :     case MetadataFetcher::MetadataReceiver::Failure::MissingConfig:
      43           0 :       return "MissingConfig";
      44           0 :     default:
      45           0 :       return "";
      46           0 :     }
      47           0 :   }
      48             : 
      49             :   void fetch(Http::RequestMessage& message, Tracing::Span& parent_span,
      50           0 :              MetadataFetcher::MetadataReceiver& receiver) override {
      51           0 :     ASSERT(!request_);
      52           0 :     complete_ = false;
      53           0 :     receiver_ = makeOptRef(receiver);
      54           0 :     const auto thread_local_cluster = cm_.getThreadLocalCluster(cluster_name_);
      55           0 :     if (thread_local_cluster == nullptr) {
      56           0 :       ENVOY_LOG(error, "{} AWS Metadata failed: [cluster = {}] not found", __func__, cluster_name_);
      57           0 :       complete_ = true;
      58           0 :       receiver_->onMetadataError(MetadataFetcher::MetadataReceiver::Failure::MissingConfig);
      59           0 :       reset();
      60           0 :       return;
      61           0 :     }
      62             : 
      63           0 :     constexpr uint64_t MAX_RETRIES = 3;
      64           0 :     constexpr uint64_t RETRY_DELAY = 1000;
      65           0 :     constexpr uint64_t TIMEOUT = 5 * 1000;
      66             : 
      67           0 :     const auto host_attributes = Http::Utility::parseAuthority(message.headers().getHostValue());
      68           0 :     const auto host = host_attributes.host_;
      69           0 :     const auto path = message.headers().getPathValue();
      70           0 :     const auto scheme = message.headers().getSchemeValue();
      71           0 :     const auto method = message.headers().getMethodValue();
      72             : 
      73           0 :     const size_t query_offset = path.find('?');
      74             :     // Sanitize the path before logging.
      75             :     // However, the route debug log will still display the entire path.
      76             :     // So safely store the Envoy logs at debug level.
      77           0 :     const absl::string_view sanitized_path =
      78           0 :         query_offset != absl::string_view::npos ? path.substr(0, query_offset) : path;
      79           0 :     ENVOY_LOG(debug, "fetch AWS Metadata from the cluster {} at [uri = {}]", cluster_name_,
      80           0 :               fmt::format("{}://{}{}", scheme, host, sanitized_path));
      81             : 
      82           0 :     Http::RequestHeaderMapPtr headersPtr =
      83           0 :         Envoy::Http::createHeaderMap<Envoy::Http::RequestHeaderMapImpl>(
      84           0 :             {{Envoy::Http::Headers::get().Method, std::string(method)},
      85           0 :              {Envoy::Http::Headers::get().Host, std::string(host)},
      86           0 :              {Envoy::Http::Headers::get().Scheme, std::string(scheme)},
      87           0 :              {Envoy::Http::Headers::get().Path, std::string(path)}});
      88             : 
      89             :     // Copy the remaining headers.
      90           0 :     message.headers().iterate(
      91           0 :         [&headersPtr](const Http::HeaderEntry& entry) -> Http::HeaderMap::Iterate {
      92             :           // Skip pseudo-headers
      93           0 :           if (!entry.key().getStringView().empty() && entry.key().getStringView()[0] == ':') {
      94           0 :             return Http::HeaderMap::Iterate::Continue;
      95           0 :           }
      96           0 :           headersPtr->addCopy(Http::LowerCaseString(entry.key().getStringView()),
      97           0 :                               entry.value().getStringView());
      98           0 :           return Http::HeaderMap::Iterate::Continue;
      99           0 :         });
     100             : 
     101           0 :     auto messagePtr = std::make_unique<Envoy::Http::RequestMessageImpl>(std::move(headersPtr));
     102             : 
     103           0 :     auto options = Http::AsyncClient::RequestOptions()
     104           0 :                        .setTimeout(std::chrono::milliseconds(TIMEOUT))
     105           0 :                        .setParentSpan(parent_span)
     106           0 :                        .setSendXff(false)
     107           0 :                        .setChildSpanName("AWS Metadata Fetch");
     108             : 
     109           0 :     envoy::config::route::v3::RetryPolicy route_retry_policy;
     110           0 :     route_retry_policy.mutable_num_retries()->set_value(MAX_RETRIES);
     111           0 :     route_retry_policy.mutable_per_try_timeout()->CopyFrom(
     112           0 :         Protobuf::util::TimeUtil::MillisecondsToDuration(TIMEOUT));
     113           0 :     route_retry_policy.mutable_per_try_idle_timeout()->CopyFrom(
     114           0 :         Protobuf::util::TimeUtil::MillisecondsToDuration(RETRY_DELAY));
     115           0 :     route_retry_policy.set_retry_on("5xx,gateway-error,connect-failure,reset,refused-stream");
     116             : 
     117           0 :     options.setRetryPolicy(route_retry_policy);
     118           0 :     options.setBufferBodyForRetry(true);
     119           0 :     request_ = makeOptRefFromPtr(
     120           0 :         thread_local_cluster->httpAsyncClient().send(std::move(messagePtr), *this, options));
     121           0 :   }
     122             : 
     123             :   // HTTP async receive method on success.
     124           0 :   void onSuccess(const Http::AsyncClient::Request&, Http::ResponseMessagePtr&& response) override {
     125           0 :     ASSERT(receiver_);
     126           0 :     complete_ = true;
     127           0 :     const uint64_t status_code = Http::Utility::getResponseStatus(response->headers());
     128           0 :     if (status_code == enumToInt(Http::Code::OK)) {
     129           0 :       ENVOY_LOG(debug, "{}: fetch AWS Metadata [cluster = {}]: success", __func__, cluster_name_);
     130           0 :       if (response->body().length() != 0) {
     131           0 :         const auto body = response->bodyAsString();
     132           0 :         receiver_->onMetadataSuccess(std::move(body));
     133           0 :       } else {
     134           0 :         ENVOY_LOG(debug, "{}: fetch AWS Metadata [cluster = {}]: body is empty", __func__,
     135           0 :                   cluster_name_);
     136           0 :         receiver_->onMetadataError(MetadataFetcher::MetadataReceiver::Failure::InvalidMetadata);
     137           0 :       }
     138           0 :     } else {
     139           0 :       if (response->body().length() != 0) {
     140           0 :         ENVOY_LOG(debug, "{}: fetch AWS Metadata [cluster = {}]: response status code {}, body: {}",
     141           0 :                   __func__, cluster_name_, status_code, response->bodyAsString());
     142           0 :       } else {
     143           0 :         ENVOY_LOG(debug,
     144           0 :                   "{}: fetch AWS Metadata [cluster = {}]: response status code {}, body is empty",
     145           0 :                   __func__, cluster_name_, status_code);
     146           0 :       }
     147           0 :       receiver_->onMetadataError(MetadataFetcher::MetadataReceiver::Failure::Network);
     148           0 :     }
     149           0 :     reset();
     150           0 :   }
     151             : 
     152             :   // HTTP async receive method on failure.
     153             :   void onFailure(const Http::AsyncClient::Request&,
     154           0 :                  Http::AsyncClient::FailureReason reason) override {
     155           0 :     ASSERT(receiver_);
     156           0 :     ENVOY_LOG(debug, "{}: fetch AWS Metadata [cluster = {}]: network error {}", __func__,
     157           0 :               cluster_name_, enumToInt(reason));
     158           0 :     complete_ = true;
     159           0 :     receiver_->onMetadataError(MetadataFetcher::MetadataReceiver::Failure::Network);
     160           0 :     reset();
     161           0 :   }
     162             : 
     163             :   // TODO(suniltheta): Add metadata fetch status into the span like it is done on ext_authz filter.
     164           0 :   void onBeforeFinalizeUpstreamSpan(Tracing::Span&, const Http::ResponseHeaderMap*) override {}
     165             : 
     166             : private:
     167             :   bool complete_{};
     168             :   Upstream::ClusterManager& cm_;
     169             :   const std::string cluster_name_;
     170             :   OptRef<MetadataFetcher::MetadataReceiver> receiver_;
     171             :   OptRef<Http::AsyncClient::Request> request_;
     172             : 
     173           0 :   void reset() { request_.reset(); }
     174             : };
     175             : } // namespace
     176             : 
     177             : MetadataFetcherPtr MetadataFetcher::create(Upstream::ClusterManager& cm,
     178           0 :                                            absl::string_view cluster_name) {
     179           0 :   return std::make_unique<MetadataFetcherImpl>(cm, cluster_name);
     180           0 : }
     181             : } // namespace Aws
     182             : } // namespace Common
     183             : } // namespace Extensions
     184             : } // namespace Envoy

Generated by: LCOV version 1.15