Skip to content

Commit

Permalink
ORCA: Added utility methods to parse ORCA response headers from backe…
Browse files Browse the repository at this point in the history
…nds. (#35422)

Signed-off-by: blake-snyder <blakesnyder@google.com>
  • Loading branch information
blake-snyder authored Aug 8, 2024
1 parent 5b22dce commit 603bc61
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 0 deletions.
25 changes: 25 additions & 0 deletions source/common/orca/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_package",
)

licenses(["notice"]) # Apache 2

envoy_package()

envoy_cc_library(
name = "orca_parser",
srcs = ["orca_parser.cc"],
hdrs = ["orca_parser.h"],
external_deps = [
"abseil_strings",
"abseil_statusor",
"fmtlib",
],
deps = [
"//envoy/http:header_map_interface",
"//source/common/common:base64_lib",
"@com_github_cncf_xds//xds/data/orca/v3:pkg_cc_proto",
],
)
45 changes: 45 additions & 0 deletions source/common/orca/orca_parser.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "source/common/orca/orca_parser.h"

#include <string>

#include "envoy/http/header_map.h"

#include "source/common/common/base64.h"
#include "source/common/common/fmt.h"

#include "absl/strings/string_view.h"

using ::Envoy::Http::HeaderMap;
using xds::data::orca::v3::OrcaLoadReport;

namespace Envoy {
namespace Orca {

namespace {

const Http::LowerCaseString& endpointLoadMetricsHeaderBin() {
CONSTRUCT_ON_FIRST_USE(Http::LowerCaseString, kEndpointLoadMetricsHeaderBin);
}

} // namespace

absl::StatusOr<OrcaLoadReport> parseOrcaLoadReportHeaders(const HeaderMap& headers) {
OrcaLoadReport load_report;

// Binary protobuf format.
if (const auto header_bin = headers.get(endpointLoadMetricsHeaderBin()); !header_bin.empty()) {
const auto header_value = header_bin[0]->value().getStringView();
const std::string decoded_value = Envoy::Base64::decode(header_value);
if (!load_report.ParseFromString(decoded_value)) {
return absl::InvalidArgumentError(
fmt::format("unable to parse binaryheader to OrcaLoadReport: {}", header_value));
}
} else {
return absl::NotFoundError("no ORCA data sent from the backend");
}

return load_report;
}

} // namespace Orca
} // namespace Envoy
19 changes: 19 additions & 0 deletions source/common/orca/orca_parser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include "envoy/http/header_map.h"

#include "absl/status/statusor.h"
#include "xds/data/orca/v3/orca_load_report.pb.h"

namespace Envoy {
namespace Orca {

// Header used to send ORCA load metrics from the backend.
static constexpr absl::string_view kEndpointLoadMetricsHeaderBin = "endpoint-load-metrics-bin";

// Parses ORCA load metrics from a header map into an OrcaLoadReport proto.
// Supports serialized binary formats.
absl::StatusOr<xds::data::orca::v3::OrcaLoadReport>
parseOrcaLoadReportHeaders(const Envoy::Http::HeaderMap& headers);
} // namespace Orca
} // namespace Envoy
26 changes: 26 additions & 0 deletions test/common/orca/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_test",
"envoy_package",
)

licenses(["notice"]) # Apache 2

envoy_package()

envoy_cc_test(
name = "orca_parser_test",
srcs = ["orca_parser_test.cc"],
external_deps = [
"abseil_status",
"abseil_strings",
"fmtlib",
],
deps = [
"//source/common/common:base64_lib",
"//source/common/orca:orca_parser",
"//test/test_common:status_utility_lib",
"//test/test_common:utility_lib",
"@com_github_cncf_xds//xds/data/orca/v3:pkg_cc_proto",
],
)
72 changes: 72 additions & 0 deletions test/common/orca/orca_parser_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include <string>

#include "source/common/common/base64.h"
#include "source/common/orca/orca_parser.h"

#include "test/test_common/status_utility.h"
#include "test/test_common/utility.h"

#include "absl/status/status.h"
#include "xds/data/orca/v3/orca_load_report.pb.h"

namespace Envoy {
namespace Orca {
namespace {

// Returns an example OrcaLoadReport proto with all fields populated.
static xds::data::orca::v3::OrcaLoadReport exampleOrcaLoadReport() {
xds::data::orca::v3::OrcaLoadReport orca_load_report;
orca_load_report.set_cpu_utilization(0.7);
orca_load_report.set_application_utilization(0.8);
orca_load_report.set_mem_utilization(0.9);
orca_load_report.set_eps(2);
orca_load_report.set_rps_fractional(1000);
orca_load_report.mutable_named_metrics()->insert({"foo", 123});
orca_load_report.mutable_named_metrics()->insert({"bar", 0.2});
return orca_load_report;
}

TEST(OrcaParserUtilTest, NoHeaders) {
Http::TestRequestHeaderMapImpl headers{};
// parseOrcaLoadReport returns error when no ORCA data is sent from
// the backend.
EXPECT_THAT(parseOrcaLoadReportHeaders(headers),
StatusHelpers::HasStatus(absl::NotFoundError("no ORCA data sent from the backend")));
}

TEST(OrcaParserUtilTest, MissingOrcaHeaders) {
Http::TestRequestHeaderMapImpl headers{{"wrong-header", "wrong-value"}};
// parseOrcaLoadReport returns error when no ORCA data is sent from
// the backend.
EXPECT_THAT(parseOrcaLoadReportHeaders(headers),
StatusHelpers::HasStatus(absl::NotFoundError("no ORCA data sent from the backend")));
}

TEST(OrcaParserUtilTest, BinaryHeader) {
const std::string proto_string =
TestUtility::getProtobufBinaryStringFromMessage(exampleOrcaLoadReport());
const auto orca_load_report_header_bin =
Envoy::Base64::encode(proto_string.c_str(), proto_string.length());
Http::TestRequestHeaderMapImpl headers{
{std::string(kEndpointLoadMetricsHeaderBin), orca_load_report_header_bin}};
EXPECT_THAT(parseOrcaLoadReportHeaders(headers),
StatusHelpers::IsOkAndHolds(ProtoEq(exampleOrcaLoadReport())));
}

TEST(OrcaParserUtilTest, InvalidBinaryHeader) {
const std::string proto_string =
TestUtility::getProtobufBinaryStringFromMessage(exampleOrcaLoadReport());
// Force a bad base64 encoding by shortening the length of the output.
const auto orca_load_report_header_bin =
Envoy::Base64::encode(proto_string.c_str(), proto_string.length() / 2);
Http::TestRequestHeaderMapImpl headers{
{std::string(kEndpointLoadMetricsHeaderBin), orca_load_report_header_bin}};
EXPECT_THAT(parseOrcaLoadReportHeaders(headers),
StatusHelpers::HasStatus(
absl::StatusCode::kInvalidArgument,
testing::HasSubstr("unable to parse binaryheader to OrcaLoadReport")));
}

} // namespace
} // namespace Orca
} // namespace Envoy

0 comments on commit 603bc61

Please sign in to comment.