From 9feddc11baec8ee31c957e0b6b8f8988a0ef9d59 Mon Sep 17 00:00:00 2001 From: code Date: Thu, 1 Aug 2024 07:42:54 +0800 Subject: [PATCH 001/130] date formatter: fix/enhance the precision of date formatter (#35450) Commit Message: date formatter: fix the precision of date formatter Additional Description: In the previous implementation, the `DateFormatter` doesn't provide the full featured `absl::FormatTime` support. (Only seconds level precision is supported and the extended subseconds specifiers (`%E#S, %E*S, %E#f, %E*f` cannot work correctly). This PR refactored the DateFormatter class to provide related support. Risk Level: mid, core class. Testing: unit. Docs Changes: n/a. Release Notes: n/a. Platform Specific Features: n/a. --------- Signed-off-by: wbpcode --- changelogs/current.yaml | 4 + source/common/common/BUILD | 1 + source/common/common/utility.cc | 338 ++++++++++++++++++----------- source/common/common/utility.h | 118 +++++++--- test/common/common/utility_test.cc | 54 ++++- 5 files changed, 353 insertions(+), 162 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 7a0ac131d20b..ffe96071d8fc 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -100,6 +100,10 @@ new_features: change: | added %UPSTREAM_CLUSTER_RAW% access log formatter to log the original upstream cluster name, regardless of whether ``alt_stat_name`` is set. +- area: formatter + change: | + Added full feature absl::FormatTime() support to the DateFormatter. This allows the timepoint formatters (like + ``%START_TIME%``) to use ``%E#S``, ``%E*S``, ``%E#f`` and ``%E*f`` to format the subsecond part of the timepoint. - area: sockets change: | Added socket ``type`` field for specifying a socket type to apply the socket option to under :ref:`SocketOption diff --git a/source/common/common/BUILD b/source/common/common/BUILD index 2f4558fb3c6c..ec85b28e5ba1 100644 --- a/source/common/common/BUILD +++ b/source/common/common/BUILD @@ -476,6 +476,7 @@ envoy_cc_library( "//envoy/common:interval_set_interface", "//envoy/common:time_interface", "//source/common/singleton:const_singleton", + "@com_googlesource_code_re2//:re2", ], ) diff --git a/source/common/common/utility.cc b/source/common/common/utility.cc index 463683960a20..91dcd9d0f263 100644 --- a/source/common/common/utility.cc +++ b/source/common/common/utility.cc @@ -18,13 +18,13 @@ #include "source/common/common/hash.h" #include "source/common/singleton/const_singleton.h" -#include "absl/container/node_hash_map.h" #include "absl/strings/ascii.h" #include "absl/strings/match.h" #include "absl/strings/str_join.h" #include "absl/strings/str_replace.h" #include "absl/strings/str_split.h" #include "absl/time/time.h" +#include "re2/re2.h" #include "spdlog/spdlog.h" namespace Envoy { @@ -33,9 +33,11 @@ namespace { class SpecifierConstantValues { public: - // This captures three groups: subsecond-specifier, subsecond-specifier width and - // second-specifier. - const std::regex PATTERN{"(%([1-9])?f)|(%s)", std::regex::optimize}; + // This pattern contains three parts: + // 1. %[1-9*]?f: Envoy subseconds specifier with an optional width. + // 2. %s: Envoy seconds specifier. + // 3. %E[1-9*][Sf]: absl::FormatTime() seconds/subseconds specifier. + const re2::RE2 RE2_PATTERN{"(%[1-9*]?f|%s|%E[1-9*][Sf])"}; }; using SpecifierConstants = ConstSingleton; @@ -68,160 +70,240 @@ const std::string errorDetails(int error_code) { #endif } -std::string DateFormatter::fromTime(const SystemTime& time) const { - struct CachedTime { - // The string length of a number of seconds since the Epoch. E.g. for "1528270093", the length - // is 10. - size_t seconds_length; - - // A container object to hold a absl::FormatTime string, its timestamp (in seconds) and a list - // of position offsets for each specifier found in a format string. - struct Formatted { - // The resulted string after format string is passed to absl::FormatTime at a given point in - // time. - std::string str; - - // A timestamp (in seconds) when this object is created. - std::chrono::seconds epoch_time_seconds; - - // List of offsets for each specifier found in a format string. This is needed to compensate - // the position of each recorded specifier due to the possible size change of the previous - // segment (after being formatted). - SpecifierOffsets specifier_offsets; - }; - // A map is used to keep different formatted format strings at a given second. - absl::node_hash_map formatted; - }; - static thread_local CachedTime cached_time_utc; - static thread_local CachedTime cached_time_local; +std::string DateFormatter::fromTime(SystemTime time) const { + // A map is used to keep different formatted format strings at a given seconds. + static thread_local CachedTimes CACHE; + static thread_local CachedTimes CACHE_LOCAL; - const std::chrono::nanoseconds epoch_time_ns = - std::chrono::duration_cast(time.time_since_epoch()); + if (specifiers_.empty()) { + return {}; + } - const std::chrono::seconds epoch_time_seconds = - std::chrono::duration_cast(epoch_time_ns); + CachedTimes& cached_times = local_time_ ? CACHE_LOCAL : CACHE; + + const auto epoch_time_ss = + std::chrono::duration_cast(time.time_since_epoch()); + + const auto iter = cached_times.find(raw_format_string_, raw_format_hash_); + + if (iter == cached_times.end() || iter->second.epoch_time_seconds != epoch_time_ss) { + // No cached entry found for the given format string and time. - CachedTime& cached_time = local_time_ ? cached_time_local : cached_time_utc; - const auto& item = cached_time.formatted.find(raw_format_string_); - if (item == cached_time.formatted.end() || - item->second.epoch_time_seconds != epoch_time_seconds) { // Remove all the expired cached items. - for (auto it = cached_time.formatted.cbegin(); it != cached_time.formatted.cend();) { - if (it->second.epoch_time_seconds != epoch_time_seconds) { + for (auto it = cached_times.cbegin(); it != cached_times.cend();) { + if (it->second.epoch_time_seconds != epoch_time_ss) { auto next_it = std::next(it); - cached_time.formatted.erase(it); + cached_times.erase(it); it = next_it; } else { ++it; } } - const time_t current_time = std::chrono::system_clock::to_time_t(time); - - // Build a new formatted format string at current time. - CachedTime::Formatted formatted; - const std::string seconds_str = fmt::format_int(epoch_time_seconds.count()).str(); - formatted.str = - fromTimeAndPrepareSpecifierOffsets(current_time, formatted.specifier_offsets, seconds_str); - cached_time.seconds_length = seconds_str.size(); + auto new_iter = cached_times.emplace( + std::make_pair(raw_format_string_, formatTimeAndOffsets(time, epoch_time_ss))); - // Stamp the formatted string using the current epoch time in seconds, and then cache it in. - formatted.epoch_time_seconds = epoch_time_seconds; - cached_time.formatted.emplace(std::make_pair(raw_format_string_, formatted)); + ASSERT(new_iter.second); + return new_iter.first->second.formatted; } - const auto& formatted = cached_time.formatted.at(raw_format_string_); - ASSERT(specifiers_.size() == formatted.specifier_offsets.size()); + const auto& formatted = iter->second; + ASSERT(specifiers_.size() == formatted.offsets.size()); - // Copy the current cached formatted format string, then replace its subseconds part (when it has - // non-zero width) by correcting its position using prepared subseconds offsets. - std::string formatted_str = formatted.str; - std::string nanoseconds = fmt::format_int(epoch_time_ns.count()).str(); - // Special case handling for beginning of time, we should never need to do this outside of - // tests or a time machine. - if (nanoseconds.size() < 10) { - nanoseconds = std::string(10 - nanoseconds.size(), '0') + nanoseconds; - } - - for (size_t i = 0; i < specifiers_.size(); ++i) { - const auto& specifier = specifiers_.at(i); + // Copy the current cached formatted format string, then replace its subseconds specifier + // (when it has non-zero width) by correcting its position using prepared subseconds offsets. + std::string formatted_str = formatted.formatted; - // When specifier.width_ is zero, skip the replacement. This is the last segment or it has no - // specifier. - if (specifier.width_ > 0 && !specifier.second_) { - ASSERT(specifier.position_ + formatted.specifier_offsets.at(i) < formatted_str.size()); - formatted_str.replace(specifier.position_ + formatted.specifier_offsets.at(i), - specifier.width_, - nanoseconds.substr(cached_time.seconds_length, specifier.width_)); + for (size_t i = specifiers_.size(); i != 0; i--) { + if (specifiers_[i - 1].subsecondsSpecifier()) { + const auto offset = formatted.offsets[i - 1]; + const auto substr = specifiers_[i - 1].subsecondsToString(time); + formatted_str.replace(offset.offset, offset.length, substr); } } - - ASSERT(formatted_str.size() == formatted.str.size()); return formatted_str; } -void DateFormatter::parse(const std::string& format_string) { - std::string suffix = format_string; - std::smatch matched; - // "step" is the last specifier's position + the last specifier's width. It's not the current - // position in "format_string" because the length has changed. It is actually the index which - // points to the end of the last specifier in formatted string (generated in the future). - size_t step = 0; - while (regex_search(suffix, matched, SpecifierConstants::get().PATTERN)) { - // The std::smatch matched for (%([1-9])?f)|(%s): [all, subsecond-specifier, subsecond-specifier - // width, second-specifier]. - const std::string& width_specifier = matched[2]; - const std::string& second_specifier = matched[3]; - - // In the template string to be used in runtime substitution, the width is the number of - // characters to be replaced. - const size_t width = width_specifier.empty() ? 9 : width_specifier.at(0) - '0'; - - ASSERT(!suffix.empty()); - // This records matched position, the width of current subsecond pattern, and also the string - // segment before the matched position. These values will be used later at data path. - specifiers_.emplace_back( - second_specifier.empty() - ? Specifier(step + matched.position(), width, suffix.substr(0, matched.position())) - : Specifier(step + matched.position(), suffix.substr(0, matched.position()))); - step = specifiers_.back().position_ + specifiers_.back().width_; - suffix = matched.suffix(); +void DateFormatter::parse(absl::string_view format_string) { + absl::string_view format = format_string; + absl::string_view matched; + + // PartialMatch will return the leftmost-longest match. And the matched will be mutated + // to point to matched piece + while (re2::RE2::PartialMatch(format, SpecifierConstants::get().RE2_PATTERN, &matched)) { + ASSERT(!matched.empty()); + + // Get matched position based the matched and format view. For example: + // this-is-prefix-string-%s-this-is-suffix-string + // |------ prefix -------|| | + // | matched | + // |------------------ format ------------------| + const size_t prefix_length = matched.data() - format.data(); + + std::string prefix_string = std::string(format.substr(0, prefix_length)); + + Specifier::SpecifierType specifier{}; + uint8_t width{}; + + if (matched.size() == 2) { + // Handle the seconds specifier (%s) or subseconds specifier without width (%f). + + ASSERT(matched[1] == 's' || matched[1] == 'f'); + if (matched[1] == 's') { + specifier = Specifier::SpecifierType::Second; + } else { + specifier = Specifier::SpecifierType::EnvoySubsecond; + width = 9; + } + } else if (matched.size() == 3) { + // Handle subseconds specifier with width (%#f, %*f, '#' means number between 1-9, + // '*' is literal). + + ASSERT(matched[2] == 'f'); + specifier = Specifier::SpecifierType::EnvoySubsecond; + width = matched[1] == '*' ? std::numeric_limits::max() : matched[1] - '0'; + } else { + // To improve performance, we will cache the formatted string for every second. + // And when doing the runtime substitution, we will get the cached result and + // replace the subseconds part. + // In order to make sure the subseconds part of absl::FormatTime() can be replaced, + // we need to handle the subseconds specifier of absl::FormatTime() here. + + // Handle the absl subseconds specifier (%E#S, %E*S, %E#f, %E*f, '#' means number + // between 1-9, '*' is literal). + + ASSERT(matched.size() == 4); + ASSERT(matched[3] == 'S' || matched[3] == 'f'); + + if (matched[3] == 'S') { + // %E#S or %E*S includes the seconds, dot, and subseconds. For example, %E6S + // will output "30.654321". + // We will let the absl::FormatTime() to handle the seconds parts. So we need + // to add the seconds specifier to the prefix string to ensure the final + // formatted string is correct. + prefix_string.push_back('%'); + prefix_string.push_back('S'); + + specifier = Specifier::SpecifierType::AbslSubsecondS; + } else { + specifier = Specifier::SpecifierType::AbslSubsecondF; + } + + width = matched[2] == '*' ? std::numeric_limits::max() : matched[2] - '0'; + } + + if (!prefix_string.empty()) { + specifiers_.push_back(Specifier(prefix_string)); + } + + ASSERT(format.size() >= prefix_length + matched.size()); + ASSERT(specifier != Specifier::SpecifierType::String); + specifiers_.emplace_back(Specifier(specifier, width)); + format = format.substr(prefix_length + matched.size()); } // To capture the segment after the last specifier pattern of a format string by creating a zero // width specifier. E.g. %3f-this-is-the-last-%s-segment-%Y-until-this. - if (!suffix.empty()) { - Specifier specifier(step, 0, suffix); - specifiers_.emplace_back(specifier); + if (!format.empty()) { + specifiers_.emplace_back(Specifier(format)); + } +} + +std::string DateFormatter::Specifier::subsecondsToString(SystemTime time) const { + ASSERT(specifier_ > SpecifierType::Second); + ASSERT(width_ > 0); + + absl::string_view nanoseconds; + + std::string string_buffer; + fmt::format_int formatted(std::chrono::nanoseconds(time.time_since_epoch()).count()); + + // 1. Get the subseconds part of the formatted time. + if (formatted.size() < 9) { + // Special case. This should never happen in actual practice. + string_buffer = std::string(9 - formatted.size(), '0'); + string_buffer.append(formatted.data(), formatted.size()); + nanoseconds = string_buffer; + } else { + nanoseconds = absl::string_view(formatted.data() + formatted.size() - 9, 9); + } + ASSERT(nanoseconds.size() == 9); + + // 2. Calculate the actual width that will be used. + uint8_t width = width_; + if (width == std::numeric_limits::max()) { + // Dynamic width specifier. Remove trailing zeros. + if (const auto i = nanoseconds.find_last_not_of('0'); i != absl::string_view::npos) { + width = i + 1; + } else { + // This only happens for subseconds specifiers with dynamic width (%E*S, %E*f, %*f) + // and the subseconds are all zeros. + width = 0; + } } + + // 3. Handle the specifiers of %E#S and %E*S. The seconds part will be handled by + // absl::FormatTime() by string specifier. So we only need to return the dot and + // subseconds part. + if (specifier_ == SpecifierType::AbslSubsecondS) { + if (width == 0) { + // No subseconds and dot for %E*S in this case. + return {}; + } + + std::string output; + output.push_back('.'); // Add the dot. + output.append(nanoseconds.data(), width); + return output; + } + + // 4. Handle the specifiers of %E#f, %E*f, and %f, %#f, %*f. At least one subsecond digit + // will be returned for these specifiers even if the subseconds are all zeros and dynamic + // width is used. + return std::string(nanoseconds.substr(0, std::max(width, 1))); +} + +std::string DateFormatter::Specifier::toString(SystemTime time, + std::chrono::seconds epoch_time_seconds, + bool local) const { + switch (specifier_) { + case SpecifierType::String: + ASSERT(!string_.empty()); + // Handle the string first. It may be absl::FormatTime() format string. + if (absl_format_) { + return absl::FormatTime(string_, absl::FromChrono(time), + local ? absl::LocalTimeZone() : absl::UTCTimeZone()); + } else { + return string_; + } + case SpecifierType::Second: + // Handle the seconds specifier. + return fmt::format_int(epoch_time_seconds.count()).str(); + case SpecifierType::EnvoySubsecond: + case SpecifierType::AbslSubsecondS: + case SpecifierType::AbslSubsecondF: + // Handle the sub-seconds specifier. + return subsecondsToString(time); + } + + return {}; // Should never reach here. Make the gcc happy. } -std::string -DateFormatter::fromTimeAndPrepareSpecifierOffsets(time_t time, SpecifierOffsets& specifier_offsets, - const absl::string_view seconds_str) const { - std::string formatted_time; +DateFormatter::CacheableTime +DateFormatter::formatTimeAndOffsets(SystemTime time, + std::chrono::seconds epoch_time_seconds) const { + CacheableTime ret; + ret.epoch_time_seconds = epoch_time_seconds; + ret.formatted.reserve(64); + ret.offsets.reserve(specifiers_.size()); - int32_t previous = 0; - specifier_offsets.reserve(specifiers_.size()); for (const auto& specifier : specifiers_) { - std::string current_format = - absl::FormatTime(specifier.segment_, absl::FromTimeT(time), - local_time_ ? absl::LocalTimeZone() : absl::UTCTimeZone()); - absl::StrAppend(&formatted_time, current_format, - specifier.second_ ? seconds_str : std::string(specifier.width_, '?')); - - // This computes and saves offset of each specifier's pattern to correct its position after the - // previous string segment is formatted. An offset can be a negative value. - // - // If the current specifier is a second specifier (%s), it needs to be corrected by 2. - const int32_t offset = - (current_format.length() + (specifier.second_ ? (seconds_str.size() - 2) : 0)) - - specifier.segment_.size(); - specifier_offsets.emplace_back(previous + offset); - previous += offset; - } - - return formatted_time; + const auto offset = ret.formatted.size(); + ret.formatted.append(specifier.toString(time, epoch_time_seconds, local_time_)); + ret.offsets.push_back({offset, ret.formatted.size() - offset}); + } + + return ret; } std::string DateFormatter::now(TimeSource& time_source) const { diff --git a/source/common/common/utility.h b/source/common/common/utility.h index 872904b6a47a..47d1082da1cd 100644 --- a/source/common/common/utility.h +++ b/source/common/common/utility.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" +#include "absl/container/node_hash_map.h" #include "absl/strings/string_view.h" namespace Envoy { @@ -44,8 +46,9 @@ const std::string errorDetails(int error_code); */ class DateFormatter { public: - DateFormatter(const std::string& format_string, bool local_time = false) - : raw_format_string_(format_string), local_time_(local_time) { + DateFormatter(absl::string_view format_string, bool local_time = false) + : raw_format_string_(format_string), + raw_format_hash_(CachedTimes::hasher{}(raw_format_string_)), local_time_(local_time) { parse(format_string); } @@ -53,7 +56,7 @@ class DateFormatter { * @return std::string representing the GMT/UTC time based on the input time, * or local zone time when local_time_ is true. */ - std::string fromTime(const SystemTime& time) const; + std::string fromTime(SystemTime time) const; /** * @param time_source time keeping source. @@ -67,48 +70,101 @@ class DateFormatter { const std::string& formatString() const { return raw_format_string_; } private: - void parse(const std::string& format_string); - - using SpecifierOffsets = std::vector; - std::string fromTimeAndPrepareSpecifierOffsets(time_t time, SpecifierOffsets& specifier_offsets, - const absl::string_view seconds_str) const; + void parse(absl::string_view format_string); // A container to hold a specifiers (%f, %Nf, %s) found in a format string. - struct Specifier { - // To build a subsecond-specifier. - Specifier(const size_t position, const size_t width, const std::string& segment) - : position_(position), width_(width), segment_(segment), second_(false) {} + class Specifier { + public: + enum class SpecifierType : uint8_t { + String = 0, // means the specifier is a string specifier. + Second, // means the specifier is a seconds specifier. + EnvoySubsecond, // subseconds specifiers %f, %#f and %*f that supported by Envoy. + AbslSubsecondS, // subseconds specifiers %E#S, %E*S. + AbslSubsecondF, // subseconds specifiers %E#f, %E*f. + }; + + // To build a seconds or subseconds specifier. + Specifier(SpecifierType specifier, uint8_t width) : specifier_(specifier), width_(width) { + // String specifier should call another constructor. + ASSERT(specifier != SpecifierType::String); + + if (specifier == SpecifierType::Second) { + // Seconds specifier. + ASSERT(width == 0); + } else { + ASSERT(width >= 1); + ASSERT(width <= 9 || width == std::numeric_limits::max()); + } + } - // To build a second-specifier (%s), the number of characters to be replaced is always 2. - Specifier(const size_t position, const std::string& segment) - : position_(position), width_(2), segment_(segment), second_(true) {} + // To build a string specifier. + Specifier(absl::string_view string) + : string_(string), absl_format_(string_.find('%') != std::string::npos), + specifier_(SpecifierType::String) { + ASSERT(!string.empty()); + } - // The position/index of a specifier in a format string. - const size_t position_; + // Format a time point based on the specifier. + std::string toString(SystemTime time, std::chrono::seconds epoch_time_seconds, + bool local_time_zone) const; - // The width of a specifier, e.g. given %3f, the width is 3. If %f is set as the - // specifier, the width value should be 9 (the number of nanosecond digits). - const size_t width_; + // Format a time point based on the specifier. This should only be called for subseconds + // specifiers. + std::string subsecondsToString(SystemTime time) const; - // The string before the current specifier's position and after the previous found specifier. A - // segment may include absl::FormatTime accepted specifiers. E.g. given + /** + * @return bool whether the specifier is a subseconds specifier. + */ + bool subsecondsSpecifier() const { return specifier_ > SpecifierType::Second; } + + private: + // The format string before the current specifier's position and after the previous found + // specifier. The string may include absl::FormatTime accepted specifiers. E.g. given // "%3f-this-i%s-a-segment-%4f", the current specifier is "%4f" and the segment is - // "-this-i%s-a-segment-". - const std::string segment_; + // "-a-segment-". + const std::string string_; + // If the string_ contains absl::FormatTime accepted specifiers, this is true. Or string_ + // will be treated as raw string. + const bool absl_format_{}; + + // Specifier type. + const SpecifierType specifier_{}; - // As an indication that this specifier is a %s (expect to be replaced by seconds since the - // epoch). - const bool second_; + // The width of a sub-second specifier, e.g. given %3f, the width is 3. If %f is set as the + // specifier, the width value should be 9 (the number of nanosecond digits). + // The allowed values are: + // 0: only when the specifier is seconds specifier or string specifier. + // 1-9: fixed width of subseconds specifier. + // 255: std::numeric_limits::max(), full subseconds precision with dynamic width. + const uint8_t width_{}; }; - // This holds all specifiers found in a given format string. - std::vector specifiers_; + // A struct to hold the offset and length of a specifier result in the format string. + struct SpecifierOffset { + size_t offset{}; + size_t length{}; + }; + + struct CacheableTime { + std::chrono::seconds epoch_time_seconds; + + std::string formatted; + std::vector offsets; + }; + using CachedTimes = absl::node_hash_map; + + CacheableTime formatTimeAndOffsets(SystemTime time, + std::chrono::seconds epoch_time_seconds) const; // This is the format string as supplied in configuration, e.g. "foo %3f bar". const std::string raw_format_string_; + const size_t raw_format_hash_{}; + + // Use local time zone instead of UTC if this is set to true. + const bool local_time_{}; - // Whether use local time zone. - const bool local_time_; + // This holds all specifiers found in a given format string. + std::vector specifiers_; }; /** diff --git a/test/common/common/utility_test.cc b/test/common/common/utility_test.cc index 3e9550a546d6..10e52d189f56 100644 --- a/test/common/common/utility_test.cc +++ b/test/common/common/utility_test.cc @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -1043,6 +1044,56 @@ TEST(DateFormatter, FromTime) { const SystemTime time2(std::chrono::seconds(0)); EXPECT_EQ("1970-01-01T00:00:00.000Z", DateFormatter("%Y-%m-%dT%H:%M:%S.000Z").fromTime(time2)); EXPECT_EQ("aaa00", DateFormatter(std::string(3, 'a') + "%H").fromTime(time2)); + + const SystemTime time3(std::chrono::milliseconds(1522796769321)); + EXPECT_EQ("2018-04-03T23:06:09.321Z", DateFormatter("%Y-%m-%dT%H:%M:%E3SZ").fromTime(time3)); + EXPECT_EQ("aaa23", DateFormatter(std::string(3, 'a') + "%H").fromTime(time1)); + const SystemTime time4(std::chrono::seconds(0)); + EXPECT_EQ("1970-01-01T00:00:00.000Z", DateFormatter("%Y-%m-%dT%H:%M:%E3SZ").fromTime(time4)); + EXPECT_EQ("aaa00", DateFormatter(std::string(3, 'a') + "%H").fromTime(time2)); + + const SystemTime time5(std::chrono::milliseconds(321)); + EXPECT_EQ("1970-01-01T00:00:00.321Z", DateFormatter("%Y-%m-%dT%H:%M:%E3SZ").fromTime(time5)); + EXPECT_EQ("aaa00", DateFormatter(std::string(3, 'a') + "%H").fromTime(time2)); +} + +TEST(DateFormatter, DateFormatterVsAbslFormatTime) { + const std::string format = "%Y-%m-%dT%H:%M:%E3SZ %Ez %E*z %E8S %E*S %E5f %E*f %E4Y %ET"; + + SystemTime zero_time(std::chrono::seconds(0)); + + EXPECT_EQ(DateFormatter(format).fromTime(zero_time), + absl::FormatTime(format, absl::FromChrono(zero_time), absl::UTCTimeZone())); + + std::mt19937 prng(1); + std::uniform_int_distribution distribution(-10, 20); + + SystemTime now = std::chrono::system_clock::now(); // NO_CHECK_FORMAT(real_time) + + for (size_t i = 0; i < 20; i++) { + auto time = now + std::chrono::milliseconds(static_cast(distribution(prng))); + // UTC time zone. + EXPECT_EQ(DateFormatter(format).fromTime(time), + absl::FormatTime(format, absl::FromChrono(time), absl::UTCTimeZone())); + + // Local time zone. + EXPECT_EQ(DateFormatter(format, true).fromTime(time), + absl::FormatTime(format, absl::FromChrono(time), absl::LocalTimeZone())); + } +} + +TEST(DateFormatter, HybridAbsl) { + const std::string format = "%Y-%m-%dT%H:%M:%E3SZ %E6S %E*S %E4f %E*f %S. %s %3f %6f %9f"; + + const SystemTime time1(std::chrono::seconds(1522796769) + std::chrono::microseconds(123450)); + + EXPECT_EQ( + "2018-04-03T23:06:09.123Z 09.123450 09.12345 1234 12345 09. 1522796769 123 123450 123450000", + DateFormatter(format).fromTime(time1)); + + const SystemTime time2(std::chrono::seconds(0)); + EXPECT_EQ("1970-01-01T00:00:00.000Z 00.000000 00 0000 0 00. 0 000 000000 000000000", + DateFormatter(format).fromTime(time2)); } TEST(DateFormatter, FromTimeLocalTimeZone) { @@ -1084,9 +1135,6 @@ TEST(DateFormatter, ParseLongString) { EXPECT_EQ(expected_output, output); } -// Verify that two DateFormatter patterns with the same ??? patterns but -// different format strings don't false share cache entries. This is a -// regression test for when they did. TEST(DateFormatter, FromTimeSameWildcard) { const SystemTime time1(std::chrono::seconds(1522796769) + std::chrono::milliseconds(142)); EXPECT_EQ("2018-04-03T23:06:09.000Z142", From 9a474a30a1b9ecbfe1e9d1a5190ee8aef2b29041 Mon Sep 17 00:00:00 2001 From: Eitan Yarmush Date: Thu, 1 Aug 2024 01:18:16 -0400 Subject: [PATCH 002/130] Add the ability to set the hits_addend for a given rate_limit request via a hardcoded dynamic metadata field (#34184) Commit Message: Adds the ability to set the hits_addend for a given rate_limit request via a hardcoded dynamic metadata field: envoy.ratelimit:hits_addend. Additional Description: Risk Level: Low Testing: Added unit test. I have also manually tested this using gloo-edge as the control-plane. Docs Changes: Release Notes: Platform Specific Features: N/A [Optional Runtime guard:] N/A [Optional Fixes #Issue] N/A [Optional Fixes commit #PR or SHA] N/A [Optional Deprecated:] N/A [Optional [API Considerations](https://github.com/envoyproxy/envoy/blob/main/api/review_checklist.md):] N/A --------- Signed-off-by: Eitan Yarmush Signed-off-by: code Co-authored-by: code --- api/envoy/service/ratelimit/v3/rls.proto | 2 + changelogs/current.yaml | 4 ++ .../advanced/well_known_filter_state.rst | 5 ++ .../extensions/filters/http/ratelimit/BUILD | 2 + .../filters/http/ratelimit/ratelimit.cc | 32 +++++++++- .../filters/http/ratelimit/ratelimit_test.cc | 61 +++++++++++++++++++ 6 files changed, 105 insertions(+), 1 deletion(-) diff --git a/api/envoy/service/ratelimit/v3/rls.proto b/api/envoy/service/ratelimit/v3/rls.proto index 7375aceb5c2b..d69a323d88b7 100644 --- a/api/envoy/service/ratelimit/v3/rls.proto +++ b/api/envoy/service/ratelimit/v3/rls.proto @@ -49,6 +49,8 @@ message RateLimitRequest { // Rate limit requests can optionally specify the number of hits a request adds to the matched // limit. If the value is not set in the message, a request increases the matched limit by 1. + // This value can be overridden by setting filter state value ``envoy.ratelimit.hits_addend`` + // to the desired number. Invalid number (< 0) or number will be ignored. uint32 hits_addend = 3; } diff --git a/changelogs/current.yaml b/changelogs/current.yaml index ffe96071d8fc..cf1201c0c0a9 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -115,6 +115,10 @@ new_features: ` to allow overriding TLS certificate selection behavior. An extension can select certificate base on the incoming SNI, in both sync and async mode. +- area: ratelimit + change: | + Added the ability to modify :ref:`hits_addend ` + by setting by setting filter state value ``envoy.ratelimit.hits_addend`` to the desired value. - area: access_log change: | Added new access log command operators ``%START_TIME_LOCAL%`` and ``%EMIT_TIME_LOCAL%``, diff --git a/docs/root/configuration/advanced/well_known_filter_state.rst b/docs/root/configuration/advanced/well_known_filter_state.rst index f238dd207854..edcfc2e321ff 100644 --- a/docs/root/configuration/advanced/well_known_filter_state.rst +++ b/docs/root/configuration/advanced/well_known_filter_state.rst @@ -68,6 +68,11 @@ The following lists the filter state object keys used by the Envoy extensions: ` override on a per-connection basis. Accepts a count of milliseconds number string as a constructor. +``envoy.ratelimit.hits_addend`` + :ref:`Rate Limit Hits Addend + ` override on a per-route basis. + Accepts a number string as a constructor. + Filter state object fields -------------------------- diff --git a/source/extensions/filters/http/ratelimit/BUILD b/source/extensions/filters/http/ratelimit/BUILD index fd4c15c81ace..6d56028db50d 100644 --- a/source/extensions/filters/http/ratelimit/BUILD +++ b/source/extensions/filters/http/ratelimit/BUILD @@ -20,11 +20,13 @@ envoy_cc_library( ":ratelimit_headers_lib", "//envoy/http:codes_interface", "//envoy/ratelimit:ratelimit_interface", + "//envoy/stream_info:uint32_accessor_interface", "//source/common/common:assert_lib", "//source/common/common:empty_string", "//source/common/common:enum_to_int", "//source/common/http:codes_lib", "//source/common/router:config_lib", + "//source/common/stream_info:uint32_accessor_lib", "//source/extensions/filters/common/ratelimit:ratelimit_client_interface", "//source/extensions/filters/common/ratelimit:stat_names_lib", "@envoy_api//envoy/extensions/filters/http/ratelimit/v3:pkg_cc_proto", diff --git a/source/extensions/filters/http/ratelimit/ratelimit.cc b/source/extensions/filters/http/ratelimit/ratelimit.cc index 382029e5f3db..7052f8f793ed 100644 --- a/source/extensions/filters/http/ratelimit/ratelimit.cc +++ b/source/extensions/filters/http/ratelimit/ratelimit.cc @@ -4,6 +4,7 @@ #include #include "envoy/http/codes.h" +#include "envoy/stream_info/stream_info.h" #include "source/common/common/assert.h" #include "source/common/common/enum_to_int.h" @@ -11,6 +12,7 @@ #include "source/common/http/codes.h" #include "source/common/http/header_utility.h" #include "source/common/router/config_impl.h" +#include "source/common/stream_info/uint32_accessor_impl.h" #include "source/extensions/filters/http/ratelimit/ratelimit_headers.h" namespace Envoy { @@ -18,6 +20,26 @@ namespace Extensions { namespace HttpFilters { namespace RateLimitFilter { +namespace { +constexpr absl::string_view HitsAddendFilterStateKey = "envoy.ratelimit.hits_addend"; + +class HitsAddendObjectFactory : public StreamInfo::FilterState::ObjectFactory { +public: + std::string name() const override { return std::string(HitsAddendFilterStateKey); } + std::unique_ptr + createFromBytes(absl::string_view data) const override { + uint32_t hits_addend = 0; + if (absl::SimpleAtoi(data, &hits_addend)) { + return std::make_unique(hits_addend); + } + return nullptr; + } +}; + +REGISTER_FACTORY(HitsAddendObjectFactory, StreamInfo::FilterState::ObjectFactory); + +} // namespace + struct RcDetailsValues { // This request went above the configured limits for the rate limit filter. const std::string RateLimited = "request_rate_limited"; @@ -64,11 +86,19 @@ void Filter::initiateCall(const Http::RequestHeaderMap& headers) { break; } + const StreamInfo::UInt32Accessor* hits_addend_filter_state = + callbacks_->streamInfo().filterState()->getDataReadOnly( + HitsAddendFilterStateKey); + double hits_addend = 0; + if (hits_addend_filter_state != nullptr) { + hits_addend = hits_addend_filter_state->value(); + } + if (!descriptors.empty()) { state_ = State::Calling; initiating_call_ = true; client_->limit(*this, getDomain(), descriptors, callbacks_->activeSpan(), - callbacks_->streamInfo(), 0); + callbacks_->streamInfo(), hits_addend); initiating_call_ = false; } } diff --git a/test/extensions/filters/http/ratelimit/ratelimit_test.cc b/test/extensions/filters/http/ratelimit/ratelimit_test.cc index 8207ba087fab..6caa81e8eb5f 100644 --- a/test/extensions/filters/http/ratelimit/ratelimit_test.cc +++ b/test/extensions/filters/http/ratelimit/ratelimit_test.cc @@ -3,11 +3,13 @@ #include #include "envoy/extensions/filters/http/ratelimit/v3/rate_limit.pb.h" +#include "envoy/stream_info/stream_info.h" #include "source/common/buffer/buffer_impl.h" #include "source/common/common/empty_string.h" #include "source/common/http/context_impl.h" #include "source/common/http/headers.h" +#include "source/common/stream_info/uint32_accessor_impl.h" #include "source/extensions/filters/http/ratelimit/ratelimit.h" #include "test/extensions/filters/common/ratelimit/mocks.h" @@ -257,6 +259,53 @@ TEST_F(HttpRateLimitFilterTest, OkResponse) { 1U, filter_callbacks_.clusterInfo()->statsScope().counterFromStatName(ratelimit_ok_).value()); } +TEST_F(HttpRateLimitFilterTest, OkResponseWithAdditionalHitsAddend) { + setUpTest(filter_config_); + InSequence s; + + filter_callbacks_.stream_info_.filter_state_->setData( + "envoy.ratelimit.hits_addend", std::make_unique(5), + StreamInfo::FilterState::StateType::ReadOnly); + EXPECT_CALL(filter_callbacks_.route_->route_entry_.rate_limit_policy_, getApplicableRateLimit(0)); + + EXPECT_CALL(route_rate_limit_, populateDescriptors(_, _, _, _)) + .WillOnce(SetArgReferee<0>(descriptor_)); + + EXPECT_CALL(filter_callbacks_.route_->virtual_host_.rate_limit_policy_, + getApplicableRateLimit(0)); + + EXPECT_CALL(*client_, limit(_, "foo", + testing::ContainerEq(std::vector{ + {{{"descriptor_key", "descriptor_value"}}}}), + _, _, 5)) + .WillOnce( + WithArgs<0>(Invoke([&](Filters::Common::RateLimit::RequestCallbacks& callbacks) -> void { + request_callbacks_ = &callbacks; + }))); + + request_headers_.addCopy(Http::Headers::get().RequestId, "requestid"); + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->decodeHeaders(request_headers_, false)); + Http::MetadataMap metadata_map{{"metadata", "metadata"}}; + EXPECT_EQ(Http::FilterMetadataStatus::Continue, filter_->decodeMetadata(metadata_map)); + EXPECT_EQ(Http::FilterDataStatus::StopIterationAndWatermark, filter_->decodeData(data_, false)); + EXPECT_EQ(Http::FilterTrailersStatus::StopIteration, filter_->decodeTrailers(request_trailers_)); + EXPECT_EQ(Http::Filter1xxHeadersStatus::Continue, filter_->encode1xxHeaders(response_headers_)); + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); + EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data_, false)); + EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers_)); + + EXPECT_CALL(filter_callbacks_, continueDecoding()); + EXPECT_CALL(filter_callbacks_.stream_info_, + setResponseFlag(StreamInfo::CoreResponseFlag::RateLimited)) + .Times(0); + request_callbacks_->complete(Filters::Common::RateLimit::LimitStatus::OK, nullptr, nullptr, + nullptr, "", nullptr); + + EXPECT_EQ( + 1U, filter_callbacks_.clusterInfo()->statsScope().counterFromStatName(ratelimit_ok_).value()); +} + TEST_F(HttpRateLimitFilterTest, OkResponseWithHeaders) { setUpTest(filter_config_); InSequence s; @@ -1667,6 +1716,18 @@ TEST_F(HttpRateLimitFilterTest, StatsWithPrefix) { EXPECT_EQ("request_rate_limited", filter_callbacks_.details()); } +TEST(ObjectFactory, HitsAddend) { + const std::string name = "envoy.ratelimit.hits_addend"; + auto* factory = + Registry::FactoryRegistry::getFactory(name); + ASSERT_NE(nullptr, factory); + EXPECT_EQ(name, factory->name()); + const std::string hits_addend = std::to_string(1234); + auto object = factory->createFromBytes(hits_addend); + ASSERT_NE(nullptr, object); + EXPECT_EQ(hits_addend, object->serializeAsString()); +} + } // namespace } // namespace RateLimitFilter } // namespace HttpFilters From 6e55a3716fe9faa21d25d184f3d1c522de9cb696 Mon Sep 17 00:00:00 2001 From: "dependency-envoy[bot]" <148525496+dependency-envoy[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 10:08:05 +0100 Subject: [PATCH 003/130] deps/api: Bump `rules_proto` -> 6.0.2 (#35156) Created by Envoy dependency bot for @phlax Fix #34792 Signed-off-by: dependency-envoy[bot] <148525496+dependency-envoy[bot]@users.noreply.github.com> --------- Signed-off-by: Ryan Northey Co-authored-by: dependency-envoy[bot] <148525496+dependency-envoy[bot]@users.noreply.github.com> Co-authored-by: Ryan Northey From d469f34faa7c4ae046f8e61d1ec16f345d7d4c57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:39:07 +0100 Subject: [PATCH 004/130] build(deps): bump setuptools from 72.0.0 to 72.1.0 in /tools/base (#35492) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/base/requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index effde79fa116..5608dac5fc67 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -1671,7 +1671,7 @@ zstandard==0.23.0 \ # via envoy-base-utils # The following packages are considered to be unsafe in a requirements file: -setuptools==72.0.0 \ - --hash=sha256:5a0d9c6a2f332881a0153f629d8000118efd33255cfa802757924c53312c76da \ - --hash=sha256:98b4d786a12fadd34eabf69e8d014b84e5fc655981e4ff419994700434ace132 +setuptools==72.1.0 \ + --hash=sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1 \ + --hash=sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec # via -r requirements.in From a833cbfae33675b02b860bbb457c7aa89374f944 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 10:39:18 +0000 Subject: [PATCH 005/130] build(deps): bump protobuf from 5.27.2 to 5.27.3 in /tools/base (#35537) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/base/requirements.txt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index 5608dac5fc67..c90fee557dff 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -1019,18 +1019,18 @@ ply==3.11 \ --hash=sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3 \ --hash=sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce # via -r requirements.in -protobuf==5.27.2 \ - --hash=sha256:0e341109c609749d501986b835f667c6e1e24531096cff9d34ae411595e26505 \ - --hash=sha256:176c12b1f1c880bf7a76d9f7c75822b6a2bc3db2d28baa4d300e8ce4cde7409b \ - --hash=sha256:354d84fac2b0d76062e9b3221f4abbbacdfd2a4d8af36bab0474f3a0bb30ab38 \ - --hash=sha256:4fadd8d83e1992eed0248bc50a4a6361dc31bcccc84388c54c86e530b7f58863 \ - --hash=sha256:54330f07e4949d09614707c48b06d1a22f8ffb5763c159efd5c0928326a91470 \ - --hash=sha256:610e700f02469c4a997e58e328cac6f305f649826853813177e6290416e846c6 \ - --hash=sha256:7fc3add9e6003e026da5fc9e59b131b8f22b428b991ccd53e2af8071687b4fce \ - --hash=sha256:9e8f199bf7f97bd7ecebffcae45ebf9527603549b2b562df0fbc6d4d688f14ca \ - --hash=sha256:a109916aaac42bff84702fb5187f3edadbc7c97fc2c99c5ff81dd15dcce0d1e5 \ - --hash=sha256:b848dbe1d57ed7c191dfc4ea64b8b004a3f9ece4bf4d0d80a367b76df20bf36e \ - --hash=sha256:f3ecdef226b9af856075f28227ff2c90ce3a594d092c39bee5513573f25e2714 +protobuf==5.27.3 \ + --hash=sha256:043853dcb55cc262bf2e116215ad43fa0859caab79bb0b2d31b708f128ece035 \ + --hash=sha256:16ddf3f8c6c41e1e803da7abea17b1793a97ef079a912e42351eabb19b2cffe7 \ + --hash=sha256:68248c60d53f6168f565a8c76dc58ba4fa2ade31c2d1ebdae6d80f969cdc2d4f \ + --hash=sha256:82460903e640f2b7e34ee81a947fdaad89de796d324bcbc38ff5430bcdead82c \ + --hash=sha256:8572c6533e544ebf6899c360e91d6bcbbee2549251643d32c52cf8a5de295ba5 \ + --hash=sha256:a55c48f2a2092d8e213bd143474df33a6ae751b781dd1d1f4d953c128a415b25 \ + --hash=sha256:af7c0b7cfbbb649ad26132e53faa348580f844d9ca46fd3ec7ca48a1ea5db8a1 \ + --hash=sha256:b8a994fb3d1c11156e7d1e427186662b64694a62b55936b2b9348f0a7c6625ce \ + --hash=sha256:c2a105c24f08b1e53d6c7ffe69cb09d0031512f0b72f812dd4005b8112dbe91e \ + --hash=sha256:c84eee2c71ed83704f1afbf1a85c3171eab0fd1ade3b399b3fad0884cbcca8bf \ + --hash=sha256:dcb307cd4ef8fec0cf52cb9105a03d06fbb5275ce6d84a6ae33bc6cf84e0a07b # via # -r requirements.in # envoy-base-utils From fd66419bf21500816066c683db8532bab170ebf6 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Thu, 1 Aug 2024 09:16:56 -0400 Subject: [PATCH 006/130] exceptions: cleaning up macros (#35482) Signed-off-by: Alyssa Wilk --- envoy/common/exception.h | 11 +++-- .../api_listener_manager.cc | 2 +- source/common/config/datasource.cc | 4 +- .../common/filesystem/inotify/watcher_impl.cc | 2 +- .../common/filesystem/kqueue/watcher_impl.cc | 10 ++--- .../common/filesystem/win32/watcher_impl.cc | 2 +- source/common/filter/config_discovery_impl.h | 8 ++-- .../common/grpc/async_client_manager_impl.cc | 4 +- .../listener_manager/listener_manager_impl.cc | 2 +- source/common/network/resolver_impl.cc | 2 +- source/common/protobuf/visitor.cc | 4 +- source/common/router/config_impl.cc | 4 +- source/common/router/header_parser.cc | 6 +-- source/common/router/rds_impl.cc | 2 +- source/common/runtime/runtime_impl.cc | 10 ++--- source/common/secret/sds_api.cc | 2 +- source/common/stats/tag_producer_impl.cc | 2 +- source/common/tls/ocsp/ocsp.cc | 42 +++++++++---------- .../common/upstream/cluster_factory_impl.cc | 6 +-- .../common/upstream/cluster_manager_impl.cc | 16 +++---- .../upstream/health_discovery_service.cc | 2 +- source/common/upstream/upstream_impl.cc | 14 +++---- .../clusters/dynamic_forward_proxy/cluster.cc | 2 +- .../dns_cache_manager_impl.cc | 2 +- .../filters/http/match_delegate/config.cc | 2 +- .../network/dns_resolver/cares/dns_impl.cc | 2 +- .../transport_sockets/http_11_proxy/config.cc | 2 +- .../internal_upstream/config.cc | 2 +- .../proxy_protocol/config.cc | 2 +- .../transport_sockets/starttls/config.cc | 8 ++-- .../transport_sockets/tap/config.cc | 4 +- .../transport_sockets/tcp_stats/config.cc | 4 +- source/extensions/upstreams/http/config.cc | 8 ++-- source/server/api_listener_impl.cc | 2 +- source/server/configuration_impl.cc | 2 +- source/server/server.cc | 2 +- 36 files changed, 100 insertions(+), 101 deletions(-) diff --git a/envoy/common/exception.h b/envoy/common/exception.h index 327fa906c0fb..0688b440dfbc 100644 --- a/envoy/common/exception.h +++ b/envoy/common/exception.h @@ -57,15 +57,14 @@ class EnvoyException : public std::runtime_error { // the macros above. #define THROW_IF_STATUS_NOT_OK(variable, throw_action) THROW_IF_NOT_OK_REF(variable.status()); -// TODO(alyssawilk) remove in favor of RETURN_IF_NOT_OK -#define RETURN_IF_STATUS_NOT_OK(variable) \ - if (!variable.status().ok()) { \ - return variable.status(); \ +#define RETURN_IF_NOT_OK_REF(variable) \ + if (const absl::Status& temp_status = variable; !temp_status.ok()) { \ + return temp_status; \ } // Make sure this works for functions without calling the functoin twice as well. -#define RETURN_IF_NOT_OK(variable) \ - if (absl::Status temp_status = variable; !temp_status.ok()) { \ +#define RETURN_IF_NOT_OK(status_fn) \ + if (absl::Status temp_status = (status_fn); !temp_status.ok()) { \ return temp_status; \ } diff --git a/mobile/library/common/extensions/listener_managers/api_listener_manager/api_listener_manager.cc b/mobile/library/common/extensions/listener_managers/api_listener_manager/api_listener_manager.cc index 8a4f04320cbe..c89d321179b8 100644 --- a/mobile/library/common/extensions/listener_managers/api_listener_manager/api_listener_manager.cc +++ b/mobile/library/common/extensions/listener_managers/api_listener_manager/api_listener_manager.cc @@ -37,7 +37,7 @@ ApiListenerManagerImpl::addOrUpdateListener(const envoy::config::listener::v3::L } if (!api_listener_ && !added_via_api) { auto listener_or_error = HttpApiListener::create(config, server_, config.name()); - RETURN_IF_STATUS_NOT_OK(listener_or_error); + RETURN_IF_NOT_OK(listener_or_error.status()); api_listener_ = std::move(listener_or_error.value()); return true; } else { diff --git a/source/common/config/datasource.cc b/source/common/config/datasource.cc index 14457b3bff28..c086a2cd9339 100644 --- a/source/common/config/datasource.cc +++ b/source/common/config/datasource.cc @@ -40,7 +40,7 @@ absl::StatusOr readFile(const std::string& path, Api::Api& api, boo } auto file_content_or_error = file_system.fileReadToEnd(path); - RETURN_IF_STATUS_NOT_OK(file_content_or_error); + RETURN_IF_NOT_OK_REF(file_content_or_error.status()); if (!allow_empty && file_content_or_error.value().empty()) { return absl::InvalidArgumentError(fmt::format("file {} is empty", path)); @@ -118,7 +118,7 @@ absl::StatusOr DataSourceProvider::create(const ProtoData Api::Api& api, bool allow_empty, uint64_t max_size) { auto initial_data_or_error = read(source, allow_empty, api, max_size); - RETURN_IF_STATUS_NOT_OK(initial_data_or_error); + RETURN_IF_NOT_OK_REF(initial_data_or_error.status()); // read() only validates the size of the file and does not check the size of inline data. // We check the size of inline data here. diff --git a/source/common/filesystem/inotify/watcher_impl.cc b/source/common/filesystem/inotify/watcher_impl.cc index 549798e00bfc..534cd67f1933 100644 --- a/source/common/filesystem/inotify/watcher_impl.cc +++ b/source/common/filesystem/inotify/watcher_impl.cc @@ -36,7 +36,7 @@ absl::Status WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnCh // Because of general inotify pain, we always watch the directory that the file lives in, // and then synthetically raise per file events. auto result_or_error = file_system_.splitPathFromFilename(path); - RETURN_IF_STATUS_NOT_OK(result_or_error); + RETURN_IF_NOT_OK_REF(result_or_error.status()); const PathSplitResult result = result_or_error.value(); const uint32_t watch_mask = IN_MODIFY | IN_MOVED_TO; diff --git a/source/common/filesystem/kqueue/watcher_impl.cc b/source/common/filesystem/kqueue/watcher_impl.cc index 507cfcd8aab4..5827c19590ae 100644 --- a/source/common/filesystem/kqueue/watcher_impl.cc +++ b/source/common/filesystem/kqueue/watcher_impl.cc @@ -36,7 +36,7 @@ WatcherImpl::~WatcherImpl() { absl::Status WatcherImpl::addWatch(absl::string_view path, uint32_t events, Watcher::OnChangedCb cb) { absl::StatusOr watch_or_error = addWatch(path, events, cb, false); - RETURN_IF_STATUS_NOT_OK(watch_or_error); + RETURN_IF_NOT_OK_REF(watch_or_error.status()); if (watch_or_error.value() == nullptr) { return absl::InvalidArgumentError(absl::StrCat("invalid watch path ", path)); } @@ -56,7 +56,7 @@ absl::StatusOr WatcherImpl::addWatch(absl::string_vie } const auto result_or_error = file_system_.splitPathFromFilename(path); - RETURN_IF_STATUS_NOT_OK(result_or_error); + RETURN_IF_NOT_OK_REF(result_or_error.status()); watch_fd = open(std::string(result_or_error.value().directory_).c_str(), 0); if (watch_fd == -1) { return nullptr; @@ -116,7 +116,7 @@ absl::Status WatcherImpl::onKqueueEvent() { absl::StatusOr pathname_or_error = file_system_.splitPathFromFilename(file->file_); - RETURN_IF_STATUS_NOT_OK(pathname_or_error); + RETURN_IF_NOT_OK_REF(pathname_or_error.status()); PathSplitResult& pathname = pathname_or_error.value(); if (file->watching_dir_) { @@ -129,7 +129,7 @@ absl::Status WatcherImpl::onKqueueEvent() { if (event.fflags & NOTE_WRITE) { // directory was written -- check if the file we're actually watching appeared auto file_or_error = addWatch(file->file_, file->events_, file->callback_, true); - RETURN_IF_STATUS_NOT_OK(file_or_error); + RETURN_IF_NOT_OK_REF(file_or_error.status()); FileWatchPtr new_file = file_or_error.value(); if (new_file != nullptr) { removeWatch(file); @@ -150,7 +150,7 @@ absl::Status WatcherImpl::onKqueueEvent() { removeWatch(file); auto file_or_error = addWatch(file->file_, file->events_, file->callback_, true); - RETURN_IF_STATUS_NOT_OK(file_or_error); + RETURN_IF_NOT_OK_REF(file_or_error.status()); FileWatchPtr new_file = file_or_error.value(); if (new_file == nullptr) { return absl::OkStatus(); diff --git a/source/common/filesystem/win32/watcher_impl.cc b/source/common/filesystem/win32/watcher_impl.cc index 6cb9d00a1bc7..58905d996d22 100644 --- a/source/common/filesystem/win32/watcher_impl.cc +++ b/source/common/filesystem/win32/watcher_impl.cc @@ -56,7 +56,7 @@ absl::Status WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnCh } const absl::StatusOr result_or_error = file_system_.splitPathFromFilename(path); - RETURN_IF_STATUS_NOT_OK(result_or_error); + RETURN_IF_NOT_OK_REF(result_or_error.status()); const PathSplitResult& result = result_or_error.value(); // ReadDirectoryChangesW only has a Unicode version, so we need // to use wide strings here diff --git a/source/common/filter/config_discovery_impl.h b/source/common/filter/config_discovery_impl.h index 1aaec8a0ab3d..643e19eaffe6 100644 --- a/source/common/filter/config_discovery_impl.h +++ b/source/common/filter/config_discovery_impl.h @@ -111,7 +111,7 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa absl::Status onConfigUpdate(const Protobuf::Message& message, const std::string&, Config::ConfigAppliedCb applied_on_all_threads) override { const absl::StatusOr config_or_error = instantiateFilterFactory(message); - RETURN_IF_STATUS_NOT_OK(config_or_error); + RETURN_IF_NOT_OK_REF(config_or_error.status()); update(config_or_error.value(), applied_on_all_threads); return absl::OkStatus(); } @@ -120,7 +120,7 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa absl::optional cb; if (default_configuration_) { auto cb_or_error = instantiateFilterFactory(*default_configuration_); - RETURN_IF_STATUS_NOT_OK(cb_or_error); + RETURN_IF_NOT_OK_REF(cb_or_error.status()); cb = cb_or_error.value(); } update(cb, applied_on_all_threads); @@ -225,7 +225,7 @@ class HttpDynamicFilterConfigProviderImpl message.GetTypeName()); absl::StatusOr error_or_factory = factory->createFilterFactoryFromProto(message, getStatPrefix(), factory_context_); - RETURN_IF_STATUS_NOT_OK(error_or_factory); + RETURN_IF_NOT_OK_REF(error_or_factory.status()); return NamedHttpFilterFactoryCb{factory->name(), error_or_factory.value()}; } @@ -257,7 +257,7 @@ class NetworkDynamicFilterConfigProviderImplBase message.GetTypeName()); absl::StatusOr cb_or_error = factory->createFilterFactoryFromProto(message, factory_context_); - RETURN_IF_STATUS_NOT_OK(cb_or_error); + RETURN_IF_NOT_OK_REF(cb_or_error.status()); return cb_or_error.value(); } diff --git a/source/common/grpc/async_client_manager_impl.cc b/source/common/grpc/async_client_manager_impl.cc index 9d3bf65d132c..aabd46907636 100644 --- a/source/common/grpc/async_client_manager_impl.cc +++ b/source/common/grpc/async_client_manager_impl.cc @@ -167,7 +167,7 @@ absl::StatusOr AsyncClientManagerImpl::getOrCreateRawAs } auto factory_or_error = factoryForGrpcService(config_with_hash_key.config(), scope, skip_cluster_check); - RETURN_IF_STATUS_NOT_OK(factory_or_error); + RETURN_IF_NOT_OK_REF(factory_or_error.status()); client = factory_or_error.value()->createUncachedRawAsyncClient(); raw_async_client_cache_->setCache(config_with_hash_key, client); return client; @@ -183,7 +183,7 @@ AsyncClientManagerImpl::getOrCreateRawAsyncClientWithHashKey( } auto factory_or_error = factoryForGrpcService(config_with_hash_key.config(), scope, skip_cluster_check); - RETURN_IF_STATUS_NOT_OK(factory_or_error); + RETURN_IF_NOT_OK_REF(factory_or_error.status()); client = factory_or_error.value()->createUncachedRawAsyncClient(); raw_async_client_cache_->setCache(config_with_hash_key, client); return client; diff --git a/source/common/listener_manager/listener_manager_impl.cc b/source/common/listener_manager/listener_manager_impl.cc index f0841e119d3e..05ad90dcade9 100644 --- a/source/common/listener_manager/listener_manager_impl.cc +++ b/source/common/listener_manager/listener_manager_impl.cc @@ -474,7 +474,7 @@ ListenerManagerImpl::addOrUpdateListener(const envoy::config::listener::v3::List } if (!api_listener_ && !added_via_api) { auto listener_or_error = HttpApiListener::create(config, server_, config.name()); - RETURN_IF_STATUS_NOT_OK(listener_or_error); + RETURN_IF_NOT_OK_REF(listener_or_error.status()); api_listener_ = std::move(listener_or_error.value()); return true; } else { diff --git a/source/common/network/resolver_impl.cc b/source/common/network/resolver_impl.cc index c336580b6697..7f7b5ece9686 100644 --- a/source/common/network/resolver_impl.cc +++ b/source/common/network/resolver_impl.cc @@ -87,7 +87,7 @@ resolveProtoSocketAddress(const envoy::config::core::v3::SocketAddress& socket_a return absl::InvalidArgumentError(fmt::format("Unknown address resolver: {}", resolver_name)); } auto instance_or_error = resolver->resolve(socket_address); - RETURN_IF_STATUS_NOT_OK(instance_or_error); + RETURN_IF_NOT_OK_REF(instance_or_error.status()); return std::move(instance_or_error.value()); } diff --git a/source/common/protobuf/visitor.cc b/source/common/protobuf/visitor.cc index 6f16e6060d9b..f03a84e7f536 100644 --- a/source/common/protobuf/visitor.cc +++ b/source/common/protobuf/visitor.cc @@ -31,11 +31,11 @@ absl::Status traverseMessageWorker(ConstProtoVisitor& visitor, const Protobuf::M RETURN_IF_NOT_OK(MessageUtil::unpackTo(*any_message, *inner_message)); } else if (message.GetTypeName() == "xds.type.v3.TypedStruct") { auto output_or_error = Helper::convertTypedStruct(message); - RETURN_IF_STATUS_NOT_OK(output_or_error); + RETURN_IF_NOT_OK_REF(output_or_error.status()); std::tie(inner_message, target_type_url) = std::move(output_or_error.value()); } else if (message.GetTypeName() == "udpa.type.v1.TypedStruct") { auto output_or_error = Helper::convertTypedStruct(message); - RETURN_IF_STATUS_NOT_OK(output_or_error); + RETURN_IF_NOT_OK_REF(output_or_error.status()); std::tie(inner_message, target_type_url) = std::move(output_or_error.value()); } diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index 51bb67a87147..a031d98faa38 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -1253,7 +1253,7 @@ RouteEntryImplBase::buildPathRewriter(envoy::config::route::v3::Route route, route.route().path_rewrite_policy().typed_config(), validator, factory); absl::StatusOr rewriter = factory.createPathRewriter(*config); - RETURN_IF_STATUS_NOT_OK(rewriter); + RETURN_IF_NOT_OK_REF(rewriter.status()); return rewriter.value(); } @@ -1271,7 +1271,7 @@ RouteEntryImplBase::buildPathMatcher(envoy::config::route::v3::Route route, route.match().path_match_policy().typed_config(), validator, factory); absl::StatusOr matcher = factory.createPathMatcher(*config); - RETURN_IF_STATUS_NOT_OK(matcher); + RETURN_IF_NOT_OK_REF(matcher.status()); return matcher.value(); } diff --git a/source/common/router/header_parser.cc b/source/common/router/header_parser.cc index cd8e9b5ee893..4d33c0ce4580 100644 --- a/source/common/router/header_parser.cc +++ b/source/common/router/header_parser.cc @@ -89,7 +89,7 @@ HeaderParser::configure(const Protobuf::RepeatedPtrField& hea HeaderParserPtr header_parser(new HeaderParser()); for (const auto& header_value_option : headers_to_add) { auto entry_or_error = HeadersToAddEntry::create(header_value_option); - RETURN_IF_STATUS_NOT_OK(entry_or_error); + RETURN_IF_NOT_OK_REF(entry_or_error.status()); header_parser->headers_to_add_.emplace_back( Http::LowerCaseString(header_value_option.header().key()), std::move(entry_or_error.value())); @@ -105,7 +105,7 @@ absl::StatusOr HeaderParser::configure( for (const auto& header_value : headers_to_add) { auto entry_or_error = HeadersToAddEntry::create(header_value, append_action); - RETURN_IF_STATUS_NOT_OK(entry_or_error); + RETURN_IF_NOT_OK_REF(entry_or_error.status()); header_parser->headers_to_add_.emplace_back(Http::LowerCaseString(header_value.key()), std::move(entry_or_error.value())); } @@ -117,7 +117,7 @@ absl::StatusOr HeaderParser::configure(const Protobuf::RepeatedPtrField& headers_to_add, const Protobuf::RepeatedPtrField& headers_to_remove) { auto parser_or_error = configure(headers_to_add); - RETURN_IF_STATUS_NOT_OK(parser_or_error); + RETURN_IF_NOT_OK_REF(parser_or_error.status()); HeaderParserPtr header_parser = std::move(parser_or_error.value()); for (const auto& header : headers_to_remove) { diff --git a/source/common/router/rds_impl.cc b/source/common/router/rds_impl.cc index 78409903f215..43cb301978ea 100644 --- a/source/common/router/rds_impl.cc +++ b/source/common/router/rds_impl.cc @@ -50,7 +50,7 @@ absl::Status RdsRouteConfigSubscription::beforeProviderUpdate( resume_rds); auto subscription_or_error = VhdsSubscription::createVhdsSubscription( config_update_info_, factory_context_, stat_prefix_, route_config_provider_); - RETURN_IF_STATUS_NOT_OK(subscription_or_error); + RETURN_IF_NOT_OK_REF(subscription_or_error.status()); vhds_subscription_ = std::move(subscription_or_error.value()); vhds_subscription_->registerInitTargetWithInitManager( noop_init_manager == nullptr ? local_init_manager_ : *noop_init_manager); diff --git a/source/common/runtime/runtime_impl.cc b/source/common/runtime/runtime_impl.cc index e2a0619393e4..917ef6e655a1 100644 --- a/source/common/runtime/runtime_impl.cc +++ b/source/common/runtime/runtime_impl.cc @@ -439,9 +439,9 @@ absl::Status DiskLayer::walkDirectory(const std::string& path, const std::string Filesystem::Directory directory(path); Filesystem::DirectoryIteratorImpl it = directory.begin(); - RETURN_IF_STATUS_NOT_OK(it); + RETURN_IF_NOT_OK_REF(it.status()); for (; it != directory.end(); ++it) { - RETURN_IF_STATUS_NOT_OK(it); + RETURN_IF_NOT_OK_REF(it.status()); Filesystem::DirectoryEntry entry = *it; std::string full_path = path + "/" + entry.name_; std::string full_prefix; @@ -465,7 +465,7 @@ absl::Status DiskLayer::walkDirectory(const std::string& path, const std::string // Read the file and remove any comments. A comment is a line starting with a '#' character. // Comments are useful for placeholder files with no value. auto file_or_error = api.fileSystem().fileReadToEnd(full_path); - RETURN_IF_STATUS_NOT_OK(file_or_error); + RETURN_IF_NOT_OK_REF(file_or_error.status()); const std::string text_file{file_or_error.value()}; const auto lines = StringUtil::splitToken(text_file, "\n"); @@ -492,7 +492,7 @@ absl::Status DiskLayer::walkDirectory(const std::string& path, const std::string #endif } } - RETURN_IF_STATUS_NOT_OK(it); + RETURN_IF_NOT_OK_REF(it.status()); return absl::OkStatus(); } @@ -721,7 +721,7 @@ absl::Status RtdsSubscription::onConfigRemoved( absl::Status LoaderImpl::loadNewSnapshot() { auto snapshot_or_error = createNewSnapshot(); - RETURN_IF_STATUS_NOT_OK(snapshot_or_error); + RETURN_IF_NOT_OK_REF(snapshot_or_error.status()); std::shared_ptr ptr = std::move(snapshot_or_error.value()); tls_->set([ptr](Event::Dispatcher&) -> ThreadLocal::ThreadLocalObjectSharedPtr { return std::static_pointer_cast(ptr); diff --git a/source/common/secret/sds_api.cc b/source/common/secret/sds_api.cc index a8ab37af3ee6..620bffb4972d 100644 --- a/source/common/secret/sds_api.cc +++ b/source/common/secret/sds_api.cc @@ -126,7 +126,7 @@ absl::Status SdsApi::onConfigUpdate(const std::vectoraddWatch(absl::StrCat(result_or_error.value().directory_, "/"), Filesystem::Watcher::Events::MovedTo, [this](uint32_t) { diff --git a/source/common/stats/tag_producer_impl.cc b/source/common/stats/tag_producer_impl.cc index e01fd800f668..082f98a722bf 100644 --- a/source/common/stats/tag_producer_impl.cc +++ b/source/common/stats/tag_producer_impl.cc @@ -158,7 +158,7 @@ TagProducerImpl::addDefaultExtractors(const envoy::config::metrics::v3::StatsCon for (const auto& desc : Config::TagNames::get().descriptorVec()) { auto extractor_or_error = TagExtractorImplBase::createTagExtractor( desc.name_, desc.regex_, desc.substr_, desc.negative_match_, desc.re_type_); - RETURN_IF_STATUS_NOT_OK(extractor_or_error); + RETURN_IF_NOT_OK_REF(extractor_or_error.status()); addExtractor(std::move(extractor_or_error.value())); } for (const auto& desc : Config::TagNames::get().tokenizedDescriptorVec()) { diff --git a/source/common/tls/ocsp/ocsp.cc b/source/common/tls/ocsp/ocsp.cc index b6b52eda7649..51ef8566a6b1 100644 --- a/source/common/tls/ocsp/ocsp.cc +++ b/source/common/tls/ocsp/ocsp.cc @@ -45,9 +45,9 @@ absl::Status skipResponderId(CBS& cbs) { // (excluding the tag and length fields) auto opt1 = Asn1Utility::getOptional(cbs, CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1); - RETURN_IF_STATUS_NOT_OK(opt1); + RETURN_IF_NOT_OK_REF(opt1.status()); auto opt2 = Asn1Utility::getOptional(cbs, CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 2); - RETURN_IF_STATUS_NOT_OK(opt2); + RETURN_IF_NOT_OK_REF(opt2.status()); if (opt1.value() || opt2.value()) { return absl::OkStatus(); @@ -64,11 +64,11 @@ absl::Status skipCertStatus(CBS& cbs) { // unknown [2] IMPLICIT UnknownInfo // } auto opt1 = Asn1Utility::getOptional(cbs, CBS_ASN1_CONTEXT_SPECIFIC | 0); - RETURN_IF_STATUS_NOT_OK(opt1); + RETURN_IF_NOT_OK_REF(opt1.status()); auto opt2 = Asn1Utility::getOptional(cbs, CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1); - RETURN_IF_STATUS_NOT_OK(opt2); + RETURN_IF_NOT_OK_REF(opt2.status()); auto opt3 = Asn1Utility::getOptional(cbs, CBS_ASN1_CONTEXT_SPECIFIC | 2); - RETURN_IF_STATUS_NOT_OK(opt3); + RETURN_IF_NOT_OK_REF(opt3.status()); if (!(opt1.value() || opt2.value() || opt3.value())) { return absl::InvalidArgumentError( @@ -181,14 +181,14 @@ absl::StatusOr> Asn1OcspUtility::parseOcspResponse } auto status_or_error = Asn1OcspUtility::parseResponseStatus(elem); - RETURN_IF_STATUS_NOT_OK(status_or_error); + RETURN_IF_NOT_OK_REF(status_or_error.status()); auto opt = Asn1Utility::getOptional(elem, CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0); - RETURN_IF_STATUS_NOT_OK(opt); + RETURN_IF_NOT_OK_REF(opt.status()); auto maybe_bytes = opt.value(); ResponsePtr resp = nullptr; if (maybe_bytes) { auto resp_or_error = Asn1OcspUtility::parseResponseBytes(maybe_bytes.value()); - RETURN_IF_STATUS_NOT_OK(resp_or_error); + RETURN_IF_NOT_OK_REF(resp_or_error.status()); resp = std::move(resp_or_error.value()); } @@ -243,7 +243,7 @@ absl::StatusOr Asn1OcspUtility::parseResponseBytes(CBS& cbs) { } auto parse_or_error = Asn1Utility::parseOid(elem); - RETURN_IF_STATUS_NOT_OK(parse_or_error); + RETURN_IF_NOT_OK_REF(parse_or_error.status()); auto oid_str = parse_or_error.value(); if (!CBS_get_asn1(&elem, &response, CBS_ASN1_OCTETSTRING)) { return absl::InvalidArgumentError("Expected ASN.1 OCTETSTRING for response"); @@ -271,7 +271,7 @@ Asn1OcspUtility::parseBasicOcspResponse(CBS& cbs) { "OCSP BasicOCSPResponse is not a wellf-formed ASN.1 SEQUENCE"); } auto response_or_error = Asn1OcspUtility::parseResponseData(elem); - RETURN_IF_STATUS_NOT_OK(response_or_error); + RETURN_IF_NOT_OK_REF(response_or_error.status()); // The `signatureAlgorithm` and `signature` are ignored because OCSP // responses are expected to be delivered from a reliable source. // Optional additional certs are ignored. @@ -295,11 +295,11 @@ absl::StatusOr Asn1OcspUtility::parseResponseData(CBS& cbs) { // only support v1, the value of v1 is 0x00 auto version_or_error = Asn1Utility::getOptional(elem, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0); - RETURN_IF_STATUS_NOT_OK(version_or_error); + RETURN_IF_NOT_OK_REF(version_or_error.status()); auto version_cbs = version_or_error.value(); if (version_cbs.has_value()) { auto version_or_error = Asn1Utility::parseInteger(*version_cbs); - RETURN_IF_STATUS_NOT_OK(version_or_error); + RETURN_IF_NOT_OK_REF(version_or_error.status()); auto version = version_or_error.value(); if (version != "00") { return absl::InvalidArgumentError( @@ -309,10 +309,10 @@ absl::StatusOr Asn1OcspUtility::parseResponseData(CBS& cbs) { auto status = skipResponderId(elem); RETURN_IF_NOT_OK(status); - RETURN_IF_STATUS_NOT_OK(Asn1Utility::skip(elem, CBS_ASN1_GENERALIZEDTIME)); + RETURN_IF_NOT_OK_REF(Asn1Utility::skip(elem, CBS_ASN1_GENERALIZEDTIME).status()); auto responses_or_error = Asn1Utility::parseSequenceOf( elem, [](CBS& cbs) -> absl::StatusOr { return {parseSingleResponse(cbs)}; }); - RETURN_IF_STATUS_NOT_OK(responses_or_error); + RETURN_IF_NOT_OK_REF(responses_or_error.status()); // Extensions currently ignored. return {std::move(responses_or_error.value())}; @@ -332,14 +332,14 @@ absl::StatusOr Asn1OcspUtility::parseSingleResponse(CBS& cbs) { } auto id_or_error = Asn1OcspUtility::parseCertId(elem); - RETURN_IF_STATUS_NOT_OK(id_or_error); + RETURN_IF_NOT_OK_REF(id_or_error.status()); RETURN_IF_NOT_OK(skipCertStatus(elem)); auto this_update_or_error = Asn1Utility::parseGeneralizedTime(elem); - RETURN_IF_STATUS_NOT_OK(this_update_or_error); + RETURN_IF_NOT_OK_REF(this_update_or_error.status()); auto next_update_or_error = Asn1Utility::parseOptional( elem, Asn1Utility::parseGeneralizedTime, CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0); - RETURN_IF_STATUS_NOT_OK(next_update_or_error); + RETURN_IF_NOT_OK_REF(next_update_or_error.status()); // Extensions currently ignored. return SingleResponse{id_or_error.value(), this_update_or_error.value(), @@ -358,11 +358,11 @@ absl::StatusOr Asn1OcspUtility::parseCertId(CBS& cbs) { return absl::InvalidArgumentError("OCSP CertID is not a well-formed ASN.1 SEQUENCE"); } - RETURN_IF_STATUS_NOT_OK(Asn1Utility::skip(elem, CBS_ASN1_SEQUENCE)); - RETURN_IF_STATUS_NOT_OK(Asn1Utility::skip(elem, CBS_ASN1_OCTETSTRING)); - RETURN_IF_STATUS_NOT_OK(Asn1Utility::skip(elem, CBS_ASN1_OCTETSTRING)); + RETURN_IF_NOT_OK_REF(Asn1Utility::skip(elem, CBS_ASN1_SEQUENCE).status()); + RETURN_IF_NOT_OK_REF(Asn1Utility::skip(elem, CBS_ASN1_OCTETSTRING).status()); + RETURN_IF_NOT_OK_REF(Asn1Utility::skip(elem, CBS_ASN1_OCTETSTRING).status()); auto serial_number_or_error = Asn1Utility::parseInteger(elem); - RETURN_IF_STATUS_NOT_OK(serial_number_or_error); + RETURN_IF_NOT_OK_REF(serial_number_or_error.status()); return {serial_number_or_error.value()}; } diff --git a/source/common/upstream/cluster_factory_impl.cc b/source/common/upstream/cluster_factory_impl.cc index 1897c7f3b1bb..a4324b8d472d 100644 --- a/source/common/upstream/cluster_factory_impl.cc +++ b/source/common/upstream/cluster_factory_impl.cc @@ -97,7 +97,7 @@ ClusterFactoryImplBase::create(const envoy::config::cluster::v3::Cluster& cluste absl::StatusOr> status_or_cluster = createClusterImpl(cluster, context); - RETURN_IF_STATUS_NOT_OK(status_or_cluster); + RETURN_IF_NOT_OK_REF(status_or_cluster.status()); std::pair& new_cluster_pair = status_or_cluster.value(); @@ -110,7 +110,7 @@ ClusterFactoryImplBase::create(const envoy::config::cluster::v3::Cluster& cluste } else { auto checker_or_error = HealthCheckerFactory::create(cluster.health_checks()[0], *new_cluster_pair.first, server_context); - RETURN_IF_STATUS_NOT_OK(checker_or_error); + RETURN_IF_NOT_OK_REF(checker_or_error.status()); new_cluster_pair.first->setHealthChecker(checker_or_error.value()); } } @@ -119,7 +119,7 @@ ClusterFactoryImplBase::create(const envoy::config::cluster::v3::Cluster& cluste *new_cluster_pair.first, cluster, server_context.mainThreadDispatcher(), server_context.runtime(), context.outlierEventLogger(), server_context.api().randomGenerator()); - RETURN_IF_STATUS_NOT_OK(detector_or_error); + RETURN_IF_NOT_OK_REF(detector_or_error.status()); new_cluster_pair.first->setOutlierDetector(detector_or_error.value()); return status_or_cluster; diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index d62f54f9ec29..c0e8f515788b 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -417,7 +417,7 @@ ClusterManagerImpl::initialize(const envoy::config::bootstrap::v3::Bootstrap& bo auto status_or_cluster = loadCluster(cluster, MessageUtil::hash(cluster), "", /*added_via_api=*/false, required_for_ads, active_clusters_); - RETURN_IF_STATUS_NOT_OK(status_or_cluster); + RETURN_IF_NOT_OK_REF(status_or_cluster.status()); } } @@ -437,7 +437,7 @@ ClusterManagerImpl::initialize(const envoy::config::bootstrap::v3::Bootstrap& bo dyn_resources.ads_config(), random_, Envoy::Config::SubscriptionFactory::RetryInitialDelayMs, Envoy::Config::SubscriptionFactory::RetryMaxDelayMs); - RETURN_IF_STATUS_NOT_OK(strategy_or_error); + RETURN_IF_NOT_OK_REF(strategy_or_error.status()); JitteredExponentialBackOffStrategyPtr backoff_strategy = std::move(strategy_or_error.value()); const bool use_eds_cache = @@ -458,12 +458,12 @@ ClusterManagerImpl::initialize(const envoy::config::bootstrap::v3::Bootstrap& bo } auto factory_primary_or_error = Config::Utility::factoryForGrpcApiConfigSource( *async_client_manager_, dyn_resources.ads_config(), *stats_.rootScope(), false, 0); - RETURN_IF_STATUS_NOT_OK(factory_primary_or_error); + RETURN_IF_NOT_OK_REF(factory_primary_or_error.status()); Grpc::AsyncClientFactoryPtr factory_failover = nullptr; if (Runtime::runtimeFeatureEnabled("envoy.restart_features.xds_failover_support")) { auto factory_failover_or_error = Config::Utility::factoryForGrpcApiConfigSource( *async_client_manager_, dyn_resources.ads_config(), *stats_.rootScope(), false, 1); - RETURN_IF_STATUS_NOT_OK(factory_failover_or_error); + RETURN_IF_NOT_OK_REF(factory_failover_or_error.status()); factory_failover = std::move(factory_failover_or_error.value()); } ads_mux_ = factory->create( @@ -489,12 +489,12 @@ ClusterManagerImpl::initialize(const envoy::config::bootstrap::v3::Bootstrap& bo } auto factory_primary_or_error = Config::Utility::factoryForGrpcApiConfigSource( *async_client_manager_, dyn_resources.ads_config(), *stats_.rootScope(), false, 0); - RETURN_IF_STATUS_NOT_OK(factory_primary_or_error); + RETURN_IF_NOT_OK_REF(factory_primary_or_error.status()); Grpc::AsyncClientFactoryPtr factory_failover = nullptr; if (Runtime::runtimeFeatureEnabled("envoy.restart_features.xds_failover_support")) { auto factory_failover_or_error = Config::Utility::factoryForGrpcApiConfigSource( *async_client_manager_, dyn_resources.ads_config(), *stats_.rootScope(), false, 1); - RETURN_IF_STATUS_NOT_OK(factory_failover_or_error); + RETURN_IF_NOT_OK_REF(factory_failover_or_error.status()); factory_failover = std::move(factory_failover_or_error.value()); } ads_mux_ = factory->create( @@ -553,7 +553,7 @@ ClusterManagerImpl::initialize(const envoy::config::bootstrap::v3::Bootstrap& bo if (!dyn_resources.cds_resources_locator().empty()) { auto url_or_error = Config::XdsResourceIdentifier::decodeUrl(dyn_resources.cds_resources_locator()); - RETURN_IF_STATUS_NOT_OK(url_or_error); + RETURN_IF_NOT_OK_REF(url_or_error.status()); cds_resources_locator = std::make_unique(std::move(url_or_error.value())); } @@ -594,7 +594,7 @@ absl::Status ClusterManagerImpl::initializeSecondaryClusters( RETURN_IF_NOT_OK(status); auto factory_or_error = Config::Utility::factoryForGrpcApiConfigSource( *async_client_manager_, load_stats_config, *stats_.rootScope(), false, 0); - RETURN_IF_STATUS_NOT_OK(factory_or_error); + RETURN_IF_NOT_OK_REF(factory_or_error.status()); load_stats_reporter_ = std::make_unique( local_info_, *this, *stats_.rootScope(), factory_or_error.value()->createUncachedRawAsyncClient(), dispatcher_); diff --git a/source/common/upstream/health_discovery_service.cc b/source/common/upstream/health_discovery_service.cc index 1ef4adfc54c6..b1b219263584 100644 --- a/source/common/upstream/health_discovery_service.cc +++ b/source/common/upstream/health_discovery_service.cc @@ -440,7 +440,7 @@ absl::Status HdsCluster::updateHealthchecks( // If it does not, create a new one. auto checker_or_error = Upstream::HealthCheckerFactory::create(health_check, *this, server_context_); - RETURN_IF_STATUS_NOT_OK(checker_or_error); + RETURN_IF_NOT_OK_REF(checker_or_error.status()); auto new_health_checker = checker_or_error.value(); health_checkers_map.insert({health_check, new_health_checker}); health_checkers.push_back(new_health_checker); diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index f3d2066aed66..f725214c948f 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -124,7 +124,7 @@ parseExtensionProtocolOptions( for (const auto& it : config.typed_extension_protocol_options()) { auto& name = it.first; auto object_or_error = createProtocolOptionsConfig(name, it.second, factory_context); - RETURN_IF_STATUS_NOT_OK(object_or_error); + RETURN_IF_NOT_OK_REF(object_or_error.status()); if (object_or_error.value() != nullptr) { options[name] = std::move(object_or_error.value()); } @@ -235,7 +235,7 @@ parseBindConfig(::Envoy::OptRef bind_ auto address_or_error = ::Envoy::Network::Address::resolveProtoSocketAddress(bind_config->source_address()); - RETURN_IF_STATUS_NOT_OK(address_or_error); + RETURN_IF_NOT_OK_REF(address_or_error.status()); upstream_local_address.address_ = address_or_error.value(); } upstream_local_address.socket_options_ = std::make_shared(); @@ -251,7 +251,7 @@ parseBindConfig(::Envoy::OptRef bind_ UpstreamLocalAddress extra_upstream_local_address; auto address_or_error = ::Envoy::Network::Address::resolveProtoSocketAddress(extra_source_address.address()); - RETURN_IF_STATUS_NOT_OK(address_or_error); + RETURN_IF_NOT_OK_REF(address_or_error.status()); extra_upstream_local_address.address_ = address_or_error.value(); extra_upstream_local_address.socket_options_ = @@ -275,7 +275,7 @@ parseBindConfig(::Envoy::OptRef bind_ UpstreamLocalAddress additional_upstream_local_address; auto address_or_error = ::Envoy::Network::Address::resolveProtoSocketAddress(additional_source_address); - RETURN_IF_STATUS_NOT_OK(address_or_error); + RETURN_IF_NOT_OK_REF(address_or_error.status()); additional_upstream_local_address.address_ = address_or_error.value(); additional_upstream_local_address.socket_options_ = std::make_shared<::Envoy::Network::ConnectionSocket::Options>(); @@ -373,10 +373,10 @@ createUpstreamLocalAddressSelector( envoy::config::core::v3::BindConfig{})), buildClusterSocketOptions(cluster_config, bootstrap_bind_config.value_or( envoy::config::core::v3::BindConfig{}))); - RETURN_IF_STATUS_NOT_OK(config_or_error); + RETURN_IF_NOT_OK_REF(config_or_error.status()); auto selector_or_error = local_address_selector_factory->createLocalAddressSelector( config_or_error.value(), cluster_name); - RETURN_IF_STATUS_NOT_OK(selector_or_error); + RETURN_IF_NOT_OK_REF(selector_or_error.status()); return selector_or_error.value(); } @@ -981,7 +981,7 @@ createOptions(const envoy::config::cluster::v3::Cluster& config, config.protocol_selection() == envoy::config::cluster::v3::Cluster::USE_DOWNSTREAM_PROTOCOL, config.has_http2_protocol_options(), validation_visitor); - RETURN_IF_STATUS_NOT_OK(options_or_error); + RETURN_IF_NOT_OK_REF(options_or_error.status()); return options_or_error.value(); } diff --git a/source/extensions/clusters/dynamic_forward_proxy/cluster.cc b/source/extensions/clusters/dynamic_forward_proxy/cluster.cc index d80ca3e83eac..4ec0a8298ac9 100644 --- a/source/extensions/clusters/dynamic_forward_proxy/cluster.cc +++ b/source/extensions/clusters/dynamic_forward_proxy/cluster.cc @@ -499,7 +499,7 @@ ClusterFactory::createClusterWithConfig( Extensions::Common::DynamicForwardProxy::DnsCacheManagerSharedPtr cache_manager = cache_manager_factory.get(); auto dns_cache_or_error = cache_manager->getCache(proto_config.dns_cache_config()); - RETURN_IF_STATUS_NOT_OK(dns_cache_or_error); + RETURN_IF_NOT_OK_REF(dns_cache_or_error.status()); absl::Status creation_status = absl::OkStatus(); auto new_cluster = std::shared_ptr( diff --git a/source/extensions/common/dynamic_forward_proxy/dns_cache_manager_impl.cc b/source/extensions/common/dynamic_forward_proxy/dns_cache_manager_impl.cc index e15cfe325af1..cb98cfae2b09 100644 --- a/source/extensions/common/dynamic_forward_proxy/dns_cache_manager_impl.cc +++ b/source/extensions/common/dynamic_forward_proxy/dns_cache_manager_impl.cc @@ -27,7 +27,7 @@ absl::StatusOr DnsCacheManagerImpl::getCache( } auto cache_or_status = DnsCacheImpl::createDnsCacheImpl(context_, config); - RETURN_IF_STATUS_NOT_OK(cache_or_status); + RETURN_IF_NOT_OK_REF(cache_or_status.status()); DnsCacheSharedPtr new_cache = std::move(cache_or_status.value()); caches_.emplace(config.name(), ActiveCache{config, new_cache}); return new_cache; diff --git a/source/extensions/filters/http/match_delegate/config.cc b/source/extensions/filters/http/match_delegate/config.cc index e944c7b47bcb..5651c9ca8f40 100644 --- a/source/extensions/filters/http/match_delegate/config.cc +++ b/source/extensions/filters/http/match_delegate/config.cc @@ -312,7 +312,7 @@ absl::StatusOr MatchDelegateConfig::createFilterFa auto message = Config::Utility::translateAnyToFactoryConfig( proto_config.extension_config().typed_config(), validation, factory); auto filter_factory_or_error = factory.createFilterFactoryFromProto(*message, prefix, context); - RETURN_IF_STATUS_NOT_OK(filter_factory_or_error); + RETURN_IF_NOT_OK_REF(filter_factory_or_error.status()); auto filter_factory = filter_factory_or_error.value(); Factory::MatchTreeValidationVisitor validation_visitor(*factory.matchingRequirements()); diff --git a/source/extensions/network/dns_resolver/cares/dns_impl.cc b/source/extensions/network/dns_resolver/cares/dns_impl.cc index 46986aadeb3a..04fa3622e7cf 100644 --- a/source/extensions/network/dns_resolver/cares/dns_impl.cc +++ b/source/extensions/network/dns_resolver/cares/dns_impl.cc @@ -583,7 +583,7 @@ class CaresDnsResolverFactory : public DnsResolverFactory, resolvers.reserve(resolver_addrs.size()); for (const auto& resolver_addr : resolver_addrs) { auto address_or_error = Network::Address::resolveProtoAddress(resolver_addr); - RETURN_IF_STATUS_NOT_OK(address_or_error); + RETURN_IF_NOT_OK_REF(address_or_error.status()); resolvers.push_back(std::move(address_or_error.value())); } } diff --git a/source/extensions/transport_sockets/http_11_proxy/config.cc b/source/extensions/transport_sockets/http_11_proxy/config.cc index a54ead186aac..0e95e6a34743 100644 --- a/source/extensions/transport_sockets/http_11_proxy/config.cc +++ b/source/extensions/transport_sockets/http_11_proxy/config.cc @@ -25,7 +25,7 @@ UpstreamHttp11ConnectSocketConfigFactory::createTransportSocketFactory( outer_config.transport_socket(), context.messageValidationVisitor(), inner_config_factory); auto factory_or_error = inner_config_factory.createTransportSocketFactory(*inner_factory_config, context); - RETURN_IF_STATUS_NOT_OK(factory_or_error); + RETURN_IF_NOT_OK_REF(factory_or_error.status()); return std::make_unique(std::move(factory_or_error.value())); } diff --git a/source/extensions/transport_sockets/internal_upstream/config.cc b/source/extensions/transport_sockets/internal_upstream/config.cc index dfef2a21e662..d8b31db91e9b 100644 --- a/source/extensions/transport_sockets/internal_upstream/config.cc +++ b/source/extensions/transport_sockets/internal_upstream/config.cc @@ -40,7 +40,7 @@ class InternalUpstreamConfigFactory inner_config_factory); auto factory_or_error = inner_config_factory.createTransportSocketFactory(*inner_factory_config, context); - RETURN_IF_STATUS_NOT_OK(factory_or_error); + RETURN_IF_NOT_OK_REF(factory_or_error.status()); return std::make_unique(context, outer_config, std::move(factory_or_error.value())); } diff --git a/source/extensions/transport_sockets/proxy_protocol/config.cc b/source/extensions/transport_sockets/proxy_protocol/config.cc index 05551ad61f28..fcde4faafad4 100644 --- a/source/extensions/transport_sockets/proxy_protocol/config.cc +++ b/source/extensions/transport_sockets/proxy_protocol/config.cc @@ -26,7 +26,7 @@ UpstreamProxyProtocolSocketConfigFactory::createTransportSocketFactory( outer_config.transport_socket(), context.messageValidationVisitor(), inner_config_factory); auto factory_or_error = inner_config_factory.createTransportSocketFactory(*inner_factory_config, context); - RETURN_IF_STATUS_NOT_OK(factory_or_error); + RETURN_IF_NOT_OK_REF(factory_or_error.status()); return std::make_unique( std::move(factory_or_error.value()), outer_config.config(), context.statsScope()); } diff --git a/source/extensions/transport_sockets/starttls/config.cc b/source/extensions/transport_sockets/starttls/config.cc index 7d4552a7a1e6..a2f0722df7e6 100644 --- a/source/extensions/transport_sockets/starttls/config.cc +++ b/source/extensions/transport_sockets/starttls/config.cc @@ -20,11 +20,11 @@ DownstreamStartTlsSocketFactory::createTransportSocketFactory( auto raw_or_error = raw_socket_config_factory.createTransportSocketFactory( outer_config.cleartext_socket_config(), context, server_names); - RETURN_IF_STATUS_NOT_OK(raw_or_error); + RETURN_IF_NOT_OK_REF(raw_or_error.status()); auto factory_or_error = tls_socket_config_factory.createTransportSocketFactory( outer_config.tls_socket_config(), context, server_names); - RETURN_IF_STATUS_NOT_OK(factory_or_error); + RETURN_IF_NOT_OK_REF(factory_or_error.status()); return std::make_unique(std::move(raw_or_error.value()), std::move(factory_or_error.value())); @@ -43,11 +43,11 @@ UpstreamStartTlsSocketFactory::createTransportSocketFactory( auto raw_or_error = raw_socket_config_factory.createTransportSocketFactory( outer_config.cleartext_socket_config(), context); - RETURN_IF_STATUS_NOT_OK(raw_or_error); + RETURN_IF_NOT_OK_REF(raw_or_error.status()); auto factory_or_error = tls_socket_config_factory.createTransportSocketFactory( outer_config.tls_socket_config(), context); - RETURN_IF_STATUS_NOT_OK(factory_or_error); + RETURN_IF_NOT_OK_REF(factory_or_error.status()); return std::make_unique(std::move(raw_or_error.value()), std::move(factory_or_error.value())); diff --git a/source/extensions/transport_sockets/tap/config.cc b/source/extensions/transport_sockets/tap/config.cc index 2bb7c37f22f7..b1417d9beda3 100644 --- a/source/extensions/transport_sockets/tap/config.cc +++ b/source/extensions/transport_sockets/tap/config.cc @@ -47,7 +47,7 @@ UpstreamTapSocketConfigFactory::createTransportSocketFactory( outer_config.transport_socket(), context.messageValidationVisitor(), inner_config_factory); auto factory_or_error = inner_config_factory.createTransportSocketFactory(*inner_factory_config, context); - RETURN_IF_STATUS_NOT_OK(factory_or_error); + RETURN_IF_NOT_OK_REF(factory_or_error.status()); auto& server_context = context.serverFactoryContext(); return std::make_unique( @@ -72,7 +72,7 @@ DownstreamTapSocketConfigFactory::createTransportSocketFactory( outer_config.transport_socket(), context.messageValidationVisitor(), inner_config_factory); auto factory_or_error = inner_config_factory.createTransportSocketFactory(*inner_factory_config, context, server_names); - RETURN_IF_STATUS_NOT_OK(factory_or_error); + RETURN_IF_NOT_OK_REF(factory_or_error.status()); auto& server_context = context.serverFactoryContext(); return std::make_unique( outer_config, diff --git a/source/extensions/transport_sockets/tcp_stats/config.cc b/source/extensions/transport_sockets/tcp_stats/config.cc index f3702c051ad3..938fc6c14e63 100644 --- a/source/extensions/transport_sockets/tcp_stats/config.cc +++ b/source/extensions/transport_sockets/tcp_stats/config.cc @@ -93,7 +93,7 @@ class UpstreamTcpStatsConfigFactory inner_config_factory); auto factory_or_error = inner_config_factory.createTransportSocketFactory(*inner_factory_config, context); - RETURN_IF_STATUS_NOT_OK(factory_or_error); + RETURN_IF_NOT_OK_REF(factory_or_error.status()); return std::make_unique(context, outer_config, std::move(factory_or_error.value())); } @@ -119,7 +119,7 @@ class DownstreamTcpStatsConfigFactory inner_config_factory); auto factory_or_error = inner_config_factory.createTransportSocketFactory( *inner_factory_config, context, server_names); - RETURN_IF_STATUS_NOT_OK(factory_or_error); + RETURN_IF_NOT_OK_REF(factory_or_error.status()); return std::make_unique(context, outer_config, std::move(factory_or_error.value())); } diff --git a/source/extensions/upstreams/http/config.cc b/source/extensions/upstreams/http/config.cc index ce9330bd4661..8acda93047c8 100644 --- a/source/extensions/upstreams/http/config.cc +++ b/source/extensions/upstreams/http/config.cc @@ -189,11 +189,11 @@ ProtocolOptionsConfigImpl::createProtocolOptionsConfig( const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options, Server::Configuration::ServerFactoryContext& server_context) { auto options_or_error = Http2::Utility::initializeAndValidateOptions(getHttp2Options(options)); - RETURN_IF_STATUS_NOT_OK(options_or_error); + RETURN_IF_NOT_OK_REF(options_or_error.status()); auto cache_options_or_error = getAlternateProtocolsCacheOptions(options, server_context); - RETURN_IF_STATUS_NOT_OK(cache_options_or_error); + RETURN_IF_NOT_OK_REF(cache_options_or_error.status()); auto validator_factory_or_error = createHeaderValidatorFactory(options, server_context); - RETURN_IF_STATUS_NOT_OK(validator_factory_or_error); + RETURN_IF_NOT_OK_REF(validator_factory_or_error.status()); return std::shared_ptr(new ProtocolOptionsConfigImpl( options, options_or_error.value(), std::move(validator_factory_or_error.value()), cache_options_or_error.value(), server_context)); @@ -208,7 +208,7 @@ ProtocolOptionsConfigImpl::createProtocolOptionsConfig( bool use_downstream_protocol, bool use_http2, ProtobufMessage::ValidationVisitor& validation_visitor) { auto options_or_error = Http2::Utility::initializeAndValidateOptions(http2_options); - RETURN_IF_STATUS_NOT_OK(options_or_error); + RETURN_IF_NOT_OK_REF(options_or_error.status()); return std::shared_ptr(new ProtocolOptionsConfigImpl( http1_settings, options_or_error.value(), common_options, upstream_options, use_downstream_protocol, use_http2, validation_visitor)); diff --git a/source/server/api_listener_impl.cc b/source/server/api_listener_impl.cc index a9dfa065d89c..7f5d4e8fe9ad 100644 --- a/source/server/api_listener_impl.cc +++ b/source/server/api_listener_impl.cc @@ -45,7 +45,7 @@ absl::StatusOr> HttpApiListener::create(const envoy::config::listener::v3::Listener& config, Server::Instance& server, const std::string& name) { auto address_or_error = Network::Address::resolveProtoAddress(config.address()); - RETURN_IF_STATUS_NOT_OK(address_or_error); + RETURN_IF_NOT_OK_REF(address_or_error.status()); return std::unique_ptr( new HttpApiListener(std::move(address_or_error.value()), config, server, name)); } diff --git a/source/server/configuration_impl.cc b/source/server/configuration_impl.cc index 883b47531507..8a9f763d4a53 100644 --- a/source/server/configuration_impl.cc +++ b/source/server/configuration_impl.cc @@ -140,7 +140,7 @@ absl::Status MainImpl::initialize(const envoy::config::bootstrap::v3::Bootstrap& ENVOY_LOG(debug, "listener #{}:", i); absl::StatusOr update_or_error = server.listenerManager().addOrUpdateListener(listeners[i], "", false); - RETURN_IF_STATUS_NOT_OK(update_or_error); + RETURN_IF_NOT_OK_REF(update_or_error.status()); } RETURN_IF_NOT_OK(initializeWatchdogs(bootstrap, server)); // This has to happen after ClusterManager initialization, as it depends on config from diff --git a/source/server/server.cc b/source/server/server.cc index 2b43921d973a..a8fbc195549e 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -503,7 +503,7 @@ absl::Status InstanceBase::initializeOrThrow(Network::Address::InstanceConstShar // stats. auto producer_or_error = Stats::TagProducerImpl::createTagProducer(bootstrap_.stats_config(), options_.statsTags()); - RETURN_IF_STATUS_NOT_OK(producer_or_error); + RETURN_IF_NOT_OK_REF(producer_or_error.status()); stats_store_.setTagProducer(std::move(producer_or_error.value())); stats_store_.setStatsMatcher(std::make_unique( bootstrap_.stats_config(), stats_store_.symbolTable(), server_contexts_)); From ec61a3cc7abd77d1dfa5a6e96253492edf7d6bf5 Mon Sep 17 00:00:00 2001 From: "Antonio V. Leonti" <53806445+antoniovleonti@users.noreply.github.com> Date: Thu, 1 Aug 2024 09:33:40 -0400 Subject: [PATCH 007/130] clean up doEndStream (#35506) There are some variables that are only used if check_for_deferred_close is true. Move them to be inside the `if (check_for_deferred_close)` block. Signed-off-by: antoniovleonti --- source/common/http/conn_manager_impl.cc | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc index 2b709f2eebd2..77aa6d7a9490 100644 --- a/source/common/http/conn_manager_impl.cc +++ b/source/common/http/conn_manager_impl.cc @@ -280,17 +280,18 @@ void ConnectionManagerImpl::doEndStream(ActiveStream& stream, bool check_for_def drain_state_ = DrainState::Closing; } - // If HTTP/1.0 has no content length, it is framed by close and won't consider - // the request complete until the FIN is read. Don't delay close in this case. - bool http_10_sans_cl = (codec_->protocol() == Protocol::Http10) && - (!stream.response_headers_ || !stream.response_headers_->ContentLength()); - // We also don't delay-close in the case of HTTP/1.1 where the request is - // fully read, as there's no race condition to avoid. - const bool connection_close = - stream.filter_manager_.streamInfo().shouldDrainConnectionUponCompletion(); - bool request_complete = stream.filter_manager_.remoteDecodeComplete(); - if (check_for_deferred_close) { + // If HTTP/1.0 has no content length, it is framed by close and won't consider + // the request complete until the FIN is read. Don't delay close in this case. + const bool http_10_sans_cl = + (codec_->protocol() == Protocol::Http10) && + (!stream.response_headers_ || !stream.response_headers_->ContentLength()); + // We also don't delay-close in the case of HTTP/1.1 where the request is + // fully read, as there's no race condition to avoid. + const bool connection_close = + stream.filter_manager_.streamInfo().shouldDrainConnectionUponCompletion(); + const bool request_complete = stream.filter_manager_.remoteDecodeComplete(); + // Don't do delay close for HTTP/1.0 or if the request is complete. checkForDeferredClose(connection_close && (request_complete || http_10_sans_cl)); } From ff94c296f27be2f4a6cd71a2d6b0898cec6c2100 Mon Sep 17 00:00:00 2001 From: yanjunxiang-google <78807980+yanjunxiang-google@users.noreply.github.com> Date: Thu, 1 Aug 2024 10:11:07 -0400 Subject: [PATCH 008/130] Adding HTTP service support for Envoy external processing (#35489) This is to address the 1st step, i.e, the API change needed for https://github.com/envoyproxy/envoy/issues/35488. --------- Signed-off-by: Yanjun Xiang --- .../filters/http/ext_proc/v3/ext_proc.proto | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/api/envoy/extensions/filters/http/ext_proc/v3/ext_proc.proto b/api/envoy/extensions/filters/http/ext_proc/v3/ext_proc.proto index aeaed7aa2ab7..1d79d39d99b8 100644 --- a/api/envoy/extensions/filters/http/ext_proc/v3/ext_proc.proto +++ b/api/envoy/extensions/filters/http/ext_proc/v3/ext_proc.proto @@ -5,6 +5,7 @@ package envoy.extensions.filters.http.ext_proc.v3; import "envoy/config/common/mutation_rules/v3/mutation_rules.proto"; import "envoy/config/core/v3/base.proto"; import "envoy/config/core/v3/grpc_service.proto"; +import "envoy/config/core/v3/http_service.proto"; import "envoy/extensions/filters/http/ext_proc/v3/processing_mode.proto"; import "envoy/type/matcher/v3/string.proto"; @@ -98,7 +99,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // ` object in a namespace matching the filter // name. // -// [#next-free-field: 20] +// [#next-free-field: 21] message ExternalProcessor { // Describes the route cache action to be taken when an external processor response // is received in response to request headers. @@ -125,7 +126,18 @@ message ExternalProcessor { // Configuration for the gRPC service that the filter will communicate with. // The filter supports both the "Envoy" and "Google" gRPC clients. - config.core.v3.GrpcService grpc_service = 1 [(validate.rules).message = {required: true}]; + // Only one of ``grpc_service`` or ``http_service`` can be set. + // It is required that one of them must be set. + config.core.v3.GrpcService grpc_service = 1 + [(udpa.annotations.field_migrate).oneof_promotion = "ext_proc_service_type"]; + + // [#not-implemented-hide:] + // Configuration for the HTTP service that the filter will communicate with. + // Only one of ``http_service`` or + // :ref:`grpc_service `. + // can be set. It is required that one of them must be set. + ExtProcHttpService http_service = 20 + [(udpa.annotations.field_migrate).oneof_promotion = "ext_proc_service_type"]; // By default, if the gRPC stream cannot be established, or if it is closed // prematurely with an error, the filter will fail. Specifically, if the @@ -265,6 +277,12 @@ message ExternalProcessor { google.protobuf.Duration deferred_close_timeout = 19; } +// ExtProcHttpService is used for HTTP communication between the filter and the external processing service. +message ExtProcHttpService { + // Sets the HTTP service which the external processing requests must be sent to. + config.core.v3.HttpService http_service = 1; +} + // The MetadataOptions structure defines options for the sending and receiving of // dynamic metadata. Specifically, which namespaces to send to the server, whether // metadata returned by the server may be written, and how that metadata may be written. From 8b9b23b9a1d479b7e11212c865c647f2f78737e8 Mon Sep 17 00:00:00 2001 From: "Vikas Choudhary (vikasc)" Date: Thu, 1 Aug 2024 20:19:58 +0530 Subject: [PATCH 009/130] Remove feature flag upstream_allow_connect_with_2xx (#35521) Signed-off-by: Vikas Choudhary --- changelogs/current.yaml | 3 +++ source/common/router/upstream_codec_filter.cc | 5 +---- source/common/runtime/runtime_features.cc | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index cf1201c0c0a9..28ea9a0ef938 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -80,6 +80,9 @@ removed_config_or_runtime: - area: stateful_session change: | Removed ``envoy.reloadable_features.stateful_session_encode_ttl_in_cookie`` runtime flag and legacy code paths. +- area: upstream + change: | + Removed runtime flag ``envoy.reloadable_features.upstream_allow_connect_with_2xx`` and legacy code paths. - area: upstream flow control change: | Removed ``envoy.reloadable_features.upstream_wait_for_response_headers_before_disabling_read`` runtime flag diff --git a/source/common/router/upstream_codec_filter.cc b/source/common/router/upstream_codec_filter.cc index 56ac395dd40b..846645a5e000 100644 --- a/source/common/router/upstream_codec_filter.cc +++ b/source/common/router/upstream_codec_filter.cc @@ -148,10 +148,7 @@ void UpstreamCodecFilter::CodecBridge::decodeHeaders(Http::ResponseHeaderMapPtr& filter_.callbacks_->dispatcher().timeSource()); if (filter_.callbacks_->upstreamCallbacks()->pausedForConnect() && - ((Http::Utility::getResponseStatus(*headers) == 200) || - ((Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.upstream_allow_connect_with_2xx")) && - (Http::CodeUtility::is2xx(Http::Utility::getResponseStatus(*headers)))))) { + ((Http::CodeUtility::is2xx(Http::Utility::getResponseStatus(*headers))))) { filter_.callbacks_->upstreamCallbacks()->setPausedForConnect(false); filter_.callbacks_->continueDecoding(); } diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 55cf4e0e57d7..b2fe1912886b 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -90,7 +90,6 @@ RUNTIME_GUARD(envoy_reloadable_features_tcp_tunneling_send_downstream_fin_on_ups RUNTIME_GUARD(envoy_reloadable_features_test_feature_true); RUNTIME_GUARD(envoy_reloadable_features_udp_socket_apply_aggregated_read_limit); RUNTIME_GUARD(envoy_reloadable_features_uhv_allow_malformed_url_encoding); -RUNTIME_GUARD(envoy_reloadable_features_upstream_allow_connect_with_2xx); RUNTIME_GUARD(envoy_reloadable_features_upstream_remote_address_use_connection); RUNTIME_GUARD(envoy_reloadable_features_use_http3_header_normalisation); RUNTIME_GUARD(envoy_reloadable_features_use_typed_metadata_in_proxy_protocol_listener); From 0fa9e603085952a80d78559fd8596dafa14cef6b Mon Sep 17 00:00:00 2001 From: Bin Wu <46450037+wu-bin@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:30:25 -0400 Subject: [PATCH 010/130] Ensure every instance of StreamFilter and PassThroughFilter to contain only one copy of StreamFilterBase. (#35341) Change Stream(Decode|Encode)Filter to inherit from StreamFilterBase virtually. Move the default implementation of PassThrough(Decoder|Encode)Filter::onDestroy into StreamFilterBase. --------- Signed-off-by: Bin Wu --- envoy/http/filter.h | 4 ++-- source/extensions/filters/http/common/pass_through_filter.h | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/envoy/http/filter.h b/envoy/http/filter.h index 1caebab6a46c..49d2609a1152 100644 --- a/envoy/http/filter.h +++ b/envoy/http/filter.h @@ -896,7 +896,7 @@ class StreamFilterBase { /** * Stream decoder filter interface. */ -class StreamDecoderFilter : public StreamFilterBase { +class StreamDecoderFilter : public virtual StreamFilterBase { public: /** * Called with decoded headers, optionally indicating end of stream. @@ -1112,7 +1112,7 @@ class StreamEncoderFilterCallbacks : public virtual StreamFilterCallbacks { /** * Stream encoder filter interface. */ -class StreamEncoderFilter : public StreamFilterBase { +class StreamEncoderFilter : public virtual StreamFilterBase { public: /** * Called with supported 1xx headers. diff --git a/source/extensions/filters/http/common/pass_through_filter.h b/source/extensions/filters/http/common/pass_through_filter.h index ad299e47da15..e6291b9e3dd1 100644 --- a/source/extensions/filters/http/common/pass_through_filter.h +++ b/source/extensions/filters/http/common/pass_through_filter.h @@ -62,6 +62,10 @@ class PassThroughEncoderFilter : public virtual StreamEncoderFilter { // A filter which passes all data through with Continue status. class PassThroughFilter : public StreamFilter, public PassThroughDecoderFilter, - public PassThroughEncoderFilter {}; + public PassThroughEncoderFilter { +public: + // Http::StreamFilterBase + void onDestroy() override {} +}; } // namespace Http } // namespace Envoy From 770fa7ba2b61e11b267e27b0d4c4861c4b20c11a Mon Sep 17 00:00:00 2001 From: chenylh <15801565898@163.com> Date: Fri, 2 Aug 2024 00:46:58 +0800 Subject: [PATCH 011/130] optimeize unescape slash path (#34934) --------- Signed-off-by: chenylh --- source/common/http/path_utility.cc | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/source/common/http/path_utility.cc b/source/common/http/path_utility.cc index ce5a88b1d037..c37b7473e25e 100644 --- a/source/common/http/path_utility.cc +++ b/source/common/http/path_utility.cc @@ -1,9 +1,9 @@ #include "source/common/http/path_utility.h" #include "source/common/common/logger.h" -#include "source/common/runtime/runtime_features.h" #include "absl/strings/str_join.h" +#include "absl/strings/str_replace.h" #include "absl/strings/str_split.h" #include "absl/types/optional.h" #include "url/url_canon.h" @@ -24,16 +24,6 @@ absl::optional canonicalizePath(absl::string_view original_path) { output.Complete(); return absl::make_optional(std::move(canonical_path)); } - -void unescapeInPath(std::string& path, absl::string_view escape_sequence, - absl::string_view substitution) { - std::vector split = absl::StrSplit(path, escape_sequence); - if (split.size() == 1) { - return; - } - path = absl::StrJoin(split, substitution); -} - } // namespace /* static */ @@ -92,13 +82,14 @@ PathUtil::UnescapeSlashesResult PathUtil::unescapeSlashes(RequestHeaderMap& head } const absl::string_view query = absl::ClippedSubstr(original_path, query_start); - // TODO(yanavlasov): optimize this by adding case insensitive matcher - std::string decoded_path{path}; - unescapeInPath(decoded_path, "%2F", "/"); - unescapeInPath(decoded_path, "%2f", "/"); - unescapeInPath(decoded_path, "%5C", "\\"); - unescapeInPath(decoded_path, "%5c", "\\"); - headers.setPath(absl::StrCat(decoded_path, query)); + static const std::vector> replacements{ + {"%2F", "/"}, + {"%2f", "/"}, + {"%5C", "\\"}, + {"%5c", "\\"}, + }; + headers.setPath(absl::StrCat(absl::StrReplaceAll(path, replacements), query)); + // Path length will not match if there were unescaped %2f or %5c return headers.getPathValue().length() != original_length ? UnescapeSlashesResult::FoundAndUnescaped From 0e03184359729cf95d8ed56a2b82ab93dde2d6bf Mon Sep 17 00:00:00 2001 From: ohadvano <49730675+ohadvano@users.noreply.github.com> Date: Thu, 1 Aug 2024 21:50:27 +0300 Subject: [PATCH 012/130] async_tcp_client: remove callbacks if connection was not closed (#35410) When the ``AsyncTcpClient`` is being destroyed but it also has an active client connection, there's a crash since during the instance destruction, the ``ClientConnection`` object would also be destroyed, causing ``raiseEvent`` to be called back to ``AsyncTcpClient`` while it is being destroyed Caught with the following stack trace: ``` Caught Segmentation fault, suspect faulting address 0x0 Backtrace (use tools/stack_decode.py to get line numbers): Envoy version: ee8c765a07037033766ea556c032120b497152b3/1.27.0/Clean/RELEASE/BoringSSL #0: __restore_rt [0x7d80ab903420] #1: Envoy::Extensions::AccessLoggers::Fluentd::FluentdAccessLoggerImpl::onEvent() [0x58313528746b] #2: Envoy::Tcp::AsyncTcpClientImpl::onEvent() [0x5831359da00a] #3: Envoy::Network::ConnectionImplBase::raiseConnectionEvent() [0x583135f0521d] #4: Envoy::Network::ConnectionImpl::raiseEvent() [0x583135e9fed9] #5: Envoy::Network::ConnectionImpl::closeSocket() [0x583135e9f90c] #6: Envoy::Network::ConnectionImpl::close() [0x583135e9e54c] #7: Envoy::Network::ConnectionImpl::~ConnectionImpl() [0x583135e9de5c] #8: Envoy::Network::ClientConnectionImpl::~ClientConnectionImpl() [0x5831355fd25e] #9: Envoy::Tcp::AsyncTcpClientImpl::~AsyncTcpClientImpl() [0x5831359da247] #10: Envoy::Extensions::AccessLoggers::Fluentd::FluentdAccessLoggerImpl::~FluentdAccessLoggerImpl() [0x583135289350] #11: Envoy::Extensions::AccessLoggers::Fluentd::FluentdAccessLog::ThreadLocalLogger::~ThreadLocalLogger() [0x58313528adbf] #12: Envoy::ThreadLocal::InstanceImpl::shutdownThread() [0x58313560373a] #13: Envoy::Server::WorkerImpl::threadRoutine() [0x583135630c0a] #14: Envoy::Thread::ThreadImplPosix::ThreadImplPosix()::{lambda()#1}::__invoke() [0x5831364e88d5] #15: start_thread [0x7d80ab8f7609] ``` Risk Level: low Testing: unit tests Docs Changes: none Release Notes: none Platform Specific Features: none --------- Signed-off-by: Ohad Vano --- source/common/tcp/async_tcp_client_impl.cc | 12 +++- source/common/tcp/async_tcp_client_impl.h | 2 + test/common/tcp/async_tcp_client_impl_test.cc | 13 +++- .../filters/test_network_async_tcp_filter.cc | 9 ++- .../test_network_async_tcp_filter.proto | 1 + .../tcp_async_client_integration_test.cc | 61 ++++++++++++++----- 6 files changed, 79 insertions(+), 19 deletions(-) diff --git a/source/common/tcp/async_tcp_client_impl.cc b/source/common/tcp/async_tcp_client_impl.cc index 4d6482cb5dc6..0d6ce288c8ed 100644 --- a/source/common/tcp/async_tcp_client_impl.cc +++ b/source/common/tcp/async_tcp_client_impl.cc @@ -24,6 +24,14 @@ AsyncTcpClientImpl::AsyncTcpClientImpl(Event::Dispatcher& dispatcher, connect_timer_(dispatcher.createTimer([this]() { onConnectTimeout(); })), enable_half_close_(enable_half_close) {} +AsyncTcpClientImpl::~AsyncTcpClientImpl() { + if (connection_) { + connection_->removeConnectionCallbacks(*this); + } + + close(Network::ConnectionCloseType::NoFlush); +} + bool AsyncTcpClientImpl::connect() { if (connection_) { return false; @@ -69,7 +77,8 @@ void AsyncTcpClientImpl::onConnectTimeout() { } void AsyncTcpClientImpl::close(Network::ConnectionCloseType type) { - if (connection_) { + if (connection_ && !closing_) { + closing_ = true; connection_->close(type); } } @@ -127,6 +136,7 @@ void AsyncTcpClientImpl::onEvent(Network::ConnectionEvent event) { detected_close_ = connection_->detectedCloseType(); } + closing_ = false; dispatcher_.deferredDelete(std::move(connection_)); if (callbacks_) { callbacks_->onEvent(event); diff --git a/source/common/tcp/async_tcp_client_impl.h b/source/common/tcp/async_tcp_client_impl.h index ef965ca68cc5..2f239b757028 100644 --- a/source/common/tcp/async_tcp_client_impl.h +++ b/source/common/tcp/async_tcp_client_impl.h @@ -28,6 +28,7 @@ class AsyncTcpClientImpl : public AsyncTcpClient, AsyncTcpClientImpl(Event::Dispatcher& dispatcher, Upstream::ThreadLocalCluster& thread_local_cluster, Upstream::LoadBalancerContext* context, bool enable_half_close); + ~AsyncTcpClientImpl(); void close(Network::ConnectionCloseType type) override; @@ -106,6 +107,7 @@ class AsyncTcpClientImpl : public AsyncTcpClient, Event::TimerPtr connect_timer_; AsyncTcpClientCallbacks* callbacks_{}; Network::DetectedCloseType detected_close_{Network::DetectedCloseType::Normal}; + bool closing_{false}; bool connected_{false}; bool enable_half_close_{false}; }; diff --git a/test/common/tcp/async_tcp_client_impl_test.cc b/test/common/tcp/async_tcp_client_impl_test.cc index f185545a1223..409808bdfde7 100644 --- a/test/common/tcp/async_tcp_client_impl_test.cc +++ b/test/common/tcp/async_tcp_client_impl_test.cc @@ -18,6 +18,15 @@ using testing::Return; namespace Envoy { namespace Tcp { +class CustomMockClientConnection : public Network::MockClientConnection { +public: + ~CustomMockClientConnection() { + if (state_ != Connection::State::Closed) { + raiseEvent(Network::ConnectionEvent::LocalClose); + } + }; +}; + class AsyncTcpClientImplTest : public Event::TestUsingSimulatedTime, public testing::Test { public: AsyncTcpClientImplTest() = default; @@ -32,7 +41,7 @@ class AsyncTcpClientImplTest : public Event::TestUsingSimulatedTime, public test } void expectCreateConnection(bool trigger_connected = true) { - connection_ = new NiceMock(); + connection_ = new NiceMock(); Upstream::MockHost::MockCreateConnectionData conn_info; connection_->streamInfo().setAttemptCount(1); conn_info.connection_ = connection_; @@ -59,7 +68,7 @@ class AsyncTcpClientImplTest : public Event::TestUsingSimulatedTime, public test NiceMock* connect_timer_; NiceMock dispatcher_; NiceMock cluster_manager_; - Network::MockClientConnection* connection_{}; + CustomMockClientConnection* connection_{}; NiceMock callbacks_; }; diff --git a/test/integration/filters/test_network_async_tcp_filter.cc b/test/integration/filters/test_network_async_tcp_filter.cc index 6116b2d01dc1..58c80d793153 100644 --- a/test/integration/filters/test_network_async_tcp_filter.cc +++ b/test/integration/filters/test_network_async_tcp_filter.cc @@ -41,7 +41,8 @@ class TestNetworkAsyncTcpFilter : public Network::ReadFilter { const test::integration::filters::TestNetworkAsyncTcpFilterConfig& config, Stats::Scope& scope, Upstream::ClusterManager& cluster_manager) : stats_(generateStats("test_network_async_tcp_filter", scope)), - cluster_name_(config.cluster_name()), cluster_manager_(cluster_manager) { + cluster_name_(config.cluster_name()), kill_after_on_data_(config.kill_after_on_data()), + cluster_manager_(cluster_manager) { const auto thread_local_cluster = cluster_manager_.getThreadLocalCluster(cluster_name_); options_ = std::make_shared(true); if (thread_local_cluster != nullptr) { @@ -60,6 +61,11 @@ class TestNetworkAsyncTcpFilter : public Network::ReadFilter { data.length()); client_->write(data, end_stream); + if (kill_after_on_data_) { + Tcp::AsyncTcpClient* c1 = client_.release(); + delete c1; + } + return Network::FilterStatus::StopIteration; } @@ -166,6 +172,7 @@ class TestNetworkAsyncTcpFilter : public Network::ReadFilter { TestNetworkAsyncTcpFilterStats stats_; Tcp::AsyncTcpClientPtr client_; absl::string_view cluster_name_; + bool kill_after_on_data_; std::unique_ptr request_callbacks_; std::unique_ptr downstream_callbacks_; Upstream::ClusterManager& cluster_manager_; diff --git a/test/integration/filters/test_network_async_tcp_filter.proto b/test/integration/filters/test_network_async_tcp_filter.proto index bcb4d9beee34..fc84979375bb 100644 --- a/test/integration/filters/test_network_async_tcp_filter.proto +++ b/test/integration/filters/test_network_async_tcp_filter.proto @@ -4,4 +4,5 @@ package test.integration.filters; message TestNetworkAsyncTcpFilterConfig { string cluster_name = 1; + bool kill_after_on_data = 2; } diff --git a/test/integration/tcp_async_client_integration_test.cc b/test/integration/tcp_async_client_integration_test.cc index 89c4e29c1771..f0a9932bbc0a 100644 --- a/test/integration/tcp_async_client_integration_test.cc +++ b/test/integration/tcp_async_client_integration_test.cc @@ -1,3 +1,4 @@ +#include "test/integration/filters/test_network_async_tcp_filter.pb.h" #include "test/integration/integration.h" #include "gtest/gtest.h" @@ -16,15 +17,37 @@ class TcpAsyncClientIntegrationTest : public testing::TestWithParam void { + test::integration::filters::TestNetworkAsyncTcpFilterConfig proto_config; + TestUtility::loadFromYaml(yaml, proto_config); + + auto* listener = bootstrap.mutable_static_resources()->mutable_listeners(0); + auto* filter_chain = listener->mutable_filter_chains(0); + auto* filter = filter_chain->mutable_filters(0); + filter->mutable_typed_config()->PackFrom(proto_config); + }); + + BaseIntegrationTest::initialize(); + } }; INSTANTIATE_TEST_SUITE_P(IpVersions, TcpAsyncClientIntegrationTest, testing::ValuesIn(TestEnvironment::getIpVersionsForTest())); TEST_P(TcpAsyncClientIntegrationTest, SingleRequest) { - enableHalfClose(true); - initialize(); + init(); std::string request("request"); std::string response("response"); @@ -51,8 +74,7 @@ TEST_P(TcpAsyncClientIntegrationTest, SingleRequest) { } TEST_P(TcpAsyncClientIntegrationTest, MultipleRequestFrames) { - enableHalfClose(true); - initialize(); + init(); std::string data_frame_1("data_frame_1"); std::string data_frame_2("data_frame_2"); @@ -85,8 +107,7 @@ TEST_P(TcpAsyncClientIntegrationTest, MultipleRequestFrames) { } TEST_P(TcpAsyncClientIntegrationTest, MultipleResponseFrames) { - enableHalfClose(true); - initialize(); + init(); std::string data_frame_1("data_frame_1"); std::string response_1("response_1"); @@ -116,8 +137,7 @@ TEST_P(TcpAsyncClientIntegrationTest, Reconnect) { return; } - enableHalfClose(true); - initialize(); + init(); IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("listener_0")); ASSERT_TRUE(tcp_client->write("hello1", false)); @@ -143,11 +163,24 @@ TEST_P(TcpAsyncClientIntegrationTest, Reconnect) { test_server_->waitForGaugeEq("cluster.cluster_0.upstream_cx_active", 0); } +TEST_P(TcpAsyncClientIntegrationTest, ClientTearDown) { + init(true); + + std::string request("request"); + + IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("listener_0")); + ASSERT_TRUE(tcp_client->write(request, true)); + FakeRawConnectionPtr fake_upstream_connection; + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); + ASSERT_TRUE(fake_upstream_connection->waitForData(request.size())); + + tcp_client->close(); +} + #if ENVOY_PLATFORM_ENABLE_SEND_RST // Test if RST close can be detected from downstream and upstream is closed by RST. TEST_P(TcpAsyncClientIntegrationTest, TestClientCloseRST) { - enableHalfClose(true); - initialize(); + init(); std::string request("request"); std::string response("response"); @@ -178,8 +211,7 @@ TEST_P(TcpAsyncClientIntegrationTest, TestClientCloseRST) { // Test if RST close can be detected from upstream. TEST_P(TcpAsyncClientIntegrationTest, TestUpstreamCloseRST) { - enableHalfClose(true); - initialize(); + init(); std::string request("request"); std::string response("response"); @@ -212,8 +244,7 @@ TEST_P(TcpAsyncClientIntegrationTest, TestUpstreamCloseRST) { // the client. The behavior is different for windows, since RST support is literally supported for // unix like system, disabled the test for windows. TEST_P(TcpAsyncClientIntegrationTest, TestDownstremHalfClosedThenRST) { - enableHalfClose(true); - initialize(); + init(); std::string request("request"); std::string response("response"); From 97bc67a06a3908821c933b437f9268886383d210 Mon Sep 17 00:00:00 2001 From: Will Lampert Date: Thu, 1 Aug 2024 15:53:22 -0400 Subject: [PATCH 013/130] quic: fix flaky ConnectionDebugVisitor test (#35549) Commit Message: Switch to using `wait_for_log_contains` to fix race condition between stats increment and log flushing when expecting a LOG line caused by QUIC connection closing. Additional Description: Risk Level: Testing: Docs Changes: Release Notes: Platform Specific Features: Fixes #34492 Signed-off-by: Will Lampert --- test/integration/quic_http_integration_test.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/integration/quic_http_integration_test.cc b/test/integration/quic_http_integration_test.cc index 9f0cc45724a7..989588059958 100644 --- a/test/integration/quic_http_integration_test.cc +++ b/test/integration/quic_http_integration_test.cc @@ -2421,12 +2421,10 @@ TEST_P(QuicHttpIntegrationTest, ConnectionDebugVisitor) { EXPECT_TRUE(response->waitForEndStream()); ASSERT_TRUE(response->complete()); - // TODO(https://github.com/envoyproxy/envoy/issues/34492) fix - return; EnvoyQuicClientSession* quic_session = static_cast(codec_client_->connection()); std::string listener = version_ == Network::Address::IpVersion::v4 ? "127.0.0.1_0" : "[__1]_0"; - EXPECT_LOG_CONTAINS( + WAIT_FOR_LOG_CONTAINS( "info", fmt::format("Quic connection from {} with id {} closed {} with details:", quic_connection_->self_address().ToString(), From 61dd73c82a1204169fd36172485bda2bfc15df1e Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Thu, 1 Aug 2024 15:37:09 -0700 Subject: [PATCH 014/130] build: Updates Go to 1.22.5 (#35535) Go 1.20 has already reached EOF a few months ago. Signed-off-by: Takeshi Yoneda --- bazel/dependency_imports.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bazel/dependency_imports.bzl b/bazel/dependency_imports.bzl index 1bf6c54a2588..6f782ef73263 100644 --- a/bazel/dependency_imports.bzl +++ b/bazel/dependency_imports.bzl @@ -18,7 +18,7 @@ load("@rules_rust//rust:defs.bzl", "rust_common") load("@rules_rust//rust:repositories.bzl", "rules_rust_dependencies", "rust_register_toolchains", "rust_repository_set") # go version for rules_go -GO_VERSION = "1.20" +GO_VERSION = "1.22.5" JQ_VERSION = "1.7" YQ_VERSION = "4.24.4" From 6b47c1b6a5f372836a1ce49bab70b7bc0a4c2ef9 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Thu, 1 Aug 2024 19:28:26 -0500 Subject: [PATCH 015/130] mobile: Specify the port number for the IPv6 probing code (#35556) Since we are probing the Google DNS server, we should specify the DNS port, which is 53. Risk Level: low Testing: n/a Docs Changes: n/a Release Notes: n/a Platform Specific Features: mobile Signed-off-by: Fredy Wijaya --- mobile/library/common/internal_engine.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mobile/library/common/internal_engine.cc b/mobile/library/common/internal_engine.cc index 587613480e30..68bea2d73c05 100644 --- a/mobile/library/common/internal_engine.cc +++ b/mobile/library/common/internal_engine.cc @@ -16,6 +16,7 @@ namespace { constexpr absl::Duration ENGINE_RUNNING_TIMEOUT = absl::Seconds(30); // Google DNS address used for IPv6 probes. constexpr absl::string_view IPV6_PROBE_ADDRESS = "2001:4860:4860::8888"; +constexpr uint32_t IPV6_PROBE_PORT = 53; } // namespace static std::atomic current_stream_handle_{0}; @@ -415,8 +416,9 @@ bool InternalEngine::hasIpV6Connectivity() { } Network::IoSocketHandleImpl socket_handle(socket_result.return_value_, /* socket_v6only= */ true, {domain}); - Api::SysCallIntResult connect_result = socket_handle.connect( - std::make_shared(std::string(IPV6_PROBE_ADDRESS))); + Api::SysCallIntResult connect_result = + socket_handle.connect(std::make_shared( + std::string(IPV6_PROBE_ADDRESS), IPV6_PROBE_PORT)); bool has_ipv6_connectivity = connect_result.return_value_ == 0; if (has_ipv6_connectivity) { ENVOY_LOG(trace, "Found IPv6 connectivity."); From 0970bd38623c9e4b127889da4ee3d3e99d42447a Mon Sep 17 00:00:00 2001 From: phlax Date: Fri, 2 Aug 2024 08:03:10 +0100 Subject: [PATCH 016/130] deps: Remove duplicated rules_proto_grpc dep (#35544) Signed-off-by: Ryan Northey --- api/bazel/repositories.bzl | 4 ---- api/bazel/repository_locations.bzl | 11 ----------- bazel/repositories.bzl | 6 +++--- bazel/repositories_extra.bzl | 2 -- bazel/repository_locations.bzl | 2 +- source/extensions/transport_sockets/alts/BUILD | 2 +- 6 files changed, 5 insertions(+), 22 deletions(-) diff --git a/api/bazel/repositories.bzl b/api/bazel/repositories.bzl index 59506e03b60a..6ca1e5d1fc7f 100644 --- a/api/bazel/repositories.bzl +++ b/api/bazel/repositories.bzl @@ -61,10 +61,6 @@ def api_dependencies(): external_http_archive( name = "com_github_chrusty_protoc_gen_jsonschema", ) - external_http_archive( - name = "rules_proto_grpc", - ) - external_http_archive( name = "envoy_toolshed", ) diff --git a/api/bazel/repository_locations.bzl b/api/bazel/repository_locations.bzl index 8a1241373156..a9862e1dfb39 100644 --- a/api/bazel/repository_locations.bzl +++ b/api/bazel/repository_locations.bzl @@ -175,17 +175,6 @@ REPOSITORY_LOCATIONS_SPEC = dict( use_category = ["api"], release_date = "2024-03-27", ), - rules_proto_grpc = dict( - project_name = "rules_proto_grpc", - project_desc = "Bazel rules for building Protobuf and gRPC code and libraries from proto_library targets ", - project_url = "https://github.com/rules-proto-grpc/rules_proto_grpc", - version = "4.6.0", - sha256 = "2a0860a336ae836b54671cbbe0710eec17c64ef70c4c5a88ccfd47ea6e3739bd", - strip_prefix = "rules_proto_grpc-{version}", - urls = ["https://github.com/rules-proto-grpc/rules_proto_grpc/releases/download/{version}/rules_proto_grpc-{version}.tar.gz"], - use_category = ["build"], - release_date = "2023-12-14", - ), envoy_toolshed = dict( project_name = "envoy_toolshed", project_desc = "Tooling, libraries, runners and checkers for Envoy proxy's CI", diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index ae60f76f4d34..77fab4c64e09 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -302,7 +302,7 @@ def envoy_dependencies(skip_targets = []): _com_github_google_tcmalloc() _com_github_gperftools_gperftools() _com_github_grpc_grpc() - _com_github_rules_proto_grpc() + _rules_proto_grpc() _com_github_unicode_org_icu() _com_github_intel_ipp_crypto_crypto_mb() _com_github_intel_ipp_crypto_crypto_mb_fips() @@ -1226,8 +1226,8 @@ def _com_github_grpc_grpc(): actual = "@com_github_grpc_grpc//test/core/tsi/alts/fake_handshaker:transport_security_common_proto", ) -def _com_github_rules_proto_grpc(): - external_http_archive("com_github_rules_proto_grpc") +def _rules_proto_grpc(): + external_http_archive("rules_proto_grpc") def _re2(): external_http_archive("com_googlesource_code_re2") diff --git a/bazel/repositories_extra.bzl b/bazel/repositories_extra.bzl index 6bdfe928ccb9..9414073fc7fd 100644 --- a/bazel/repositories_extra.bzl +++ b/bazel/repositories_extra.bzl @@ -1,6 +1,5 @@ load("@aspect_bazel_lib//lib:repositories.bzl", "aspect_bazel_lib_dependencies") load("@bazel_features//:deps.bzl", "bazel_features_deps") -load("@com_github_rules_proto_grpc//:repositories.bzl", "rules_proto_grpc_toolchains") load("@emsdk//:deps.bzl", emsdk_deps = "deps") load("@proxy_wasm_cpp_host//bazel/cargo/wasmtime:crates.bzl", "wasmtime_fetch_remote_crates") load("@rules_python//python:repositories.bzl", "py_repositories", "python_register_toolchains") @@ -21,7 +20,6 @@ def envoy_dependencies_extra( emsdk_deps() raze_fetch_remote_crates() wasmtime_fetch_remote_crates() - rules_proto_grpc_toolchains() py_repositories() # Registers underscored Python minor version - eg `python3_10` diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 8e93dd784593..ffc1ecdc37b5 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -398,7 +398,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( license = "Apache-2.0", license_url = "https://github.com/grpc/grpc/blob/v{version}/LICENSE", ), - com_github_rules_proto_grpc = dict( + rules_proto_grpc = dict( project_name = "Protobuf and gRPC rules for Bazel", project_desc = "Bazel rules for building Protobuf and gRPC code and libraries from proto_library targets", project_url = "https://github.com/rules-proto-grpc/rules_proto_grpc", diff --git a/source/extensions/transport_sockets/alts/BUILD b/source/extensions/transport_sockets/alts/BUILD index f1d4c0b33e01..fe03259a7338 100644 --- a/source/extensions/transport_sockets/alts/BUILD +++ b/source/extensions/transport_sockets/alts/BUILD @@ -1,4 +1,4 @@ -load("@com_github_rules_proto_grpc//cpp:defs.bzl", "cpp_grpc_library") +load("@rules_proto_grpc//cpp:defs.bzl", "cpp_grpc_library") load( "//bazel:envoy_build_system.bzl", "envoy_cc_extension", From 75622fe0ec992ab3709933bfc149069e12146b8d Mon Sep 17 00:00:00 2001 From: danzh Date: Fri, 2 Aug 2024 03:28:52 -0400 Subject: [PATCH 017/130] udp: deprecate `envoy.restart_features.udp_read_normalize_addresses` (#35529) Commit Message: remove `envoy.restart_features.udp_read_normalize_addresses` and update change log. Risk Level: low Testing: N/A Docs Changes: N/A Release Notes: Yes Platform Specific Features: N/A Fixes #30430 --------- Signed-off-by: Dan Zhang Co-authored-by: Dan Zhang --- changelogs/current.yaml | 3 +++ source/common/network/io_socket_handle_impl.cc | 7 +++---- source/common/network/io_socket_handle_impl.h | 4 ---- source/common/runtime/runtime_features.cc | 1 - test/integration/quic_http_integration_test.cc | 11 ++--------- 5 files changed, 8 insertions(+), 18 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 28ea9a0ef938..1ea05a70da00 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -80,6 +80,9 @@ removed_config_or_runtime: - area: stateful_session change: | Removed ``envoy.reloadable_features.stateful_session_encode_ttl_in_cookie`` runtime flag and legacy code paths. +- area: udp + change: | + Removed ``envoy.restart_features.udp_read_normalize_addresses`` runtime flag and legacy code paths. - area: upstream change: | Removed runtime flag ``envoy.reloadable_features.upstream_allow_connect_with_2xx`` and legacy code paths. diff --git a/source/common/network/io_socket_handle_impl.cc b/source/common/network/io_socket_handle_impl.cc index c1d9131b260c..edfa56f6cc3d 100644 --- a/source/common/network/io_socket_handle_impl.cc +++ b/source/common/network/io_socket_handle_impl.cc @@ -231,16 +231,15 @@ Api::IoCallUint64Result IoSocketHandleImpl::sendmsg(const Buffer::RawSlice* slic Address::InstanceConstSharedPtr IoSocketHandleImpl::getOrCreateEnvoyAddressInstance(sockaddr_storage ss, socklen_t ss_len) { if (recent_received_addresses_ == nullptr) { - return Address::addressFromSockAddrOrDie(ss, ss_len, fd_, - socket_v6only_ || !udp_read_normalize_addresses_); + return Address::addressFromSockAddrOrDie(ss, ss_len, fd_, socket_v6only_); } quic::QuicSocketAddress quic_address(ss); auto it = recent_received_addresses_->Lookup(quic_address); if (it != recent_received_addresses_->end()) { return *it->second; } - Address::InstanceConstSharedPtr new_address = Address::addressFromSockAddrOrDie( - ss, ss_len, fd_, socket_v6only_ || !udp_read_normalize_addresses_); + Address::InstanceConstSharedPtr new_address = + Address::addressFromSockAddrOrDie(ss, ss_len, fd_, socket_v6only_); recent_received_addresses_->Insert( quic_address, std::make_unique(new_address)); return new_address; diff --git a/source/common/network/io_socket_handle_impl.h b/source/common/network/io_socket_handle_impl.h index 3eb943a4157b..e919cc0c6bac 100644 --- a/source/common/network/io_socket_handle_impl.h +++ b/source/common/network/io_socket_handle_impl.h @@ -32,8 +32,6 @@ class IoSocketHandleImpl : public IoSocketHandleBaseImpl { absl::optional domain = absl::nullopt, size_t address_cache_max_capacity = 0) : IoSocketHandleBaseImpl(fd, socket_v6only, domain), - udp_read_normalize_addresses_( - Runtime::runtimeFeatureEnabled("envoy.restart_features.udp_read_normalize_addresses")), receive_ecn_(Runtime::runtimeFeatureEnabled("envoy.reloadable_features.quic_receive_ecn")) { if (address_cache_max_capacity > 0) { recent_received_addresses_ = @@ -108,8 +106,6 @@ class IoSocketHandleImpl : public IoSocketHandleBaseImpl { const size_t cmsg_space_{CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct in_pktinfo)) + CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(uint16_t))}; - const bool udp_read_normalize_addresses_; - // Latches a copy of the runtime feature "envoy.reloadable_features.quic_receive_ecn". const bool receive_ecn_; diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index b2fe1912886b..c8944b7d78b8 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -100,7 +100,6 @@ RUNTIME_GUARD(envoy_reloadable_features_xdstp_path_avoid_colon_encoding); RUNTIME_GUARD(envoy_restart_features_allow_client_socket_creation_failure); RUNTIME_GUARD(envoy_restart_features_allow_slot_destroy_on_worker_threads); RUNTIME_GUARD(envoy_restart_features_quic_handle_certs_with_shared_tls_code); -RUNTIME_GUARD(envoy_restart_features_udp_read_normalize_addresses); RUNTIME_GUARD(envoy_restart_features_use_fast_protobuf_hash); // Begin false flags. Most of them should come with a TODO to flip true. diff --git a/test/integration/quic_http_integration_test.cc b/test/integration/quic_http_integration_test.cc index 989588059958..538b88a82b07 100644 --- a/test/integration/quic_http_integration_test.cc +++ b/test/integration/quic_http_integration_test.cc @@ -2258,15 +2258,8 @@ TEST_P(QuicHttpIntegrationSPATest, UsesPreferredAddressDualStack) { ASSERT_TRUE(response->complete()); EXPECT_EQ("127.0.0.2", quic_connection_->peer_address().host().ToString()); - if (Runtime::runtimeFeatureEnabled("envoy.restart_features.udp_read_normalize_addresses")) { - test_server_->waitForCounterGe( - "listener.[__]_0.quic.connection.num_packets_rx_on_preferred_address", 2u); - } else { - EXPECT_EQ( - 0u, - test_server_->counter("listener.[__]_0.quic.connection.num_packets_rx_on_preferred_address") - ->value()); - } + test_server_->waitForCounterGe( + "listener.[__]_0.quic.connection.num_packets_rx_on_preferred_address", 2u); } TEST_P(QuicHttpIntegrationTest, PreferredAddressDroppedByIncompatibleListenerFilter) { From 3757d99a26c5ad11c096c4ddbbb0f484d6cf3bb9 Mon Sep 17 00:00:00 2001 From: Fernando Cainelli Date: Fri, 2 Aug 2024 04:35:51 -0300 Subject: [PATCH 018/130] jwt: fix claim to header example doc (#35532) Signed-off-by: Fernando Cainelli Co-authored-by: phlax --- .../filters/http/jwt_authn/v3/config.proto | 11 ++- configs/jwt_authn.yaml | 97 +++++++++++++++++++ tools/spelling/check_spelling_pedantic.py | 8 +- 3 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 configs/jwt_authn.yaml diff --git a/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto b/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto index 41d694a014dd..85f96eeef873 100644 --- a/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto +++ b/api/envoy/extensions/filters/http/jwt_authn/v3/config.proto @@ -355,11 +355,12 @@ message JwtProvider { // Specify the claim name you want to copy in which HTTP header. For examples, following config: // The claim must be of type; string, int, double, bool. Array type claims are not supported // - // .. code-block:: yaml - // - // claim_to_headers: - // - name: x-jwt-claim-nested-claim - // claim: claim.nested.key + // .. literalinclude:: /_configs/repo/jwt_authn.yaml + // :language: yaml + // :lines: 44-48 + // :linenos: + // :lineno-start: 44 + // :caption: :download:`jwt_authn.yaml ` // // This header is only reserved for jwt claim; any other value will be overwritten. repeated JwtClaimToHeader claim_to_headers = 15; diff --git a/configs/jwt_authn.yaml b/configs/jwt_authn.yaml new file mode 100644 index 000000000000..725460fdcd9c --- /dev/null +++ b/configs/jwt_authn.yaml @@ -0,0 +1,97 @@ +# An example config to validate JWT tokens issued by Firebase. +admin: + address: + socket_address: + address: 0.0.0.0 + port_value: 9901 +static_resources: + listeners: + - name: listener_0 + address: + socket_address: + protocol: TCP + address: 0.0.0.0 + port_value: 10000 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager" + stat_prefix: ingress_http + access_log: + - name: envoy.access_loggers.stdout + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog + route_config: + name: local_route + virtual_hosts: + - name: default + domains: + - "*" + routes: + - match: + prefix: "/" + direct_response: + status: 200 + body: + inline_string: "OK" + http_filters: + - name: envoy.extensions.filters.http.jwt_authn + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication + providers: + firebase: + claim_to_headers: + - claim_name: user_id + header_name: x-firebase-uid + - claim_name: firebase.sign_in_provider + header_name: x-firebase-provider + issuer: https://securetoken.google.com/example.com:example-project-1234567890 + audiences: + - example.com:example-project-1234567890 + remoteJwks: + httpUri: + uri: https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com + cluster: www.googleapis.com + timeout: 1s + async_fetch: + fast_listener: false + retry_policy: + num_retries: 10 + forward: true + jwt_cache_config: + jwt_cache_size: 1024 + rules: + - match: + prefix: "/" + requires: + provider_name: firebase + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - name: www.googleapis.com + type: STRICT_DNS + connect_timeout: 2s + lb_policy: ROUND_ROBIN + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + upstream_http_protocol_options: + auto_sni: true + auto_config: + http2_protocol_options: {} + load_assignment: + cluster_name: www.googleapis.com + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: www.googleapis.com + port_value: 443 + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + sni: www.googleapis.com diff --git a/tools/spelling/check_spelling_pedantic.py b/tools/spelling/check_spelling_pedantic.py index d94f60a1aed6..082122dcf417 100755 --- a/tools/spelling/check_spelling_pedantic.py +++ b/tools/spelling/check_spelling_pedantic.py @@ -106,6 +106,9 @@ def cmp(x, y): # RST code block marker. RST_CODE_BLOCK = '.. code-block::' +# RST literal include. +RST_LITERAL_INCLUDE = '.. literalinclude::' + # Path names. ABSPATH = re.compile(r'(?:\s|^)((/[A-Za-z0-9_.*-]+)+)(?:\s|$)') FILEREF = re.compile(r'(?:\s|^)([A-Za-z0-9_./-]+\.(cc|js|h|py|sh))(?:\s|$)') @@ -649,7 +652,7 @@ def extract_comments(lines): comments.append(Comment(line=line_idx, col=col, text=text, last_on_line=last_on_line)) # Handle control statements and filter out comments that are part of - # RST code block directives. + # RST code block and literal include directives. result = [] n = 0 nc = len(comments) @@ -687,7 +690,8 @@ def extract_comments(lines): break n += 1 - elif text.strip().startswith(RST_CODE_BLOCK): + elif text.strip().startswith(RST_CODE_BLOCK) or text.strip().startswith( + RST_LITERAL_INCLUDE): # Start of a code block. indent = len(INDENT.search(text).group(1)) last_line = comments[n].line From 5f26450e5c1eff8f1f0e30690c39f97f4665ef43 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Fri, 2 Aug 2024 09:56:57 -0400 Subject: [PATCH 019/130] envoy: cleaning up formatter use for Envoy Mobile (#35505) formatter code was removed from the E-M build but still included in E-M tests so still had to use the OrPanic macros. Fixed by removing it from the E-M tests as well. Risk Level: n/a Testing: CI Docs Changes: n/a Release Notes: n/a Signed-off-by: Alyssa Wilk --- .../formatter/http_specific_formatter.cc | 3 +-- .../common/formatter/stream_info_formatter.cc | 18 ++++++++---------- test/integration/BUILD | 6 +++--- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/source/common/formatter/http_specific_formatter.cc b/source/common/formatter/http_specific_formatter.cc index 8cafea9d7c2a..185311e3921a 100644 --- a/source/common/formatter/http_specific_formatter.cc +++ b/source/common/formatter/http_specific_formatter.cc @@ -194,8 +194,7 @@ GrpcStatusFormatter::Format GrpcStatusFormatter::parseFormat(absl::string_view f return GrpcStatusFormatter::Number; } - throwEnvoyExceptionOrPanic( - "GrpcStatusFormatter only supports CAMEL_STRING, SNAKE_STRING or NUMBER."); + throw EnvoyException("GrpcStatusFormatter only supports CAMEL_STRING, SNAKE_STRING or NUMBER."); } GrpcStatusFormatter::GrpcStatusFormatter(const std::string& main_header, diff --git a/source/common/formatter/stream_info_formatter.cc b/source/common/formatter/stream_info_formatter.cc index 3e40fa15b4c1..1dbca25b4e0a 100644 --- a/source/common/formatter/stream_info_formatter.cc +++ b/source/common/formatter/stream_info_formatter.cc @@ -164,7 +164,7 @@ FilterStateFormatter::create(const std::string& format, const absl::optional parsed_sub_commands = absl::StrSplit(sub_command, ':'); if (parsed_sub_commands.size() < 2 || parsed_sub_commands.size() > 3) { - throwEnvoyExceptionOrPanic( - fmt::format("Invalid common duration configuration: {}.", sub_command)); + throw EnvoyException(fmt::format("Invalid common duration configuration: {}.", sub_command)); } absl::string_view start = parsed_sub_commands[0]; @@ -442,8 +441,7 @@ CommonDurationFormatter::create(absl::string_view sub_command) { } else if (precision_str == NanosecondsPrecision) { precision = DurationPrecision::Nanoseconds; } else { - throwEnvoyExceptionOrPanic( - fmt::format("Invalid common duration precision: {}.", precision_str)); + throw EnvoyException(fmt::format("Invalid common duration precision: {}.", precision_str)); } } @@ -557,7 +555,7 @@ SystemTimeFormatter::SystemTimeFormatter(const std::string& format, TimeFieldExt // Validate the input specifier here. The formatted string may be destined for a header, and // should not contain invalid characters {NUL, LR, CF}. if (std::regex_search(format, getSystemTimeFormatNewlinePattern())) { - throwEnvoyExceptionOrPanic("Invalid header configuration. Format string contains newline."); + throw EnvoyException("Invalid header configuration. Format string contains newline."); } } diff --git a/test/integration/BUILD b/test/integration/BUILD index bfa75687bfa1..1e2e7c57c4f8 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -347,7 +347,7 @@ envoy_cc_test_binary( "abseil_symbolize", ], deps = [ - "//source/common/formatter:formatter_extension_lib", + ":common_extensions_lib", "//source/common/http:rds_lib", "//source/exe:envoy_main_common_with_core_extensions_lib", "//source/exe:platform_impl_lib", @@ -370,7 +370,7 @@ envoy_cc_test_binary( ], linkstatic = envoy_select_linkstatic(), deps = [ - "//source/common/formatter:formatter_extension_lib", + ":common_extensions_lib", "//source/exe:main_common_lib", "//source/exe:platform_impl_lib", "//source/exe:process_wide_lib", @@ -1119,6 +1119,7 @@ envoy_cc_test_library( envoy_cc_test_library( name = "common_extensions_lib", deps = [ + "//source/common/formatter:formatter_extension_lib", "//source/common/http:rds_lib", "//source/common/router:scoped_rds_lib", "//source/extensions/clusters/eds:eds_lib", @@ -1149,7 +1150,6 @@ envoy_cc_test_library( ":utility_lib", "//source/common/common:thread_lib", "//source/common/config:api_version_lib", - "//source/common/formatter:formatter_extension_lib", "//source/common/tls:context_config_lib", "//source/common/tls:context_lib", "//source/common/tls:ssl_socket_lib", From f58a812ed24bf7d552ece3642bdf9336ee1a7e20 Mon Sep 17 00:00:00 2001 From: yanavlasov Date: Fri, 2 Aug 2024 11:13:03 -0400 Subject: [PATCH 020/130] Refactor Upstream API for setting TCP tunneling mode (#35510) This API was introduced by #32991 and it conflates support for half-close with TCP proxying mode in H/1 codec. This interferes with #34461 which enables support for server half-close in H/2 proxying. This PR refactors API to separate enabling of the half-close behavior from TCP proxy behavior. Prior to #34461 half-close semativs were only enabled for TCP proxy upstreams, but #34461 introduces other cases as well. This necessitates setting half-close and TCP proxy modes separately. TCP proxy mode is now enabled in the class dedicated to the TCP proxy upstreams. --------- Signed-off-by: Yan Avlasov --- envoy/router/router.h | 10 ++++------ source/common/router/upstream_request.cc | 3 --- source/common/tcp_proxy/upstream.h | 18 +++++++++++++++++- .../upstreams/http/http/upstream_request.h | 2 +- .../upstreams/http/tcp/upstream_request.h | 2 +- .../upstreams/http/udp/upstream_request.h | 2 +- .../http/udp/upstream_request_test.cc | 2 +- 7 files changed, 25 insertions(+), 14 deletions(-) diff --git a/envoy/router/router.h b/envoy/router/router.h index b6d23856649e..0200131c27aa 100644 --- a/envoy/router/router.h +++ b/envoy/router/router.h @@ -1544,15 +1544,13 @@ class GenericUpstream { virtual void encodeTrailers(const Http::RequestTrailerMap& trailers) PURE; // TODO(vikaschoudhary16): Remove this api. - // This api is only used to enable half-close semantics on the upstream connection. - // This ideally should be done via calling connection.enableHalfClose() but since TcpProxy - // does not have access to the upstream connection, it is done via this api for now. + // This api is only used to enable TCP tunneling semantics in the upstream codec. + // TCP proxy extension uses this API when proxyingn TCP tunnel via HTTP CONNECT or POST. /** - * Enable half-close semantics on the upstream connection. Reading a remote half-close + * Enable TCP tunneling semantics on the upstream codec. Reading a remote half-close * will not fully close the connection. This is off by default. - * @param enabled Whether to set half-close semantics as enabled or disabled. */ - virtual void enableHalfClose() PURE; + virtual void enableTcpTunneling() PURE; /** * Enable/disable further data from this stream. */ diff --git a/source/common/router/upstream_request.cc b/source/common/router/upstream_request.cc index 18fb79901bec..cff1d5b08df2 100644 --- a/source/common/router/upstream_request.cc +++ b/source/common/router/upstream_request.cc @@ -596,9 +596,6 @@ void UpstreamRequest::onPoolReady(std::unique_ptr&& upstream, had_upstream_ = true; // Have the upstream use the account of the downstream. upstream_->setAccount(parent_.callbacks()->account()); - if (enable_half_close_) { - upstream_->enableHalfClose(); - } host->outlierDetector().putResult(Upstream::Outlier::Result::LocalOriginConnectSuccess); diff --git a/source/common/tcp_proxy/upstream.h b/source/common/tcp_proxy/upstream.h index 1f2135b40532..7aae5ebb47d2 100644 --- a/source/common/tcp_proxy/upstream.h +++ b/source/common/tcp_proxy/upstream.h @@ -59,6 +59,23 @@ class TcpConnPool : public GenericConnPool, public Tcp::ConnectionPool::Callback class HttpUpstream; class CombinedUpstream; +// This class is specific to TCP proxy connection pool and enables TCP proxying mode +// for HTTP upstreams. This is currently only needed for HTTP/1 client codec that half closes +// upstream network connection after encoding end_stream in TCP proxy (i.e. via CONNECT). +class RouterUpstreamRequest : public Router::UpstreamRequest { +public: + using Router::UpstreamRequest::UpstreamRequest; + + void onPoolReady(std::unique_ptr&& upstream, + Upstream::HostDescriptionConstSharedPtr host, + const Network::ConnectionInfoProvider& address_provider, + StreamInfo::StreamInfo& info, absl::optional protocol) override { + upstream->enableTcpTunneling(); + Router::UpstreamRequest::onPoolReady(std::move(upstream), host, address_provider, info, + protocol); + } +}; + class HttpConnPool : public GenericConnPool, public Http::ConnectionPool::Callbacks { public: HttpConnPool(Upstream::ThreadLocalCluster& thread_local_cluster, @@ -67,7 +84,6 @@ class HttpConnPool : public GenericConnPool, public Http::ConnectionPool::Callba Http::StreamDecoderFilterCallbacks&, Http::CodecType type, StreamInfo::StreamInfo& downstream_info); - using RouterUpstreamRequest = Router::UpstreamRequest; using RouterUpstreamRequestPtr = std::unique_ptr; ~HttpConnPool() override; diff --git a/source/extensions/upstreams/http/http/upstream_request.h b/source/extensions/upstreams/http/http/upstream_request.h index db8adfc2cc14..f8c714160774 100644 --- a/source/extensions/upstreams/http/http/upstream_request.h +++ b/source/extensions/upstreams/http/http/upstream_request.h @@ -73,7 +73,7 @@ class HttpUpstream : public Router::GenericUpstream, public Envoy::Http::StreamC void encodeTrailers(const Envoy::Http::RequestTrailerMap& trailers) override { request_encoder_->encodeTrailers(trailers); } - void enableHalfClose() override { request_encoder_->enableTcpTunneling(); } + void enableTcpTunneling() override { request_encoder_->enableTcpTunneling(); } void readDisable(bool disable) override { request_encoder_->getStream().readDisable(disable); } diff --git a/source/extensions/upstreams/http/tcp/upstream_request.h b/source/extensions/upstreams/http/tcp/upstream_request.h index 3a0df2815529..1823c48e41bb 100644 --- a/source/extensions/upstreams/http/tcp/upstream_request.h +++ b/source/extensions/upstreams/http/tcp/upstream_request.h @@ -78,7 +78,7 @@ class TcpUpstream : public Router::GenericUpstream, void encodeMetadata(const Envoy::Http::MetadataMapVector&) override {} Envoy::Http::Status encodeHeaders(const Envoy::Http::RequestHeaderMap&, bool end_stream) override; void encodeTrailers(const Envoy::Http::RequestTrailerMap&) override; - void enableHalfClose() override {} + void enableTcpTunneling() override {} void readDisable(bool disable) override; void resetStream() override; void setAccount(Buffer::BufferMemoryAccountSharedPtr) override {} diff --git a/source/extensions/upstreams/http/udp/upstream_request.h b/source/extensions/upstreams/http/udp/upstream_request.h index 5a17695d269c..34fab9e45da2 100644 --- a/source/extensions/upstreams/http/udp/upstream_request.h +++ b/source/extensions/upstreams/http/udp/upstream_request.h @@ -77,7 +77,7 @@ class UdpUpstream : public Router::GenericUpstream, void encodeTrailers(const Envoy::Http::RequestTrailerMap&) override {} void readDisable(bool) override {} void resetStream() override; - void enableHalfClose() override {} + void enableTcpTunneling() override {} void setAccount(Buffer::BufferMemoryAccountSharedPtr) override {} const StreamInfo::BytesMeterSharedPtr& bytesMeter() override { return bytes_meter_; } diff --git a/test/extensions/upstreams/http/udp/upstream_request_test.cc b/test/extensions/upstreams/http/udp/upstream_request_test.cc index a3fa14becf97..beb3be1d2e3d 100644 --- a/test/extensions/upstreams/http/udp/upstream_request_test.cc +++ b/test/extensions/upstreams/http/udp/upstream_request_test.cc @@ -46,7 +46,7 @@ class UdpUpstreamTest : public ::testing::Test { udp_upstream_ = std::make_unique(&mock_upstream_to_downstream_, std::move(mock_socket), std::move(mock_host), mock_dispatcher_); - EXPECT_NO_THROW(udp_upstream_->enableHalfClose()); + EXPECT_NO_THROW(udp_upstream_->enableTcpTunneling()); } protected: From 9ea5239ce2ab74a355f1a58ffdcc698823f1aebd Mon Sep 17 00:00:00 2001 From: DiazAlan <109677874+DiazAlan@users.noreply.github.com> Date: Fri, 2 Aug 2024 12:31:01 -0400 Subject: [PATCH 021/130] Add tag extraction rule for SRDS stats (#35527) Signed-off-by: AlanDiaz --- changelogs/current.yaml | 4 ++++ source/common/config/well_known_names.cc | 7 +++++++ source/common/config/well_known_names.h | 2 ++ test/common/stats/tag_extractor_impl_test.cc | 14 ++++++++++++++ 4 files changed, 27 insertions(+) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 1ea05a70da00..8ad729d94c33 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -6,6 +6,10 @@ behavior_changes: change: | Removed support for (long deprecated) opentracing. See `issue 27401 `_ for details. +- area: stats scoped_rds + change: | + Added new tag extraction so that scoped rds stats have their :ref:'scope_route_config_name + ' and stat prefix extracted. minor_behavior_changes: # *Changes that may cause incompatibilities for some users, but should not for most* diff --git a/source/common/config/well_known_names.cc b/source/common/config/well_known_names.cc index c9b73b1ab089..466e033c33a9 100644 --- a/source/common/config/well_known_names.cc +++ b/source/common/config/well_known_names.cc @@ -26,6 +26,8 @@ std::string expandRegex(const std::string& regex) { // Route names may contain dots and slashes in addition to // alphanumerics, underscores, and dashes. {"", R"([\w-\./]+)"}, + // Scoped Route names are named similarly to route config names. + {"", R"([\w-\.]+)"}, // Match a prefix that is either a listener plus name or cluster plus name {"", R"((?:listener|cluster)\..*?)"}, {"", R"(\d)"}}); @@ -183,6 +185,11 @@ TagNameValues::TagNameValues() { // match. addRe2(RDS_ROUTE_CONFIG, R"(^http\.\.rds\.(()\.)\w+?$)", ".rds."); + // http.[.]scoped_rds.(.) + addRe2(SCOPED_RDS_CONFIG, + R"(^http\.\.scoped_rds\.(()\.)\w+?$)", + ".scoped_rds."); + // vhost.[.]route.(.)* addTokenized(ROUTE, "vhost.*.route.$.**"); diff --git a/source/common/config/well_known_names.h b/source/common/config/well_known_names.h index 334e50cc6aa2..3e3130020ac3 100644 --- a/source/common/config/well_known_names.h +++ b/source/common/config/well_known_names.h @@ -153,6 +153,8 @@ class TagNameValues { const std::string RESPONSE_CODE_CLASS = "envoy.response_code_class"; // Route config name for RDS updates const std::string RDS_ROUTE_CONFIG = "envoy.rds_route_config"; + // Scoped route config name for RDS updates + const std::string SCOPED_RDS_CONFIG = "envoy.scoped_rds_config"; // Request route given by the Router http filter const std::string ROUTE = "envoy.route"; // Stats prefix for the ext_authz HTTP filter diff --git a/test/common/stats/tag_extractor_impl_test.cc b/test/common/stats/tag_extractor_impl_test.cc index 6bb2e8443b1e..ab408b1aa9f4 100644 --- a/test/common/stats/tag_extractor_impl_test.cc +++ b/test/common/stats/tag_extractor_impl_test.cc @@ -405,6 +405,20 @@ TEST(TagExtractorTest, DefaultTagExtractors) { regex_tester.testRegex("http.rds_connection_manager.rds.agg/route_config.1-23.update_success", "http.rds.update_success", {rds_hcm, rds_route_config}); + // SRDS. + Tag scoped_rds_hcm; + + scoped_rds_hcm.name_ = tag_names.HTTP_CONN_MANAGER_PREFIX; + scoped_rds_hcm.value_ = "scoped_rds_connection_manager"; + + Tag scoped_rds_route_config; + scoped_rds_route_config.name_ = tag_names.SCOPED_RDS_CONFIG; + scoped_rds_route_config.value_ = "scoped_route_config.123"; + + regex_tester.testRegex( + "http.scoped_rds_connection_manager.scoped_rds.scoped_route_config.123.update_success", + "http.scoped_rds.update_success", {scoped_rds_hcm, scoped_rds_route_config}); + // Listener manager worker id Tag worker_id; worker_id.name_ = tag_names.WORKER_ID; From 37bdbd183701fe09904712d112c5b33cb0e4a245 Mon Sep 17 00:00:00 2001 From: yanavlasov Date: Fri, 2 Aug 2024 12:45:26 -0400 Subject: [PATCH 022/130] Refactor state tracking variables in filter manager (#35528) Summary of changes: Fix dual use of the local_complete_ flag. Presently it used for tracking both: end_stream value in iteration of encoder filter chain. stopping decoder filter chain when half close semantics are disabled. The local_complete_ is renamed to observed_encode_end_stream_ and is now used for tracking end_stream value in encoder filter chain iteration only. The decode filter chain is stopped by using the existing flag decoder_filter_chain_aborted_, which up till now was only used to stop filter chain when reset was received. Rename remote_decode_complete_ flag to observed_decode_end_stream_ to match its purpose. Rename remoteDecodeComplete() to decoderObservedEndStream() to better match its meaning. Rename the complete() method to observedEndStream() to better reflect its use. Add stopDecoderFilterChain() (which returns the decoder_filter_chain_aborted_) and use it to check if the decoder filter chain should be stopped (instead of using the local_complete_ flag). --------- Signed-off-by: Yan Avlasov --- source/common/http/conn_manager_impl.cc | 18 ++++---- source/common/http/filter_manager.cc | 53 ++++++++++++++---------- source/common/http/filter_manager.h | 53 +++++++++++++++--------- source/common/router/upstream_request.cc | 4 +- 4 files changed, 76 insertions(+), 52 deletions(-) diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc index 77aa6d7a9490..6a9bd3c0d446 100644 --- a/source/common/http/conn_manager_impl.cc +++ b/source/common/http/conn_manager_impl.cc @@ -243,7 +243,7 @@ void ConnectionManagerImpl::doEndStream(ActiveStream& stream, bool check_for_def // here is when Envoy "ends" the stream by calling recreateStream at which point recreateStream // explicitly nulls out response_encoder to avoid the downstream being notified of the // Envoy-internal stream instance being ended. - if (stream.response_encoder_ != nullptr && (!stream.filter_manager_.remoteDecodeComplete() || + if (stream.response_encoder_ != nullptr && (!stream.filter_manager_.decoderObservedEndStream() || !stream.state_.codec_saw_local_complete_)) { // Indicate local is complete at this point so that if we reset during a continuation, we don't // raise further data or trailers. @@ -290,7 +290,7 @@ void ConnectionManagerImpl::doEndStream(ActiveStream& stream, bool check_for_def // fully read, as there's no race condition to avoid. const bool connection_close = stream.filter_manager_.streamInfo().shouldDrainConnectionUponCompletion(); - const bool request_complete = stream.filter_manager_.remoteDecodeComplete(); + const bool request_complete = stream.filter_manager_.decoderObservedEndStream(); // Don't do delay close for HTTP/1.0 or if the request is complete. checkForDeferredClose(connection_close && (request_complete || http_10_sans_cl)); @@ -925,21 +925,21 @@ void ConnectionManagerImpl::ActiveStream::onIdleTimeout() { connection_manager_.stats_.named_.downstream_rq_idle_timeout_.inc(); filter_manager_.streamInfo().setResponseFlag(StreamInfo::CoreResponseFlag::StreamIdleTimeout); - sendLocalReply(Http::Utility::maybeRequestTimeoutCode(filter_manager_.remoteDecodeComplete()), + sendLocalReply(Http::Utility::maybeRequestTimeoutCode(filter_manager_.decoderObservedEndStream()), "stream timeout", nullptr, absl::nullopt, StreamInfo::ResponseCodeDetails::get().StreamIdleTimeout); } void ConnectionManagerImpl::ActiveStream::onRequestTimeout() { connection_manager_.stats_.named_.downstream_rq_timeout_.inc(); - sendLocalReply(Http::Utility::maybeRequestTimeoutCode(filter_manager_.remoteDecodeComplete()), + sendLocalReply(Http::Utility::maybeRequestTimeoutCode(filter_manager_.decoderObservedEndStream()), "request timeout", nullptr, absl::nullopt, StreamInfo::ResponseCodeDetails::get().RequestOverallTimeout); } void ConnectionManagerImpl::ActiveStream::onRequestHeaderTimeout() { connection_manager_.stats_.named_.downstream_rq_header_timeout_.inc(); - sendLocalReply(Http::Utility::maybeRequestTimeoutCode(filter_manager_.remoteDecodeComplete()), + sendLocalReply(Http::Utility::maybeRequestTimeoutCode(filter_manager_.decoderObservedEndStream()), "request header timeout", nullptr, absl::nullopt, StreamInfo::ResponseCodeDetails::get().RequestHeaderTimeout); } @@ -947,7 +947,7 @@ void ConnectionManagerImpl::ActiveStream::onRequestHeaderTimeout() { void ConnectionManagerImpl::ActiveStream::onStreamMaxDurationReached() { ENVOY_STREAM_LOG(debug, "Stream max duration time reached", *this); connection_manager_.stats_.named_.downstream_rq_max_duration_reached_.inc(); - sendLocalReply(Http::Utility::maybeRequestTimeoutCode(filter_manager_.remoteDecodeComplete()), + sendLocalReply(Http::Utility::maybeRequestTimeoutCode(filter_manager_.decoderObservedEndStream()), "downstream duration timeout", nullptr, Grpc::Status::WellKnownGrpcStatus::DeadlineExceeded, StreamInfo::ResponseCodeDetails::get().MaxDurationTimeout); @@ -1113,7 +1113,7 @@ bool ConnectionManagerImpl::ActiveStream::validateTrailers() { void ConnectionManagerImpl::ActiveStream::maybeEndDecode(bool end_stream) { // If recreateStream is called, the HCM rewinds state and may send more encodeData calls. - if (end_stream && !filter_manager_.remoteDecodeComplete()) { + if (end_stream && !filter_manager_.decoderObservedEndStream()) { filter_manager_.streamInfo().downstreamTiming().onLastDownstreamRxByteReceived( connection_manager_.dispatcher_->timeSource()); ENVOY_STREAM_LOG(debug, "request end stream", *this); @@ -1136,7 +1136,7 @@ void ConnectionManagerImpl::ActiveStream::decodeHeaders(RequestHeaderMapSharedPt *headers); // We only want to record this when reading the headers the first time, not when recreating // a stream. - if (!filter_manager_.remoteDecodeComplete()) { + if (!filter_manager_.decoderObservedEndStream()) { filter_manager_.streamInfo().downstreamTiming().onLastDownstreamHeaderRxByteReceived( connection_manager_.dispatcher_->timeSource()); } @@ -1764,7 +1764,7 @@ void ConnectionManagerImpl::ActiveStream::encodeHeaders(ResponseHeaderMap& heade // If we are destroying a stream before remote is complete and the connection does not support // multiplexing, we should disconnect since we don't want to wait around for the request to // finish. - if (!filter_manager_.remoteDecodeComplete()) { + if (!filter_manager_.decoderObservedEndStream()) { if (connection_manager_.codec_->protocol() < Protocol::Http2) { connection_manager_.drain_state_ = DrainState::Closing; } diff --git a/source/common/http/filter_manager.cc b/source/common/http/filter_manager.cc index b2ab8f0d4c83..d499e03483eb 100644 --- a/source/common/http/filter_manager.cc +++ b/source/common/http/filter_manager.cc @@ -91,7 +91,7 @@ void ActiveStreamFilterBase::commonContinue() { // future. if (!headers_continued_) { headers_continued_ = true; - doHeaders(complete() && !bufferedData() && !hasTrailers()); + doHeaders(observedEndStream() && !bufferedData() && !hasTrailers()); } doMetadata(); @@ -102,7 +102,7 @@ void ActiveStreamFilterBase::commonContinue() { // on doData() to do so. const bool had_trailers_before_data = hasTrailers(); if (bufferedData()) { - doData(complete() && !had_trailers_before_data); + doData(observedEndStream() && !had_trailers_before_data); } if (had_trailers_before_data) { @@ -197,7 +197,7 @@ bool ActiveStreamFilterBase::commonHandleAfterDataCallback(FilterDataStatus stat status == FilterDataStatus::StopIterationAndWatermark) { buffer_was_streaming = status == FilterDataStatus::StopIterationAndWatermark; commonHandleBufferData(provided_data); - } else if (complete() && !hasTrailers() && !bufferedData() && + } else if (observedEndStream() && !hasTrailers() && !bufferedData() && // If the stream is destroyed, no need to handle the data buffer or trailers. // This can occur if the filter calls sendLocalReply. !parent_.state_.destroyed_) { @@ -347,13 +347,13 @@ bool ActiveStreamDecoderFilter::canContinue() { // continue to further filters. A concrete example of this is a filter buffering data, the // last data frame comes in and the filter continues, but the final buffering takes the stream // over the high watermark such that a 413 is returned. - return !parent_.state_.local_complete_; + return !parent_.stopDecoderFilterChain(); } bool ActiveStreamEncoderFilter::canContinue() { // As with ActiveStreamDecoderFilter::canContinue() make sure we do not // continue if a local reply has been sent. - return !parent_.state_.remote_encode_complete_; + return !parent_.state_.encoder_filter_chain_complete_; } Buffer::InstancePtr ActiveStreamDecoderFilter::createBuffer() { @@ -369,7 +369,7 @@ Buffer::InstancePtr& ActiveStreamDecoderFilter::bufferedData() { return parent_.buffered_request_data_; } -bool ActiveStreamDecoderFilter::complete() { return parent_.remoteDecodeComplete(); } +bool ActiveStreamDecoderFilter::observedEndStream() { return parent_.decoderObservedEndStream(); } void ActiveStreamDecoderFilter::doHeaders(bool end_stream) { parent_.decodeHeaders(this, *parent_.filter_manager_callbacks_.requestHeaders(), end_stream); @@ -557,7 +557,7 @@ void FilterManager::decodeHeaders(ActiveStreamDecoderFilter* filter, RequestHead (*entry)->processed_headers_ = true; const auto continue_iteration = (*entry)->commonHandleAfterHeadersCallback(status, end_stream); - ENVOY_BUG(!continue_iteration || !state_.local_complete_, + ENVOY_BUG(!continue_iteration || !stopDecoderFilterChain(), fmt::format( "filter={} did not return StopAll or StopIteration after sending a local reply.", (*entry)->filter_context_.config_name)); @@ -568,7 +568,7 @@ void FilterManager::decodeHeaders(ActiveStreamDecoderFilter* filter, RequestHead } // Skip processing metadata after sending local reply - if (state_.local_complete_ && std::next(entry) != decoder_filters_.end()) { + if (stopDecoderFilterChain() && std::next(entry) != decoder_filters_.end()) { maybeContinueDecoding(continue_data_entry); return; } @@ -616,7 +616,7 @@ void FilterManager::decodeData(ActiveStreamDecoderFilter* filter, Buffer::Instan // If a response is complete or a reset has been sent, filters do not care about further body // data. Just drop it. - if (state_.local_complete_) { + if (stopDecoderFilterChain()) { return; } @@ -755,8 +755,9 @@ void FilterManager::addDecodedData(ActiveStreamDecoderFilter& filter, Buffer::In MetadataMapVector& FilterManager::addDecodedMetadata() { return *getRequestMetadataMapVector(); } void FilterManager::decodeTrailers(ActiveStreamDecoderFilter* filter, RequestTrailerMap& trailers) { - // See decodeData() above for why we check local_complete_ here. - if (state_.local_complete_) { + // If a response is complete or a reset has been sent, filters do not care about further body + // data. Just drop it. + if (stopDecoderFilterChain()) { return; } @@ -858,13 +859,19 @@ FilterManager::commonEncodePrefix(ActiveStreamEncoderFilter* filter, bool end_st ENVOY_STREAM_LOG(trace, "commonEncodePrefix end_stream: {}, isHalfCloseEnabled: {}", *this, end_stream, filter_manager_callbacks_.isHalfCloseEnabled()); if (filter == nullptr) { - // half close is enabled in case tcp proxying is done with http1 encoder. In this case, we - // should not set the local_complete_ flag to true when end_stream is true. - // setting local_complete_ to true will cause any data sent in the upstream direction to be - // dropped. - if (end_stream && !filter_manager_callbacks_.isHalfCloseEnabled()) { - ASSERT(!state_.local_complete_); - state_.local_complete_ = true; + if (end_stream) { + ASSERT(!state_.observed_encode_end_stream_); + state_.observed_encode_end_stream_ = true; + + // When half close semantics are disabled, receiving end stream from the upstream causes + // decoder filter to stop, as neither filters nor upstream is interested in downstream data. + // half close is enabled in case tcp proxying is done with http1 encoder. In this case, we + // should not stop decoder filter chain when end_stream is true, as it will cause any data + // sent in the upstream direction to be + // dropped. + if (!filter_manager_callbacks_.isHalfCloseEnabled()) { + state_.decoder_filter_chain_aborted_ = true; + } } return encoder_filters_.begin(); } @@ -1439,8 +1446,8 @@ void FilterManager::encodeTrailers(ActiveStreamEncoderFilter* filter, void FilterManager::maybeEndEncode(bool end_stream) { if (end_stream) { - ASSERT(!state_.remote_encode_complete_); - state_.remote_encode_complete_ = true; + ASSERT(!state_.encoder_filter_chain_complete_); + state_.encoder_filter_chain_complete_ = true; filter_manager_callbacks_.endStream(); } } @@ -1579,7 +1586,7 @@ bool ActiveStreamDecoderFilter::recreateStream(const ResponseHeaderMap* headers) // Because the filter's and the HCM view of if the stream has a body and if // the stream is complete may differ, re-check bytesReceived() to make sure // there was no body from the HCM's point of view. - if (!complete()) { + if (!observedEndStream()) { return false; } @@ -1624,7 +1631,9 @@ Buffer::InstancePtr ActiveStreamEncoderFilter::createBuffer() { Buffer::InstancePtr& ActiveStreamEncoderFilter::bufferedData() { return parent_.buffered_response_data_; } -bool ActiveStreamEncoderFilter::complete() { return parent_.state_.local_complete_; } +bool ActiveStreamEncoderFilter::observedEndStream() { + return parent_.state_.observed_encode_end_stream_; +} bool ActiveStreamEncoderFilter::has1xxHeaders() { return parent_.state_.has_1xx_headers_ && !continued_1xx_headers_; } diff --git a/source/common/http/filter_manager.h b/source/common/http/filter_manager.h index ef774fc28638..392f0bdcd239 100644 --- a/source/common/http/filter_manager.h +++ b/source/common/http/filter_manager.h @@ -91,7 +91,7 @@ struct ActiveStreamFilterBase : public virtual StreamFilterCallbacks, virtual bool canContinue() PURE; virtual Buffer::InstancePtr createBuffer() PURE; virtual Buffer::InstancePtr& bufferedData() PURE; - virtual bool complete() PURE; + virtual bool observedEndStream() PURE; virtual bool has1xxHeaders() PURE; virtual void do1xxHeaders() PURE; virtual void doHeaders(bool end_stream) PURE; @@ -212,7 +212,7 @@ struct ActiveStreamDecoderFilter : public ActiveStreamFilterBase, bool canContinue() override; Buffer::InstancePtr createBuffer() override; Buffer::InstancePtr& bufferedData() override; - bool complete() override; + bool observedEndStream() override; bool has1xxHeaders() override { return false; } void do1xxHeaders() override { IS_ENVOY_BUG("unexpected 1xx headers"); } void doHeaders(bool end_stream) override; @@ -304,7 +304,7 @@ struct ActiveStreamEncoderFilter : public ActiveStreamFilterBase, bool canContinue() override; Buffer::InstancePtr createBuffer() override; Buffer::InstancePtr& bufferedData() override; - bool complete() override; + bool observedEndStream() override; bool has1xxHeaders() override; void do1xxHeaders() override; void doHeaders(bool end_stream) override; @@ -745,7 +745,7 @@ class FilterManager : public ScopeTrackedObject, * @param end_stream whether the request is header only. */ void decodeHeaders(RequestHeaderMap& headers, bool end_stream) { - state_.remote_decode_complete_ = end_stream; + state_.observed_decode_end_stream_ = end_stream; decodeHeaders(nullptr, headers, end_stream); } @@ -755,7 +755,7 @@ class FilterManager : public ScopeTrackedObject, * @param end_stream whether this data is the end of the request. */ void decodeData(Buffer::Instance& data, bool end_stream) { - state_.remote_decode_complete_ = end_stream; + state_.observed_decode_end_stream_ = end_stream; decodeData(nullptr, data, end_stream, FilterIterationStartState::CanStartFromCurrent); } @@ -764,7 +764,7 @@ class FilterManager : public ScopeTrackedObject, * @param trailers the trailers to decode. */ void decodeTrailers(RequestTrailerMap& trailers) { - state_.remote_decode_complete_ = true; + state_.observed_decode_end_stream_ = true; decodeTrailers(nullptr, trailers); } @@ -819,8 +819,12 @@ class FilterManager : public ScopeTrackedObject, /** * Marks local processing as complete. + * TODO(yanvlasov): deprecate and decommission this function. */ - void setLocalComplete() { state_.local_complete_ = true; } + void setLocalComplete() { + state_.observed_encode_end_stream_ = true; + state_.decoder_filter_chain_aborted_ = true; + } /** * Whether the filters have been destroyed. @@ -830,7 +834,7 @@ class FilterManager : public ScopeTrackedObject, /** * Whether remote processing has been marked as complete. */ - virtual bool remoteDecodeComplete() const { return state_.remote_decode_complete_; } + virtual bool decoderObservedEndStream() const { return state_.observed_decode_end_stream_; } /** * Instructs the FilterManager to not create a filter chain. This makes it possible to issue @@ -864,18 +868,27 @@ class FilterManager : public ScopeTrackedObject, protected: struct State { State() - : remote_decode_complete_(false), remote_encode_complete_(false), local_complete_(false), - has_1xx_headers_(false), created_filter_chain_(false), is_head_request_(false), - is_grpc_request_(false), non_100_response_headers_encoded_(false), - under_on_local_reply_(false), decoder_filter_chain_aborted_(false), - encoder_filter_chain_aborted_(false), saw_downstream_reset_(false) {} + : encoder_filter_chain_complete_(false), observed_decode_end_stream_(false), + observed_encode_end_stream_(false), has_1xx_headers_(false), created_filter_chain_(false), + is_head_request_(false), is_grpc_request_(false), + non_100_response_headers_encoded_(false), under_on_local_reply_(false), + decoder_filter_chain_aborted_(false), encoder_filter_chain_aborted_(false), + saw_downstream_reset_(false) {} uint32_t filter_call_state_{0}; - bool remote_decode_complete_ : 1; - bool remote_encode_complete_ : 1; - bool local_complete_ : 1; // This indicates that local is complete prior to filter processing. - // A filter can still stop the stream from being complete as seen - // by the codec. + // Set after encoder filter chain has completed iteration. Prevents further calls to encoder + // filters. + bool encoder_filter_chain_complete_ : 1; + + // Set `true` when the filter manager observes end stream on the decoder path (from downstream + // client) before iteration of the decoder filter chain begins. This flag is used for setting + // end_stream value when resuming decoder filter chain iteration. + bool observed_decode_end_stream_ : 1; + // Set `true` when the filter manager observes end stream on the encoder path (from upstream + // server or Envoy's local reply) before iteration of the encoder filter chain begins. This flag + // is used for setting end_stream value when resuming encoder filter chain iteration. + bool observed_encode_end_stream_ : 1; + // By default, we will assume there are no 1xx. If encode1xxHeaders // is ever called, this is set to true so commonContinue resumes processing the 1xx. bool has_1xx_headers_ : 1; @@ -1018,6 +1031,8 @@ class FilterManager : public ScopeTrackedObject, return request_metadata_map_vector_.get(); } + bool stopDecoderFilterChain() { return state_.decoder_filter_chain_aborted_; } + FilterManagerCallbacks& filter_manager_callbacks_; Event::Dispatcher& dispatcher_; // This is unset if there is no downstream connection, e.g. for health check or @@ -1142,7 +1157,7 @@ class DownstreamFilterManager : public FilterManager { * For the DownstreamFilterManager rely on external state, to handle the case * of internal redirects. */ - bool remoteDecodeComplete() const override { + bool decoderObservedEndStream() const override { return streamInfo().downstreamTiming() && streamInfo().downstreamTiming()->lastDownstreamRxByteReceived().has_value(); } diff --git a/source/common/router/upstream_request.cc b/source/common/router/upstream_request.cc index cff1d5b08df2..b14ad939a6be 100644 --- a/source/common/router/upstream_request.cc +++ b/source/common/router/upstream_request.cc @@ -68,8 +68,8 @@ class UpstreamFilterManager : public Http::FilterManager { absl::string_view details) override { state().decoder_filter_chain_aborted_ = true; state().encoder_filter_chain_aborted_ = true; - state().remote_encode_complete_ = true; - state().local_complete_ = true; + state().encoder_filter_chain_complete_ = true; + state().observed_encode_end_stream_ = true; // TODO(alyssawilk) this should be done through the router to play well with hedging. upstream_request_.parent_.callbacks()->sendLocalReply(code, body, modify_headers, grpc_status, details); From 6c645a13d190a75cbd957e208e469ee5078eb4c9 Mon Sep 17 00:00:00 2001 From: code Date: Sat, 3 Aug 2024 01:12:04 +0800 Subject: [PATCH 023/130] improve substitution formatter parsing performance (#35498) Commit Message: improve substitution formatter parsing performance Additional Description: See https://github.com/envoyproxy/envoy/issues/35466 Previous parsing performance result: ``` --------------------------------------------------------------------------- Benchmark Time CPU Iterations --------------------------------------------------------------------------- BM_AccessLogFormatterSetup 75744 ns 75605 ns 9401 ``` Latest parsing performance result: ``` --------------------------------------------------------------------------- Benchmark Time CPU Iterations --------------------------------------------------------------------------- BM_AccessLogFormatterSetup 8932 ns 8917 ns 78153 ``` Risk Level: mid. Core code change. Testing: unit. Docs Changes: n/a. Release Notes: no behavior change. Platform Specific Features: n/a. --------- Signed-off-by: wbpcode --- envoy/formatter/substitution_formatter_base.h | 4 +- .../formatter/http_specific_formatter.cc | 89 +++--- .../formatter/http_specific_formatter.h | 14 +- .../common/formatter/stream_info_formatter.cc | 279 +++++++++--------- .../common/formatter/stream_info_formatter.h | 38 +-- .../formatter/substitution_format_utility.cc | 19 +- .../formatter/substitution_format_utility.h | 22 +- .../formatter/substitution_formatter.cc | 28 +- .../common/formatter/substitution_formatter.h | 54 ++-- .../network/generic_proxy/access_log.cc | 4 +- source/extensions/formatter/cel/cel.cc | 4 +- source/extensions/formatter/cel/cel.h | 6 +- .../extensions/formatter/metadata/metadata.cc | 106 ++++--- .../extensions/formatter/metadata/metadata.h | 16 +- .../req_without_query/req_without_query.cc | 18 +- .../req_without_query/req_without_query.h | 14 +- test/common/formatter/command_extension.cc | 8 +- test/common/formatter/command_extension.h | 8 +- .../substitution_formatter_speed_test.cc | 17 +- .../formatter/substitution_formatter_test.cc | 12 +- 20 files changed, 370 insertions(+), 390 deletions(-) diff --git a/envoy/formatter/substitution_formatter_base.h b/envoy/formatter/substitution_formatter_base.h index 04b9b1fb25e7..459712a5919d 100644 --- a/envoy/formatter/substitution_formatter_base.h +++ b/envoy/formatter/substitution_formatter_base.h @@ -81,8 +81,8 @@ template class CommandParserBase { * @return FormattterProviderPtr substitution provider for the parsed command. */ virtual FormatterProviderBasePtr - parse(const std::string& command, const std::string& command_arg, - absl::optional& max_length) const PURE; + parse(absl::string_view command, absl::string_view command_arg, + absl::optional max_length) const PURE; }; template diff --git a/source/common/formatter/http_specific_formatter.cc b/source/common/formatter/http_specific_formatter.cc index 185311e3921a..a46c9559c9f5 100644 --- a/source/common/formatter/http_specific_formatter.cc +++ b/source/common/formatter/http_specific_formatter.cc @@ -42,8 +42,8 @@ AccessLogTypeFormatter::formatValueWithContext(const HttpFormatterContext& conte return ValueUtil::stringValue(AccessLogType_Name(context.accessLogType())); } -HeaderFormatter::HeaderFormatter(const std::string& main_header, - const std::string& alternative_header, +HeaderFormatter::HeaderFormatter(absl::string_view main_header, + absl::string_view alternative_header, absl::optional max_length) : main_header_(main_header), alternative_header_(alternative_header), max_length_(max_length) {} @@ -81,8 +81,8 @@ ProtobufWkt::Value HeaderFormatter::formatValue(const Http::HeaderMap& headers) return ValueUtil::stringValue(std::string(val)); } -ResponseHeaderFormatter::ResponseHeaderFormatter(const std::string& main_header, - const std::string& alternative_header, +ResponseHeaderFormatter::ResponseHeaderFormatter(absl::string_view main_header, + absl::string_view alternative_header, absl::optional max_length) : HeaderFormatter(main_header, alternative_header, max_length) {} @@ -98,8 +98,8 @@ ResponseHeaderFormatter::formatValueWithContext(const HttpFormatterContext& cont return HeaderFormatter::formatValue(context.responseHeaders()); } -RequestHeaderFormatter::RequestHeaderFormatter(const std::string& main_header, - const std::string& alternative_header, +RequestHeaderFormatter::RequestHeaderFormatter(absl::string_view main_header, + absl::string_view alternative_header, absl::optional max_length) : HeaderFormatter(main_header, alternative_header, max_length) {} @@ -115,8 +115,8 @@ RequestHeaderFormatter::formatValueWithContext(const HttpFormatterContext& conte return HeaderFormatter::formatValue(context.requestHeaders()); } -ResponseTrailerFormatter::ResponseTrailerFormatter(const std::string& main_header, - const std::string& alternative_header, +ResponseTrailerFormatter::ResponseTrailerFormatter(absl::string_view main_header, + absl::string_view alternative_header, absl::optional max_length) : HeaderFormatter(main_header, alternative_header, max_length) {} @@ -298,96 +298,85 @@ BuiltInHttpCommandParser::getKnownFormatters() { FormatterProviderLookupTbl, {{"REQ", {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED, - [](const std::string& format, absl::optional& max_length) { - std::string main_header, alternative_header; - - THROW_IF_NOT_OK(SubstitutionFormatUtils::parseSubcommandHeaders(format, main_header, - alternative_header)); - - return std::make_unique(main_header, alternative_header, - max_length); + [](absl::string_view format, absl::optional max_length) { + auto result = SubstitutionFormatUtils::parseSubcommandHeaders(format); + THROW_IF_STATUS_NOT_OK(result, throw); + return std::make_unique(result.value().first, + result.value().second, max_length); }}}, {"RESP", {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED, - [](const std::string& format, absl::optional& max_length) { - std::string main_header, alternative_header; - - THROW_IF_NOT_OK(SubstitutionFormatUtils::parseSubcommandHeaders(format, main_header, - alternative_header)); - - return std::make_unique(main_header, alternative_header, - max_length); + [](absl::string_view format, absl::optional max_length) { + auto result = SubstitutionFormatUtils::parseSubcommandHeaders(format); + THROW_IF_STATUS_NOT_OK(result, throw); + return std::make_unique(result.value().first, + result.value().second, max_length); }}}, {"TRAILER", {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED, - [](const std::string& format, absl::optional& max_length) { - std::string main_header, alternative_header; - - THROW_IF_NOT_OK(SubstitutionFormatUtils::parseSubcommandHeaders(format, main_header, - alternative_header)); - - return std::make_unique(main_header, alternative_header, - max_length); + [](absl::string_view format, absl::optional max_length) { + auto result = SubstitutionFormatUtils::parseSubcommandHeaders(format); + THROW_IF_STATUS_NOT_OK(result, throw); + return std::make_unique(result.value().first, + result.value().second, max_length); }}}, {"LOCAL_REPLY_BODY", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional&) { + [](absl::string_view, absl::optional) { return std::make_unique(); }}}, {"ACCESS_LOG_TYPE", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional&) { + [](absl::string_view, absl::optional) { return std::make_unique(); }}}, {"GRPC_STATUS", {CommandSyntaxChecker::PARAMS_OPTIONAL, - [](const std::string& format, const absl::optional&) { + [](absl::string_view format, absl::optional) { return std::make_unique("grpc-status", "", absl::optional(), GrpcStatusFormatter::parseFormat(format)); }}}, {"GRPC_STATUS_NUMBER", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, const absl::optional&) { + [](absl::string_view, absl::optional) { return std::make_unique("grpc-status", "", absl::optional(), GrpcStatusFormatter::Number); }}}, {"REQUEST_HEADERS_BYTES", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional&) { + [](absl::string_view, absl::optional) { return std::make_unique( HeadersByteSizeFormatter::HeaderType::RequestHeaders); }}}, {"RESPONSE_HEADERS_BYTES", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional&) { + [](absl::string_view, absl::optional) { return std::make_unique( HeadersByteSizeFormatter::HeaderType::ResponseHeaders); }}}, {"RESPONSE_TRAILERS_BYTES", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional&) { + [](absl::string_view, absl::optional) { return std::make_unique( HeadersByteSizeFormatter::HeaderType::ResponseTrailers); }}}, {"STREAM_INFO_REQ", {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED, - [](const std::string& format, absl::optional& max_length) { - std::string main_header, alternative_header; - THROW_IF_NOT_OK(SubstitutionFormatUtils::parseSubcommandHeaders(format, main_header, - alternative_header)); - - return std::make_unique(main_header, alternative_header, - max_length); + [](absl::string_view format, absl::optional max_length) { + auto result = SubstitutionFormatUtils::parseSubcommandHeaders(format); + THROW_IF_STATUS_NOT_OK(result, throw); + return std::make_unique(result.value().first, + result.value().second, max_length); }}}, {"TRACE_ID", - {CommandSyntaxChecker::COMMAND_ONLY, [](const std::string&, absl::optional&) { + {CommandSyntaxChecker::COMMAND_ONLY, [](absl::string_view, absl::optional) { return std::make_unique(); }}}}); } -FormatterProviderPtr BuiltInHttpCommandParser::parse(const std::string& command, - const std::string& subcommand, - absl::optional& max_length) const { +FormatterProviderPtr BuiltInHttpCommandParser::parse(absl::string_view command, + absl::string_view subcommand, + absl::optional max_length) const { const FormatterProviderLookupTbl& providers = getKnownFormatters(); auto it = providers.find(command); diff --git a/source/common/formatter/http_specific_formatter.h b/source/common/formatter/http_specific_formatter.h index 34dc7958cf89..0afd9327781d 100644 --- a/source/common/formatter/http_specific_formatter.h +++ b/source/common/formatter/http_specific_formatter.h @@ -53,7 +53,7 @@ class AccessLogTypeFormatter : public FormatterProvider { class HeaderFormatter { public: - HeaderFormatter(const std::string& main_header, const std::string& alternative_header, + HeaderFormatter(absl::string_view main_header, absl::string_view alternative_header, absl::optional max_length); protected: @@ -97,7 +97,7 @@ class HeadersByteSizeFormatter : public FormatterProvider { */ class RequestHeaderFormatter : public FormatterProvider, HeaderFormatter { public: - RequestHeaderFormatter(const std::string& main_header, const std::string& alternative_header, + RequestHeaderFormatter(absl::string_view main_header, absl::string_view alternative_header, absl::optional max_length); // FormatterProvider @@ -114,7 +114,7 @@ class RequestHeaderFormatter : public FormatterProvider, HeaderFormatter { */ class ResponseHeaderFormatter : public FormatterProvider, HeaderFormatter { public: - ResponseHeaderFormatter(const std::string& main_header, const std::string& alternative_header, + ResponseHeaderFormatter(absl::string_view main_header, absl::string_view alternative_header, absl::optional max_length); // FormatterProvider @@ -131,7 +131,7 @@ class ResponseHeaderFormatter : public FormatterProvider, HeaderFormatter { */ class ResponseTrailerFormatter : public FormatterProvider, HeaderFormatter { public: - ResponseTrailerFormatter(const std::string& main_header, const std::string& alternative_header, + ResponseTrailerFormatter(absl::string_view main_header, absl::string_view alternative_header, absl::optional max_length); // FormatterProvider @@ -205,12 +205,12 @@ class BuiltInHttpCommandParser : public CommandParser { BuiltInHttpCommandParser() = default; // CommandParser - FormatterProviderPtr parse(const std::string& command, const std::string& subcommand, - absl::optional& max_length) const override; + FormatterProviderPtr parse(absl::string_view command, absl::string_view subcommand, + absl::optional max_length) const override; private: using FormatterProviderCreateFunc = - std::function&)>; + std::function)>; using FormatterProviderLookupTbl = absl::flat_hash_map - #include "source/common/common/random_generator.h" #include "source/common/config/metadata.h" #include "source/common/http/utility.h" @@ -10,6 +8,7 @@ #include "absl/strings/str_format.h" #include "absl/strings/str_replace.h" +#include "re2/re2.h" namespace Envoy { namespace Formatter { @@ -18,8 +17,8 @@ namespace { static const std::string DefaultUnspecifiedValueString = "-"; -const std::regex& getSystemTimeFormatNewlinePattern() { - CONSTRUCT_ON_FIRST_USE(std::regex, "%[-_0^#]*[1-9]*(E|O)?n"); +const re2::RE2& getSystemTimeFormatNewlinePattern() { + CONSTRUCT_ON_FIRST_USE(re2::RE2, "%[-_0^#]*[1-9]*(E|O)?n"); } Network::Address::InstanceConstSharedPtr @@ -47,11 +46,11 @@ getUpstreamRemoteAddress(const StreamInfo::StreamInfo& stream_info) { } // namespace -MetadataFormatter::MetadataFormatter(const std::string& filter_namespace, - const std::vector& path, +MetadataFormatter::MetadataFormatter(absl::string_view filter_namespace, + const std::vector& path, absl::optional max_length, MetadataFormatter::GetMetadataFunction get_func) - : filter_namespace_(filter_namespace), path_(path), max_length_(max_length), + : filter_namespace_(filter_namespace), path_(path.begin(), path.end()), max_length_(max_length), get_func_(get_func) {} absl::optional @@ -116,16 +115,16 @@ ProtobufWkt::Value MetadataFormatter::formatValue(const StreamInfo::StreamInfo& // TODO(glicht): Consider adding support for route/listener/cluster metadata as suggested by // @htuch. See: https://github.com/envoyproxy/envoy/issues/3006 -DynamicMetadataFormatter::DynamicMetadataFormatter(const std::string& filter_namespace, - const std::vector& path, +DynamicMetadataFormatter::DynamicMetadataFormatter(absl::string_view filter_namespace, + const std::vector& path, absl::optional max_length) : MetadataFormatter(filter_namespace, path, max_length, [](const StreamInfo::StreamInfo& stream_info) { return &stream_info.dynamicMetadata(); }) {} -ClusterMetadataFormatter::ClusterMetadataFormatter(const std::string& filter_namespace, - const std::vector& path, +ClusterMetadataFormatter::ClusterMetadataFormatter(absl::string_view filter_namespace, + const std::vector& path, absl::optional max_length) : MetadataFormatter(filter_namespace, path, max_length, [](const StreamInfo::StreamInfo& stream_info) @@ -137,9 +136,9 @@ ClusterMetadataFormatter::ClusterMetadataFormatter(const std::string& filter_nam return &cluster_info.value()->metadata(); }) {} -UpstreamHostMetadataFormatter::UpstreamHostMetadataFormatter(const std::string& filter_namespace, - const std::vector& path, - absl::optional max_length) +UpstreamHostMetadataFormatter::UpstreamHostMetadataFormatter( + absl::string_view filter_namespace, const std::vector& path, + absl::optional max_length) : MetadataFormatter(filter_namespace, path, max_length, [](const StreamInfo::StreamInfo& stream_info) -> const envoy::config::core::v3::Metadata* { @@ -155,9 +154,9 @@ UpstreamHostMetadataFormatter::UpstreamHostMetadataFormatter(const std::string& }) {} std::unique_ptr -FilterStateFormatter::create(const std::string& format, const absl::optional& max_length, +FilterStateFormatter::create(absl::string_view format, absl::optional max_length, bool is_upstream) { - std::string key, serialize_type, field_name; + absl::string_view key, serialize_type, field_name; static constexpr absl::string_view PLAIN_SERIALIZATION{"PLAIN"}; static constexpr absl::string_view TYPED_SERIALIZATION{"TYPED"}; static constexpr absl::string_view FIELD_SERIALIZATION{"FIELD"}; @@ -168,7 +167,7 @@ FilterStateFormatter::create(const std::string& format, const absl::optional max_length, +FilterStateFormatter::FilterStateFormatter(absl::string_view key, absl::optional max_length, bool serialize_as_string, bool is_upstream, - const std::string& field_name) + absl::string_view field_name) : key_(key), max_length_(max_length), is_upstream_(is_upstream) { if (!field_name.empty()) { format_ = FilterStateFormat::Field; - field_name_ = field_name; + field_name_ = std::string(field_name); factory_ = Registry::FactoryRegistry::getFactory(key); } else if (serialize_as_string) { format_ = FilterStateFormat::String; @@ -496,14 +494,14 @@ ProtobufWkt::Value CommonDurationFormatter::formatValue(const StreamInfo::Stream // A SystemTime formatter that extracts the startTime from StreamInfo. Must be provided // an access log command that starts with `START_TIME`. -StartTimeFormatter::StartTimeFormatter(const std::string& format) +StartTimeFormatter::StartTimeFormatter(absl::string_view format) : SystemTimeFormatter( format, std::make_unique( [](const StreamInfo::StreamInfo& stream_info) -> absl::optional { return stream_info.startTime(); })) {} -DownstreamPeerCertVStartFormatter::DownstreamPeerCertVStartFormatter(const std::string& format) +DownstreamPeerCertVStartFormatter::DownstreamPeerCertVStartFormatter(absl::string_view format) : SystemTimeFormatter( format, std::make_unique( [](const StreamInfo::StreamInfo& stream_info) -> absl::optional { @@ -513,7 +511,7 @@ DownstreamPeerCertVStartFormatter::DownstreamPeerCertVStartFormatter(const std:: ? connection_info->validFromPeerCertificate() : absl::optional(); })) {} -DownstreamPeerCertVEndFormatter::DownstreamPeerCertVEndFormatter(const std::string& format) +DownstreamPeerCertVEndFormatter::DownstreamPeerCertVEndFormatter(absl::string_view format) : SystemTimeFormatter( format, std::make_unique( [](const StreamInfo::StreamInfo& stream_info) -> absl::optional { @@ -523,7 +521,7 @@ DownstreamPeerCertVEndFormatter::DownstreamPeerCertVEndFormatter(const std::stri ? connection_info->expirationPeerCertificate() : absl::optional(); })) {} -UpstreamPeerCertVStartFormatter::UpstreamPeerCertVStartFormatter(const std::string& format) +UpstreamPeerCertVStartFormatter::UpstreamPeerCertVStartFormatter(absl::string_view format) : SystemTimeFormatter( format, std::make_unique( [](const StreamInfo::StreamInfo& stream_info) -> absl::optional { @@ -535,7 +533,7 @@ UpstreamPeerCertVStartFormatter::UpstreamPeerCertVStartFormatter(const std::stri ->validFromPeerCertificate() : absl::optional(); })) {} -UpstreamPeerCertVEndFormatter::UpstreamPeerCertVEndFormatter(const std::string& format) +UpstreamPeerCertVEndFormatter::UpstreamPeerCertVEndFormatter(absl::string_view format) : SystemTimeFormatter( format, std::make_unique( [](const StreamInfo::StreamInfo& stream_info) -> absl::optional { @@ -548,13 +546,13 @@ UpstreamPeerCertVEndFormatter::UpstreamPeerCertVEndFormatter(const std::string& : absl::optional(); })) {} -SystemTimeFormatter::SystemTimeFormatter(const std::string& format, TimeFieldExtractorPtr f, +SystemTimeFormatter::SystemTimeFormatter(absl::string_view format, TimeFieldExtractorPtr f, bool local_time) : date_formatter_(format, local_time), time_field_extractor_(std::move(f)), local_time_(local_time) { // Validate the input specifier here. The formatted string may be destined for a header, and // should not contain invalid characters {NUL, LR, CF}. - if (std::regex_search(format, getSystemTimeFormatNewlinePattern())) { + if (re2::RE2::PartialMatch(format, getSystemTimeFormatNewlinePattern())) { throw EnvoyException("Invalid header configuration. Format string contains newline."); } } @@ -576,11 +574,12 @@ SystemTimeFormatter::formatValue(const StreamInfo::StreamInfo& stream_info) cons return ValueUtil::optionalStringValue(format(stream_info)); } -EnvironmentFormatter::EnvironmentFormatter(const std::string& key, +EnvironmentFormatter::EnvironmentFormatter(absl::string_view key, absl::optional max_length) { ASSERT(!key.empty()); - const char* env_value = std::getenv(key.c_str()); + const std::string key_str = std::string(key); + const char* env_value = std::getenv(key_str.c_str()); if (env_value != nullptr) { std::string env_string = env_value; SubstitutionFormatUtils::truncate(env_string, max_length); @@ -828,7 +827,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide { {"REQUEST_DURATION", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { StreamInfo::TimingUtility timing(stream_info); @@ -837,7 +836,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"REQUEST_TX_DURATION", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { StreamInfo::TimingUtility timing(stream_info); @@ -846,7 +845,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"RESPONSE_DURATION", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { StreamInfo::TimingUtility timing(stream_info); @@ -855,7 +854,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"RESPONSE_TX_DURATION", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { StreamInfo::TimingUtility timing(stream_info); @@ -872,7 +871,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_HANDSHAKE_DURATION", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { StreamInfo::TimingUtility timing(stream_info); @@ -881,7 +880,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"ROUNDTRIP_DURATION", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { StreamInfo::TimingUtility timing(stream_info); @@ -890,7 +889,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"BYTES_RECEIVED", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { return stream_info.bytesReceived(); @@ -898,7 +897,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"BYTES_RETRANSMITTED", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { return stream_info.bytesRetransmitted(); @@ -906,7 +905,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"PACKETS_RETRANSMITTED", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { return stream_info.packetsRetransmitted(); @@ -914,7 +913,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_WIRE_BYTES_RECEIVED", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { const auto& bytes_meter = stream_info.getUpstreamBytesMeter(); @@ -923,7 +922,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_HEADER_BYTES_RECEIVED", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { const auto& bytes_meter = stream_info.getUpstreamBytesMeter(); @@ -932,7 +931,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_WIRE_BYTES_RECEIVED", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { const auto& bytes_meter = stream_info.getDownstreamBytesMeter(); @@ -941,7 +940,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_HEADER_BYTES_RECEIVED", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { const auto& bytes_meter = stream_info.getDownstreamBytesMeter(); @@ -950,7 +949,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"PROTOCOL", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { return SubstitutionFormatUtils::protocolToString(stream_info.protocol()); @@ -958,7 +957,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_PROTOCOL", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { return stream_info.upstreamInfo() @@ -969,7 +968,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"RESPONSE_CODE", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { return stream_info.responseCode().value_or(0); @@ -977,7 +976,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"RESPONSE_CODE_DETAILS", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { return stream_info.responseCodeDetails(); @@ -985,7 +984,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"CONNECTION_TERMINATION_DETAILS", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { return stream_info.connectionTerminationDetails(); @@ -993,7 +992,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"BYTES_SENT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { return stream_info.bytesSent(); @@ -1001,7 +1000,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_WIRE_BYTES_SENT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { const auto& bytes_meter = stream_info.getUpstreamBytesMeter(); @@ -1010,7 +1009,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_HEADER_BYTES_SENT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { const auto& bytes_meter = stream_info.getUpstreamBytesMeter(); @@ -1019,7 +1018,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_WIRE_BYTES_SENT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { const auto& bytes_meter = stream_info.getDownstreamBytesMeter(); @@ -1028,7 +1027,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_HEADER_BYTES_SENT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { const auto& bytes_meter = stream_info.getDownstreamBytesMeter(); @@ -1037,7 +1036,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DURATION", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { return stream_info.currentDuration(); @@ -1045,12 +1044,12 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"COMMON_DURATION", {CommandSyntaxChecker::PARAMS_REQUIRED, - [](const std::string& sub_command, absl::optional) { + [](absl::string_view sub_command, absl::optional) { return CommonDurationFormatter::create(sub_command); }}}, {"RESPONSE_FLAGS", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { return StreamInfo::ResponseFlagUtils::toShortString(stream_info); @@ -1058,7 +1057,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"RESPONSE_FLAGS_LONG", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { return StreamInfo::ResponseFlagUtils::toString(stream_info); @@ -1066,7 +1065,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_HOST_NAME", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) -> absl::optional { const auto opt_ref = stream_info.upstreamInfo(); @@ -1087,7 +1086,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_HOST", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return StreamInfoAddressFormatterProvider::withPort( [](const StreamInfo::StreamInfo& stream_info) -> Network::Address::InstanceConstSharedPtr { @@ -1104,7 +1103,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_CONNECTION_ID", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { uint64_t upstream_connection_id = 0; @@ -1117,7 +1116,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_CLUSTER", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { std::string upstream_cluster_name; @@ -1134,7 +1133,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_CLUSTER_RAW", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { std::string upstream_cluster_name; @@ -1150,7 +1149,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_LOCAL_ADDRESS", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return StreamInfoAddressFormatterProvider::withPort( [](const StreamInfo::StreamInfo& stream_info) -> Network::Address::InstanceConstSharedPtr { @@ -1162,7 +1161,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_LOCAL_ADDRESS_WITHOUT_PORT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return StreamInfoAddressFormatterProvider::withoutPort( [](const StreamInfo::StreamInfo& stream_info) -> Network::Address::InstanceConstSharedPtr { @@ -1174,7 +1173,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_LOCAL_PORT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return StreamInfoAddressFormatterProvider::justPort( [](const StreamInfo::StreamInfo& stream_info) -> Network::Address::InstanceConstSharedPtr { @@ -1186,7 +1185,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_REMOTE_ADDRESS", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return StreamInfoAddressFormatterProvider::withPort( [](const StreamInfo::StreamInfo& stream_info) -> Network::Address::InstanceConstSharedPtr { @@ -1195,7 +1194,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_REMOTE_ADDRESS_WITHOUT_PORT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return StreamInfoAddressFormatterProvider::withoutPort( [](const StreamInfo::StreamInfo& stream_info) -> Network::Address::InstanceConstSharedPtr { @@ -1204,7 +1203,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_REMOTE_PORT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return StreamInfoAddressFormatterProvider::justPort( [](const StreamInfo::StreamInfo& stream_info) -> Network::Address::InstanceConstSharedPtr { @@ -1213,7 +1212,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_REQUEST_ATTEMPT_COUNT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { return stream_info.attemptCount().value_or(0); @@ -1221,7 +1220,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_TLS_CIPHER", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return connection_info.ciphersuiteString(); @@ -1229,7 +1228,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_TLS_VERSION", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return connection_info.tlsVersion(); @@ -1237,7 +1236,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_TLS_SESSION_ID", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return connection_info.sessionId(); @@ -1245,7 +1244,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_PEER_ISSUER", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return connection_info.issuerPeerCertificate(); @@ -1253,7 +1252,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_PEER_CERT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return connection_info.urlEncodedPemEncodedPeerCertificate(); @@ -1261,7 +1260,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_PEER_SUBJECT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return connection_info.subjectPeerCertificate(); @@ -1269,7 +1268,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_LOCAL_ADDRESS", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return StreamInfoAddressFormatterProvider::withPort( [](const StreamInfo::StreamInfo& stream_info) { return stream_info.downstreamAddressProvider().localAddress(); @@ -1277,7 +1276,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_LOCAL_ADDRESS_WITHOUT_PORT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return StreamInfoAddressFormatterProvider::withoutPort( [](const Envoy::StreamInfo::StreamInfo& stream_info) { return stream_info.downstreamAddressProvider().localAddress(); @@ -1285,7 +1284,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_LOCAL_PORT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return StreamInfoAddressFormatterProvider::justPort( [](const Envoy::StreamInfo::StreamInfo& stream_info) { return stream_info.downstreamAddressProvider().localAddress(); @@ -1293,7 +1292,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_REMOTE_ADDRESS", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return StreamInfoAddressFormatterProvider::withPort( [](const StreamInfo::StreamInfo& stream_info) { return stream_info.downstreamAddressProvider().remoteAddress(); @@ -1301,7 +1300,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return StreamInfoAddressFormatterProvider::withoutPort( [](const StreamInfo::StreamInfo& stream_info) { return stream_info.downstreamAddressProvider().remoteAddress(); @@ -1309,7 +1308,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_REMOTE_PORT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return StreamInfoAddressFormatterProvider::justPort( [](const Envoy::StreamInfo::StreamInfo& stream_info) { return stream_info.downstreamAddressProvider().remoteAddress(); @@ -1317,7 +1316,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_DIRECT_REMOTE_ADDRESS", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return StreamInfoAddressFormatterProvider::withPort( [](const StreamInfo::StreamInfo& stream_info) { return stream_info.downstreamAddressProvider().directRemoteAddress(); @@ -1325,7 +1324,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_DIRECT_REMOTE_ADDRESS_WITHOUT_PORT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return StreamInfoAddressFormatterProvider::withoutPort( [](const StreamInfo::StreamInfo& stream_info) { return stream_info.downstreamAddressProvider().directRemoteAddress(); @@ -1333,7 +1332,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_DIRECT_REMOTE_PORT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return StreamInfoAddressFormatterProvider::justPort( [](const Envoy::StreamInfo::StreamInfo& stream_info) { return stream_info.downstreamAddressProvider().directRemoteAddress(); @@ -1341,7 +1340,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"CONNECTION_ID", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { return stream_info.downstreamAddressProvider().connectionID().value_or(0); @@ -1349,7 +1348,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"REQUESTED_SERVER_NAME", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { absl::optional result; @@ -1362,7 +1361,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"ROUTE_NAME", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { absl::optional result; @@ -1375,7 +1374,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_PEER_URI_SAN", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return absl::StrJoin(connection_info.uriSanPeerCertificate(), ","); @@ -1383,7 +1382,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_PEER_DNS_SAN", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return absl::StrJoin(connection_info.dnsSansPeerCertificate(), ","); @@ -1391,7 +1390,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_PEER_IP_SAN", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return absl::StrJoin(connection_info.ipSansPeerCertificate(), ","); @@ -1399,7 +1398,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_LOCAL_URI_SAN", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return absl::StrJoin(connection_info.uriSanLocalCertificate(), ","); @@ -1407,7 +1406,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_LOCAL_DNS_SAN", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return absl::StrJoin(connection_info.dnsSansLocalCertificate(), ","); @@ -1415,7 +1414,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_LOCAL_IP_SAN", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return absl::StrJoin(connection_info.ipSansLocalCertificate(), ","); @@ -1423,7 +1422,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_PEER_URI_SAN", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return absl::StrJoin(connection_info.uriSanPeerCertificate(), ","); @@ -1431,7 +1430,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_PEER_DNS_SAN", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return absl::StrJoin(connection_info.dnsSansPeerCertificate(), ","); @@ -1439,7 +1438,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_PEER_IP_SAN", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return absl::StrJoin(connection_info.ipSansPeerCertificate(), ","); @@ -1447,7 +1446,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_LOCAL_URI_SAN", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return absl::StrJoin(connection_info.uriSanLocalCertificate(), ","); @@ -1455,7 +1454,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_LOCAL_DNS_SAN", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return absl::StrJoin(connection_info.dnsSansLocalCertificate(), ","); @@ -1463,7 +1462,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_LOCAL_IP_SAN", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return absl::StrJoin(connection_info.ipSansLocalCertificate(), ","); @@ -1471,7 +1470,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_PEER_SUBJECT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return connection_info.subjectPeerCertificate(); @@ -1479,7 +1478,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_LOCAL_SUBJECT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return connection_info.subjectLocalCertificate(); @@ -1487,7 +1486,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_TLS_SESSION_ID", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return connection_info.sessionId(); @@ -1495,7 +1494,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_TLS_CIPHER", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return connection_info.ciphersuiteString(); @@ -1503,7 +1502,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_TLS_VERSION", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return connection_info.tlsVersion(); @@ -1511,7 +1510,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_PEER_FINGERPRINT_256", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return connection_info.sha256PeerCertificateDigest(); @@ -1519,7 +1518,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_PEER_FINGERPRINT_1", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return connection_info.sha1PeerCertificateDigest(); @@ -1527,7 +1526,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_PEER_SERIAL", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return connection_info.serialNumberPeerCertificate(); @@ -1535,7 +1534,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_PEER_ISSUER", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return connection_info.issuerPeerCertificate(); @@ -1543,7 +1542,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_PEER_CERT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const Ssl::ConnectionInfo& connection_info) { return connection_info.urlEncodedPemEncodedPeerCertificate(); @@ -1551,7 +1550,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DOWNSTREAM_TRANSPORT_FAILURE_REASON", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { absl::optional result; @@ -1564,7 +1563,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UPSTREAM_TRANSPORT_FAILURE_REASON", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { absl::optional result; @@ -1585,14 +1584,14 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"HOSTNAME", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { absl::optional hostname = SubstitutionFormatUtils::getHostname(); return std::make_unique( [hostname](const StreamInfo::StreamInfo&) { return hostname; }); }}}, {"FILTER_CHAIN_NAME", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) -> absl::optional { if (const auto info = stream_info.downstreamAddressProvider().filterChainInfo(); @@ -1606,7 +1605,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"VIRTUAL_CLUSTER_NAME", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) -> absl::optional { return stream_info.virtualClusterName(); @@ -1614,7 +1613,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"TLS_JA3_FINGERPRINT", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) { absl::optional result; @@ -1626,7 +1625,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"UNIQUE_ID", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, const absl::optional&) { + [](absl::string_view, const absl::optional&) { return std::make_unique( [](const StreamInfo::StreamInfo&) -> absl::optional { return absl::make_optional(Random::RandomUtility::uuid()); @@ -1634,7 +1633,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"STREAM_ID", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, absl::optional) { + [](absl::string_view, absl::optional) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) -> absl::optional { auto provider = stream_info.getStreamIdProvider(); @@ -1650,7 +1649,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"START_TIME", {CommandSyntaxChecker::PARAMS_OPTIONAL, - [](const std::string& format, absl::optional) { + [](absl::string_view format, absl::optional) { return std::make_unique( format, std::make_unique( @@ -1660,7 +1659,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"START_TIME_LOCAL", {CommandSyntaxChecker::PARAMS_OPTIONAL, - [](const std::string& format, absl::optional) { + [](absl::string_view format, absl::optional) { return std::make_unique( format, std::make_unique( @@ -1671,7 +1670,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"EMIT_TIME", {CommandSyntaxChecker::PARAMS_OPTIONAL, - [](const std::string& format, absl::optional) { + [](absl::string_view format, absl::optional) { return std::make_unique( format, std::make_unique( @@ -1681,7 +1680,7 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"EMIT_TIME_LOCAL", {CommandSyntaxChecker::PARAMS_OPTIONAL, - [](const std::string& format, absl::optional) { + [](absl::string_view format, absl::optional) { return std::make_unique( format, std::make_unique( @@ -1692,9 +1691,9 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"DYNAMIC_METADATA", {CommandSyntaxChecker::PARAMS_REQUIRED, - [](const std::string& format, absl::optional max_length) { - std::string filter_namespace; - std::vector path; + [](absl::string_view format, absl::optional max_length) { + absl::string_view filter_namespace; + std::vector path; SubstitutionFormatUtils::parseSubcommand(format, ':', filter_namespace, path); return std::make_unique(filter_namespace, path, max_length); @@ -1702,18 +1701,18 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide {"CLUSTER_METADATA", {CommandSyntaxChecker::PARAMS_REQUIRED, - [](const std::string& format, absl::optional max_length) { - std::string filter_namespace; - std::vector path; + [](absl::string_view format, absl::optional max_length) { + absl::string_view filter_namespace; + std::vector path; SubstitutionFormatUtils::parseSubcommand(format, ':', filter_namespace, path); return std::make_unique(filter_namespace, path, max_length); }}}, {"UPSTREAM_METADATA", {CommandSyntaxChecker::PARAMS_REQUIRED, - [](const std::string& format, absl::optional max_length) { - std::string filter_namespace; - std::vector path; + [](absl::string_view format, absl::optional max_length) { + absl::string_view filter_namespace; + std::vector path; SubstitutionFormatUtils::parseSubcommand(format, ':', filter_namespace, path); return std::make_unique(filter_namespace, path, @@ -1721,42 +1720,42 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide }}}, {"FILTER_STATE", {CommandSyntaxChecker::PARAMS_OPTIONAL | CommandSyntaxChecker::LENGTH_ALLOWED, - [](const std::string& format, absl::optional max_length) { + [](absl::string_view format, absl::optional max_length) { return FilterStateFormatter::create(format, max_length, false); }}}, {"UPSTREAM_FILTER_STATE", {CommandSyntaxChecker::PARAMS_OPTIONAL | CommandSyntaxChecker::LENGTH_ALLOWED, - [](const std::string& format, absl::optional max_length) { + [](absl::string_view format, absl::optional max_length) { return FilterStateFormatter::create(format, max_length, true); }}}, {"DOWNSTREAM_PEER_CERT_V_START", {CommandSyntaxChecker::PARAMS_OPTIONAL, - [](const std::string& format, absl::optional) { + [](absl::string_view format, absl::optional) { return std::make_unique(format); }}}, {"DOWNSTREAM_PEER_CERT_V_END", {CommandSyntaxChecker::PARAMS_OPTIONAL, - [](const std::string& format, absl::optional) { + [](absl::string_view format, absl::optional) { return std::make_unique(format); }}}, {"UPSTREAM_PEER_CERT_V_START", {CommandSyntaxChecker::PARAMS_OPTIONAL, - [](const std::string& format, absl::optional) { + [](absl::string_view format, absl::optional) { return std::make_unique(format); }}}, {"UPSTREAM_PEER_CERT_V_END", {CommandSyntaxChecker::PARAMS_OPTIONAL, - [](const std::string& format, absl::optional) { + [](absl::string_view format, absl::optional) { return std::make_unique(format); }}}, {"ENVIRONMENT", {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED, - [](const std::string& key, absl::optional max_length) { + [](absl::string_view key, absl::optional max_length) { return std::make_unique(key, max_length); }}}, {"UPSTREAM_CONNECTION_POOL_READY_DURATION", {CommandSyntaxChecker::COMMAND_ONLY, - [](const std::string&, const absl::optional&) { + [](absl::string_view, const absl::optional&) { return std::make_unique( [](const StreamInfo::StreamInfo& stream_info) -> absl::optional { @@ -1782,8 +1781,8 @@ class BuiltInStreamInfoCommandParser : public StreamInfoCommandParser { BuiltInStreamInfoCommandParser() = default; // StreamInfoCommandParser - StreamInfoFormatterProviderPtr parse(const std::string& command, const std::string& sub_command, - absl::optional& max_length) const override { + StreamInfoFormatterProviderPtr parse(absl::string_view command, absl::string_view sub_command, + absl::optional max_length) const override { auto it = getKnownStreamInfoFormatterProviders().find(command); diff --git a/source/common/formatter/stream_info_formatter.h b/source/common/formatter/stream_info_formatter.h index 8de144eb0d4f..0cc80e0d911c 100644 --- a/source/common/formatter/stream_info_formatter.h +++ b/source/common/formatter/stream_info_formatter.h @@ -20,7 +20,7 @@ namespace Envoy { namespace Formatter { using StreamInfoFormatterProviderCreateFunc = - std::function)>; + std::function)>; enum class DurationPrecision { Milliseconds, Microseconds, Nanoseconds }; @@ -33,7 +33,7 @@ class MetadataFormatter : public StreamInfoFormatterProvider { public: using GetMetadataFunction = std::function; - MetadataFormatter(const std::string& filter_namespace, const std::vector& path, + MetadataFormatter(absl::string_view filter_namespace, const std::vector& path, absl::optional max_length, GetMetadataFunction get); // StreamInfoFormatterProvider @@ -57,8 +57,9 @@ class MetadataFormatter : public StreamInfoFormatterProvider { */ class DynamicMetadataFormatter : public MetadataFormatter { public: - DynamicMetadataFormatter(const std::string& filter_namespace, - const std::vector& path, absl::optional max_length); + DynamicMetadataFormatter(absl::string_view filter_namespace, + const std::vector& path, + absl::optional max_length); }; /** @@ -66,8 +67,9 @@ class DynamicMetadataFormatter : public MetadataFormatter { */ class ClusterMetadataFormatter : public MetadataFormatter { public: - ClusterMetadataFormatter(const std::string& filter_namespace, - const std::vector& path, absl::optional max_length); + ClusterMetadataFormatter(absl::string_view filter_namespace, + const std::vector& path, + absl::optional max_length); }; /** @@ -75,8 +77,8 @@ class ClusterMetadataFormatter : public MetadataFormatter { */ class UpstreamHostMetadataFormatter : public MetadataFormatter { public: - UpstreamHostMetadataFormatter(const std::string& filter_namespace, - const std::vector& path, + UpstreamHostMetadataFormatter(absl::string_view filter_namespace, + const std::vector& path, absl::optional max_length); }; @@ -88,11 +90,11 @@ enum class FilterStateFormat { String, Proto, Field }; class FilterStateFormatter : public StreamInfoFormatterProvider { public: static std::unique_ptr - create(const std::string& format, const absl::optional& max_length, bool is_upstream); + create(absl::string_view format, absl::optional max_length, bool is_upstream); - FilterStateFormatter(const std::string& key, absl::optional max_length, + FilterStateFormatter(absl::string_view key, absl::optional max_length, bool serialize_as_string, bool is_upstream = false, - const std::string& field_name = ""); + absl::string_view field_name = {}); // StreamInfoFormatterProvider absl::optional format(const StreamInfo::StreamInfo&) const override; @@ -169,7 +171,7 @@ class SystemTimeFormatter : public StreamInfoFormatterProvider { std::function(const StreamInfo::StreamInfo& stream_info)>; using TimeFieldExtractorPtr = std::unique_ptr; - SystemTimeFormatter(const std::string& format, TimeFieldExtractorPtr f, bool local_time = false); + SystemTimeFormatter(absl::string_view format, TimeFieldExtractorPtr f, bool local_time = false); // StreamInfoFormatterProvider absl::optional format(const StreamInfo::StreamInfo&) const override; @@ -187,7 +189,7 @@ class SystemTimeFormatter : public StreamInfoFormatterProvider { */ class StartTimeFormatter : public SystemTimeFormatter { public: - StartTimeFormatter(const std::string& format); + StartTimeFormatter(absl::string_view format); }; /** @@ -196,7 +198,7 @@ class StartTimeFormatter : public SystemTimeFormatter { */ class DownstreamPeerCertVStartFormatter : public SystemTimeFormatter { public: - DownstreamPeerCertVStartFormatter(const std::string& format); + DownstreamPeerCertVStartFormatter(absl::string_view format); }; /** @@ -205,7 +207,7 @@ class DownstreamPeerCertVStartFormatter : public SystemTimeFormatter { */ class DownstreamPeerCertVEndFormatter : public SystemTimeFormatter { public: - DownstreamPeerCertVEndFormatter(const std::string& format); + DownstreamPeerCertVEndFormatter(absl::string_view format); }; /** @@ -214,7 +216,7 @@ class DownstreamPeerCertVEndFormatter : public SystemTimeFormatter { */ class UpstreamPeerCertVStartFormatter : public SystemTimeFormatter { public: - UpstreamPeerCertVStartFormatter(const std::string& format); + UpstreamPeerCertVStartFormatter(absl::string_view format); }; /** @@ -223,7 +225,7 @@ class UpstreamPeerCertVStartFormatter : public SystemTimeFormatter { */ class UpstreamPeerCertVEndFormatter : public SystemTimeFormatter { public: - UpstreamPeerCertVEndFormatter(const std::string& format); + UpstreamPeerCertVEndFormatter(absl::string_view format); }; /** @@ -231,7 +233,7 @@ class UpstreamPeerCertVEndFormatter : public SystemTimeFormatter { */ class EnvironmentFormatter : public StreamInfoFormatterProvider { public: - EnvironmentFormatter(const std::string& key, absl::optional max_length); + EnvironmentFormatter(absl::string_view key, absl::optional max_length); // StreamInfoFormatterProvider absl::optional format(const StreamInfo::StreamInfo&) const override; diff --git a/source/common/formatter/substitution_format_utility.cc b/source/common/formatter/substitution_format_utility.cc index 1ec0b15cae98..919783fa997c 100644 --- a/source/common/formatter/substitution_format_utility.cc +++ b/source/common/formatter/substitution_format_utility.cc @@ -14,10 +14,10 @@ namespace Formatter { static const std::string DefaultUnspecifiedValueString = "-"; -absl::Status CommandSyntaxChecker::verifySyntax(CommandSyntaxFlags flags, - const std::string& command, - const std::string& subcommand, - const absl::optional& length) { +absl::Status CommandSyntaxChecker::verifySyntax(CommandSyntaxChecker::CommandSyntaxFlags flags, + absl::string_view command, + absl::string_view subcommand, + absl::optional length) { if ((flags == COMMAND_ONLY) && ((subcommand.length() != 0) || length.has_value())) { return absl::InvalidArgumentError( fmt::format("{} does not take any parameters or length", command)); @@ -92,12 +92,11 @@ absl::string_view SubstitutionFormatUtils::truncateStringView(absl::string_view return str.substr(0, max_length.value()); } -absl::Status SubstitutionFormatUtils::parseSubcommandHeaders(const std::string& subcommand, - std::string& main_header, - std::string& alternative_header) { +absl::StatusOr +SubstitutionFormatUtils::parseSubcommandHeaders(absl::string_view subcommand) { + absl::string_view main_header, alternative_header; // subs is used only to check if there are more than 2 headers separated by '?'. - std::vector subs; - alternative_header = ""; + std::vector subs; parseSubcommand(subcommand, '?', main_header, alternative_header, subs); if (!subs.empty()) { return absl::InvalidArgumentError( @@ -120,7 +119,7 @@ absl::Status SubstitutionFormatUtils::parseSubcommandHeaders(const std::string& "Invalid header configuration. Format string contains null or newline."); } } - return absl::OkStatus(); + return {std::make_pair(main_header, alternative_header)}; } } // namespace Formatter diff --git a/source/common/formatter/substitution_format_utility.h b/source/common/formatter/substitution_format_utility.h index b1176f4d0760..56a17e1cc56b 100644 --- a/source/common/formatter/substitution_format_utility.h +++ b/source/common/formatter/substitution_format_utility.h @@ -24,8 +24,8 @@ class CommandSyntaxChecker { static constexpr CommandSyntaxFlags LENGTH_ALLOWED = 1 << 2; static absl::Status verifySyntax(CommandSyntaxChecker::CommandSyntaxFlags flags, - const std::string& command, const std::string& subcommand, - const absl::optional& length); + absl::string_view command, absl::string_view subcommand, + absl::optional length); }; /** @@ -66,9 +66,8 @@ class SubstitutionFormatUtils { * See doc: * https://envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/access_log#format-rules */ - static absl::Status parseSubcommandHeaders(const std::string& subcommand, - std::string& main_header, - std::string& alternative_header); + using HeaderPair = std::pair; + static absl::StatusOr parseSubcommandHeaders(absl::string_view subcommand); /* Variadic function template to parse the subcommand and assign found tokens to sequence of params. @@ -85,24 +84,23 @@ class SubstitutionFormatUtils { untouched. */ template - static void parseSubcommand(const std::string& subcommand, const char separator, + static void parseSubcommand(absl::string_view subcommand, const char separator, Tokens&&... params) { - std::vector tokens; - tokens = absl::StrSplit(subcommand, separator); + std::vector tokens = absl::StrSplit(subcommand, separator); std::vector::iterator it = tokens.begin(); ( [&](auto& param) { if (it != tokens.end()) { if constexpr (std::is_same_v::type, - std::string>) { - // Compile time handler for std::string. - param = std::string(*it); + absl::string_view>) { + // Compile time handler for absl::string_view. + param = *it; it++; } else { // Compile time handler for container type. It will catch all remaining tokens and // move iterator to the end. do { - param.push_back(std::string(*it)); + param.push_back(*it); it++; } while (it != tokens.end()); } diff --git a/source/common/formatter/substitution_formatter.cc b/source/common/formatter/substitution_formatter.cc index 5a71a9b48ece..a3278cf5d71b 100644 --- a/source/common/formatter/substitution_formatter.cc +++ b/source/common/formatter/substitution_formatter.cc @@ -3,7 +3,7 @@ namespace Envoy { namespace Formatter { -const std::regex& SubstitutionFormatParser::commandWithArgsRegex() { +const re2::RE2& SubstitutionFormatParser::commandWithArgsRegex() { // The following regex is used to check validity of the formatter command and to // extract groups. // The formatter command has the following format: @@ -35,20 +35,20 @@ const std::regex& SubstitutionFormatParser::commandWithArgsRegex() { // Group is used only to specify allowed characters. | | // | | | // | | | - // _________________ _______________ _____________ - // | | | | | | - CONSTRUCT_ON_FIRST_USE(std::regex, + // _________________ _____________ _____________ + // | | | | | | + CONSTRUCT_ON_FIRST_USE(re2::RE2, R"EOF(^%((?:[A-Z]|[0-9]|_)+)(?:\((.*?)\))?(?::([0-9]+))?%)EOF"); - // |__________________| |______| |______| - // | | | - // Capturing group specifying COMMAND -- | | - // The index of this group is 1. | | - // | | - // Capturing group for SUBCOMMAND. If present, it will ----- | - // contain SUBCOMMAND without "(" and ")". The index | - // of SUBCOMMAND group is 2. | - // | - // Capturing group for LENGTH. If present, it will ------------------------- + // |__________________| |___| |______| + // | | | + // Capturing group specifying COMMAND --- | | + // The index of this group is 1. | | + // | | + // Capturing group for SUBCOMMAND. If present, it will --- | + // contain SUBCOMMAND without "(" and ")". The index | + // of SUBCOMMAND group is 2. | + // | + // Capturing group for LENGTH. If present, it will ---------------------- // contain just number without ":". The index of // LENGTH group is 3. // clang-format on diff --git a/source/common/formatter/substitution_formatter.h b/source/common/formatter/substitution_formatter.h index 8353ff19c37d..abf6ff5a69c7 100644 --- a/source/common/formatter/substitution_formatter.h +++ b/source/common/formatter/substitution_formatter.h @@ -17,6 +17,7 @@ #include "source/common/json/json_loader.h" #include "absl/types/optional.h" +#include "re2/re2.h" namespace Envoy { namespace Formatter { @@ -28,7 +29,7 @@ namespace Formatter { template class PlainStringFormatterBase : public FormatterProviderBase { public: - PlainStringFormatterBase(const std::string& str) { str_.set_string_value(str); } + PlainStringFormatterBase(absl::string_view str) { str_.set_string_value(str); } // FormatterProviderBase absl::optional formatWithContext(const FormatterContext&, @@ -102,19 +103,21 @@ class SubstitutionFormatParser { parse(absl::string_view format, const std::vector>& command_parsers = {}) { std::string current_token; + current_token.reserve(32); std::vector> formatters; - for (size_t pos = 0; pos < format.size(); ++pos) { + for (size_t pos = 0; pos < format.size();) { if (format[pos] != '%') { - current_token += format[pos]; + current_token.push_back(format[pos]); + pos++; continue; } // escape '%%' if (format.size() > pos + 1) { if (format[pos + 1] == '%') { - current_token += '%'; - pos++; + current_token.push_back('%'); + pos += 2; continue; } } @@ -122,35 +125,22 @@ class SubstitutionFormatParser { if (!current_token.empty()) { formatters.emplace_back(FormatterProviderBasePtr{ new PlainStringFormatterBase(current_token)}); - current_token = ""; + current_token.clear(); } - std::smatch m; - const std::string search_space = std::string(format.substr(pos)); - if (!std::regex_search(search_space, m, commandWithArgsRegex())) { + absl::string_view sub_format = format.substr(pos); + const size_t sub_format_size = sub_format.size(); + + absl::string_view command, command_arg; + absl::optional max_len; + + if (!re2::RE2::Consume(&sub_format, commandWithArgsRegex(), &command, &command_arg, + &max_len)) { throwEnvoyExceptionOrPanic( fmt::format("Incorrect configuration: {}. Couldn't find valid command at position {}", format, pos)); } - const std::string match = m.str(0); - // command is at at index 1. - const std::string command = m.str(1); - // subcommand is at index 2. - const std::string subcommand = m.str(2); - // optional length is at index 3. If present, validate that it is valid integer. - absl::optional max_length; - if (m.str(3).length() != 0) { - size_t length_value; - if (!absl::SimpleAtoi(m.str(3), &length_value)) { - throwEnvoyExceptionOrPanic(absl::StrCat("Length must be an integer, given: ", m.str(3))); - } - max_length = length_value; - } - std::vector path; - - const size_t command_end_position = pos + m.str(0).length() - 1; - bool added = false; // The order of the following parsers is because the historical behavior. And we keep it @@ -159,7 +149,7 @@ class SubstitutionFormatParser { // First, try the built-in command parsers. for (const auto& cmd : BuiltInCommandParserFactoryHelper::commandParsers()) { - auto formatter = cmd->parse(command, subcommand, max_length); + auto formatter = cmd->parse(command, command_arg, max_len); if (formatter) { formatters.push_back(std::move(formatter)); added = true; @@ -170,7 +160,7 @@ class SubstitutionFormatParser { // Next, try the command parsers provided by the user. if (!added) { for (const auto& cmd : command_parsers) { - auto formatter = cmd->parse(command, subcommand, max_length); + auto formatter = cmd->parse(command, command_arg, max_len); if (formatter) { formatters.push_back(std::move(formatter)); added = true; @@ -182,7 +172,7 @@ class SubstitutionFormatParser { // Finally, try the command parsers that are built-in and context-independent. if (!added) { for (const auto& cmd : BuiltInStreamInfoCommandParserFactoryHelper::commandParsers()) { - auto formatter = cmd->parse(command, subcommand, max_length); + auto formatter = cmd->parse(command, command_arg, max_len); if (formatter) { formatters.push_back(std::make_unique>( std::move(formatter))); @@ -196,7 +186,7 @@ class SubstitutionFormatParser { throwEnvoyExceptionOrPanic(fmt::format("Not supported field in StreamInfo: {}", command)); } - pos = command_end_position; + pos += (sub_format_size - sub_format.size()); } if (!current_token.empty() || format.empty()) { @@ -210,7 +200,7 @@ class SubstitutionFormatParser { } private: - static const std::regex& commandWithArgsRegex(); + static const re2::RE2& commandWithArgsRegex(); }; inline constexpr absl::string_view DefaultUnspecifiedValueStringView = "-"; diff --git a/source/extensions/filters/network/generic_proxy/access_log.cc b/source/extensions/filters/network/generic_proxy/access_log.cc index ce635a84b410..a96df953494d 100644 --- a/source/extensions/filters/network/generic_proxy/access_log.cc +++ b/source/extensions/filters/network/generic_proxy/access_log.cc @@ -55,8 +55,8 @@ class SimpleCommandParser : public CommandParser { using ProviderFuncTable = absl::flat_hash_map; // CommandParser - FormatterProviderPtr parse(const std::string& command, const std::string& command_arg, - absl::optional& max_length) const override { + FormatterProviderPtr parse(absl::string_view command, absl::string_view command_arg, + absl::optional max_length) const override { const auto& provider_func_table = providerFuncTable(); const auto func_iter = provider_func_table.find(std::string(command)); if (func_iter == provider_func_table.end()) { diff --git a/source/extensions/formatter/cel/cel.cc b/source/extensions/formatter/cel/cel.cc index ffee3e8c73ff..dffd22063470 100644 --- a/source/extensions/formatter/cel/cel.cc +++ b/source/extensions/formatter/cel/cel.cc @@ -53,8 +53,8 @@ CELFormatter::formatValueWithContext(const Envoy::Formatter::HttpFormatterContex } ::Envoy::Formatter::FormatterProviderPtr -CELFormatterCommandParser::parse(const std::string& command, const std::string& subcommand, - absl::optional& max_length) const { +CELFormatterCommandParser::parse(absl::string_view command, absl::string_view subcommand, + absl::optional max_length) const { #if defined(USE_CEL_PARSER) if (command == "CEL") { auto parse_status = google::api::expr::parser::Parse(subcommand); diff --git a/source/extensions/formatter/cel/cel.h b/source/extensions/formatter/cel/cel.h index 2c5a600a3397..c7ad51af7e68 100644 --- a/source/extensions/formatter/cel/cel.h +++ b/source/extensions/formatter/cel/cel.h @@ -37,9 +37,9 @@ class CELFormatterCommandParser : public ::Envoy::Formatter::CommandParser { CELFormatterCommandParser(Server::Configuration::CommonFactoryContext& context) : local_info_(context.localInfo()), expr_builder_(Extensions::Filters::Common::Expr::getBuilder(context)){}; - ::Envoy::Formatter::FormatterProviderPtr parse(const std::string& command, - const std::string& subcommand, - absl::optional& max_length) const override; + ::Envoy::Formatter::FormatterProviderPtr parse(absl::string_view command, + absl::string_view subcommand, + absl::optional max_length) const override; private: const ::Envoy::LocalInfo::LocalInfo& local_info_; diff --git a/source/extensions/formatter/metadata/metadata.cc b/source/extensions/formatter/metadata/metadata.cc index 1046dc95d4a0..6dfc147eac86 100644 --- a/source/extensions/formatter/metadata/metadata.cc +++ b/source/extensions/formatter/metadata/metadata.cc @@ -16,13 +16,13 @@ namespace Formatter { // Metadata formatter for route's metadata. class RouteMetadataFormatter : public ::Envoy::Formatter::MetadataFormatter { public: - RouteMetadataFormatter(const std::string& filter_namespace, const std::vector& path, + RouteMetadataFormatter(absl::string_view filter_namespace, + const std::vector& path, absl::optional max_length) : ::Envoy::Formatter::MetadataFormatter(filter_namespace, path, max_length, [](const StreamInfo::StreamInfo& stream_info) -> const envoy::config::core::v3::Metadata* { auto route = stream_info.route(); - if (route == nullptr) { return nullptr; } @@ -33,8 +33,9 @@ class RouteMetadataFormatter : public ::Envoy::Formatter::MetadataFormatter { // Metadata formatter for listener metadata. class ListenerMetadataFormatter : public ::Envoy::Formatter::MetadataFormatter { public: - ListenerMetadataFormatter(const std::string& filter_namespace, - const std::vector& path, absl::optional max_length) + ListenerMetadataFormatter(absl::string_view filter_namespace, + const std::vector& path, + absl::optional max_length) : ::Envoy::Formatter::MetadataFormatter( filter_namespace, path, max_length, [](const StreamInfo::StreamInfo& stream_info) @@ -50,8 +51,8 @@ class ListenerMetadataFormatter : public ::Envoy::Formatter::MetadataFormatter { // Metadata formatter for virtual host metadata. class VirtualHostMetadataFormatter : public ::Envoy::Formatter::MetadataFormatter { public: - VirtualHostMetadataFormatter(const std::string& filter_namespace, - const std::vector& path, + VirtualHostMetadataFormatter(absl::string_view filter_namespace, + const std::vector& path, absl::optional max_length) : ::Envoy::Formatter::MetadataFormatter(filter_namespace, path, max_length, [](const StreamInfo::StreamInfo& stream_info) @@ -65,59 +66,68 @@ class VirtualHostMetadataFormatter : public ::Envoy::Formatter::MetadataFormatte }) {} }; -// Constructor registers all types of supported metadata along with the -// handlers accessing the required metadata type. -MetadataFormatterCommandParser::MetadataFormatterCommandParser() { - metadata_formatter_providers_["DYNAMIC"] = [](const std::string& filter_namespace, - const std::vector& path, - absl::optional max_length) { - return std::make_unique<::Envoy::Formatter::DynamicMetadataFormatter>(filter_namespace, path, - max_length); - }; - metadata_formatter_providers_["CLUSTER"] = [](const std::string& filter_namespace, - const std::vector& path, - absl::optional max_length) { - return std::make_unique<::Envoy::Formatter::ClusterMetadataFormatter>(filter_namespace, path, - max_length); - }; - metadata_formatter_providers_["ROUTE"] = [](const std::string& filter_namespace, - const std::vector& path, - absl::optional max_length) { - return std::make_unique(filter_namespace, path, max_length); - }; - metadata_formatter_providers_["UPSTREAM_HOST"] = [](const std::string& filter_namespace, - const std::vector& path, - absl::optional max_length) { - return std::make_unique<::Envoy::Formatter::UpstreamHostMetadataFormatter>(filter_namespace, - path, max_length); - }; +// Map used to dispatch types of metadata to individual handlers which will +// access required metadata object. +using FormatterProviderFunc = std::function<::Envoy::Formatter::StreamInfoFormatterProviderPtr( + absl::string_view filter_namespace, const std::vector& path, + absl::optional max_length)>; - metadata_formatter_providers_["LISTENER"] = [](const std::string& filter_namespace, - const std::vector& path, - absl::optional max_length) { - return std::make_unique(filter_namespace, path, max_length); - }; +using FormatterProviderFuncTable = absl::flat_hash_map; - metadata_formatter_providers_["VIRTUAL_HOST"] = [](const std::string& filter_namespace, - const std::vector& path, - absl::optional max_length) { - return std::make_unique(filter_namespace, path, max_length); - }; +const auto& formatterProviderFuncTable() { + CONSTRUCT_ON_FIRST_USE( + FormatterProviderFuncTable, + { + {"DYNAMIC", + [](absl::string_view filter_namespace, const std::vector& path, + absl::optional max_length) { + return std::make_unique<::Envoy::Formatter::DynamicMetadataFormatter>( + filter_namespace, path, max_length); + }}, + {"CLUSTER", + [](absl::string_view filter_namespace, const std::vector& path, + absl::optional max_length) { + return std::make_unique<::Envoy::Formatter::ClusterMetadataFormatter>( + filter_namespace, path, max_length); + }}, + {"ROUTE", + [](absl::string_view filter_namespace, const std::vector& path, + absl::optional max_length) { + return std::make_unique(filter_namespace, path, max_length); + }}, + {"UPSTREAM_HOST", + [](absl::string_view filter_namespace, const std::vector& path, + absl::optional max_length) { + return std::make_unique<::Envoy::Formatter::UpstreamHostMetadataFormatter>( + filter_namespace, path, max_length); + }}, + {"LISTENER", + [](absl::string_view filter_namespace, const std::vector& path, + absl::optional max_length) { + return std::make_unique(filter_namespace, path, max_length); + }}, + {"VIRTUAL_HOST", + [](absl::string_view filter_namespace, const std::vector& path, + absl::optional max_length) { + return std::make_unique(filter_namespace, path, + max_length); + }}, + }); } ::Envoy::Formatter::FormatterProviderPtr -MetadataFormatterCommandParser::parse(const std::string& command, const std::string& subcommand, - absl::optional& max_length) const { +MetadataFormatterCommandParser::parse(absl::string_view command, absl::string_view subcommand, + absl::optional max_length) const { if (command == "METADATA") { // Extract type of metadata and keys. - std::string type, filter_namespace; - std::vector path; + absl::string_view type, filter_namespace; + std::vector path; ::Envoy::Formatter::SubstitutionFormatUtils::parseSubcommand(subcommand, ':', type, filter_namespace, path); - auto provider = metadata_formatter_providers_.find(type); - if (provider == metadata_formatter_providers_.end()) { + auto provider = formatterProviderFuncTable().find(type); + if (provider == formatterProviderFuncTable().end()) { throw EnvoyException(absl::StrCat(type, " is not supported type of metadata")); } diff --git a/source/extensions/formatter/metadata/metadata.h b/source/extensions/formatter/metadata/metadata.h index b6c9a02066fd..c4e8e287b863 100644 --- a/source/extensions/formatter/metadata/metadata.h +++ b/source/extensions/formatter/metadata/metadata.h @@ -15,18 +15,10 @@ namespace Formatter { // Access log handler for METADATA() command. class MetadataFormatterCommandParser : public ::Envoy::Formatter::CommandParser { public: - MetadataFormatterCommandParser(); - ::Envoy::Formatter::FormatterProviderPtr parse(const std::string& command, - const std::string& subcommand, - absl::optional& max_length) const override; - -private: - // Map used to dispatch types of metadata to individual handlers which will - // access required metadata object. - using FormatterProviderFunc = std::function<::Envoy::Formatter::StreamInfoFormatterProviderPtr( - const std::string& filter_namespace, const std::vector& path, - absl::optional max_length)>; - std::map metadata_formatter_providers_; + MetadataFormatterCommandParser() = default; + ::Envoy::Formatter::FormatterProviderPtr parse(absl::string_view command, + absl::string_view subcommand, + absl::optional max_length) const override; }; } // namespace Formatter diff --git a/source/extensions/formatter/req_without_query/req_without_query.cc b/source/extensions/formatter/req_without_query/req_without_query.cc index 4bcf9025f63c..cf5e07eaac60 100644 --- a/source/extensions/formatter/req_without_query/req_without_query.cc +++ b/source/extensions/formatter/req_without_query/req_without_query.cc @@ -21,8 +21,8 @@ void truncate(std::string& str, absl::optional max_length) { } // namespace -ReqWithoutQuery::ReqWithoutQuery(const std::string& main_header, - const std::string& alternative_header, +ReqWithoutQuery::ReqWithoutQuery(absl::string_view main_header, + absl::string_view alternative_header, absl::optional max_length) : main_header_(main_header), alternative_header_(alternative_header), max_length_(max_length) {} @@ -66,16 +66,14 @@ const Http::HeaderEntry* ReqWithoutQuery::findHeader(const Http::HeaderMap& head } ::Envoy::Formatter::FormatterProviderPtr -ReqWithoutQueryCommandParser::parse(const std::string& command, const std::string& subcommand, - absl::optional& max_length) const { +ReqWithoutQueryCommandParser::parse(absl::string_view command, absl::string_view subcommand, + absl::optional max_length) const { if (command == "REQ_WITHOUT_QUERY") { - std::string main_header, alternative_header; - - THROW_IF_NOT_OK(Envoy::Formatter::SubstitutionFormatUtils::parseSubcommandHeaders( - subcommand, main_header, alternative_header)); - return std::make_unique(main_header, alternative_header, max_length); + auto status_or = Envoy::Formatter::SubstitutionFormatUtils::parseSubcommandHeaders(subcommand); + THROW_IF_NOT_OK_REF(status_or.status()); + return std::make_unique(status_or.value().first, status_or.value().second, + max_length); } - return nullptr; } diff --git a/source/extensions/formatter/req_without_query/req_without_query.h b/source/extensions/formatter/req_without_query/req_without_query.h index b2d16922ef61..a4c1be0df82e 100644 --- a/source/extensions/formatter/req_without_query/req_without_query.h +++ b/source/extensions/formatter/req_without_query/req_without_query.h @@ -14,7 +14,7 @@ namespace Formatter { class ReqWithoutQuery : public ::Envoy::Formatter::FormatterProvider { public: - ReqWithoutQuery(const std::string& main_header, const std::string& alternative_header, + ReqWithoutQuery(absl::string_view main_header, absl::string_view alternative_header, absl::optional max_length); absl::optional @@ -26,17 +26,17 @@ class ReqWithoutQuery : public ::Envoy::Formatter::FormatterProvider { private: const Http::HeaderEntry* findHeader(const Http::HeaderMap& headers) const; - Http::LowerCaseString main_header_; - Http::LowerCaseString alternative_header_; - absl::optional max_length_; + const Http::LowerCaseString main_header_; + const Http::LowerCaseString alternative_header_; + const absl::optional max_length_; }; class ReqWithoutQueryCommandParser : public ::Envoy::Formatter::CommandParser { public: ReqWithoutQueryCommandParser() = default; - ::Envoy::Formatter::FormatterProviderPtr parse(const std::string& command, - const std::string& subcommand, - absl::optional& max_length) const override; + ::Envoy::Formatter::FormatterProviderPtr parse(absl::string_view command, + absl::string_view subcommand, + absl::optional max_length) const override; }; } // namespace Formatter diff --git a/test/common/formatter/command_extension.cc b/test/common/formatter/command_extension.cc index aaa57140ce0b..b196a924b4c5 100644 --- a/test/common/formatter/command_extension.cc +++ b/test/common/formatter/command_extension.cc @@ -16,8 +16,8 @@ TestFormatter::formatValueWithContext(const HttpFormatterContext& context, return ValueUtil::stringValue(formatWithContext(context, stream_info).value()); } -FormatterProviderPtr TestCommandParser::parse(const std::string& command, const std::string&, - absl::optional&) const { +FormatterProviderPtr TestCommandParser::parse(absl::string_view command, absl::string_view, + absl::optional) const { if (command == "COMMAND_EXTENSION") { return std::make_unique(); } @@ -54,8 +54,8 @@ AdditionalFormatter::formatValueWithContext(const HttpFormatterContext& context, return ValueUtil::stringValue(formatWithContext(context, stream_info).value()); } -FormatterProviderPtr AdditionalCommandParser::parse(const std::string& command, const std::string&, - absl::optional&) const { +FormatterProviderPtr AdditionalCommandParser::parse(absl::string_view command, absl::string_view, + absl::optional) const { if (command == "ADDITIONAL_EXTENSION") { return std::make_unique(); } diff --git a/test/common/formatter/command_extension.h b/test/common/formatter/command_extension.h index ac922147c951..6b3e89b3342e 100644 --- a/test/common/formatter/command_extension.h +++ b/test/common/formatter/command_extension.h @@ -24,8 +24,8 @@ class TestFormatter : public FormatterProvider { class TestCommandParser : public CommandParser { public: - FormatterProviderPtr parse(const std::string& command, const std::string& subcommand, - absl::optional& max_length) const override; + FormatterProviderPtr parse(absl::string_view command, absl::string_view subcommand, + absl::optional max_length) const override; }; class TestCommandFactory : public CommandParserFactory { @@ -52,8 +52,8 @@ class AdditionalFormatter : public FormatterProvider { class AdditionalCommandParser : public CommandParser { public: - FormatterProviderPtr parse(const std::string& command, const std::string& subcommand, - absl::optional& max_length) const override; + FormatterProviderPtr parse(absl::string_view command, absl::string_view subcommand, + absl::optional max_length) const override; }; class AdditionalCommandFactory : public CommandParserFactory { diff --git a/test/common/formatter/substitution_formatter_speed_test.cc b/test/common/formatter/substitution_formatter_speed_test.cc index e304dd5c52a7..30cfd83768d6 100644 --- a/test/common/formatter/substitution_formatter_speed_test.cc +++ b/test/common/formatter/substitution_formatter_speed_test.cc @@ -75,7 +75,8 @@ BENCHMARK(BM_AccessLogFormatterSetup); // NOLINTNEXTLINE(readability-identifier-naming) static void BM_AccessLogFormatter(benchmark::State& state) { - MockTimeSystem time_system; + testing::NiceMock time_system; + std::unique_ptr stream_info = makeStreamInfo(time_system); static const char* LogFormat = "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT% %START_TIME(%Y/%m/%dT%H:%M:%S%z %s)% " @@ -96,7 +97,8 @@ BENCHMARK(BM_AccessLogFormatter); // NOLINTNEXTLINE(readability-identifier-naming) static void BM_StructAccessLogFormatter(benchmark::State& state) { - MockTimeSystem time_system; + testing::NiceMock time_system; + std::unique_ptr stream_info = makeStreamInfo(time_system); std::unique_ptr struct_formatter = makeStructFormatter(false); @@ -110,7 +112,8 @@ BENCHMARK(BM_StructAccessLogFormatter); // NOLINTNEXTLINE(readability-identifier-naming) static void BM_TypedStructAccessLogFormatter(benchmark::State& state) { - MockTimeSystem time_system; + testing::NiceMock time_system; + std::unique_ptr stream_info = makeStreamInfo(time_system); std::unique_ptr typed_struct_formatter = makeStructFormatter(true); @@ -125,7 +128,8 @@ BENCHMARK(BM_TypedStructAccessLogFormatter); // NOLINTNEXTLINE(readability-identifier-naming) static void BM_JsonAccessLogFormatter(benchmark::State& state) { - MockTimeSystem time_system; + testing::NiceMock time_system; + std::unique_ptr stream_info = makeStreamInfo(time_system); std::unique_ptr json_formatter = makeJsonFormatter(false); @@ -139,7 +143,8 @@ BENCHMARK(BM_JsonAccessLogFormatter); // NOLINTNEXTLINE(readability-identifier-naming) static void BM_TypedJsonAccessLogFormatter(benchmark::State& state) { - MockTimeSystem time_system; + testing::NiceMock time_system; + std::unique_ptr stream_info = makeStreamInfo(time_system); std::unique_ptr typed_json_formatter = makeJsonFormatter(true); @@ -155,7 +160,7 @@ BENCHMARK(BM_TypedJsonAccessLogFormatter); // NOLINTNEXTLINE(readability-identifier-naming) static void BM_FormatterCommandParsing(benchmark::State& state) { const std::string token = "Listener:namespace:key"; - std::string listener, names, key; + absl::string_view listener, names, key; for (auto _ : state) { // NOLINT: Silences warning about dead store Formatter::SubstitutionFormatUtils::parseSubcommand(token, ':', listener, names, key); } diff --git a/test/common/formatter/substitution_formatter_test.cc b/test/common/formatter/substitution_formatter_test.cc index a9613ce63bf9..d48d36c3d40e 100644 --- a/test/common/formatter/substitution_formatter_test.cc +++ b/test/common/formatter/substitution_formatter_test.cc @@ -165,20 +165,20 @@ REGISTER_FACTORY(TestSerializedStringFilterStateFactory, StreamInfo::FilterState // extracting tokens. TEST(SubstitutionFormatParser, commandParser) { std::vector tokens; - std::string token1; + absl::string_view token1; std::string command = "item1"; SubstitutionFormatUtils::parseSubcommand(command, ':', token1); ASSERT_EQ(token1, "item1"); - std::string token2; + absl::string_view token2; command = "item1:item2"; SubstitutionFormatUtils::parseSubcommand(command, ':', token1, token2); ASSERT_EQ(token1, "item1"); ASSERT_EQ(token2, "item2"); // Three tokens. - std::string token3; + absl::string_view token3; command = "item1?item2?item3"; SubstitutionFormatUtils::parseSubcommand(command, '?', token1, token2, token3); ASSERT_EQ(token1, "item1"); @@ -196,7 +196,7 @@ TEST(SubstitutionFormatParser, commandParser) { // Command string has 2 tokens but 3 are expected. // The third extracted token should be empty. command = "item1?item2"; - token3.erase(); + token3 = {}; SubstitutionFormatUtils::parseSubcommand(command, '?', token1, token2, token3); ASSERT_EQ(token1, "item1"); ASSERT_EQ(token2, "item2"); @@ -205,7 +205,7 @@ TEST(SubstitutionFormatParser, commandParser) { // Command string has 4 tokens. Get first 2 into the strings // and remaining 2 into a vector of strings. command = "item1?item2?item3?item4"; - std::vector bucket; + std::vector bucket; SubstitutionFormatUtils::parseSubcommand(command, '?', token1, token2, bucket); ASSERT_EQ(token1, "item1"); ASSERT_EQ(token2, "item2"); @@ -1087,7 +1087,6 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { for (auto& precision : precisions) { const std::string sub_command = absl::StrCat(time_points[start_index], ":", time_points[end_index], ":", precision); - std::cout << sub_command << std::endl; StreamInfoFormatter duration_format("COMMON_DURATION", sub_command); if (start_index == end_index && start_index == 0) { @@ -1165,7 +1164,6 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { const std::string sub_command = absl::StrCat(time_points[start_index], ":", time_points[end_index], ":", precision); - std::cout << sub_command << std::endl; StreamInfoFormatter duration_format("COMMON_DURATION", sub_command); From b5bbfba79379eaf4824c3a96c4d9d19bd498a31a Mon Sep 17 00:00:00 2001 From: Dean Liu Date: Sat, 3 Aug 2024 11:31:09 -0700 Subject: [PATCH 024/130] docs: typos and minor clarifications to oauth2 http filter (#35581) Signed-off-by: Dean Liu --- api/envoy/extensions/filters/http/oauth2/v3/oauth.proto | 2 +- docs/root/configuration/http/http_filters/oauth2_filter.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto b/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto index 703e3428b240..3a1d42731d1b 100644 --- a/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto +++ b/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto @@ -153,7 +153,7 @@ message OAuth2Config { // // If this value is not set, it will default to ``604800s``. In this case, the cookie with the refresh token will be expired // in a week. - // This setting is only considered if ``use_refresh_token`` is set to true, otherwise the authorization server expiration or ``defaul_expires_in`` is used. + // This setting is only considered if ``use_refresh_token`` is set to true, otherwise the authorization server expiration or ``default_expires_in`` is used. google.protobuf.Duration default_refresh_token_expires_in = 15; // If set to true, Envoy will not set a cookie for ID Token even if one is received from the Identity Provider. This may be useful in cases where the ID diff --git a/docs/root/configuration/http/http_filters/oauth2_filter.rst b/docs/root/configuration/http/http_filters/oauth2_filter.rst index a38a32858304..40a17d3fca9c 100644 --- a/docs/root/configuration/http/http_filters/oauth2_filter.rst +++ b/docs/root/configuration/http/http_filters/oauth2_filter.rst @@ -41,7 +41,7 @@ with the same value. The OAuth filter encodes URLs in query parameters using the `URL encoding algorithm. `_ -When receiving request redirected from the authorization service the Oauth filer decodes URLs from query parameters. +When receiving request redirected from the authorization service the Oauth filter decodes URLs from query parameters. However the encoded character sequences that represent ASCII control characters or extended ASCII codepoints are not decoded. The characters without defined meaning in URL according to `RFC 3986 `_ are also left undecoded. Specifically the following characters are left in the encoded form: @@ -252,8 +252,8 @@ during those requests by enabling the :ref:`use_refresh_token ` provides -possibility to update access token by using a refresh token. By default after expiration the user is always redirected to the authorization endpoint to log in again. -By enabling this flag a new access token is obtained using by a refresh token without redirecting the user to log in again. This requires the refresh token to be provided by authorization_endpoint when the user logs in. +the possibility to update access token by using a refresh token. By default after expiration the user is always redirected to the authorization endpoint to log in again. +By enabling this flag a new access token is obtained using the refresh token without redirecting the user to log in again. This requires the refresh token to be provided by the authorization_endpoint when the user logs in. If the attempt to get an access token by using a refresh token fails then the user is redirected to the authorization endpoint as usual. Generally, allowlisting is inadvisable from a security standpoint. From f1f1afba3256c094dc298fc5113da3e40ee48cbb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 12:25:11 +0100 Subject: [PATCH 025/130] build(deps): bump flake8 from 7.1.0 to 7.1.1 in /tools/base (#35587) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/base/requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index c90fee557dff..05683ddfc126 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -549,9 +549,9 @@ fasteners==0.19 \ # via # google-apitools # gsutil -flake8==7.1.0 \ - --hash=sha256:2e416edcc62471a64cea09353f4e7bdba32aeb079b6e360554c659a122b1bc6a \ - --hash=sha256:48a07b626b55236e0fb4784ee69a465fbf59d79eec1f5b4785c3d3bc57d17aa5 +flake8==7.1.1 \ + --hash=sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38 \ + --hash=sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213 # via # -r requirements.in # envoy-code-check From 2310615a94f43a93d0277b05e91326960055f69f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 12:25:33 +0100 Subject: [PATCH 026/130] build(deps): bump actions/upload-artifact from 4.3.4 to 4.3.5 (#35586) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 99a10164ed77..40649c09044a 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -33,7 +33,7 @@ jobs: publish_results: true - name: "Upload artifact" - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 with: name: SARIF file path: results.sarif From 797a30261190b92cd7be7ce09a73f0edb2ca5d9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 12:25:51 +0100 Subject: [PATCH 027/130] build(deps): bump envoyproxy/toolshed from actions-v0.2.31 to 0.2.33 (#35585) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/_cache.yml | 8 ++++---- .github/workflows/_finish.yml | 8 ++++---- .github/workflows/_load.yml | 10 +++++----- .github/workflows/_load_env.yml | 8 ++++---- .github/workflows/_request.yml | 10 +++++----- .github/workflows/_run.yml | 14 +++++++------- .github/workflows/_stage_publish.yml | 4 ++-- .github/workflows/_start.yml | 10 +++++----- .github/workflows/codeql-daily.yml | 2 +- .github/workflows/codeql-push.yml | 2 +- .github/workflows/command.yml | 6 +++--- .github/workflows/envoy-dependency.yml | 18 +++++++++--------- .github/workflows/envoy-release.yml | 22 +++++++++++----------- .github/workflows/envoy-sync.yml | 4 ++-- .github/workflows/garbage.yml | 2 +- 15 files changed, 64 insertions(+), 64 deletions(-) diff --git a/.github/workflows/_cache.yml b/.github/workflows/_cache.yml index bc2b7658f80d..c6f7e79a32e4 100644 --- a/.github/workflows/_cache.yml +++ b/.github/workflows/_cache.yml @@ -39,20 +39,20 @@ jobs: docker: runs-on: ubuntu-22.04 steps: - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 id: appauth name: Appauth (mutex lock) with: app_id: ${{ secrets.app-id }} key: ${{ secrets.app-key }} - - uses: envoyproxy/toolshed/gh-actions/docker/cache/prime@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/docker/cache/prime@actions-v0.2.33 id: docker name: Prime Docker cache (${{ inputs.image-tag }}) with: image-tag: ${{ inputs.image-tag }} lock-token: ${{ steps.appauth.outputs.token }} lock-repository: ${{ inputs.lock-repository }} - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 id: data name: Cache data with: @@ -60,7 +60,7 @@ jobs: input: | cached: ${{ steps.docker.outputs.cached }} key: ${{ inputs.image-tag }} - - uses: envoyproxy/toolshed/gh-actions/json/table@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/json/table@actions-v0.2.33 name: Summary with: json: ${{ steps.data.outputs.value }} diff --git a/.github/workflows/_finish.yml b/.github/workflows/_finish.yml index 8ef40d156571..d5b638c4dce8 100644 --- a/.github/workflows/_finish.yml +++ b/.github/workflows/_finish.yml @@ -36,7 +36,7 @@ jobs: actions: read contents: read steps: - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 name: Incoming data id: needs with: @@ -87,7 +87,7 @@ jobs: summary: "Check has finished", text: $text}}}} - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 name: Print summary with: input: ${{ toJSON(steps.needs.outputs.value).summary-title }} @@ -95,13 +95,13 @@ jobs: "## \(.)" options: -Rr output-path: GITHUB_STEP_SUMMARY - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 name: Appauth id: appauth with: app_id: ${{ secrets.app-id }} key: ${{ secrets.app-key }} - - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.33 name: Update check with: action: update diff --git a/.github/workflows/_load.yml b/.github/workflows/_load.yml index 4f16bf83aa89..aa311ab3862f 100644 --- a/.github/workflows/_load.yml +++ b/.github/workflows/_load.yml @@ -91,7 +91,7 @@ jobs: # Handle any failure in triggering job # Remove any `checks` we dont care about # Prepare a check request - - uses: envoyproxy/toolshed/gh-actions/github/env/load@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/github/env/load@actions-v0.2.33 name: Load env id: data with: @@ -102,13 +102,13 @@ jobs: GH_TOKEN: ${{ github.token }} # Update the check - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 name: Appauth id: appauth with: app_id: ${{ secrets.app-id }} key: ${{ secrets.app-key }} - - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.33 name: Update check if: ${{ fromJSON(steps.data.outputs.data).data.check.action == 'RUN' }} with: @@ -116,7 +116,7 @@ jobs: checks: ${{ toJSON(fromJSON(steps.data.outputs.data).checks) }} token: ${{ steps.appauth.outputs.token }} - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 name: Print request summary with: input: | @@ -136,7 +136,7 @@ jobs: | $summary.summary as $summary | "${{ inputs.template-request-summary }}" - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 id: request-output name: Load request with: diff --git a/.github/workflows/_load_env.yml b/.github/workflows/_load_env.yml index 988b8669eba6..8b10499d3eaa 100644 --- a/.github/workflows/_load_env.yml +++ b/.github/workflows/_load_env.yml @@ -63,18 +63,18 @@ jobs: request: ${{ steps.env.outputs.data }} trusted: true steps: - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 id: started name: Create timestamp with: options: -r filter: | now - - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 id: checkout name: Checkout Envoy repository - name: Generate environment variables - uses: envoyproxy/toolshed/gh-actions/envoy/ci/env@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/envoy/ci/env@actions-v0.2.33 id: env with: branch-name: ${{ inputs.branch-name }} @@ -86,7 +86,7 @@ jobs: - name: Request summary id: summary - uses: envoyproxy/toolshed/gh-actions/github/env/summary@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/github/env/summary@actions-v0.2.33 with: actor: ${{ toJSON(fromJSON(steps.env.outputs.data).request.actor) }} base-sha: ${{ fromJSON(steps.env.outputs.data).request.base-sha }} diff --git a/.github/workflows/_request.yml b/.github/workflows/_request.yml index b992ddadf00a..68fa1e46706a 100644 --- a/.github/workflows/_request.yml +++ b/.github/workflows/_request.yml @@ -40,14 +40,14 @@ jobs: env: ${{ steps.data.outputs.value }} config: ${{ steps.config.outputs.config }} steps: - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 id: started name: Create timestamp with: options: -r filter: | now - - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 id: checkout name: Checkout Envoy repository with: @@ -60,7 +60,7 @@ jobs: # *ALL* variables collected should be treated as untrusted and should be sanitized before # use - name: Generate environment variables from commit - uses: envoyproxy/toolshed/gh-actions/envoy/ci/request@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/envoy/ci/request@actions-v0.2.33 id: env with: branch-name: ${{ steps.checkout.outputs.branch-name }} @@ -71,7 +71,7 @@ jobs: vars: ${{ toJSON(vars) }} - name: Request summary id: summary - uses: envoyproxy/toolshed/gh-actions/github/env/summary@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/github/env/summary@actions-v0.2.33 with: actor: ${{ toJSON(fromJSON(steps.env.outputs.data).request.actor) }} base-sha: ${{ fromJSON(steps.env.outputs.data).request.base-sha }} @@ -87,7 +87,7 @@ jobs: target-branch: ${{ fromJSON(steps.env.outputs.data).request.target-branch }} - name: Environment data - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 id: data with: input: | diff --git a/.github/workflows/_run.yml b/.github/workflows/_run.yml index 993114d2a11b..6cfc51d31cdf 100644 --- a/.github/workflows/_run.yml +++ b/.github/workflows/_run.yml @@ -158,7 +158,7 @@ jobs: name: ${{ inputs.command }} ${{ inputs.target }} timeout-minutes: ${{ inputs.timeout-minutes }} steps: - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 id: started name: Create timestamp with: @@ -166,7 +166,7 @@ jobs: filter: | now # This controls which input vars are exposed to the run action (and related steps) - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 name: Context id: context with: @@ -187,11 +187,11 @@ jobs: | . * {$config, $check} - if: ${{ inputs.cache-build-image }} name: Restore Docker cache ${{ inputs.cache-build-image && format('({0})', inputs.cache-build-image) || '' }} - uses: envoyproxy/toolshed/gh-actions/docker/cache/restore@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/docker/cache/restore@actions-v0.2.33 with: image_tag: ${{ inputs.cache-build-image }} - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 id: appauth name: Appauth if: ${{ inputs.trusted }} @@ -202,7 +202,7 @@ jobs: # - the workaround is to allow the token to be passed through. token: ${{ github.token }} token-ok: true - - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 id: checkout name: Checkout Envoy repository with: @@ -219,7 +219,7 @@ jobs: token: ${{ inputs.trusted && steps.appauth.outputs.token || github.token }} # This is currently only use by mobile-docs and can be removed once they are updated to the newer website - - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 id: checkout-extra name: Checkout extra repository (for publishing) if: ${{ inputs.checkout-extra }} @@ -227,7 +227,7 @@ jobs: config: ${{ inputs.checkout-extra }} ssh-key: ${{ inputs.trusted && inputs.ssh-key-extra || '' }} - - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.33 name: Run CI ${{ inputs.command }} ${{ inputs.target }} with: args: ${{ inputs.args != '--' && inputs.args || inputs.target }} diff --git a/.github/workflows/_stage_publish.yml b/.github/workflows/_stage_publish.yml index 5bcc3a48d16d..c2572d61eca9 100644 --- a/.github/workflows/_stage_publish.yml +++ b/.github/workflows/_stage_publish.yml @@ -98,12 +98,12 @@ jobs: needs: - publish steps: - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 id: appauth with: app_id: ${{ secrets.ENVOY_CI_SYNC_APP_ID }} key: ${{ secrets.ENVOY_CI_SYNC_APP_KEY }} - - uses: envoyproxy/toolshed/gh-actions/dispatch@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/dispatch@actions-v0.2.33 with: ref: main repository: ${{ fromJSON(inputs.request).request.version.dev && 'envoyproxy/envoy-website' || 'envoyproxy/archive' }} diff --git a/.github/workflows/_start.yml b/.github/workflows/_start.yml index 928cbf774b45..6756eaa93d00 100644 --- a/.github/workflows/_start.yml +++ b/.github/workflows/_start.yml @@ -54,7 +54,7 @@ jobs: start: runs-on: ubuntu-22.04 steps: - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 id: check-config name: Prepare check data with: @@ -77,13 +77,13 @@ jobs: | .skipped.output.summary = "${{ inputs.skipped-summary }}" | .skipped.output.text = "" - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 name: Appauth id: appauth with: app_id: ${{ secrets.app-id }} key: ${{ secrets.app-key }} - - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.33 name: Start checks id: checks with: @@ -94,7 +94,7 @@ jobs: ${{ fromJSON(inputs.env).summary.summary }} token: ${{ steps.appauth.outputs.token }} - - uses: envoyproxy/toolshed/gh-actions/json/table@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/json/table@actions-v0.2.33 name: Summary with: collapse-open: true @@ -118,7 +118,7 @@ jobs: output-path: GITHUB_STEP_SUMMARY title: Checks started/skipped - - uses: envoyproxy/toolshed/gh-actions/github/env/save@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/github/env/save@actions-v0.2.33 name: Save env id: data with: diff --git a/.github/workflows/codeql-daily.yml b/.github/workflows/codeql-daily.yml index 2fa30e48687d..23bbd77df104 100644 --- a/.github/workflows/codeql-daily.yml +++ b/.github/workflows/codeql-daily.yml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Free disk space - uses: envoyproxy/toolshed/gh-actions/diskspace@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/diskspace@actions-v0.2.33 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/codeql-push.yml b/.github/workflows/codeql-push.yml index f701b13ae904..8b9b16a2f184 100644 --- a/.github/workflows/codeql-push.yml +++ b/.github/workflows/codeql-push.yml @@ -61,7 +61,7 @@ jobs: - name: Free disk space if: ${{ env.BUILD_TARGETS != '' }} - uses: envoyproxy/toolshed/gh-actions/diskspace@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/diskspace@actions-v0.2.33 - name: Initialize CodeQL if: ${{ env.BUILD_TARGETS != '' }} diff --git a/.github/workflows/command.yml b/.github/workflows/command.yml index 89107f990d51..fdcd0722471a 100644 --- a/.github/workflows/command.yml +++ b/.github/workflows/command.yml @@ -28,7 +28,7 @@ jobs: && github.actor != 'dependabot[bot]' }} steps: - - uses: envoyproxy/toolshed/gh-actions/github/command@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/github/command@actions-v0.2.33 name: Parse command from comment id: command with: @@ -37,14 +37,14 @@ jobs: ^/(retest) # /retest - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 if: ${{ steps.command.outputs.command == 'retest' }} id: appauth-retest name: Appauth (retest) with: key: ${{ secrets.ENVOY_CI_APP_KEY }} app_id: ${{ secrets.ENVOY_CI_APP_ID }} - - uses: envoyproxy/toolshed/gh-actions/retest@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/retest@actions-v0.2.33 if: ${{ steps.command.outputs.command == 'retest' }} name: Retest with: diff --git a/.github/workflows/envoy-dependency.yml b/.github/workflows/envoy-dependency.yml index cc1521ae54b0..faacf1d8b11f 100644 --- a/.github/workflows/envoy-dependency.yml +++ b/.github/workflows/envoy-dependency.yml @@ -53,16 +53,16 @@ jobs: steps: - id: appauth name: Appauth - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 with: app_id: ${{ secrets.ENVOY_CI_DEP_APP_ID }} key: ${{ secrets.ENVOY_CI_DEP_APP_KEY }} - id: checkout name: Checkout Envoy repository - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 with: token: ${{ steps.appauth.outputs.token }} - - uses: envoyproxy/toolshed/gh-actions/bson@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/bson@actions-v0.2.33 id: update name: Update dependency (${{ inputs.dependency }}) with: @@ -97,13 +97,13 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: envoyproxy/toolshed/gh-actions/upload/diff@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/upload/diff@actions-v0.2.33 name: Upload diff with: name: ${{ inputs.dependency }}-${{ steps.update.outputs.output }} - name: Create a PR if: ${{ inputs.pr }} - uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.33 with: base: main body: | @@ -134,11 +134,11 @@ jobs: steps: - id: appauth name: Appauth - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 with: app_id: ${{ secrets.ENVOY_CI_DEP_APP_ID }} key: ${{ secrets.ENVOY_CI_DEP_APP_KEY }} - - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 id: checkout name: Checkout Envoy repository with: @@ -180,7 +180,7 @@ jobs: - name: Check Docker SHAs id: build-images - uses: envoyproxy/toolshed/gh-actions/docker/shas@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/docker/shas@actions-v0.2.33 with: images: | sha: envoyproxy/envoy-build-ubuntu:${{ steps.build-tools.outputs.tag }} @@ -209,7 +209,7 @@ jobs: name: Update SHAs working-directory: envoy - name: Create a PR - uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.33 with: base: main body: Created by Envoy dependency bot diff --git a/.github/workflows/envoy-release.yml b/.github/workflows/envoy-release.yml index 777f35c1384c..4a3fa9adabfb 100644 --- a/.github/workflows/envoy-release.yml +++ b/.github/workflows/envoy-release.yml @@ -55,14 +55,14 @@ jobs: steps: - id: appauth name: App auth - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 with: app_id: ${{ secrets.ENVOY_CI_PUBLISH_APP_ID }} key: ${{ secrets.ENVOY_CI_PUBLISH_APP_KEY }} - id: checkout name: Checkout Envoy repository - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 with: committer-name: ${{ env.COMMITTER_NAME }} committer-email: ${{ env.COMMITTER_EMAIL }} @@ -83,10 +83,10 @@ jobs: name: Check changelog summary - if: ${{ inputs.author }} name: Validate signoff email - uses: envoyproxy/toolshed/gh-actions/email/validate@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/email/validate@actions-v0.2.33 with: email: ${{ inputs.author }} - - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.33 name: Create release with: source: | @@ -111,7 +111,7 @@ jobs: name: Release version id: release - name: Create a PR - uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.33 with: base: ${{ github.ref_name }} commit: false @@ -136,20 +136,20 @@ jobs: steps: - id: appauth name: App auth - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 with: app_id: ${{ secrets.ENVOY_CI_PUBLISH_APP_ID }} key: ${{ secrets.ENVOY_CI_PUBLISH_APP_KEY }} - id: checkout name: Checkout Envoy repository - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 with: committer-name: ${{ env.COMMITTER_NAME }} committer-email: ${{ env.COMMITTER_EMAIL }} strip-prefix: release/ token: ${{ steps.appauth.outputs.token }} - - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.33 name: Sync version histories with: command: >- @@ -159,7 +159,7 @@ jobs: -- --signoff="${{ env.COMMITTER_NAME }} <${{ env.COMMITTER_EMAIL }}>" - name: Create a PR - uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.33 with: append-commit-message: true base: ${{ github.ref_name }} @@ -189,13 +189,13 @@ jobs: steps: - id: appauth name: App auth - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 with: app_id: ${{ secrets.ENVOY_CI_PUBLISH_APP_ID }} key: ${{ secrets.ENVOY_CI_PUBLISH_APP_KEY }} - name: Checkout repository - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 with: committer-name: ${{ env.COMMITTER_NAME }} committer-email: ${{ env.COMMITTER_EMAIL }} diff --git a/.github/workflows/envoy-sync.yml b/.github/workflows/envoy-sync.yml index 4b85692f4035..65cc0fdd2161 100644 --- a/.github/workflows/envoy-sync.yml +++ b/.github/workflows/envoy-sync.yml @@ -31,12 +31,12 @@ jobs: - data-plane-api - mobile-website steps: - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 id: appauth with: app_id: ${{ secrets.ENVOY_CI_SYNC_APP_ID }} key: ${{ secrets.ENVOY_CI_SYNC_APP_KEY }} - - uses: envoyproxy/toolshed/gh-actions/dispatch@actions-v0.2.31 + - uses: envoyproxy/toolshed/gh-actions/dispatch@actions-v0.2.33 with: repository: "envoyproxy/${{ matrix.downstream }}" ref: main diff --git a/.github/workflows/garbage.yml b/.github/workflows/garbage.yml index ecca34630557..cb4be6ab5fff 100644 --- a/.github/workflows/garbage.yml +++ b/.github/workflows/garbage.yml @@ -33,7 +33,7 @@ jobs: pool-id: 17 steps: - name: Remove dead AZP agents (${{ matrix.target }}) - uses: envoyproxy/toolshed/gh-actions/azp/agent-cleanup@actions-v0.2.31 + uses: envoyproxy/toolshed/gh-actions/azp/agent-cleanup@actions-v0.2.33 with: azp-org: cncf azp-token: ${{ secrets.AZP_TOKEN }} From f2be4aeb66c157ee491e8d15bb33f2d14ecef657 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Mon, 5 Aug 2024 09:12:42 -0400 Subject: [PATCH 028/130] http/3: allow refreshing brokenness (#35508) Risk Level: low (new functions for Envoy) Testing: new unit tests Docs Changes: n/a Release Notes: n/a --------- Signed-off-by: Alyssa Wilk --- envoy/http/http_server_properties_cache.h | 13 ++++++++++ mobile/library/common/internal_engine.cc | 10 +++++++ mobile/library/common/internal_engine.h | 1 + .../http/http_server_properties_cache_impl.cc | 8 ++++++ .../http/http_server_properties_cache_impl.h | 1 + ...tp_server_properties_cache_manager_impl.cc | 7 +++++ ...ttp_server_properties_cache_manager_impl.h | 2 ++ source/common/runtime/runtime_features.cc | 2 ++ .../http_server_properties_cache_impl_test.cc | 26 ++++++++++++++++++- ...tp_server_properties_cache_manager_test.cc | 9 +++++++ .../mocks/http/http_server_properties_cache.h | 2 ++ 11 files changed, 80 insertions(+), 1 deletion(-) diff --git a/envoy/http/http_server_properties_cache.h b/envoy/http/http_server_properties_cache.h index c980b178abe1..24edbfae378b 100644 --- a/envoy/http/http_server_properties_cache.h +++ b/envoy/http/http_server_properties_cache.h @@ -174,6 +174,11 @@ class HttpServerPropertiesCache { */ virtual HttpServerPropertiesCache::Http3StatusTracker& getOrCreateHttp3StatusTracker(const Origin& origin) PURE; + + /** + * Changes any origins with status "Broken" for HTTP/3 to "Failed Recently" + */ + virtual void resetBrokenness() PURE; }; using HttpServerPropertiesCacheSharedPtr = std::shared_ptr; @@ -195,6 +200,14 @@ class HttpServerPropertiesCacheManager { virtual HttpServerPropertiesCacheSharedPtr getCache(const envoy::config::core::v3::AlternateProtocolsCacheOptions& config, Event::Dispatcher& dispatcher) PURE; + + using CacheFn = std::function; + + /** + * Run the supplied function on each HttpServerPropertiesCache on this thread. + * @param cache_fn supplies the function to run. + */ + virtual void forEachThreadLocalCache(CacheFn cache_fn) PURE; }; using HttpServerPropertiesCacheManagerSharedPtr = std::shared_ptr; diff --git a/mobile/library/common/internal_engine.cc b/mobile/library/common/internal_engine.cc index 68bea2d73c05..c5b774cda157 100644 --- a/mobile/library/common/internal_engine.cc +++ b/mobile/library/common/internal_engine.cc @@ -5,6 +5,7 @@ #include "source/common/api/os_sys_calls_impl.h" #include "source/common/common/lock_guard.h" #include "source/common/common/utility.h" +#include "source/common/http/http_server_properties_cache_manager_impl.h" #include "source/common/network/io_socket_handle_impl.h" #include "source/common/runtime/runtime_features.h" @@ -295,6 +296,15 @@ envoy_status_t InternalEngine::setPreferredNetwork(NetworkType network) { connectivity_manager_->dnsCache()->setIpVersionToRemove(absl::nullopt); } } + if (Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.reset_brokenness_on_nework_change")) { + Http::HttpServerPropertiesCacheManager& cache_manager = + server_->httpServerPropertiesCacheManager(); + + Http::HttpServerPropertiesCacheManager::CacheFn clear_brokenness = + [](Http::HttpServerPropertiesCache& cache) { cache.resetBrokenness(); }; + cache_manager.forEachThreadLocalCache(clear_brokenness); + } connectivity_manager_->refreshDns(configuration_key, true); }); } diff --git a/mobile/library/common/internal_engine.h b/mobile/library/common/internal_engine.h index 7a977d56f609..4b7ab545ccf6 100644 --- a/mobile/library/common/internal_engine.h +++ b/mobile/library/common/internal_engine.h @@ -115,6 +115,7 @@ class InternalEngine : public Logger::Loggable { * `setIpVersionToRemove` in the DNS cache implementation to remove the IPv6 addresses from * the DNS response in the subsequent DNS resolutions. * - Force refresh the hosts in the DNS cache (will take `setIpVersionToRemove` into account). + * - Optionally (if configured) clear HTTP/3 broken status. */ envoy_status_t setPreferredNetwork(NetworkType network); diff --git a/source/common/http/http_server_properties_cache_impl.cc b/source/common/http/http_server_properties_cache_impl.cc index b694cc512f66..1b6cc15ba6e1 100644 --- a/source/common/http/http_server_properties_cache_impl.cc +++ b/source/common/http/http_server_properties_cache_impl.cc @@ -296,6 +296,14 @@ HttpServerPropertiesCacheImpl::getOrCreateHttp3StatusTracker(const Origin& origi return *it->second.h3_status_tracker; } +void HttpServerPropertiesCacheImpl::resetBrokenness() { + for (auto& protocol : protocols_) { + if (protocol.second.h3_status_tracker && protocol.second.h3_status_tracker->isHttp3Broken()) { + protocol.second.h3_status_tracker->markHttp3FailedRecently(); + } + } +} + absl::string_view HttpServerPropertiesCacheImpl::getCanonicalSuffix(absl::string_view hostname) { for (const std::string& suffix : canonical_suffixes_) { if (absl::EndsWith(hostname, suffix)) { diff --git a/source/common/http/http_server_properties_cache_impl.h b/source/common/http/http_server_properties_cache_impl.h index 801fa958028e..d728f21b8267 100644 --- a/source/common/http/http_server_properties_cache_impl.h +++ b/source/common/http/http_server_properties_cache_impl.h @@ -91,6 +91,7 @@ class HttpServerPropertiesCacheImpl : public HttpServerPropertiesCache, size_t size() const override; HttpServerPropertiesCache::Http3StatusTracker& getOrCreateHttp3StatusTracker(const Origin& origin) override; + void resetBrokenness() override; private: // Time source used to check expiration of entries. diff --git a/source/common/http/http_server_properties_cache_manager_impl.cc b/source/common/http/http_server_properties_cache_manager_impl.cc index b9beaabcf024..13f717e64c22 100644 --- a/source/common/http/http_server_properties_cache_manager_impl.cc +++ b/source/common/http/http_server_properties_cache_manager_impl.cc @@ -73,5 +73,12 @@ HttpServerPropertiesCacheSharedPtr HttpServerPropertiesCacheManagerImpl::getCach return new_cache; } +void HttpServerPropertiesCacheManagerImpl::forEachThreadLocalCache(CacheFn cache_fn) { + for (auto& entry : (*slot_).caches_) { + HttpServerPropertiesCache& cache = *entry.second.cache_; + cache_fn(cache); + } +} + } // namespace Http } // namespace Envoy diff --git a/source/common/http/http_server_properties_cache_manager_impl.h b/source/common/http/http_server_properties_cache_manager_impl.h index 8ab68df28e3f..31ef6af80fa2 100644 --- a/source/common/http/http_server_properties_cache_manager_impl.h +++ b/source/common/http/http_server_properties_cache_manager_impl.h @@ -36,6 +36,8 @@ class HttpServerPropertiesCacheManagerImpl : public HttpServerPropertiesCacheMan getCache(const envoy::config::core::v3::AlternateProtocolsCacheOptions& options, Event::Dispatcher& dispatcher) override; + void forEachThreadLocalCache(CacheFn cache_fn) override; + private: // Contains a cache and the options associated with it. struct CacheWithOptions { diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index c8944b7d78b8..7ee8b79281bb 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -149,6 +149,8 @@ FALSE_RUNTIME_GUARD(envoy_reloadable_features_reresolve_if_no_connections); FALSE_RUNTIME_GUARD(envoy_restart_features_xds_failover_support); // TODO(fredyw): evaluate and either make this a config knob or remove. FALSE_RUNTIME_GUARD(envoy_reloadable_features_dns_cache_set_ip_version_to_remove); +// TODO(alyssawilk): evaluate and make this a config knob or remove. +FALSE_RUNTIME_GUARD(envoy_reloadable_features_reset_brokenness_on_nework_change); // A flag to set the maximum TLS version for google_grpc client to TLS1.2, when needed for // compliance restrictions. diff --git a/test/common/http/http_server_properties_cache_impl_test.cc b/test/common/http/http_server_properties_cache_impl_test.cc index 3efb2ae0646b..bcbf40ac3ec8 100644 --- a/test/common/http/http_server_properties_cache_impl_test.cc +++ b/test/common/http/http_server_properties_cache_impl_test.cc @@ -48,6 +48,7 @@ class HttpServerPropertiesCacheImplTest : public testing::TestWithParamgetOrCreateHttp3StatusTracker(origin1_).markHttp3Broken(); EXPECT_EQ(1u, protocols_->size()); EXPECT_TRUE(protocols_->getOrCreateHttp3StatusTracker(origin1_).isHttp3Broken()); + EXPECT_TRUE(protocols_->getOrCreateHttp3StatusTracker(origin1_).isHttp3Broken()); - // Fetch HTTP/3 status for another origin should overwrite the cache. + // Fetch HTTP/3 status for another origin should overwrite the cache because + // it's limited to one entry. EXPECT_FALSE(protocols_->getOrCreateHttp3StatusTracker(origin2_).isHttp3Broken()); EXPECT_EQ(1u, protocols_->size()); EXPECT_FALSE(protocols_->getOrCreateHttp3StatusTracker(origin1_).isHttp3Broken()); } +TEST_P(HttpServerPropertiesCacheImplTest, ClearBrokenness) { + initialize(); + EXPECT_EQ(0u, protocols_->size()); + + protocols_->getOrCreateHttp3StatusTracker(origin1_).markHttp3Broken(); + protocols_->getOrCreateHttp3StatusTracker(origin2_).markHttp3Confirmed(); + protocols_->getOrCreateHttp3StatusTracker(origin3_).markHttp3Broken(); + + EXPECT_EQ(3u, protocols_->size()); + EXPECT_TRUE(protocols_->getOrCreateHttp3StatusTracker(origin1_).isHttp3Broken()); + EXPECT_TRUE(protocols_->getOrCreateHttp3StatusTracker(origin3_).isHttp3Broken()); + EXPECT_TRUE(protocols_->getOrCreateHttp3StatusTracker(origin2_).isHttp3Confirmed()); + + protocols_->resetBrokenness(); + + EXPECT_TRUE(protocols_->getOrCreateHttp3StatusTracker(origin1_).hasHttp3FailedRecently()); + EXPECT_TRUE(protocols_->getOrCreateHttp3StatusTracker(origin3_).hasHttp3FailedRecently()); + EXPECT_TRUE(protocols_->getOrCreateHttp3StatusTracker(origin2_).isHttp3Confirmed()); +} + TEST_P(HttpServerPropertiesCacheImplTest, CanonicalSuffix) { std::string suffix = ".example.com"; std::string host1 = "first.example.com"; diff --git a/test/common/http/http_server_properties_cache_manager_test.cc b/test/common/http/http_server_properties_cache_manager_test.cc index e29d8ac47349..cfad02480867 100644 --- a/test/common/http/http_server_properties_cache_manager_test.cc +++ b/test/common/http/http_server_properties_cache_manager_test.cc @@ -94,6 +94,15 @@ TEST_F(HttpServerPropertiesCacheManagerTest, GetCacheForDifferentOptions) { HttpServerPropertiesCacheSharedPtr cache2 = manager_->getCache(options2_, dispatcher_); EXPECT_NE(nullptr, cache2); EXPECT_NE(cache1, cache2); + + int num_caches = 0; + Http::HttpServerPropertiesCacheManager::CacheFn count_caches = + [&](Http::HttpServerPropertiesCache& cache) { + EXPECT_TRUE(&cache == cache1.get() || &cache == cache2.get()); + ++num_caches; + }; + manager_->forEachThreadLocalCache(count_caches); + EXPECT_EQ(num_caches, 2); } TEST_F(HttpServerPropertiesCacheManagerTest, GetCacheForConflictingOptions) { diff --git a/test/mocks/http/http_server_properties_cache.h b/test/mocks/http/http_server_properties_cache.h index 2eced57f9f87..d29ed5ce1cb5 100644 --- a/test/mocks/http/http_server_properties_cache.h +++ b/test/mocks/http/http_server_properties_cache.h @@ -21,6 +21,7 @@ class MockHttpServerPropertiesCache : public HttpServerPropertiesCache { MOCK_METHOD(size_t, size, (), (const)); MOCK_METHOD(HttpServerPropertiesCache::Http3StatusTracker&, getOrCreateHttp3StatusTracker, (const Origin& origin)); + MOCK_METHOD(void, resetBrokenness, ()); }; class MockHttpServerPropertiesCacheManager : public HttpServerPropertiesCacheManager { @@ -30,6 +31,7 @@ class MockHttpServerPropertiesCacheManager : public HttpServerPropertiesCacheMan MOCK_METHOD(HttpServerPropertiesCacheSharedPtr, getCache, (const envoy::config::core::v3::AlternateProtocolsCacheOptions& config, Event::Dispatcher& dispatcher)); + MOCK_METHOD(void, forEachThreadLocalCache, (HttpServerPropertiesCacheManager::CacheFn)); }; } // namespace Http From d06a903c76d74c62240f9471dc0b5c0f8f94e327 Mon Sep 17 00:00:00 2001 From: RenjieTang Date: Mon, 5 Aug 2024 06:54:41 -0700 Subject: [PATCH 029/130] [mobile] Prefer IPv6 over IPv4 address for iOS (#35555) Commit Message: Prefer IPv6 over IPv4 address for iOS Additional Description: This matches Android behavior https://github.com/envoyproxy/envoy/blob/97bc67a06a3908821c933b437f9268886383d210/source/extensions/network/dns_resolver/getaddrinfo/getaddrinfo.cc#L76 Risk Level: Low Testing: existing tests Docs Changes: n/a Release Notes: n/a Platform Specific Features: iOS only --------- Signed-off-by: Renjie Tang --- changelogs/current.yaml | 4 ++++ source/common/runtime/runtime_features.cc | 1 + .../network/dns_resolver/apple/apple_dns_impl.cc | 12 +++++++++--- .../dns_resolver/apple/apple_dns_impl_test.cc | 4 ++-- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 8ad729d94c33..95b1c2dbf4a9 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -133,5 +133,9 @@ new_features: change: | Added new access log command operators ``%START_TIME_LOCAL%`` and ``%EMIT_TIME_LOCAL%``, similar to ``%START_TIME%`` and ``%EMIT_TIME%``, but use local time zone. +- area: dns + change: | + Prefer using IPv6 address when addresses from both families are available. + Can be reverted by setting ``envoy.reloadable_features.prefer_ipv6_dns_on_macos`` to false. deprecated: diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 7ee8b79281bb..a83c9e56ba00 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -68,6 +68,7 @@ RUNTIME_GUARD(envoy_reloadable_features_no_extension_lookup_by_name); RUNTIME_GUARD(envoy_reloadable_features_no_timer_based_rate_limit_token_bucket); RUNTIME_GUARD(envoy_reloadable_features_normalize_host_for_preresolve_dfp_dns); RUNTIME_GUARD(envoy_reloadable_features_original_dst_rely_on_idle_timeout); +RUNTIME_GUARD(envoy_reloadable_features_prefer_ipv6_dns_on_macos); // Fixes fail-open behaviour of failure_mode_allow for external authz grpc servers. RUNTIME_GUARD(envoy_reloadable_features_process_ext_authz_grpc_error_codes_as_errors); RUNTIME_GUARD(envoy_reloadable_features_proxy_104); diff --git a/source/extensions/network/dns_resolver/apple/apple_dns_impl.cc b/source/extensions/network/dns_resolver/apple/apple_dns_impl.cc index b4d41989a604..cf2694c66096 100644 --- a/source/extensions/network/dns_resolver/apple/apple_dns_impl.cc +++ b/source/extensions/network/dns_resolver/apple/apple_dns_impl.cc @@ -216,9 +216,15 @@ std::list& AppleDnsResolverImpl::PendingResolution::finalAddressLis pending_response_.all_responses_.insert(pending_response_.all_responses_.end(), pending_response_.v4_responses_.begin(), pending_response_.v4_responses_.end()); - pending_response_.all_responses_.insert(pending_response_.all_responses_.end(), - pending_response_.v6_responses_.begin(), - pending_response_.v6_responses_.end()); + if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.prefer_ipv6_dns_on_macos")) { + pending_response_.all_responses_.insert(pending_response_.all_responses_.end(), + pending_response_.v6_responses_.begin(), + pending_response_.v6_responses_.end()); + } else { + pending_response_.all_responses_.insert(pending_response_.all_responses_.begin(), + pending_response_.v6_responses_.begin(), + pending_response_.v6_responses_.end()); + } return pending_response_.all_responses_; } IS_ENVOY_BUG("unexpected DnsLookupFamily enum"); diff --git a/test/extensions/network/dns_resolver/apple/apple_dns_impl_test.cc b/test/extensions/network/dns_resolver/apple/apple_dns_impl_test.cc index a1b86871da1d..ae263ae91fcf 100644 --- a/test/extensions/network/dns_resolver/apple/apple_dns_impl_test.cc +++ b/test/extensions/network/dns_resolver/apple/apple_dns_impl_test.cc @@ -672,8 +672,8 @@ class AppleDnsImplFakeApiTest : public testing::Test { EXPECT_NE(nullptr, response.front().addrInfo().address_->ip()->ipv6()); break; case AddressType::Both: - EXPECT_NE(nullptr, response.front().addrInfo().address_->ip()->ipv4()); - EXPECT_NE(nullptr, response.back().addrInfo().address_->ip()->ipv6()); + EXPECT_NE(nullptr, response.back().addrInfo().address_->ip()->ipv4()); + EXPECT_NE(nullptr, response.front().addrInfo().address_->ip()->ipv6()); break; default: PANIC("reached unexpected code"); From ceb0ccd731b4f7d59a1ce8146593c1bc4f2dd928 Mon Sep 17 00:00:00 2001 From: yanavlasov Date: Mon, 5 Aug 2024 09:56:21 -0400 Subject: [PATCH 030/130] deps: Update c-ares to 1.21.0 (#35530) Commit message: c-ares changes that affected tests: - resolution with empty name results in immediate error - tests were adjusted to use a bogus name to start resolution that will result in an error. - timeout of 0 no longer implies "fail immediately". c-ares replaces it with the default 5 seconds timeout. Tests were adjusted to make DNS server never respond thus triggering resolution timeout. - fix flakiness in the test that waits for 2 resolutions to complete. The flakiness was pre-existing although very low probability. With the new c-ares version it has increased, most likely due to some changes to how responses are read from the socket. Risk Level: Low Testing: Unit tests Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A --------- Signed-off-by: Yan Avlasov --- bazel/foreign_cc/cares.patch | 58 ------------------- bazel/repositories.bzl | 5 -- bazel/repository_locations.bzl | 6 +- .../dns_resolver/cares/dns_impl_test.cc | 48 +++++++++------ 4 files changed, 33 insertions(+), 84 deletions(-) delete mode 100644 bazel/foreign_cc/cares.patch diff --git a/bazel/foreign_cc/cares.patch b/bazel/foreign_cc/cares.patch deleted file mode 100644 index d666ef023f6e..000000000000 --- a/bazel/foreign_cc/cares.patch +++ /dev/null @@ -1,58 +0,0 @@ -From a070d7835d667b2fae5266fe1b790677dae47d25 Mon Sep 17 00:00:00 2001 -From: Brad House -Date: Thu, 12 Oct 2023 09:29:14 -0400 -Subject: [PATCH] Socket callbacks were passed SOCK_STREAM instead of - SOCK_DGRAM on udp - -A regression was introduced in 1.20.0 that would pass SOCK_STREAM on udp -connections due to code refactoring. If a client application validated this -data, it could cause issues as seen in gRPC. - -Fixes Issue: #571 -Fix By: Brad House (@bradh352) ---- - src/lib/ares_process.c | 10 ++++------ - 1 file changed, 4 insertions(+), 6 deletions(-) - -diff --git a/src/lib/ares_process.c b/src/lib/ares_process.c -index ca597db7ad..2f8e4de30d 100644 ---- a/src/lib/ares_process.c -+++ b/src/lib/ares_process.c -@@ -1065,6 +1065,7 @@ static ares_status_t open_socket(ares_channel channel, - unsigned short port; - struct server_connection *conn; - ares__llist_node_t *node; -+ int type = is_tcp?SOCK_STREAM:SOCK_DGRAM; - - if (is_tcp) { - port = aresx_sitous(server->addr.tcp_port? -@@ -1098,8 +1099,7 @@ static ares_status_t open_socket(ares_channel channel, - } - - /* Acquire a socket. */ -- s = ares__open_socket(channel, server->addr.family, -- is_tcp?SOCK_STREAM:SOCK_DGRAM, 0); -+ s = ares__open_socket(channel, server->addr.family, type, 0); - if (s == ARES_SOCKET_BAD) - return ARES_ECONNREFUSED; - -@@ -1129,8 +1129,7 @@ static ares_status_t open_socket(ares_channel channel, - #endif - - if (channel->sock_config_cb) { -- int err = channel->sock_config_cb(s, SOCK_STREAM, -- channel->sock_config_cb_data); -+ int err = channel->sock_config_cb(s, type, channel->sock_config_cb_data); - if (err < 0) { - ares__close_socket(channel, s); - return ARES_ECONNREFUSED; -@@ -1148,8 +1147,7 @@ static ares_status_t open_socket(ares_channel channel, - } - - if (channel->sock_create_cb) { -- int err = channel->sock_create_cb(s, SOCK_STREAM, -- channel->sock_create_cb_data); -+ int err = channel->sock_create_cb(s, type, channel->sock_create_cb_data); - if (err < 0) { - ares__close_socket(channel, s); - return ARES_ECONNREFUSED; diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index 77fab4c64e09..e54d1161e99d 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -443,11 +443,6 @@ def _com_github_c_ares_c_ares(): external_http_archive( name = "com_github_c_ares_c_ares", build_file_content = BUILD_ALL_CONTENT, - # Patch c-ares library aith commit - # https://github.com/c-ares/c-ares/commit/a070d7835d667b2fae5266fe1b790677dae47d25 - # This commit fixes an issue when the gRPC library attempts to resolve a domain name. - patches = ["@envoy//bazel/foreign_cc:cares.patch"], - patch_args = ["-p1"], ) native.bind( name = "ares", diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index ffc1ecdc37b5..da722fb42bea 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -230,12 +230,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "c-ares", project_desc = "C library for asynchronous DNS requests", project_url = "https://c-ares.haxx.se/", - version = "1.20.1", - sha256 = "de24a314844cb157909730828560628704f4f896d167dd7da0fa2fb93ea18b10", + version = "1.21.0", + sha256 = "cd7aa3af1d3ee780d6437039a7ddb7f1ec029f9c4f7aabb0197e384eb5bc2f2d", strip_prefix = "c-ares-{version}", urls = ["https://github.com/c-ares/c-ares/releases/download/cares-{underscore_version}/c-ares-{version}.tar.gz"], use_category = ["dataplane_core", "controlplane"], - release_date = "2023-10-08", + release_date = "2023-10-27", cpe = "cpe:2.3:a:c-ares_project:c-ares:*", license = "c-ares", license_url = "https://github.com/c-ares/c-ares/blob/cares-{underscore_version}/LICENSE.md", diff --git a/test/extensions/network/dns_resolver/cares/dns_impl_test.cc b/test/extensions/network/dns_resolver/cares/dns_impl_test.cc index 6bd911aa7667..b57c5e4a80c8 100644 --- a/test/extensions/network/dns_resolver/cares/dns_impl_test.cc +++ b/test/extensions/network/dns_resolver/cares/dns_impl_test.cc @@ -72,10 +72,10 @@ class TestDnsServerQuery { TestDnsServerQuery(ConnectionPtr connection, const HostMap& hosts_a, const HostMap& hosts_aaaa, const CNameMap& cnames, const std::chrono::seconds& record_ttl, const std::chrono::seconds& cname_ttl_, bool refused, bool error_on_a, - bool error_on_aaaa) + bool error_on_aaaa, bool no_response) : connection_(std::move(connection)), hosts_a_(hosts_a), hosts_aaaa_(hosts_aaaa), cnames_(cnames), record_ttl_(record_ttl), cname_ttl_(cname_ttl_), refused_(refused), - error_on_a_(error_on_a), error_on_aaaa_(error_on_aaaa) { + error_on_a_(error_on_a), error_on_aaaa_(error_on_aaaa), no_response_(no_response) { connection_->addReadFilter(Network::ReadFilterSharedPtr{new ReadFilter(*this)}); } @@ -164,7 +164,9 @@ class TestDnsServerQuery { const auto addrs = getAddrs(q_type, lookup_name); auto buf = createAddrResolutionBuffer(q_type, addrs, request, name_len, encoded_cname, encoded_name); - parent_.connection_->write(buf, false); + if (!parent_.no_response_) { + parent_.connection_->write(buf, false); + } // Reset query state, time for the next one. buffer_.drain(size_); @@ -335,21 +337,23 @@ class TestDnsServerQuery { const bool refused_; const bool error_on_a_; const bool error_on_aaaa_; + const bool no_response_{false}; }; class TestDnsServer : public TcpListenerCallbacks { public: - TestDnsServer(Event::Dispatcher& dispatcher) + TestDnsServer(Event::Dispatcher& dispatcher, bool no_response) : dispatcher_(dispatcher), record_ttl_(0), cname_ttl_(0), stream_info_(dispatcher.timeSource(), nullptr, - StreamInfo::FilterState::LifeSpan::Connection) {} + StreamInfo::FilterState::LifeSpan::Connection), + no_response_(no_response) {} void onAccept(ConnectionSocketPtr&& socket) override { Network::ConnectionPtr new_connection = dispatcher_.createServerConnection( std::move(socket), Network::Test::createRawBufferSocket(), stream_info_); - TestDnsServerQuery* query = - new TestDnsServerQuery(std::move(new_connection), hosts_a_, hosts_aaaa_, cnames_, - record_ttl_, cname_ttl_, refused_, error_on_a_, error_on_aaaa_); + TestDnsServerQuery* query = new TestDnsServerQuery( + std::move(new_connection), hosts_a_, hosts_aaaa_, cnames_, record_ttl_, cname_ttl_, + refused_, error_on_a_, error_on_aaaa_, no_response_); queries_.emplace_back(query); } @@ -389,6 +393,7 @@ class TestDnsServer : public TcpListenerCallbacks { // over. std::vector> queries_; StreamInfo::StreamInfoImpl stream_info_; + const bool no_response_{false}; }; } // namespace @@ -730,7 +735,7 @@ class DnsImplTest : public testing::TestWithParam { void SetUp() override { // Instantiate TestDnsServer and listen on a random port on the loopback address. - server_ = std::make_unique(*dispatcher_); + server_ = std::make_unique(*dispatcher_, queryTimeout()); socket_ = std::make_shared( Network::Test::getCanonicalLoopbackAddress(GetParam())); NiceMock listener_config; @@ -763,7 +768,7 @@ class DnsImplTest : public testing::TestWithParam { void resetChannel() { if (tcpOnly()) { - peer_->resetChannelTcpOnly(zeroTimeout()); + peer_->resetChannelTcpOnly(queryTimeout()); } ares_set_servers_ports_csv( peer_->channel(), socket_->connectionInfoProvider().localAddress()->asString().c_str()); @@ -951,8 +956,8 @@ class DnsImplTest : public testing::TestWithParam { } protected: - // Should the DnsResolverImpl use a zero timeout for c-ares queries? - virtual bool zeroTimeout() const { return false; } + // Should the TestDnsServer cause c-ares queries to timeout, by not responding? + virtual bool queryTimeout() const { return false; } virtual bool tcpOnly() const { return true; } virtual void updateDnsResolverOptions(){}; virtual bool setResolverInConstructor() const { return false; } @@ -980,7 +985,8 @@ INSTANTIATE_TEST_SUITE_P(IpVersions, DnsImplTest, // development, where segfaults were encountered due to callback invocations on // destruction. TEST_P(DnsImplTest, DestructPending) { - ActiveDnsQuery* query = resolveWithUnreferencedParameters("", DnsLookupFamily::V4Only, false); + ActiveDnsQuery* query = + resolveWithUnreferencedParameters("foo.bar.baz", DnsLookupFamily::V4Only, false); ASSERT_NE(nullptr, query); query->cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned); // Also validate that pending events are around to exercise the resource @@ -999,7 +1005,8 @@ TEST_P(DnsImplTest, DestructPending) { // just via UDP. // Either way, we have no tests today that cover parallel queries. We can do this is a follow up. TEST_P(DnsImplTest, DestructPendingAllQuery) { - ActiveDnsQuery* query = resolveWithUnreferencedParameters("", DnsLookupFamily::All, true); + ActiveDnsQuery* query = + resolveWithUnreferencedParameters("foo.bar.baz", DnsLookupFamily::All, true); ASSERT_NE(nullptr, query); } @@ -1019,12 +1026,12 @@ TEST_P(DnsImplTest, DestructCallback) { 1 /*get_addr_failure*/, 0 /*timeouts*/); } -// Validate basic success/fail lookup behavior. The empty request will connect +// Validate basic success/fail lookup behavior. The "foo.bar.baz" request will connect // to TestDnsServer, but localhost should resolve via the hosts file with no // asynchronous behavior or network events. TEST_P(DnsImplTest, LocalLookup) { std::list address_list; - EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("", DnsLookupFamily::V4Only)); + EXPECT_NE(nullptr, resolveWithNoRecordsExpectation("foo.bar.baz", DnsLookupFamily::V4Only)); dispatcher_->run(Event::Dispatcher::RunType::Block); if (GetParam() == Address::IpVersion::v4) { @@ -1146,7 +1153,7 @@ TEST_P(DnsImplTest, DestroyChannelOnRefused) { server_->setRefused(true); EXPECT_NE(nullptr, - resolveWithExpectations("", DnsLookupFamily::V4Only, + resolveWithExpectations("unresolvable.name", DnsLookupFamily::V4Only, DnsResolver::ResolutionStatus::Failure, {}, {}, absl::nullopt)); dispatcher_->run(Event::Dispatcher::RunType::Block); checkStats(1 /*resolve_total*/, 0 /*pending_resolutions*/, 0 /*not_found*/, @@ -1421,6 +1428,11 @@ TEST_P(DnsImplTest, Cancel) { query->cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned); dispatcher_->run(Event::Dispatcher::RunType::Block); + if (stats_store_.counter("dns.cares.resolve_total").value() < 4) { + // if c-ares did not read both responses at once, run another loop iteration + // to make it read the second response. + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + } checkStats(4 /*resolve_total*/, 0 /*pending_resolutions*/, 3 /*not_found*/, 0 /*get_addr_failure*/, 0 /*timeouts*/); } @@ -1926,7 +1938,7 @@ TEST_P(DnsImplFilterUnroutableFamiliesDontFilterTest, DontFilterAllV6) { class DnsImplZeroTimeoutTest : public DnsImplTest { protected: - bool zeroTimeout() const override { return true; } + bool queryTimeout() const override { return true; } }; // Parameterize the DNS test server socket address. From 0beb3cba1270090957ba5dc6ef112a18fad38f4a Mon Sep 17 00:00:00 2001 From: Tianyu <72890320+tyxia@users.noreply.github.com> Date: Mon, 5 Aug 2024 10:54:53 -0400 Subject: [PATCH 031/130] ext_authz: create filter from server context (#35557) Commit Message: Prior art: [ext_proc](https://github.com/envoyproxy/envoy/blob/f58a812ed24bf7d552ece3642bdf9336ee1a7e20/source/extensions/filters/http/ext_proc/config.cc#L45). And we have use this approach in production Risk Level: low Testing: unit test Signed-off-by: tyxia --- .../filters/http/ext_authz/config.cc | 24 ++++----- .../filters/http/ext_authz/config.h | 10 +++- .../filters/http/ext_authz/config_test.cc | 54 +++++++++++++++++++ 3 files changed, 72 insertions(+), 16 deletions(-) diff --git a/source/extensions/filters/http/ext_authz/config.cc b/source/extensions/filters/http/ext_authz/config.cc index 87a20cbd12e9..069029b2eb5d 100644 --- a/source/extensions/filters/http/ext_authz/config.cc +++ b/source/extensions/filters/http/ext_authz/config.cc @@ -20,17 +20,14 @@ namespace Extensions { namespace HttpFilters { namespace ExtAuthz { -Http::FilterFactoryCb ExtAuthzFilterConfig::createFilterFactoryFromProtoTyped( +Http::FilterFactoryCb ExtAuthzFilterConfig::createFilterFactoryFromProtoWithServerContextTyped( const envoy::extensions::filters::http::ext_authz::v3::ExtAuthz& proto_config, - const std::string& stats_prefix, Server::Configuration::FactoryContext& context) { - auto& server_context = context.serverFactoryContext(); - - const auto filter_config = - std::make_shared(proto_config, context.scope(), stats_prefix, server_context); + const std::string& stats_prefix, Server::Configuration::ServerFactoryContext& server_context) { + const auto filter_config = std::make_shared(proto_config, server_context.scope(), + stats_prefix, server_context); // The callback is created in main thread and executed in worker thread, variables except factory // context must be captured by value into the callback. Http::FilterFactoryCb callback; - if (proto_config.has_http_service()) { // Raw HTTP client. const uint32_t timeout_ms = PROTOBUF_GET_MS_OR_DEFAULT(proto_config.http_service().server_uri(), @@ -48,24 +45,21 @@ Http::FilterFactoryCb ExtAuthzFilterConfig::createFilterFactoryFromProtoTyped( // gRPC client. const uint32_t timeout_ms = PROTOBUF_GET_MS_OR_DEFAULT(proto_config.grpc_service(), timeout, DefaultTimeout); - THROW_IF_NOT_OK(Config::Utility::checkTransportVersion(proto_config)); Envoy::Grpc::GrpcServiceConfigWithHashKey config_with_hash_key = Envoy::Grpc::GrpcServiceConfigWithHashKey(proto_config.grpc_service()); - callback = [&context, filter_config, timeout_ms, + callback = [&server_context, filter_config, timeout_ms, config_with_hash_key](Http::FilterChainFactoryCallbacks& callbacks) { - auto client_or_error = - context.serverFactoryContext() - .clusterManager() - .grpcAsyncClientManager() - .getOrCreateRawAsyncClientWithHashKey(config_with_hash_key, context.scope(), true); + auto client_or_error = server_context.clusterManager() + .grpcAsyncClientManager() + .getOrCreateRawAsyncClientWithHashKey( + config_with_hash_key, server_context.scope(), true); THROW_IF_STATUS_NOT_OK(client_or_error, throw); auto client = std::make_unique( client_or_error.value(), std::chrono::milliseconds(timeout_ms)); callbacks.addStreamFilter(std::make_shared(filter_config, std::move(client))); }; } - return callback; } diff --git a/source/extensions/filters/http/ext_authz/config.h b/source/extensions/filters/http/ext_authz/config.h index a5cddfb6a12c..cd67240833b2 100644 --- a/source/extensions/filters/http/ext_authz/config.h +++ b/source/extensions/filters/http/ext_authz/config.h @@ -25,7 +25,15 @@ class ExtAuthzFilterConfig static constexpr uint64_t DefaultTimeout = 200; Http::FilterFactoryCb createFilterFactoryFromProtoTyped( const envoy::extensions::filters::http::ext_authz::v3::ExtAuthz& proto_config, - const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; + const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override { + return createFilterFactoryFromProtoWithServerContextTyped(proto_config, stats_prefix, + context.serverFactoryContext()); + } + + Http::FilterFactoryCb createFilterFactoryFromProtoWithServerContextTyped( + const envoy::extensions::filters::http::ext_authz::v3::ExtAuthz& proto_config, + const std::string& stats_prefix, + Server::Configuration::ServerFactoryContext& server_context) override; Router::RouteSpecificFilterConfigConstSharedPtr createRouteSpecificFilterConfigTyped( const envoy::extensions::filters::http::ext_authz::v3::ExtAuthzPerRoute& proto_config, diff --git a/test/extensions/filters/http/ext_authz/config_test.cc b/test/extensions/filters/http/ext_authz/config_test.cc index 866081c2f491..bfb2a593b411 100644 --- a/test/extensions/filters/http/ext_authz/config_test.cc +++ b/test/extensions/filters/http/ext_authz/config_test.cc @@ -163,6 +163,60 @@ TEST_F(ExtAuthzFilterHttpTest, ExtAuthzFilterFactoryTestHttp) { testFilterFactory(ext_authz_config_yaml); } +TEST_F(ExtAuthzFilterHttpTest, FilterWithServerContext) { + const std::string ext_authz_config_yaml = R"EOF( + stat_prefix: "wall" + allowed_headers: + patterns: + - exact: baz + - prefix: x- + http_service: + server_uri: + uri: "ext_authz:9000" + cluster: "ext_authz" + timeout: 0.25s + authorization_request: + headers_to_add: + - key: foo + value: bar + - key: bar + value: foo + + authorization_response: + allowed_upstream_headers: + patterns: + - exact: baz + - prefix: x-success + allowed_client_headers: + patterns: + - exact: baz + - prefix: x-fail + allowed_upstream_headers_to_append: + patterns: + - exact: baz-append + - prefix: x-append + + path_prefix: /extauth + + failure_mode_allow: true + with_request_body: + max_request_bytes: 100 + pack_as_bytes: true + )EOF"; + + ExtAuthzFilterConfig factory; + ProtobufTypes::MessagePtr proto_config = factory.createEmptyConfigProto(); + TestUtility::loadFromYaml(ext_authz_config_yaml, *proto_config); + + testing::NiceMock context; + EXPECT_CALL(context, messageValidationVisitor()); + Http::FilterFactoryCb cb = + factory.createFilterFactoryFromProtoWithServerContext(*proto_config, "stats", context); + Http::MockFilterChainFactoryCallbacks filter_callback; + EXPECT_CALL(filter_callback, addStreamFilter(_)); + cb(filter_callback); +} + class ExtAuthzFilterGrpcTest : public ExtAuthzFilterTest { public: void testFilterFactoryAndFilterWithGrpcClient(const std::string& ext_authz_config_yaml) { From 988a6d0e37a431d0072d681dca4df7510021e415 Mon Sep 17 00:00:00 2001 From: Misha Efimov Date: Mon, 5 Aug 2024 11:45:42 -0400 Subject: [PATCH 032/130] Propagate `main_thread_dispatcher` to the LB Policy via LoadBalancerFactoryContext (#35451) Commit Message: Propagate main_thread_dispatcher to LB Policy via Upstream::LoadBalancerFactoryContext. This is needed for ClientSideWeightedRoundRobin LB policy to periodically update host weight. Risk Level: Low Testing: This will be covered by ClientSideWeightedRoundRobin tests. Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A #34777 Signed-off-by: Misha Efimov --- envoy/upstream/load_balancer.h | 17 +++++++++- source/common/upstream/upstream_impl.cc | 34 ++++++++++++++----- source/common/upstream/upstream_impl.h | 6 ++-- .../cluster_provided/config.h | 3 +- .../least_request/config.h | 4 +-- .../load_balancing_policies/maglev/config.h | 3 +- .../load_balancing_policies/random/config.h | 3 +- .../ring_hash/config.h | 3 +- .../round_robin/config.h | 4 +-- .../load_balancing_policies/subset/config.cc | 10 +++--- .../load_balancing_policies/subset/config.h | 5 +-- .../subset/subset_lb_config.cc | 17 +++++----- .../subset/subset_lb_config.h | 6 ++-- .../least_request/config_test.cc | 5 +-- .../maglev/config_test.cc | 8 +++-- .../random/config_test.cc | 5 +-- .../ring_hash/config_test.cc | 8 +++-- .../round_robin/config_test.cc | 5 +-- .../load_balancing_policies/subset/BUILD | 1 + .../subset/config_test.cc | 8 +++-- .../subset/subset_benchmark.cc | 4 ++- .../subset/subset_test.cc | 13 +++---- .../load_balancers/custom_lb_policy.h | 3 +- test/mocks/upstream/load_balancer.h | 8 +++++ .../upstream/typed_load_balancer_factory.h | 2 +- 25 files changed, 127 insertions(+), 58 deletions(-) diff --git a/envoy/upstream/load_balancer.h b/envoy/upstream/load_balancer.h index c4b31ec27d2c..cc58b35e5ea3 100644 --- a/envoy/upstream/load_balancer.h +++ b/envoy/upstream/load_balancer.h @@ -243,6 +243,19 @@ class LoadBalancerConfig { }; using LoadBalancerConfigPtr = std::unique_ptr; +/** + * Context information passed to a load balancer factory to use when creating a load balancer. + */ +class LoadBalancerFactoryContext { +public: + virtual ~LoadBalancerFactoryContext() = default; + + /** + * @return Event::Dispatcher& the main thread dispatcher. + */ + virtual Event::Dispatcher& mainThreadDispatcher() PURE; +}; + /** * Factory config for load balancers. To support a load balancing policy of * LOAD_BALANCING_POLICY_CONFIG, at least one load balancer factory corresponding to a policy in @@ -273,12 +286,14 @@ class TypedLoadBalancerFactory : public Config::TypedFactory { * * @return LoadBalancerConfigPtr a new load balancer config. * + * @param lb_factory_context supplies the load balancer factory context. * @param config supplies the typed proto config of the load balancer. A dynamic_cast could * be performed on the config to the expected proto type. * @param visitor supplies the validation visitor that will be used to validate the embedded * Any proto message. */ - virtual LoadBalancerConfigPtr loadConfig(const Protobuf::Message& config, + virtual LoadBalancerConfigPtr loadConfig(LoadBalancerFactoryContext& lb_factory_context, + const Protobuf::Message& config, ProtobufMessage::ValidationVisitor& visitor) PURE; std::string category() const override { return "envoy.load_balancing_policies"; } diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index f725214c948f..5d2af3d366a3 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -380,6 +380,20 @@ createUpstreamLocalAddressSelector( return selector_or_error.value(); } +class LoadBalancerFactoryContextImpl : public Upstream::LoadBalancerFactoryContext { +public: + explicit LoadBalancerFactoryContextImpl( + Server::Configuration::ServerFactoryContext& server_context) + : server_context_(server_context) {} + + Event::Dispatcher& mainThreadDispatcher() override { + return server_context_.mainThreadDispatcher(); + } + +private: + Server::Configuration::ServerFactoryContext& server_context_; +}; + } // namespace // Allow disabling ALPN checks for transport sockets. See @@ -987,7 +1001,8 @@ createOptions(const envoy::config::cluster::v3::Cluster& config, absl::StatusOr LegacyLbPolicyConfigHelper::getTypedLbConfigFromLegacyProtoWithoutSubset( - const ClusterProto& cluster, ProtobufMessage::ValidationVisitor& visitor) { + LoadBalancerFactoryContext& lb_factory_context, const ClusterProto& cluster, + ProtobufMessage::ValidationVisitor& visitor) { LoadBalancerConfigPtr lb_config; TypedLoadBalancerFactory* lb_factory = nullptr; @@ -1030,12 +1045,13 @@ LegacyLbPolicyConfigHelper::getTypedLbConfigFromLegacyProtoWithoutSubset( ClusterProto::LbPolicy_Name(cluster.lb_policy()))); } - return Result{lb_factory, lb_factory->loadConfig(cluster, visitor)}; + return Result{lb_factory, lb_factory->loadConfig(lb_factory_context, cluster, visitor)}; } absl::StatusOr LegacyLbPolicyConfigHelper::getTypedLbConfigFromLegacyProto( - const ClusterProto& cluster, ProtobufMessage::ValidationVisitor& visitor) { + LoadBalancerFactoryContext& lb_factory_context, const ClusterProto& cluster, + ProtobufMessage::ValidationVisitor& visitor) { // Handle the lb subset config case first. // Note it is possible to have a lb_subset_config without actually having any subset selectors. // In this case the subset load balancer should not be used. @@ -1043,12 +1059,12 @@ LegacyLbPolicyConfigHelper::getTypedLbConfigFromLegacyProto( auto* lb_factory = Config::Utility::getFactoryByName( "envoy.load_balancing_policies.subset"); if (lb_factory != nullptr) { - return Result{lb_factory, lb_factory->loadConfig(cluster, visitor)}; + return Result{lb_factory, lb_factory->loadConfig(lb_factory_context, cluster, visitor)}; } return absl::InvalidArgumentError("No subset load balancer factory found"); } - return getTypedLbConfigFromLegacyProtoWithoutSubset(cluster, visitor); + return getTypedLbConfigFromLegacyProtoWithoutSubset(lb_factory_context, cluster, visitor); } using ProtocolOptionsHashMap = @@ -1188,9 +1204,10 @@ ClusterInfoImpl::ClusterInfoImpl( } else { // If load_balancing_policy is not set, we will try to convert legacy lb_policy // to load_balancing_policy and use it. + LoadBalancerFactoryContextImpl lb_factory_context(server_context); auto lb_pair = LegacyLbPolicyConfigHelper::getTypedLbConfigFromLegacyProto( - config, server_context.messageValidationVisitor()); + lb_factory_context, config, server_context.messageValidationVisitor()); if (!lb_pair.ok()) { throwEnvoyExceptionOrPanic(std::string(lb_pair.status().message())); @@ -1357,13 +1374,14 @@ ClusterInfoImpl::configureLbPolicies(const envoy::config::cluster::v3::Cluster& policy.typed_extension_config(), /*is_optional=*/true); if (factory != nullptr) { // Load and validate the configuration. + LoadBalancerFactoryContextImpl lb_factory_context(context); auto proto_message = factory->createEmptyConfigProto(); Config::Utility::translateOpaqueConfig(policy.typed_extension_config().typed_config(), context.messageValidationVisitor(), *proto_message); load_balancer_factory_ = factory; - load_balancer_config_ = - factory->loadConfig(*proto_message, context.messageValidationVisitor()); + load_balancer_config_ = factory->loadConfig(lb_factory_context, *proto_message, + context.messageValidationVisitor()); break; } diff --git a/source/common/upstream/upstream_impl.h b/source/common/upstream/upstream_impl.h index 5896e11747c6..c5867e4b5dec 100644 --- a/source/common/upstream/upstream_impl.h +++ b/source/common/upstream/upstream_impl.h @@ -89,11 +89,13 @@ class LegacyLbPolicyConfigHelper { }; static absl::StatusOr - getTypedLbConfigFromLegacyProtoWithoutSubset(const ClusterProto& cluster, + getTypedLbConfigFromLegacyProtoWithoutSubset(LoadBalancerFactoryContext& lb_factory_context, + const ClusterProto& cluster, ProtobufMessage::ValidationVisitor& visitor); static absl::StatusOr - getTypedLbConfigFromLegacyProto(const ClusterProto& cluster, + getTypedLbConfigFromLegacyProto(LoadBalancerFactoryContext& lb_factory_context, + const ClusterProto& cluster, ProtobufMessage::ValidationVisitor& visitor); }; diff --git a/source/extensions/load_balancing_policies/cluster_provided/config.h b/source/extensions/load_balancing_policies/cluster_provided/config.h index 3bad53202292..cc624f11fabe 100644 --- a/source/extensions/load_balancing_policies/cluster_provided/config.h +++ b/source/extensions/load_balancing_policies/cluster_provided/config.h @@ -30,7 +30,8 @@ class Factory Random::RandomGenerator& random, TimeSource& time_source) override; - Upstream::LoadBalancerConfigPtr loadConfig(const Protobuf::Message&, + Upstream::LoadBalancerConfigPtr loadConfig(Upstream::LoadBalancerFactoryContext&, + const Protobuf::Message&, ProtobufMessage::ValidationVisitor&) override { return std::make_unique(); } diff --git a/source/extensions/load_balancing_policies/least_request/config.h b/source/extensions/load_balancing_policies/least_request/config.h index 3d3f92372de9..746f99ab5bf4 100644 --- a/source/extensions/load_balancing_policies/least_request/config.h +++ b/source/extensions/load_balancing_policies/least_request/config.h @@ -58,9 +58,9 @@ class Factory : public Common::FactoryBase::get(&config); ASSERT(active_or_legacy.hasLegacy() || active_or_legacy.hasActive()); diff --git a/source/extensions/load_balancing_policies/maglev/config.h b/source/extensions/load_balancing_policies/maglev/config.h index 294e370c850b..486cdd319a06 100644 --- a/source/extensions/load_balancing_policies/maglev/config.h +++ b/source/extensions/load_balancing_policies/maglev/config.h @@ -28,7 +28,8 @@ class Factory : public Upstream::TypedLoadBalancerFactoryBase { Random::RandomGenerator& random, TimeSource& time_source) override; - Upstream::LoadBalancerConfigPtr loadConfig(const Protobuf::Message& config, + Upstream::LoadBalancerConfigPtr loadConfig(Upstream::LoadBalancerFactoryContext&, + const Protobuf::Message& config, ProtobufMessage::ValidationVisitor&) override { auto active_or_legacy = Common::ActiveOrLegacy::get(&config); diff --git a/source/extensions/load_balancing_policies/random/config.h b/source/extensions/load_balancing_policies/random/config.h index 25a3991f65e0..a65e6afee7a4 100644 --- a/source/extensions/load_balancing_policies/random/config.h +++ b/source/extensions/load_balancing_policies/random/config.h @@ -43,7 +43,8 @@ class Factory : public Common::FactoryBase { public: Factory() : FactoryBase("envoy.load_balancing_policies.random") {} - Upstream::LoadBalancerConfigPtr loadConfig(const Protobuf::Message& config, + Upstream::LoadBalancerConfigPtr loadConfig(Upstream::LoadBalancerFactoryContext&, + const Protobuf::Message& config, ProtobufMessage::ValidationVisitor&) override { auto typed_config = dynamic_cast(&config); if (typed_config == nullptr) { diff --git a/source/extensions/load_balancing_policies/ring_hash/config.h b/source/extensions/load_balancing_policies/ring_hash/config.h index 16af59ff05b1..636540a873e3 100644 --- a/source/extensions/load_balancing_policies/ring_hash/config.h +++ b/source/extensions/load_balancing_policies/ring_hash/config.h @@ -28,7 +28,8 @@ class Factory : public Upstream::TypedLoadBalancerFactoryBase { Random::RandomGenerator& random, TimeSource& time_source) override; - Upstream::LoadBalancerConfigPtr loadConfig(const Protobuf::Message& config, + Upstream::LoadBalancerConfigPtr loadConfig(Upstream::LoadBalancerFactoryContext&, + const Protobuf::Message& config, ProtobufMessage::ValidationVisitor&) override { auto active_or_legacy = Common::ActiveOrLegacy::get(&config); diff --git a/source/extensions/load_balancing_policies/round_robin/config.h b/source/extensions/load_balancing_policies/round_robin/config.h index c2ad4dca934b..bf78fdbe2a36 100644 --- a/source/extensions/load_balancing_policies/round_robin/config.h +++ b/source/extensions/load_balancing_policies/round_robin/config.h @@ -57,9 +57,9 @@ class Factory : public Common::FactoryBase public: Factory() : FactoryBase("envoy.load_balancing_policies.round_robin") {} - Upstream::LoadBalancerConfigPtr loadConfig(const Protobuf::Message& config, + Upstream::LoadBalancerConfigPtr loadConfig(Upstream::LoadBalancerFactoryContext&, + const Protobuf::Message& config, ProtobufMessage::ValidationVisitor&) override { - auto active_or_legacy = Common::ActiveOrLegacy::get(&config); ASSERT(active_or_legacy.hasLegacy() || active_or_legacy.hasActive()); diff --git a/source/extensions/load_balancing_policies/subset/config.cc b/source/extensions/load_balancing_policies/subset/config.cc index 05eb9588361a..a788b26061dd 100644 --- a/source/extensions/load_balancing_policies/subset/config.cc +++ b/source/extensions/load_balancing_policies/subset/config.cc @@ -71,7 +71,8 @@ SubsetLbFactory::create(OptRef lb_config, } Upstream::LoadBalancerConfigPtr -SubsetLbFactory::loadConfig(const Protobuf::Message& config, +SubsetLbFactory::loadConfig(Upstream::LoadBalancerFactoryContext& lb_factory_context, + const Protobuf::Message& config, ProtobufMessage::ValidationVisitor& visitor) { auto active_or_legacy = Common::ActiveOrLegacy::get(&config); ASSERT(active_or_legacy.hasLegacy() || active_or_legacy.hasActive()); @@ -84,13 +85,14 @@ SubsetLbFactory::loadConfig(const Protobuf::Message& config, envoy::config::cluster::v3::Cluster::LbPolicy_Name( active_or_legacy.legacy()->lb_policy()))); } - return std::make_unique(*active_or_legacy.legacy(), - visitor); + return std::make_unique( + lb_factory_context, *active_or_legacy.legacy(), visitor); } // Load the subset load balancer configuration. This will contains child load balancer // config and child load balancer factory. - return std::make_unique(*active_or_legacy.active(), visitor); + return std::make_unique(lb_factory_context, + *active_or_legacy.active(), visitor); } /** diff --git a/source/extensions/load_balancing_policies/subset/config.h b/source/extensions/load_balancing_policies/subset/config.h index 0c49d8862bfb..80addf2150fb 100644 --- a/source/extensions/load_balancing_policies/subset/config.h +++ b/source/extensions/load_balancing_policies/subset/config.h @@ -24,8 +24,9 @@ class SubsetLbFactory Random::RandomGenerator& random, TimeSource& time_source) override; - Upstream::LoadBalancerConfigPtr loadConfig(const Protobuf::Message& config, - ProtobufMessage::ValidationVisitor& visitor) override; + Upstream::LoadBalancerConfigPtr + loadConfig(Upstream::LoadBalancerFactoryContext& lb_factory_context, + const Protobuf::Message& config, ProtobufMessage::ValidationVisitor& visitor) override; }; } // namespace Subset diff --git a/source/extensions/load_balancing_policies/subset/subset_lb_config.cc b/source/extensions/load_balancing_policies/subset/subset_lb_config.cc index 2c0994306f69..500022395c0f 100644 --- a/source/extensions/load_balancing_policies/subset/subset_lb_config.cc +++ b/source/extensions/load_balancing_policies/subset/subset_lb_config.cc @@ -48,10 +48,10 @@ SubsetSelector::SubsetSelector(const Protobuf::RepeatedPtrField& se } } -SubsetLoadBalancerConfig::SubsetLoadBalancerConfig(const SubsetLbConfigProto& subset_config, - ProtobufMessage::ValidationVisitor& visitor) +SubsetLoadBalancerConfig::SubsetLoadBalancerConfig( + Upstream::LoadBalancerFactoryContext& lb_factory_context, + const SubsetLbConfigProto& subset_config, ProtobufMessage::ValidationVisitor& visitor) : subset_info_(std::make_unique(subset_config)) { - absl::InlinedVector missing_policies; for (const auto& policy : subset_config.subset_lb_policy().policies()) { @@ -64,7 +64,7 @@ SubsetLoadBalancerConfig::SubsetLoadBalancerConfig(const SubsetLbConfigProto& su Config::Utility::translateOpaqueConfig(policy.typed_extension_config().typed_config(), visitor, *sub_lb_proto_message); - child_lb_config_ = factory->loadConfig(*sub_lb_proto_message, visitor); + child_lb_config_ = factory->loadConfig(lb_factory_context, *sub_lb_proto_message, visitor); child_lb_factory_ = factory; break; } @@ -79,13 +79,14 @@ SubsetLoadBalancerConfig::SubsetLoadBalancerConfig(const SubsetLbConfigProto& su } } -SubsetLoadBalancerConfig::SubsetLoadBalancerConfig(const ClusterProto& cluster, - ProtobufMessage::ValidationVisitor& visitor) +SubsetLoadBalancerConfig::SubsetLoadBalancerConfig( + Upstream::LoadBalancerFactoryContext& lb_factory_context, const ClusterProto& cluster, + ProtobufMessage::ValidationVisitor& visitor) : subset_info_(std::make_unique(cluster.lb_subset_config())) { ASSERT(subset_info_->isEnabled()); - auto sub_lb_pair = - LegacyLbPolicyConfigHelper::getTypedLbConfigFromLegacyProtoWithoutSubset(cluster, visitor); + auto sub_lb_pair = LegacyLbPolicyConfigHelper::getTypedLbConfigFromLegacyProtoWithoutSubset( + lb_factory_context, cluster, visitor); if (!sub_lb_pair.ok()) { throw EnvoyException(std::string(sub_lb_pair.status().message())); } diff --git a/source/extensions/load_balancing_policies/subset/subset_lb_config.h b/source/extensions/load_balancing_policies/subset/subset_lb_config.h index 6199d31a6f1b..86b1deaff036 100644 --- a/source/extensions/load_balancing_policies/subset/subset_lb_config.h +++ b/source/extensions/load_balancing_policies/subset/subset_lb_config.h @@ -209,9 +209,11 @@ using DefaultLoadBalancerSubsetInfo = ConstSingleton class SubsetLoadBalancerConfig : public Upstream::LoadBalancerConfig { public: - SubsetLoadBalancerConfig(const SubsetLbConfigProto& subset_config, + SubsetLoadBalancerConfig(Upstream::LoadBalancerFactoryContext& lb_factory_context, + const SubsetLbConfigProto& subset_config, ProtobufMessage::ValidationVisitor& visitor); - SubsetLoadBalancerConfig(const ClusterProto& cluster, + SubsetLoadBalancerConfig(Upstream::LoadBalancerFactoryContext& lb_factory_context, + const ClusterProto& cluster, ProtobufMessage::ValidationVisitor& visitor); SubsetLoadBalancerConfig(LoadBalancerSubsetInfoPtr subset_info, TypedLoadBalancerFactory* child_factory, diff --git a/test/extensions/load_balancing_policies/least_request/config_test.cc b/test/extensions/load_balancing_policies/least_request/config_test.cc index 078eb4320a23..d249952aeadc 100644 --- a/test/extensions/load_balancing_policies/least_request/config_test.cc +++ b/test/extensions/load_balancing_policies/least_request/config_test.cc @@ -19,6 +19,7 @@ TEST(LeastRequestConfigTest, ValidateFail) { NiceMock cluster_info; NiceMock main_thread_priority_set; NiceMock thread_local_priority_set; + NiceMock lb_factory_context; envoy::config::core::v3::TypedExtensionConfig config; config.set_name("envoy.load_balancing_policies.least_request"); @@ -28,8 +29,8 @@ TEST(LeastRequestConfigTest, ValidateFail) { auto& factory = Config::Utility::getAndCheckFactory(config); EXPECT_EQ("envoy.load_balancing_policies.least_request", factory.name()); - auto lb_config = - factory.loadConfig(*factory.createEmptyConfigProto(), context.messageValidationVisitor()); + auto lb_config = factory.loadConfig(lb_factory_context, *factory.createEmptyConfigProto(), + context.messageValidationVisitor()); auto thread_aware_lb = factory.create(*lb_config, cluster_info, main_thread_priority_set, context.runtime_loader_, context.api_.random_, context.time_system_); diff --git a/test/extensions/load_balancing_policies/maglev/config_test.cc b/test/extensions/load_balancing_policies/maglev/config_test.cc index 4a35ed7f2c46..d061592ad62f 100644 --- a/test/extensions/load_balancing_policies/maglev/config_test.cc +++ b/test/extensions/load_balancing_policies/maglev/config_test.cc @@ -20,6 +20,7 @@ TEST(MaglevConfigTest, Validate) { NiceMock cluster_info; NiceMock main_thread_priority_set; NiceMock thread_local_priority_set; + NiceMock lb_factory_context; { envoy::config::core::v3::TypedExtensionConfig config; @@ -30,8 +31,8 @@ TEST(MaglevConfigTest, Validate) { auto& factory = Config::Utility::getAndCheckFactory(config); EXPECT_EQ("envoy.load_balancing_policies.maglev", factory.name()); - auto lb_config = - factory.loadConfig(*factory.createEmptyConfigProto(), context.messageValidationVisitor()); + auto lb_config = factory.loadConfig(lb_factory_context, *factory.createEmptyConfigProto(), + context.messageValidationVisitor()); auto thread_aware_lb = factory.create(*lb_config, cluster_info, main_thread_priority_set, context.runtime_loader_, context.api_.random_, context.time_system_); @@ -59,7 +60,8 @@ TEST(MaglevConfigTest, Validate) { auto message_ptr = factory.createEmptyConfigProto(); message_ptr->MergeFrom(config_msg); - auto lb_config = factory.loadConfig(*message_ptr, context.messageValidationVisitor()); + auto lb_config = + factory.loadConfig(lb_factory_context, *message_ptr, context.messageValidationVisitor()); EXPECT_THROW_WITH_MESSAGE(factory.create(*lb_config, cluster_info, main_thread_priority_set, context.runtime_loader_, context.api_.random_, diff --git a/test/extensions/load_balancing_policies/random/config_test.cc b/test/extensions/load_balancing_policies/random/config_test.cc index ceba884544c4..b3d30eb9871a 100644 --- a/test/extensions/load_balancing_policies/random/config_test.cc +++ b/test/extensions/load_balancing_policies/random/config_test.cc @@ -19,6 +19,7 @@ TEST(RandomConfigTest, ValidateFail) { NiceMock cluster_info; NiceMock main_thread_priority_set; NiceMock thread_local_priority_set; + NiceMock lb_factory_context; envoy::config::core::v3::TypedExtensionConfig config; config.set_name("envoy.load_balancing_policies.random"); @@ -28,8 +29,8 @@ TEST(RandomConfigTest, ValidateFail) { auto& factory = Config::Utility::getAndCheckFactory(config); EXPECT_EQ("envoy.load_balancing_policies.random", factory.name()); - auto lb_config = - factory.loadConfig(*factory.createEmptyConfigProto(), context.messageValidationVisitor()); + auto lb_config = factory.loadConfig(lb_factory_context, *factory.createEmptyConfigProto(), + context.messageValidationVisitor()); auto thread_aware_lb = factory.create(*lb_config, cluster_info, main_thread_priority_set, context.runtime_loader_, context.api_.random_, context.time_system_); diff --git a/test/extensions/load_balancing_policies/ring_hash/config_test.cc b/test/extensions/load_balancing_policies/ring_hash/config_test.cc index f94ffc01649f..c978ca41872c 100644 --- a/test/extensions/load_balancing_policies/ring_hash/config_test.cc +++ b/test/extensions/load_balancing_policies/ring_hash/config_test.cc @@ -21,6 +21,7 @@ TEST(RingHashConfigTest, Validate) { NiceMock cluster_info; NiceMock main_thread_priority_set; NiceMock thread_local_priority_set; + NiceMock lb_factory_context; { envoy::config::core::v3::TypedExtensionConfig config; @@ -31,8 +32,8 @@ TEST(RingHashConfigTest, Validate) { auto& factory = Config::Utility::getAndCheckFactory(config); EXPECT_EQ("envoy.load_balancing_policies.ring_hash", factory.name()); - auto lb_config = - factory.loadConfig(*factory.createEmptyConfigProto(), context.messageValidationVisitor()); + auto lb_config = factory.loadConfig(lb_factory_context, *factory.createEmptyConfigProto(), + context.messageValidationVisitor()); auto thread_aware_lb = factory.create(*lb_config, cluster_info, main_thread_priority_set, context.runtime_loader_, context.api_.random_, context.time_system_); @@ -64,7 +65,8 @@ TEST(RingHashConfigTest, Validate) { auto message_ptr = factory.createEmptyConfigProto(); message_ptr->MergeFrom(config_msg); - auto lb_config = factory.loadConfig(*message_ptr, context.messageValidationVisitor()); + auto lb_config = + factory.loadConfig(lb_factory_context, *message_ptr, context.messageValidationVisitor()); EXPECT_THROW_WITH_MESSAGE( factory.create(*lb_config, cluster_info, main_thread_priority_set, context.runtime_loader_, diff --git a/test/extensions/load_balancing_policies/round_robin/config_test.cc b/test/extensions/load_balancing_policies/round_robin/config_test.cc index 81a51c2b45ed..d845c71168c2 100644 --- a/test/extensions/load_balancing_policies/round_robin/config_test.cc +++ b/test/extensions/load_balancing_policies/round_robin/config_test.cc @@ -19,6 +19,7 @@ TEST(RoundRobinConfigTest, ValidateFail) { NiceMock cluster_info; NiceMock main_thread_priority_set; NiceMock thread_local_priority_set; + NiceMock lb_factory_context; envoy::config::core::v3::TypedExtensionConfig config; config.set_name("envoy.load_balancing_policies.round_robin"); @@ -28,8 +29,8 @@ TEST(RoundRobinConfigTest, ValidateFail) { auto& factory = Config::Utility::getAndCheckFactory(config); EXPECT_EQ("envoy.load_balancing_policies.round_robin", factory.name()); - auto lb_config = - factory.loadConfig(*factory.createEmptyConfigProto(), context.messageValidationVisitor()); + auto lb_config = factory.loadConfig(lb_factory_context, *factory.createEmptyConfigProto(), + context.messageValidationVisitor()); auto thread_aware_lb = factory.create(*lb_config, cluster_info, main_thread_priority_set, context.runtime_loader_, diff --git a/test/extensions/load_balancing_policies/subset/BUILD b/test/extensions/load_balancing_policies/subset/BUILD index a56bdcdbf460..689deaaf9d21 100644 --- a/test/extensions/load_balancing_policies/subset/BUILD +++ b/test/extensions/load_balancing_policies/subset/BUILD @@ -88,6 +88,7 @@ envoy_extension_cc_benchmark_binary( "//source/extensions/load_balancing_policies/random:config", "//source/extensions/load_balancing_policies/subset:config", "//test/extensions/load_balancing_policies/common:benchmark_base_tester_lib", + "//test/mocks/upstream:load_balancer_mocks", "@envoy_api//envoy/config/cluster/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/load_balancing_policies/random/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/load_balancing_policies/subset/v3:pkg_cc_proto", diff --git a/test/extensions/load_balancing_policies/subset/config_test.cc b/test/extensions/load_balancing_policies/subset/config_test.cc index fa021bc962fc..ef9662a06b57 100644 --- a/test/extensions/load_balancing_policies/subset/config_test.cc +++ b/test/extensions/load_balancing_policies/subset/config_test.cc @@ -20,6 +20,7 @@ TEST(SubsetConfigTest, SubsetConfigTest) { NiceMock cluster_info; NiceMock main_thread_priority_set; NiceMock thread_local_priority_set; + NiceMock lb_factory_context; envoy::config::core::v3::TypedExtensionConfig config; config.set_name("envoy.load_balancing_policies.subset"); @@ -50,7 +51,8 @@ TEST(SubsetConfigTest, SubsetConfigTest) { auto& factory = Config::Utility::getAndCheckFactory(config); EXPECT_EQ("envoy.load_balancing_policies.subset", factory.name()); - auto lb_config = factory.loadConfig(*config_msg, context.messageValidationVisitor()); + auto lb_config = + factory.loadConfig(lb_factory_context, *config_msg, context.messageValidationVisitor()); auto thread_aware_lb = factory.create(*lb_config, cluster_info, main_thread_priority_set, context.runtime_loader_, @@ -71,6 +73,7 @@ TEST(SubsetConfigTest, SubsetConfigTestWithUnknownSubsetLoadBalancingPolicy) { NiceMock cluster_info; NiceMock main_thread_priority_set; NiceMock thread_local_priority_set; + NiceMock lb_factory_context; envoy::config::core::v3::TypedExtensionConfig config; config.set_name("envoy.load_balancing_policies.subset"); @@ -100,7 +103,8 @@ TEST(SubsetConfigTest, SubsetConfigTestWithUnknownSubsetLoadBalancingPolicy) { EXPECT_EQ("envoy.load_balancing_policies.subset", factory.name()); EXPECT_THROW_WITH_MESSAGE( - factory.loadConfig(*config_msg, context.messageValidationVisitor()), EnvoyException, + factory.loadConfig(lb_factory_context, *config_msg, context.messageValidationVisitor()), + EnvoyException, "cluster: didn't find a registered load balancer factory implementation for subset lb with " "names from [envoy.load_balancing_policies.unknown]"); } diff --git a/test/extensions/load_balancing_policies/subset/subset_benchmark.cc b/test/extensions/load_balancing_policies/subset/subset_benchmark.cc index eaeaec500268..1d2d01281c12 100644 --- a/test/extensions/load_balancing_policies/subset/subset_benchmark.cc +++ b/test/extensions/load_balancing_policies/subset/subset_benchmark.cc @@ -16,6 +16,7 @@ #include "test/common/upstream/utility.h" #include "test/extensions/load_balancing_policies/common/benchmark_base_tester.h" #include "test/mocks/upstream/cluster_info.h" +#include "test/mocks/upstream/load_balancer.h" #include "test/test_common/simulated_time_system.h" #include "absl/types/optional.h" @@ -42,9 +43,10 @@ class SubsetLbTester : public Upstream::BaseTester { child_lb->mutable_typed_extension_config()->set_name("envoy.load_balancing_policies.random"); envoy::extensions::load_balancing_policies::random::v3::Random random_lb_config; child_lb->mutable_typed_extension_config()->mutable_typed_config()->PackFrom(random_lb_config); + NiceMock lb_factory_context; subset_config_ = std::make_unique( - subset_config_proto, ProtobufMessage::getStrictValidationVisitor()); + lb_factory_context, subset_config_proto, ProtobufMessage::getStrictValidationVisitor()); lb_ = std::make_unique(*subset_config_, *info_, priority_set_, &local_priority_set_, stats_, stats_scope_, diff --git a/test/extensions/load_balancing_policies/subset/subset_test.cc b/test/extensions/load_balancing_policies/subset/subset_test.cc index adf9b6d01b98..5788ad5e7d44 100644 --- a/test/extensions/load_balancing_policies/subset/subset_test.cc +++ b/test/extensions/load_balancing_policies/subset/subset_test.cc @@ -705,6 +705,7 @@ class SubsetLoadBalancerTest : public Event::TestUsingSimulatedTime, } std::string child_lb_name_{"envoy.load_balancing_policies.round_robin"}; + NiceMock lb_factory_context_; LoadBalancerConfigPtr child_lb_config_; NiceMock priority_set_; MockHostSet& host_set_ = *priority_set_.getMockHostSet(0); @@ -2332,8 +2333,8 @@ TEST_F(SubsetLoadBalancerTest, EnabledLocalityWeightAwareness) { Config::Utility::getFactoryByName(child_lb_name_); envoy::extensions::load_balancing_policies::round_robin::v3::RoundRobin rr_config; rr_config.mutable_locality_lb_config()->mutable_locality_weighted_lb_config(); - child_lb_config_ = - child_factory->loadConfig(rr_config, ProtobufMessage::getStrictValidationVisitor()); + child_lb_config_ = child_factory->loadConfig(lb_factory_context_, rr_config, + ProtobufMessage::getStrictValidationVisitor()); initLbConfigAndLB(); TestLoadBalancerContext context({{"version", "1.1"}}); @@ -2371,8 +2372,8 @@ TEST_F(SubsetLoadBalancerTest, EnabledScaleLocalityWeights) { Config::Utility::getFactoryByName(child_lb_name_); envoy::extensions::load_balancing_policies::round_robin::v3::RoundRobin rr_config; rr_config.mutable_locality_lb_config()->mutable_locality_weighted_lb_config(); - child_lb_config_ = - child_factory->loadConfig(rr_config, ProtobufMessage::getStrictValidationVisitor()); + child_lb_config_ = child_factory->loadConfig(lb_factory_context_, rr_config, + ProtobufMessage::getStrictValidationVisitor()); initLbConfigAndLB(); TestLoadBalancerContext context({{"version", "1.1"}}); @@ -2421,8 +2422,8 @@ TEST_F(SubsetLoadBalancerTest, EnabledScaleLocalityWeightsRounding) { Config::Utility::getFactoryByName(child_lb_name_); envoy::extensions::load_balancing_policies::round_robin::v3::RoundRobin rr_config; rr_config.mutable_locality_lb_config()->mutable_locality_weighted_lb_config(); - child_lb_config_ = - child_factory->loadConfig(rr_config, ProtobufMessage::getStrictValidationVisitor()); + child_lb_config_ = child_factory->loadConfig(lb_factory_context_, rr_config, + ProtobufMessage::getStrictValidationVisitor()); initLbConfigAndLB(); TestLoadBalancerContext context({{"version", "1.0"}}); diff --git a/test/integration/load_balancers/custom_lb_policy.h b/test/integration/load_balancers/custom_lb_policy.h index e7e129852b41..e7992df16153 100644 --- a/test/integration/load_balancers/custom_lb_policy.h +++ b/test/integration/load_balancers/custom_lb_policy.h @@ -73,7 +73,8 @@ class CustomLbFactory : public Upstream::TypedLoadBalancerFactoryBase< return std::make_unique(); } - Upstream::LoadBalancerConfigPtr loadConfig(const Protobuf::Message&, + Upstream::LoadBalancerConfigPtr loadConfig(Upstream::LoadBalancerFactoryContext&, + const Protobuf::Message&, ProtobufMessage::ValidationVisitor&) override { return std::make_unique(); } diff --git a/test/mocks/upstream/load_balancer.h b/test/mocks/upstream/load_balancer.h index 5edbd0d06b73..12cb0b7732fc 100644 --- a/test/mocks/upstream/load_balancer.h +++ b/test/mocks/upstream/load_balancer.h @@ -25,5 +25,13 @@ class MockLoadBalancer : public LoadBalancer { std::shared_ptr host_{new MockHost()}; }; + +class MockLoadBalancerFactoryContext : public LoadBalancerFactoryContext { +public: + MockLoadBalancerFactoryContext() = default; + ~MockLoadBalancerFactoryContext() = default; + MOCK_METHOD(Event::Dispatcher&, mainThreadDispatcher, ()); +}; + } // namespace Upstream } // namespace Envoy diff --git a/test/mocks/upstream/typed_load_balancer_factory.h b/test/mocks/upstream/typed_load_balancer_factory.h index 5efc47d47c3b..9cc82b98266e 100644 --- a/test/mocks/upstream/typed_load_balancer_factory.h +++ b/test/mocks/upstream/typed_load_balancer_factory.h @@ -26,7 +26,7 @@ class MockTypedLoadBalancerFactory : public TypedLoadBalancerFactory { const PrioritySet& priority_set, Runtime::Loader& runtime, Random::RandomGenerator& random, TimeSource& time_source)); - LoadBalancerConfigPtr loadConfig(const Protobuf::Message&, + LoadBalancerConfigPtr loadConfig(Upstream::LoadBalancerFactoryContext&, const Protobuf::Message&, ProtobufMessage::ValidationVisitor&) override { return std::make_unique(); } From 520d88e4cb4e8c5014531281a88dcc8076e18bfd Mon Sep 17 00:00:00 2001 From: "Vikas Choudhary (vikasc)" Date: Mon, 5 Aug 2024 23:21:00 +0530 Subject: [PATCH 033/130] Matchers: Add dynamic metadata to the http inputs (#34891) fixes: https://github.com/envoyproxy/envoy/issues/34092 --------- Signed-off-by: Vikas Choudhary --- CODEOWNERS | 4 + api/BUILD | 1 + .../network/v3/network_inputs.proto | 45 +++++ .../matching/input_matchers/metadata/v3/BUILD | 12 ++ .../input_matchers/metadata/v3/metadata.proto | 26 +++ api/versioning/BUILD | 1 + changelogs/current.yaml | 4 + .../common_messages/common_messages.rst | 1 + .../advanced/matching/matching_api.rst | 1 + envoy/http/filter.h | 3 + source/extensions/extensions_build_config.bzl | 6 + source/extensions/extensions_metadata.yaml | 14 ++ .../filters/http/rbac/rbac_filter.cc | 3 + .../matching/http/metadata_input/BUILD | 22 +++ .../http/metadata_input/meta_input.cc | 20 +++ .../matching/http/metadata_input/meta_input.h | 82 +++++++++ .../matching/input_matchers/metadata/BUILD | 36 ++++ .../input_matchers/metadata/config.cc | 29 ++++ .../matching/input_matchers/metadata/config.h | 33 ++++ .../input_matchers/metadata/matcher.cc | 28 +++ .../input_matchers/metadata/matcher.h | 32 ++++ .../matching/input_matchers/metadata/BUILD | 33 ++++ .../metadata/dyn_meta_matcher_test.cc | 162 ++++++++++++++++++ tools/extensions/extensions_schema.yaml | 1 + 24 files changed, 599 insertions(+) create mode 100644 api/envoy/extensions/matching/input_matchers/metadata/v3/BUILD create mode 100644 api/envoy/extensions/matching/input_matchers/metadata/v3/metadata.proto create mode 100644 source/extensions/matching/http/metadata_input/BUILD create mode 100644 source/extensions/matching/http/metadata_input/meta_input.cc create mode 100644 source/extensions/matching/http/metadata_input/meta_input.h create mode 100644 source/extensions/matching/input_matchers/metadata/BUILD create mode 100644 source/extensions/matching/input_matchers/metadata/config.cc create mode 100644 source/extensions/matching/input_matchers/metadata/config.h create mode 100644 source/extensions/matching/input_matchers/metadata/matcher.cc create mode 100644 source/extensions/matching/input_matchers/metadata/matcher.h create mode 100644 test/extensions/matching/input_matchers/metadata/BUILD create mode 100644 test/extensions/matching/input_matchers/metadata/dyn_meta_matcher_test.cc diff --git a/CODEOWNERS b/CODEOWNERS index 23e833dfa840..68e00305fd21 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -177,12 +177,16 @@ extensions/filters/http/oauth2 @derekargueta @mattklein123 /*/extensions/matching/input_matchers/runtime_fraction @ravenblackx @sergkir85 # CEL input matcher /*/extensions/matching/input_matchers/cel_matcher @tyxia @yanavlasov +# dynamic metadata input matcher +/*/extensions/matching/input_matchers/metadata @vikaschoudhary16 @kyessenov # environment generic input /*/extensions/matching/common_inputs/environment @donyu @mattklein123 # format string matching /*/extensions/matching/actions/format_string @kyessenov @cpakulski # CEL data input /*/extensions/matching/http/cel_input @tyxia @yanavlasov +# dynamic metadata input +/*/extensions/matching/http/metadata_input @vikaschoudhary16 @kyessenov # user space socket pair, event, connection and listener /*/extensions/io_socket/user_space @kyessenov @lambdai @soulxu /*/extensions/bootstrap/internal_listener @kyessenov @adisuissa diff --git a/api/BUILD b/api/BUILD index 7a1db4f427aa..931fbea9c428 100644 --- a/api/BUILD +++ b/api/BUILD @@ -291,6 +291,7 @@ proto_library( "//envoy/extensions/matching/common_inputs/ssl/v3:pkg", "//envoy/extensions/matching/input_matchers/consistent_hashing/v3:pkg", "//envoy/extensions/matching/input_matchers/ip/v3:pkg", + "//envoy/extensions/matching/input_matchers/metadata/v3:pkg", "//envoy/extensions/matching/input_matchers/runtime_fraction/v3:pkg", "//envoy/extensions/network/dns_resolver/apple/v3:pkg", "//envoy/extensions/network/dns_resolver/cares/v3:pkg", diff --git a/api/envoy/extensions/matching/common_inputs/network/v3/network_inputs.proto b/api/envoy/extensions/matching/common_inputs/network/v3/network_inputs.proto index 59756bc0c07b..bea415a7101f 100644 --- a/api/envoy/extensions/matching/common_inputs/network/v3/network_inputs.proto +++ b/api/envoy/extensions/matching/common_inputs/network/v3/network_inputs.proto @@ -103,3 +103,48 @@ message ApplicationProtocolInput { message FilterStateInput { string key = 1 [(validate.rules).string = {min_len: 1}]; } + +// Input that matches dynamic metadata by key. +// DynamicMetadataInput provides a general interface using ``filter`` and ``path`` to retrieve value from +// :ref:`Metadata `. +// +// For example, for the following Metadata: +// +// .. code-block:: yaml +// +// filter_metadata: +// envoy.xxx: +// prop: +// foo: bar +// xyz: +// hello: envoy +// +// The following DynamicMetadataInput will retrieve a string value "bar" from the Metadata. +// +// .. code-block:: yaml +// +// filter: envoy.xxx +// path: +// - key: prop +// - key: foo +// +// [#extension: envoy.matching.inputs.dynamic_metadata] +message DynamicMetadataInput { + // Specifies the segment in a path to retrieve value from Metadata. + // Note: Currently it's not supported to retrieve a value from a list in Metadata. This means that + // if the segment key refers to a list, it has to be the last segment in a path. + message PathSegment { + oneof segment { + option (validate.required) = true; + + // If specified, use the key to retrieve the value in a Struct. + string key = 1 [(validate.rules).string = {min_len: 1}]; + } + } + + // The filter name to retrieve the Struct from the Metadata. + string filter = 1 [(validate.rules).string = {min_len: 1}]; + + // The path to retrieve the Value from the Struct. + repeated PathSegment path = 2 [(validate.rules).repeated = {min_items: 1}]; +} diff --git a/api/envoy/extensions/matching/input_matchers/metadata/v3/BUILD b/api/envoy/extensions/matching/input_matchers/metadata/v3/BUILD new file mode 100644 index 000000000000..bfc486330911 --- /dev/null +++ b/api/envoy/extensions/matching/input_matchers/metadata/v3/BUILD @@ -0,0 +1,12 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/type/matcher/v3:pkg", + "@com_github_cncf_xds//udpa/annotations:pkg", + ], +) diff --git a/api/envoy/extensions/matching/input_matchers/metadata/v3/metadata.proto b/api/envoy/extensions/matching/input_matchers/metadata/v3/metadata.proto new file mode 100644 index 000000000000..19d74fb41bf7 --- /dev/null +++ b/api/envoy/extensions/matching/input_matchers/metadata/v3/metadata.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; + +package envoy.extensions.matching.input_matchers.metadata.v3; + +import "envoy/type/matcher/v3/value.proto"; + +import "udpa/annotations/status.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.matching.input_matchers.metadata.v3"; +option java_outer_classname = "MetadataProto"; +option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/matching/input_matchers/metadata/v3;metadatav3"; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: metadata matcher] +// [#extension: envoy.matching.matchers.metadata_matcher] + +// Metadata matcher for metadata from http matching input data. +message Metadata { + // The Metadata is matched if the value retrieved by metadata matching input is matched to this value. + type.matcher.v3.ValueMatcher value = 1 [(validate.rules).message = {required: true}]; + + // If true, the match result will be inverted. + bool invert = 4; +} diff --git a/api/versioning/BUILD b/api/versioning/BUILD index 96791470d5df..ea5e0db90006 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -230,6 +230,7 @@ proto_library( "//envoy/extensions/matching/common_inputs/ssl/v3:pkg", "//envoy/extensions/matching/input_matchers/consistent_hashing/v3:pkg", "//envoy/extensions/matching/input_matchers/ip/v3:pkg", + "//envoy/extensions/matching/input_matchers/metadata/v3:pkg", "//envoy/extensions/matching/input_matchers/runtime_fraction/v3:pkg", "//envoy/extensions/network/dns_resolver/apple/v3:pkg", "//envoy/extensions/network/dns_resolver/cares/v3:pkg", diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 95b1c2dbf4a9..d2729ce93cd2 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -125,6 +125,10 @@ new_features: ` to allow overriding TLS certificate selection behavior. An extension can select certificate base on the incoming SNI, in both sync and async mode. +- area: matching + change: | + Added dynamic metadata matcher support :ref:`Dynamic metadata input ` + and :ref:`Dynamic metadata input matcher `. - area: ratelimit change: | Added the ability to modify :ref:`hits_addend ` diff --git a/docs/root/api-v3/common_messages/common_messages.rst b/docs/root/api-v3/common_messages/common_messages.rst index a1f3488a86d8..0b5c9032b933 100644 --- a/docs/root/api-v3/common_messages/common_messages.rst +++ b/docs/root/api-v3/common_messages/common_messages.rst @@ -31,6 +31,7 @@ Common messages ../extensions/early_data/v3/default_early_data_policy.proto ../config/core/v3/http_uri.proto ../extensions/matching/input_matchers/ip/v3/ip.proto + ../extensions/matching/input_matchers/metadata/v3/metadata.proto ../extensions/matching/input_matchers/runtime_fraction/v3/runtime_fraction.proto ../config/core/v3/address.proto ../config/core/v3/protocol.proto diff --git a/docs/root/intro/arch_overview/advanced/matching/matching_api.rst b/docs/root/intro/arch_overview/advanced/matching/matching_api.rst index 8876c47814b6..09ac66e2539f 100644 --- a/docs/root/intro/arch_overview/advanced/matching/matching_api.rst +++ b/docs/root/intro/arch_overview/advanced/matching/matching_api.rst @@ -30,6 +30,7 @@ These input functions are available for matching HTTP requests: * :ref:`Response header value `. * :ref:`Response trailer value `. * :ref:`Query parameters value `. +* :ref:`Dynamic metadata `. .. _extension_category_envoy.matching.network.input: diff --git a/envoy/http/filter.h b/envoy/http/filter.h index 49d2609a1152..f9cc5450c87b 100644 --- a/envoy/http/filter.h +++ b/envoy/http/filter.h @@ -1200,6 +1200,9 @@ class HttpMatchingData { virtual const Network::ConnectionInfoProvider& connectionInfoProvider() const PURE; const StreamInfo::FilterState& filterState() const { return streamInfo().filterState(); } + const envoy::config::core::v3::Metadata& metadata() const { + return streamInfo().dynamicMetadata(); + } const Network::Address::Instance& localAddress() const { return *connectionInfoProvider().localAddress(); diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index ed7cb6a0391d..f06cd6fb1034 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -81,6 +81,7 @@ EXTENSIONS = { "envoy.matching.matchers.ip": "//source/extensions/matching/input_matchers/ip:config", "envoy.matching.matchers.runtime_fraction": "//source/extensions/matching/input_matchers/runtime_fraction:config", "envoy.matching.matchers.cel_matcher": "//source/extensions/matching/input_matchers/cel_matcher:config", + "envoy.matching.matchers.metadata_matcher": "//source/extensions/matching/input_matchers/metadata:config", # # Network Matchers @@ -109,6 +110,11 @@ EXTENSIONS = { # "envoy.matching.inputs.cel_data_input": "//source/extensions/matching/http/cel_input:cel_input_lib", + # + # Dynamic Metadata Matching Input + # + "envoy.matching.inputs.dynamic_metadata": "//source/extensions/matching/http/metadata_input:metadata_input_lib", + # # Matching actions # diff --git a/source/extensions/extensions_metadata.yaml b/source/extensions/extensions_metadata.yaml index 483b4294bc84..35893425a135 100644 --- a/source/extensions/extensions_metadata.yaml +++ b/source/extensions/extensions_metadata.yaml @@ -1565,6 +1565,13 @@ envoy.matching.inputs.filter_state: status: stable type_urls: - envoy.extensions.matching.common_inputs.network.v3.FilterStateInput +envoy.matching.inputs.dynamic_metadata: + categories: + - envoy.matching.http.input + security_posture: unknown + status: stable + type_urls: + - envoy.extensions.matching.common_inputs.network.v3.DynamicMetadataInput envoy.matching.inputs.uri_san: categories: - envoy.matching.http.input @@ -1597,6 +1604,13 @@ envoy.matching.custom_matchers.trie_matcher: status: stable type_urls: - xds.type.matcher.v3.IPMatcher +envoy.matching.matchers.metadata_matcher: + categories: + - envoy.matching.input_matchers + security_posture: unknown + status: alpha + type_urls: + - envoy.extensions.matching.input_matchers.metadata.v3.Metadata envoy.load_balancing_policies.least_request: categories: - envoy.load_balancing_policies diff --git a/source/extensions/filters/http/rbac/rbac_filter.cc b/source/extensions/filters/http/rbac/rbac_filter.cc index e5ad66b2c9b9..cefa97f010c4 100644 --- a/source/extensions/filters/http/rbac/rbac_filter.cc +++ b/source/extensions/filters/http/rbac/rbac_filter.cc @@ -47,6 +47,9 @@ absl::Status ActionValidationVisitor::performDataInputValidation( {TypeUtil::descriptorFullNameToTypeUrl( envoy::extensions::matching::common_inputs::ssl::v3::SubjectInput::descriptor() ->full_name())}, + {TypeUtil::descriptorFullNameToTypeUrl(envoy::extensions::matching::common_inputs::network:: + v3::DynamicMetadataInput::descriptor() + ->full_name())}, {TypeUtil::descriptorFullNameToTypeUrl( xds::type::matcher::v3::HttpAttributesCelMatchInput::descriptor()->full_name())}}; if (allowed_inputs_set.contains(type_url)) { diff --git a/source/extensions/matching/http/metadata_input/BUILD b/source/extensions/matching/http/metadata_input/BUILD new file mode 100644 index 000000000000..8415d265c488 --- /dev/null +++ b/source/extensions/matching/http/metadata_input/BUILD @@ -0,0 +1,22 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_extension( + name = "metadata_input_lib", + srcs = ["meta_input.cc"], + hdrs = ["meta_input.h"], + deps = [ + "//envoy/http:filter_interface", + "//envoy/matcher:matcher_interface", + "//envoy/registry", + "//source/common/config:metadata_lib", + "@envoy_api//envoy/extensions/matching/common_inputs/network/v3:pkg_cc_proto", + ], +) diff --git a/source/extensions/matching/http/metadata_input/meta_input.cc b/source/extensions/matching/http/metadata_input/meta_input.cc new file mode 100644 index 000000000000..f5542211b4b7 --- /dev/null +++ b/source/extensions/matching/http/metadata_input/meta_input.cc @@ -0,0 +1,20 @@ +#include "source/extensions/matching/http/metadata_input/meta_input.h" + +#include "envoy/registry/registry.h" + +namespace Envoy { +namespace Extensions { +namespace Matching { +namespace Http { +namespace MetadataInput { + +class HttpDynamicMetadataInputFactory + : public DynamicMetadataInputBaseFactory {}; +REGISTER_FACTORY(HttpDynamicMetadataInputFactory, + Matcher::DataInputFactory); + +} // namespace MetadataInput +} // namespace Http +} // namespace Matching +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/matching/http/metadata_input/meta_input.h b/source/extensions/matching/http/metadata_input/meta_input.h new file mode 100644 index 000000000000..cec7b0253052 --- /dev/null +++ b/source/extensions/matching/http/metadata_input/meta_input.h @@ -0,0 +1,82 @@ +#pragma once + +#include "envoy/extensions/matching/common_inputs/network/v3/network_inputs.pb.h" +#include "envoy/extensions/matching/common_inputs/network/v3/network_inputs.pb.validate.h" +#include "envoy/http/filter.h" +#include "envoy/matcher/matcher.h" + +#include "source/common/config/metadata.h" + +namespace Envoy { +namespace Extensions { +namespace Matching { +namespace Http { +namespace MetadataInput { + +class MetadataMatchData : public ::Envoy::Matcher::CustomMatchData { +public: + explicit MetadataMatchData(const ProtobufWkt::Value& value) : value_(value) {} + const ProtobufWkt::Value& value_; +}; + +template +class DynamicMetadataInput : public Matcher::DataInput { +public: + DynamicMetadataInput( + const envoy::extensions::matching::common_inputs::network::v3::DynamicMetadataInput& + input_config) + : filter_(input_config.filter()), path_(initializePath(input_config.path())) {} + + Matcher::DataInputGetResult get(const MatchingDataType& data) const override { + return {Matcher::DataInputGetResult::DataAvailability::AllDataAvailable, + std::make_unique( + Envoy::Config::Metadata::metadataValue(&data.metadata(), filter_, path_))}; + } + +private: + static std::vector initializePath( + const Protobuf::RepeatedPtrField& segments) { + std::vector path; + for (const auto& seg : segments) { + path.push_back(seg.key()); + } + return path; + } + + const std::string filter_; + const std::vector path_; +}; + +template +class DynamicMetadataInputBaseFactory : public Matcher::DataInputFactory { +public: + std::string name() const override { return "envoy.matching.inputs.dynamic_metadata"; } + + Matcher::DataInputFactoryCb + createDataInputFactoryCb(const Protobuf::Message& message, + ProtobufMessage::ValidationVisitor& validation_visitor) override { + const auto& typed_config = MessageUtil::downcastAndValidate< + const envoy::extensions::matching::common_inputs::network::v3::DynamicMetadataInput&>( + message, validation_visitor); + auto config_ptr = std::make_shared< + envoy::extensions::matching::common_inputs::network::v3::DynamicMetadataInput>( + typed_config); + return [config_ptr] { + return std::make_unique>(*config_ptr); + }; + }; + + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique< + envoy::extensions::matching::common_inputs::network::v3::DynamicMetadataInput>(); + } +}; + +DECLARE_FACTORY(HttpDymanicMetadataInputFactory); + +} // namespace MetadataInput +} // namespace Http +} // namespace Matching +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/matching/input_matchers/metadata/BUILD b/source/extensions/matching/input_matchers/metadata/BUILD new file mode 100644 index 000000000000..91710ef4ab89 --- /dev/null +++ b/source/extensions/matching/input_matchers/metadata/BUILD @@ -0,0 +1,36 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_library( + name = "metadata_lib", + srcs = ["matcher.cc"], + hdrs = ["matcher.h"], + deps = [ + "//envoy/matcher:matcher_interface", + "//source/common/common:matchers_lib", + "//source/extensions/matching/http/metadata_input:metadata_input_lib", + "@envoy_api//envoy/extensions/matching/input_matchers/metadata/v3:pkg_cc_proto", + "@envoy_api//envoy/type/matcher/v3:pkg_cc_proto", + ], +) + +envoy_cc_extension( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + deps = [ + ":metadata_lib", + "//envoy/matcher:matcher_interface", + "//envoy/registry", + "//envoy/server:factory_context_interface", + "@envoy_api//envoy/extensions/matching/input_matchers/metadata/v3:pkg_cc_proto", + ], +) diff --git a/source/extensions/matching/input_matchers/metadata/config.cc b/source/extensions/matching/input_matchers/metadata/config.cc new file mode 100644 index 000000000000..6b78ba7a8e17 --- /dev/null +++ b/source/extensions/matching/input_matchers/metadata/config.cc @@ -0,0 +1,29 @@ +#include "source/extensions/matching/input_matchers/metadata/config.h" + +namespace Envoy { +namespace Extensions { +namespace Matching { +namespace InputMatchers { +namespace Metadata { + +Envoy::Matcher::InputMatcherFactoryCb +Config::createInputMatcherFactoryCb(const Protobuf::Message& config, + Server::Configuration::ServerFactoryContext& factory_context) { + const auto& matcher_config = MessageUtil::downcastAndValidate< + const envoy::extensions::matching::input_matchers::metadata::v3::Metadata&>( + config, factory_context.messageValidationVisitor()); + const auto& value = matcher_config.value(); + const auto value_matcher = Envoy::Matchers::ValueMatcher::create(value, factory_context); + const bool invert = matcher_config.invert(); + return [value_matcher, invert]() { return std::make_unique(value_matcher, invert); }; +} +/** + * Static registration for the Metadata matcher. @see RegisterFactory. + */ +REGISTER_FACTORY(Config, Envoy::Matcher::InputMatcherFactory); + +} // namespace Metadata +} // namespace InputMatchers +} // namespace Matching +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/matching/input_matchers/metadata/config.h b/source/extensions/matching/input_matchers/metadata/config.h new file mode 100644 index 000000000000..6b6207c8bc0f --- /dev/null +++ b/source/extensions/matching/input_matchers/metadata/config.h @@ -0,0 +1,33 @@ +#pragma once + +#include "envoy/extensions/matching/input_matchers/metadata/v3/metadata.pb.h" +#include "envoy/extensions/matching/input_matchers/metadata/v3/metadata.pb.validate.h" +#include "envoy/matcher/matcher.h" +#include "envoy/server/factory_context.h" + +#include "source/common/protobuf/utility.h" +#include "source/extensions/matching/input_matchers/metadata/matcher.h" + +namespace Envoy { +namespace Extensions { +namespace Matching { +namespace InputMatchers { +namespace Metadata { + +class Config : public Envoy::Matcher::InputMatcherFactory { +public: + Envoy::Matcher::InputMatcherFactoryCb createInputMatcherFactoryCb( + const Protobuf::Message& config, + Server::Configuration::ServerFactoryContext& factory_context) override; + + std::string name() const override { return "envoy.matching.matchers.metadata_matcher"; } + + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique(); + } +}; +} // namespace Metadata +} // namespace InputMatchers +} // namespace Matching +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/matching/input_matchers/metadata/matcher.cc b/source/extensions/matching/input_matchers/metadata/matcher.cc new file mode 100644 index 000000000000..6ede36a3f174 --- /dev/null +++ b/source/extensions/matching/input_matchers/metadata/matcher.cc @@ -0,0 +1,28 @@ +#include "source/extensions/matching/input_matchers/metadata/matcher.h" + +namespace Envoy { +namespace Extensions { +namespace Matching { +namespace InputMatchers { +namespace Metadata { + +Matcher::Matcher(const Envoy::Matchers::ValueMatcherConstSharedPtr value_matcher, const bool invert) + : value_matcher_(value_matcher), invert_(invert) {} + +bool Matcher::match(const Envoy::Matcher::MatchingDataType& input) { + if (auto* ptr = absl::get_if>(&input); + ptr != nullptr) { + const Matching::Http::MetadataInput::MetadataMatchData* match_data = + dynamic_cast(ptr->get()); + if (match_data != nullptr) { + return value_matcher_->match(match_data->value_) ^ invert_; + } + } + return false; +} + +} // namespace Metadata +} // namespace InputMatchers +} // namespace Matching +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/matching/input_matchers/metadata/matcher.h b/source/extensions/matching/input_matchers/metadata/matcher.h new file mode 100644 index 000000000000..e6e8a356c7b1 --- /dev/null +++ b/source/extensions/matching/input_matchers/metadata/matcher.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "envoy/extensions/matching/input_matchers/metadata/v3/metadata.pb.h" +#include "envoy/matcher/matcher.h" +#include "envoy/type/matcher/v3/value.pb.h" + +#include "source/common/common/matchers.h" +#include "source/extensions/matching/http/metadata_input/meta_input.h" + +namespace Envoy { +namespace Extensions { +namespace Matching { +namespace InputMatchers { +namespace Metadata { + +class Matcher : public Envoy::Matcher::InputMatcher, Logger::Loggable { +public: + Matcher(const Envoy::Matchers::ValueMatcherConstSharedPtr, const bool); + bool match(const Envoy::Matcher::MatchingDataType& input) override; + +private: + Envoy::Matchers::ValueMatcherConstSharedPtr value_matcher_; + bool invert_; +}; + +} // namespace Metadata +} // namespace InputMatchers +} // namespace Matching +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/matching/input_matchers/metadata/BUILD b/test/extensions/matching/input_matchers/metadata/BUILD new file mode 100644 index 000000000000..e77588a746a2 --- /dev/null +++ b/test/extensions/matching/input_matchers/metadata/BUILD @@ -0,0 +1,33 @@ +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 = "dyn_meta_matcher_test", + srcs = ["dyn_meta_matcher_test.cc"], + extension_names = ["envoy.matching.matchers.metadata_matcher"], + deps = [ + "//source/common/matcher:matcher_lib", + "//source/extensions/matching/http/metadata_input:metadata_input_lib", + "//source/extensions/matching/input_matchers/metadata:config", + "//source/extensions/matching/input_matchers/metadata:metadata_lib", + "//test/common/matcher:test_utility_lib", + "//test/mocks/http:http_mocks", + "//test/mocks/matcher:matcher_mocks", + "//test/mocks/server:factory_context_mocks", + "//test/mocks/stream_info:stream_info_mocks", + "//test/test_common:registry_lib", + "@com_github_cncf_xds//xds/type/matcher/v3:pkg_cc_proto", + "@envoy_api//envoy/config/common/matcher/v3:pkg_cc_proto", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", + ], +) diff --git a/test/extensions/matching/input_matchers/metadata/dyn_meta_matcher_test.cc b/test/extensions/matching/input_matchers/metadata/dyn_meta_matcher_test.cc new file mode 100644 index 000000000000..58df2f51e16b --- /dev/null +++ b/test/extensions/matching/input_matchers/metadata/dyn_meta_matcher_test.cc @@ -0,0 +1,162 @@ +#include +#include + +#include "envoy/config/common/matcher/v3/matcher.pb.validate.h" +#include "envoy/config/core/v3/extension.pb.h" +#include "envoy/matcher/matcher.h" +#include "envoy/registry/registry.h" + +#include "source/common/matcher/matcher.h" +#include "source/common/protobuf/utility.h" +#include "source/extensions/matching/http/metadata_input/meta_input.h" +#include "source/extensions/matching/input_matchers/metadata/config.h" +#include "source/extensions/matching/input_matchers/metadata/matcher.h" + +#include "test/common/matcher/test_utility.h" +#include "test/mocks/matcher/mocks.h" +#include "test/mocks/server/factory_context.h" +#include "test/test_common/registry.h" +#include "test/test_common/utility.h" + +#include "gtest/gtest.h" +#include "xds/type/matcher/v3/matcher.pb.validate.h" + +namespace Envoy { +namespace Extensions { +namespace Matching { +namespace InputMatchers { +namespace Metadata { + +constexpr absl::string_view kFilterNamespace = "meta_matcher"; +constexpr absl::string_view kMetadataKey = "service_name"; +constexpr absl::string_view kMetadataValue = "test_service"; + +class MetadataMatcherTest : public ::testing::Test { +public: + MetadataMatcherTest() + : inject_action_(action_factory_), + data_(Envoy::Http::Matching::HttpMatchingDataImpl(stream_info_)) {} + + ::Envoy::Matcher::MatchTreeSharedPtr + buildMatcherTree(bool invert = false) { + envoy::extensions::matching::input_matchers::metadata::v3::Metadata meta_matcher; + meta_matcher.mutable_value()->mutable_string_match()->set_exact(kMetadataValue); + meta_matcher.set_invert(invert); + + xds::type::matcher::v3::Matcher matcher; + auto* inner_matcher = matcher.mutable_matcher_list()->add_matchers(); + auto* single_predicate = inner_matcher->mutable_predicate()->mutable_single_predicate(); + + envoy::extensions::matching::common_inputs::network::v3::DynamicMetadataInput metadata_input; + metadata_input.set_filter(kFilterNamespace); + metadata_input.mutable_path()->Add()->set_key(kMetadataKey); + single_predicate->mutable_input()->set_name("envoy.matching.inputs.dynamic_metadata"); + single_predicate->mutable_input()->mutable_typed_config()->PackFrom(metadata_input); + + auto* custom_matcher = single_predicate->mutable_custom_match(); + custom_matcher->mutable_typed_config()->PackFrom(meta_matcher); + + xds::type::matcher::v3::Matcher::OnMatch on_match; + std::string on_match_config = R"EOF( + action: + name: test_action + typed_config: + "@type": type.googleapis.com/google.protobuf.StringValue + value: match!! + )EOF"; + MessageUtil::loadFromYaml(on_match_config, on_match, + ProtobufMessage::getStrictValidationVisitor()); + + inner_matcher->mutable_on_match()->MergeFrom(on_match); + + auto string_factory_on_match = ::Envoy::Matcher::TestDataInputStringFactory("value"); + + ::Envoy::Matcher::MockMatchTreeValidationVisitor + validation_visitor; + EXPECT_CALL(validation_visitor, + performDataInputValidation( + _, "type.googleapis.com/" + "envoy.extensions.matching.common_inputs.network.v3.DynamicMetadataInput")); + ::Envoy::Matcher::MatchTreeFactory + matcher_factory(context_, factory_context_, validation_visitor); + auto match_tree = matcher_factory.create(matcher); + + return match_tree(); + } + + void setDynamicMetadata(const std::string& namespace_str, const std::string& metadata_key, + const std::string& metadata_value) { + Envoy::Config::Metadata::mutableMetadataValue(stream_info_.metadata_, namespace_str, + metadata_key) + .set_string_value(metadata_value); + } + + ::Envoy::Matcher::StringActionFactory action_factory_; + Registry::InjectFactory> inject_action_; + testing::NiceMock stream_info_; + absl::string_view context_ = ""; + testing::NiceMock factory_context_; + envoy::config::core::v3::Metadata metadata_; + + Envoy::Http::Matching::HttpMatchingDataImpl data_; +}; + +TEST_F(MetadataMatcherTest, DynamicMetadataMatched) { + setDynamicMetadata(std::string(kFilterNamespace), std::string(kMetadataKey), + std::string(kMetadataValue)); + Envoy::Http::Matching::HttpMatchingDataImpl data = + Envoy::Http::Matching::HttpMatchingDataImpl(stream_info_); + auto matcher_tree = buildMatcherTree(); + const auto result = matcher_tree->match(data_); + // The match was complete, match found. + EXPECT_EQ(result.match_state_, ::Envoy::Matcher::MatchState::MatchComplete); + EXPECT_TRUE(result.on_match_.has_value()); + EXPECT_NE(result.on_match_->action_cb_, nullptr); +} + +TEST_F(MetadataMatcherTest, DynamicMetadataNotMatched) { + setDynamicMetadata(std::string(kFilterNamespace), std::string(kMetadataKey), "wrong_service"); + Envoy::Http::Matching::HttpMatchingDataImpl data = + Envoy::Http::Matching::HttpMatchingDataImpl(stream_info_); + auto matcher_tree = buildMatcherTree(); + + const auto result = matcher_tree->match(data_); + // The match was completed, no match found. + EXPECT_EQ(result.match_state_, ::Envoy::Matcher::MatchState::MatchComplete); + EXPECT_EQ(result.on_match_, absl::nullopt); +} + +TEST_F(MetadataMatcherTest, DynamicMetadataNotMatchedWithInvert) { + setDynamicMetadata(std::string(kFilterNamespace), std::string(kMetadataKey), "wrong_service"); + Envoy::Http::Matching::HttpMatchingDataImpl data = + Envoy::Http::Matching::HttpMatchingDataImpl(stream_info_); + auto matcher_tree = buildMatcherTree(true); + + const auto result = matcher_tree->match(data_); + // The match was completed, no match found but the invert flag was set. + EXPECT_EQ(result.match_state_, ::Envoy::Matcher::MatchState::MatchComplete); + EXPECT_TRUE(result.on_match_.has_value()); + EXPECT_NE(result.on_match_->action_cb_, nullptr); +} + +TEST_F(MetadataMatcherTest, BadData) { + envoy::extensions::matching::input_matchers::metadata::v3::Metadata meta_matcher; + meta_matcher.mutable_value()->mutable_string_match()->set_exact(kMetadataValue); + const auto& matcher_config = MessageUtil::downcastAndValidate< + const envoy::extensions::matching::input_matchers::metadata::v3::Metadata&>( + meta_matcher, factory_context_.messageValidationVisitor()); + const auto& v = matcher_config.value(); + auto value_matcher = Envoy::Matchers::ValueMatcher::create(v, factory_context_); + + ::Envoy::Matcher::MatchingDataType data = absl::monostate(); + EXPECT_NO_THROW(Matcher(value_matcher, false).match(data)); + + ::Envoy::Matcher::MatchingDataType data2 = std::string("test"); + EXPECT_NO_THROW(Matcher(value_matcher, false).match(data2)); +} + +} // namespace Metadata +} // namespace InputMatchers +} // namespace Matching +} // namespace Extensions +} // namespace Envoy diff --git a/tools/extensions/extensions_schema.yaml b/tools/extensions/extensions_schema.yaml index c1e9f4ffdb1d..c8f589a5fa67 100644 --- a/tools/extensions/extensions_schema.yaml +++ b/tools/extensions/extensions_schema.yaml @@ -22,6 +22,7 @@ builtin: - envoy.matching.inputs.transport_protocol - envoy.matching.inputs.application_protocol - envoy.matching.inputs.filter_state +- envoy.matching.inputs.dynamic_metadata - envoy.matching.inputs.uri_san - envoy.matching.inputs.dns_san - envoy.matching.inputs.subject From 3e7cc5aba2075d6f3a78b75df5d0157a7dd09377 Mon Sep 17 00:00:00 2001 From: Raven Black Date: Mon, 5 Aug 2024 15:58:29 -0400 Subject: [PATCH 034/130] Make incompatible command-line args an error (#35570) Commit Message: Make incompatible command-line args an error Additional Description: Per #35073, `--enable-fine-grain-logging` and `--component-log-level` don't make sense at the same time, one of them will be overridden by the other in a way that can be surprising for the user. This PR makes it an error to apply both, so the user is not surprised by not getting the logs they expected at runtime. Risk Level: Small. May make existing command-lines with ineffective options fail. Testing: Added coverage. Docs Changes: Updated command-line options docs. Release Notes: Yes, under minor behavior changes. Platform Specific Features: n/a --------- Signed-off-by: Raven Black --- changelogs/current.yaml | 4 ++++ docs/root/operations/cli.rst | 3 ++- source/server/options_impl.cc | 5 +++++ test/server/options_impl_test.cc | 15 +++++++++++++-- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index d2729ce93cd2..df8538bad5e4 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -13,6 +13,10 @@ behavior_changes: minor_behavior_changes: # *Changes that may cause incompatibilities for some users, but should not for most* +- area: command line options + change: | + :option:`--enable-fine-grain-logging` and :option:`--component-log-level` were incompatible in that one + would make the other ineffective. Setting both options at once is now an error, to reduce potential confusion. - area: tcp change: | Added support for :ref:`connection_pool_per_downstream_connection diff --git a/docs/root/operations/cli.rst b/docs/root/operations/cli.rst index cd33505380dc..aeb70858a233 100644 --- a/docs/root/operations/cli.rst +++ b/docs/root/operations/cli.rst @@ -109,6 +109,7 @@ following are the command line options that Envoy supports. never set this option. For example, if you want ``upstream`` component to run at ``debug`` level and ``connection`` component to run at ``trace`` level, you should pass ``upstream:debug,connection:trace`` to this flag. See ``ALL_LOGGER_IDS`` in :repo:`/source/common/common/logger.h` for a list of components. + This option is incompatible with :option:`--enable-fine-grain-logging`. .. option:: --cpuset-threads @@ -193,7 +194,7 @@ following are the command line options that Envoy supports. interface. If enabled, main log macros including ``ENVOY_LOG``, ``ENVOY_CONN_LOG``, ``ENVOY_STREAM_LOG`` and ``ENVOY_FLUSH_LOG`` will use a per-file logger, and the usage doesn't need ``Envoy::Logger::Loggable`` any more. The administration interface usage is similar. Please see :ref:`Administration interface - ` for more detail. + ` for more detail. This option is incompatible with :option:`--component-log-level`. .. option:: --socket-path diff --git a/source/server/options_impl.cc b/source/server/options_impl.cc index eaa6005bb260..b72e6237249f 100644 --- a/source/server/options_impl.cc +++ b/source/server/options_impl.cc @@ -214,7 +214,12 @@ OptionsImpl::OptionsImpl(std::vector args, log_format_ = log_format.getValue(); log_format_set_ = log_format.isSet(); log_format_escaped_ = log_format_escaped.getValue(); + enable_fine_grain_logging_ = enable_fine_grain_logging.getValue(); + if (enable_fine_grain_logging_ && !component_log_level.getValue().empty()) { + throw MalformedArgvException( + "error: --component-log-level will not work with --enable-fine-grain-logging"); + } parseComponentLogLevels(component_log_level.getValue()); diff --git a/test/server/options_impl_test.cc b/test/server/options_impl_test.cc index 732ab9f03ed9..b03fdb15fee3 100644 --- a/test/server/options_impl_test.cc +++ b/test/server/options_impl_test.cc @@ -61,6 +61,13 @@ TEST_F(OptionsImplTest, InvalidMode) { EXPECT_THROW_WITH_REGEX(createOptionsImpl("envoy --mode bogus"), MalformedArgvException, "bogus"); } +TEST_F(OptionsImplTest, InvalidComponentLogLevelWithFineGrainLogging) { + EXPECT_THROW_WITH_REGEX( + createOptionsImpl("envoy --enable-fine-grain-logging --component-log-level http:info"), + MalformedArgvException, + "--component-log-level will not work with --enable-fine-grain-logging"); +} + TEST_F(OptionsImplTest, InvalidCommandLine) { EXPECT_THROW_WITH_REGEX(createOptionsImpl("envoy --blah"), MalformedArgvException, "Couldn't find match for argument"); @@ -98,7 +105,7 @@ TEST_F(OptionsImplTest, All) { "--file-flush-interval-msec 9000 " "--skip-hot-restart-on-no-parent " "--skip-hot-restart-parent-stats " - "--drain-time-s 60 --log-format [%v] --enable-fine-grain-logging --parent-shutdown-time-s 90 " + "--drain-time-s 60 --log-format [%v] --parent-shutdown-time-s 90 " "--log-path " "/foo/bar " "--disable-hot-restart --cpuset-threads --allow-unknown-static-fields " @@ -119,7 +126,7 @@ TEST_F(OptionsImplTest, All) { EXPECT_TRUE(options->skipHotRestartParentStats()); EXPECT_TRUE(options->skipHotRestartOnNoParent()); EXPECT_EQ("/foo/bar", options->logPath()); - EXPECT_EQ(true, options->enableFineGrainLogging()); + EXPECT_EQ(false, options->enableFineGrainLogging()); EXPECT_EQ("cluster", options->serviceClusterName()); EXPECT_EQ("node", options->serviceNodeName()); EXPECT_EQ("zone", options->serviceZone()); @@ -139,6 +146,10 @@ TEST_F(OptionsImplTest, All) { options = createOptionsImpl("envoy --mode init_only"); EXPECT_EQ(Server::Mode::InitOnly, options->mode()); + + // Tested separately because it's mutually exclusive with --component-log-level. + options = createOptionsImpl("envoy --enable-fine-grain-logging"); + EXPECT_EQ(true, options->enableFineGrainLogging()); } // Either variants of allow-unknown-[static-]-fields works. From 8981cf3a3eb835f35aed39e0c3f67613ca152174 Mon Sep 17 00:00:00 2001 From: danzh Date: Mon, 5 Aug 2024 21:22:15 -0400 Subject: [PATCH 035/130] quic: deprecate `envoy.reloadable_features.quic_fix_filter_manager_uaf` (#35569) Commit Message: deprecate `envoy.reloadable_features.quic_fix_filter_manager_uaf` and update change log Risk Level: low Testing: existing tests Docs Changes: N/A Release Notes: Yes Platform Specific Features: N/A Fixes #35373 --------- Signed-off-by: Dan Zhang Co-authored-by: Dan Zhang --- changelogs/current.yaml | 3 +++ source/common/quic/envoy_quic_dispatcher.cc | 15 ++++----------- .../quic/quic_filter_manager_connection_impl.cc | 5 ----- .../quic/quic_filter_manager_connection_impl.h | 3 --- source/common/runtime/runtime_features.cc | 1 - test/integration/overload_integration_test.cc | 1 - 6 files changed, 7 insertions(+), 21 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index df8538bad5e4..f0ff083bdd3d 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -88,6 +88,9 @@ removed_config_or_runtime: - area: stateful_session change: | Removed ``envoy.reloadable_features.stateful_session_encode_ttl_in_cookie`` runtime flag and legacy code paths. +- area: quic + change: | + Removed ``envoy.reloadable_features.quic_fix_filter_manager_uaf`` runtime flag and legacy code paths. - area: udp change: | Removed ``envoy.restart_features.udp_read_normalize_addresses`` runtime flag and legacy code paths. diff --git a/source/common/quic/envoy_quic_dispatcher.cc b/source/common/quic/envoy_quic_dispatcher.cc index eb295704956d..b7b5ac42ebde 100644 --- a/source/common/quic/envoy_quic_dispatcher.cc +++ b/source/common/quic/envoy_quic_dispatcher.cc @@ -181,24 +181,17 @@ void EnvoyQuicDispatcher::closeConnectionsWithFilterChain( // Retain the number of connections in the list early because closing the connection will change // the size. const size_t num_connections = connections.size(); - bool delete_sessions_immediately = false; for (size_t i = 0; i < num_connections; ++i) { Network::Connection& connection = connections.front().get(); // This will remove the connection from the list. And the last removal will remove connections // from the map as well. connection.close(Network::ConnectionCloseType::NoFlush); - if (!delete_sessions_immediately && - dynamic_cast(connection).fixQuicLifetimeIssues()) { - // If `envoy.reloadable_features.quic_fix_filter_manager_uaf` is true, the closed sessions - // need to be deleted right away to consistently handle quic lifetimes. Because upon - // returning the filter chain configs will be destroyed, and no longer safe to be accessed. - // If any filters access those configs during destruction, it'll be use-after-free - delete_sessions_immediately = true; - } } ASSERT(connections_by_filter_chain_.find(filter_chain) == connections_by_filter_chain_.end()); - if (delete_sessions_immediately) { - // Explicitly destroy closed sessions in the current call stack. + if (num_connections > 0) { + // Explicitly destroy closed sessions in the current call stack. Because upon + // returning the filter chain configs will be destroyed, and no longer safe to be accessed. + // If any filters access those configs during destruction, it'll be use-after-free DeleteSessions(); } } diff --git a/source/common/quic/quic_filter_manager_connection_impl.cc b/source/common/quic/quic_filter_manager_connection_impl.cc index c616affb15b2..24d837c6116c 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.cc +++ b/source/common/quic/quic_filter_manager_connection_impl.cc @@ -28,8 +28,6 @@ QuicFilterManagerConnectionImpl::QuicFilterManagerConnectionImpl( network_connection_->connectionSocket()->connectionInfoProvider().setConnectionID(id()); network_connection_->connectionSocket()->connectionInfoProvider().setSslConnection( Ssl::ConnectionInfoConstSharedPtr(quic_ssl_info_)); - fix_quic_lifetime_issues_ = - Runtime::runtimeFeatureEnabled("envoy.reloadable_features.quic_fix_filter_manager_uaf"); } void QuicFilterManagerConnectionImpl::addWriteFilter(Network::WriteFilterSharedPtr filter) { @@ -182,9 +180,6 @@ void QuicFilterManagerConnectionImpl::onConnectionCloseEvent( network_connection_ = nullptr; } - if (!fix_quic_lifetime_issues_) { - filter_manager_ = nullptr; - } if (!codec_stats_.has_value()) { // The connection was closed before it could be used. Stats are not recorded. return; diff --git a/source/common/quic/quic_filter_manager_connection_impl.h b/source/common/quic/quic_filter_manager_connection_impl.h index 432f2779673b..7e366650a935 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.h +++ b/source/common/quic/quic_filter_manager_connection_impl.h @@ -171,8 +171,6 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, max_headers_count_ = max_headers_count; } - bool fixQuicLifetimeIssues() const { return fix_quic_lifetime_issues_; } - protected: // Propagate connection close to network_connection_callbacks_. void onConnectionCloseEvent(const quic::QuicConnectionCloseFrame& frame, @@ -226,7 +224,6 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, EnvoyQuicSimulatedWatermarkBuffer write_buffer_watermark_simulation_; Buffer::OwnedImpl empty_buffer_; absl::optional close_type_during_initialize_; - bool fix_quic_lifetime_issues_{false}; }; } // namespace Quic diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index a83c9e56ba00..57c8c7b90657 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -73,7 +73,6 @@ RUNTIME_GUARD(envoy_reloadable_features_prefer_ipv6_dns_on_macos); RUNTIME_GUARD(envoy_reloadable_features_process_ext_authz_grpc_error_codes_as_errors); RUNTIME_GUARD(envoy_reloadable_features_proxy_104); RUNTIME_GUARD(envoy_reloadable_features_proxy_status_mapping_more_core_response_flags); -RUNTIME_GUARD(envoy_reloadable_features_quic_fix_filter_manager_uaf); RUNTIME_GUARD(envoy_reloadable_features_quic_receive_ecn); // Ignore the automated "remove this flag" issue: we should keep this for 1 year. Confirm with // @danzh2010 or @RyanTheOptimist before removing. diff --git a/test/integration/overload_integration_test.cc b/test/integration/overload_integration_test.cc index b4faa482411e..d3a62cdb2ff9 100644 --- a/test/integration/overload_integration_test.cc +++ b/test/integration/overload_integration_test.cc @@ -381,7 +381,6 @@ TEST_P(OverloadScaledTimerIntegrationTest, HTTP3CloseIdleHttpConnectionsDuringHa return; } TestScopedRuntime scoped_runtime; - scoped_runtime.mergeValues({{"envoy.reloadable_features.quic_fix_filter_manager_uaf", "true"}}); config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { auto* proof_source_config = bootstrap.mutable_static_resources() From 9bf86b30a68110c28a1b9402e9cb189d596d91de Mon Sep 17 00:00:00 2001 From: phlax Date: Tue, 6 Aug 2024 08:40:38 +0100 Subject: [PATCH 036/130] vpp/build: Minor fix for cmake build (#35583) There are multiple files named config.h and in some build environments it causes the build postscript to fail Commit Message: Additional Description: Risk Level: Testing: Docs Changes: Release Notes: Platform Specific Features: [Optional Runtime guard:] [Optional Fixes #Issue] [Optional Fixes commit #PR or SHA] [Optional Deprecated:] [Optional [API Considerations](https://github.com/envoyproxy/envoy/blob/main/api/review_checklist.md):] Signed-off-by: Ryan Northey --- contrib/vcl/source/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/vcl/source/BUILD b/contrib/vcl/source/BUILD index ee8caf6184b9..b8b3de1f7044 100644 --- a/contrib/vcl/source/BUILD +++ b/contrib/vcl/source/BUILD @@ -57,7 +57,7 @@ envoy_cmake( postfix_script = """ mkdir -p $INSTALLDIR/lib/external $INSTALLDIR/include/external \ && find . -name "*.a" | xargs -I{} cp -a {} $INSTALLDIR/lib/ \ - && find . -name "*.h" | xargs -I{} cp -a {} $INSTALLDIR/include + && find . -name "*.h" ! -name config.h | xargs -I{} cp -a {} $INSTALLDIR/include """, tags = [ "cpu:16", From ee7991d50eb146c89fe801e4cd9a45e311bbbf0a Mon Sep 17 00:00:00 2001 From: Tianyu <72890320+tyxia@users.noreply.github.com> Date: Tue, 6 Aug 2024 09:20:11 -0400 Subject: [PATCH 037/130] perf: Only buffer the body when retry is enabled. (#35515) I noticed this during perf/load test. We should only buffer the data for retry when retry is enabled (i.e., retry policy is configured) **Before fix:** ![6pMhgppUZ994Y7Z](https://github.com/user-attachments/assets/23b61fad-9dfb-4ce1-aab7-b42bca5e04a2) **After fix:** ![6Y9T4tbsBP2uYpu](https://github.com/user-attachments/assets/b6ecbb40-8336-4fb6-a680-2019ff2fbc9d) Risk Level: Low, Testing: Existing tests cover this change. Retry test will fail if i force retry buffering off. Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A Signed-off-by: tyxia --- source/extensions/filters/http/ext_proc/ext_proc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/extensions/filters/http/ext_proc/ext_proc.cc b/source/extensions/filters/http/ext_proc/ext_proc.cc index 95d551f42cb3..a03923519754 100644 --- a/source/extensions/filters/http/ext_proc/ext_proc.cc +++ b/source/extensions/filters/http/ext_proc/ext_proc.cc @@ -344,7 +344,7 @@ Filter::StreamOpenState Filter::openStream() { auto options = Http::AsyncClient::StreamOptions() .setParentSpan(decoder_callbacks_->activeSpan()) .setParentContext(grpc_context) - .setBufferBodyForRetry(true); + .setBufferBodyForRetry(grpc_service_.has_retry_policy()); ExternalProcessorStreamPtr stream_object = client_->start(*this, config_with_hash_key_, options, watermark_callbacks_); From dae96ec2b7d71be2b35cb78ddc99335beee7200a Mon Sep 17 00:00:00 2001 From: Rama Chavali Date: Tue, 6 Aug 2024 19:45:20 +0530 Subject: [PATCH 038/130] downgrade http2visitor logs to trace (#35578) Commit Message: downgrade http2visitor logs to trace Additional Description: in debug mode, these logs create lot of noise Risk Level: N/A Testing: Docs Changes: Release Notes: Platform Specific Features: [Optional Runtime guard:] [Optional Fixes #Issue] [Optional Fixes commit #PR or SHA] [Optional Deprecated:] [Optional [API Considerations](https://github.com/envoyproxy/envoy/blob/main/api/review_checklist.md):] Signed-off-by: Rama Chavali --- source/common/http/http2/codec_impl.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/source/common/http/http2/codec_impl.cc b/source/common/http/http2/codec_impl.cc index e856dcc7f00f..9171d60a8865 100644 --- a/source/common/http/http2/codec_impl.cc +++ b/source/common/http/http2/codec_impl.cc @@ -1799,7 +1799,7 @@ bool ConnectionImpl::Http2Visitor::SendDataFrame(Http2StreamId stream_id, bool ConnectionImpl::Http2Visitor::OnFrameHeader(Http2StreamId stream_id, size_t length, uint8_t type, uint8_t flags) { - ENVOY_CONN_LOG(debug, "Http2Visitor::OnFrameHeader({}, {}, {}, {})", connection_->connection_, + ENVOY_CONN_LOG(trace, "Http2Visitor::OnFrameHeader({}, {}, {}, {})", connection_->connection_, stream_id, length, int(type), int(flags)); if (type == OGHTTP2_CONTINUATION_FRAME_TYPE) { @@ -1843,7 +1843,7 @@ ConnectionImpl::Http2Visitor::OnHeaderForStream(Http2StreamId stream_id, } bool ConnectionImpl::Http2Visitor::OnEndHeadersForStream(Http2StreamId stream_id) { - ENVOY_CONN_LOG(debug, "Http2Visitor::OnEndHeadersForStream({})", connection_->connection_, + ENVOY_CONN_LOG(trace, "Http2Visitor::OnEndHeadersForStream({})", connection_->connection_, stream_id); Status status = connection_->onHeaders(stream_id, current_frame_.length, current_frame_.flags); return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status)); @@ -1851,12 +1851,12 @@ bool ConnectionImpl::Http2Visitor::OnEndHeadersForStream(Http2StreamId stream_id bool ConnectionImpl::Http2Visitor::OnBeginDataForStream(Http2StreamId stream_id, size_t payload_length) { - ENVOY_CONN_LOG(debug, "Http2Visitor::OnBeginDataForStream({}, {})", connection_->connection_, + ENVOY_CONN_LOG(trace, "Http2Visitor::OnBeginDataForStream({}, {})", connection_->connection_, stream_id, payload_length); remaining_data_payload_ = payload_length; padding_length_ = 0; if (remaining_data_payload_ == 0 && (current_frame_.flags & FLAG_END_STREAM) == 0) { - ENVOY_CONN_LOG(debug, "Http2Visitor dispatching DATA for stream {}", connection_->connection_, + ENVOY_CONN_LOG(trace, "Http2Visitor dispatching DATA for stream {}", connection_->connection_, stream_id); Status status = connection_->onBeginData(stream_id, current_frame_.length, current_frame_.flags, padding_length_); @@ -1873,13 +1873,13 @@ bool ConnectionImpl::Http2Visitor::OnDataPaddingLength(Http2StreamId stream_id, padding_length_ = padding_length; remaining_data_payload_ -= padding_length; if (remaining_data_payload_ == 0 && (current_frame_.flags & FLAG_END_STREAM) == 0) { - ENVOY_CONN_LOG(debug, "Http2Visitor dispatching DATA for stream {}", connection_->connection_, + ENVOY_CONN_LOG(trace, "Http2Visitor dispatching DATA for stream {}", connection_->connection_, stream_id); Status status = connection_->onBeginData(stream_id, current_frame_.length, current_frame_.flags, padding_length_); return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status)); } - ENVOY_CONN_LOG(debug, "Http2Visitor: remaining data payload: {}, end_stream: {}", + ENVOY_CONN_LOG(trace, "Http2Visitor: remaining data payload: {}, end_stream: {}", connection_->connection_, remaining_data_payload_, bool(current_frame_.flags & FLAG_END_STREAM)); return true; @@ -1892,24 +1892,24 @@ bool ConnectionImpl::Http2Visitor::OnDataForStream(Http2StreamId stream_id, remaining_data_payload_ -= data.size(); if (result == 0 && remaining_data_payload_ == 0 && (current_frame_.flags & FLAG_END_STREAM) == 0) { - ENVOY_CONN_LOG(debug, "Http2Visitor dispatching DATA for stream {}", connection_->connection_, + ENVOY_CONN_LOG(trace, "Http2Visitor dispatching DATA for stream {}", connection_->connection_, stream_id); Status status = connection_->onBeginData(stream_id, current_frame_.length, current_frame_.flags, padding_length_); return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status)); } - ENVOY_CONN_LOG(debug, "Http2Visitor: remaining data payload: {}, end_stream: {}", + ENVOY_CONN_LOG(trace, "Http2Visitor: remaining data payload: {}, end_stream: {}", connection_->connection_, remaining_data_payload_, bool(current_frame_.flags & FLAG_END_STREAM)); return result == 0; } bool ConnectionImpl::Http2Visitor::OnEndStream(Http2StreamId stream_id) { - ENVOY_CONN_LOG(debug, "Http2Visitor::OnEndStream({})", connection_->connection_, stream_id); + ENVOY_CONN_LOG(trace, "Http2Visitor::OnEndStream({})", connection_->connection_, stream_id); if (current_frame_.type == OGHTTP2_DATA_FRAME_TYPE) { // `onBeginData` is invoked here to ensure that the connection has successfully validated and // processed the entire DATA frame. - ENVOY_CONN_LOG(debug, "Http2Visitor dispatching DATA for stream {}", connection_->connection_, + ENVOY_CONN_LOG(trace, "Http2Visitor dispatching DATA for stream {}", connection_->connection_, stream_id); Status status = connection_->onBeginData(stream_id, current_frame_.length, current_frame_.flags, padding_length_); @@ -1926,7 +1926,7 @@ bool ConnectionImpl::Http2Visitor::OnCloseStream(Http2StreamId stream_id, Http2ErrorCode error_code) { Status status = connection_->onStreamClose(stream_id, static_cast(error_code)); if (stream_close_listener_) { - ENVOY_CONN_LOG(debug, "Http2Visitor invoking stream close listener for {}", + ENVOY_CONN_LOG(trace, "Http2Visitor invoking stream close listener for {}", connection_->connection_, stream_id); stream_close_listener_(stream_id); } From aa8d0f7d7a2866b5f81dfe46e975b80de26a0669 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 6 Aug 2024 10:15:47 -0400 Subject: [PATCH 039/130] envoy: allowing alt-svc from IPs, fixing HTTP/3 cert bypass (#35551) This adds a real HTTP/3 test for Envoy mobile's Cronet APIs. In order for this to work it fixes 2 issues in Envoy, the first that the connection grid didn't do lookups for ip based hostname. The second that ACCEPT_UNTRUSTED didn't fully work for QUIC. Risk Level: low Testing: e2e test Docs Changes: n/a Release Notes: inline [Optional Runtime guard:] guarded both functional changes --------- Signed-off-by: Alyssa Wilk --- changelogs/current.yaml | 8 + mobile/test/common/integration/test_server.cc | 11 +- mobile/test/common/integration/test_server.h | 2 +- .../integration/test_server_interface.cc | 2 +- .../engine/testing/HttpTestServerFactory.java | 6 +- .../engine/testing/QuicTestServerTest.java | 2 +- mobile/test/java/org/chromium/net/BUILD | 27 ++++ .../org/chromium/net/CronetHttp3Test.java | 139 ++++++++++++++++++ .../jni/jni_http_proxy_test_server_factory.cc | 2 +- .../test/jni/jni_http_test_server_factory.cc | 5 +- .../kotlin/integration/ReceiveDataTest.kt | 1 + .../kotlin/integration/ReceiveTrailersTest.kt | 1 + .../test/kotlin/integration/SendDataTest.kt | 1 + source/common/http/conn_pool_grid.cc | 15 +- .../common/quic/envoy_quic_proof_verifier.cc | 11 +- .../common/quic/envoy_quic_proof_verifier.h | 8 +- .../quic_client_transport_socket_factory.cc | 7 +- source/common/runtime/runtime_features.cc | 2 + test/common/http/conn_pool_grid_test.cc | 38 ++++- 19 files changed, 260 insertions(+), 28 deletions(-) create mode 100644 mobile/test/java/org/chromium/net/CronetHttp3Test.java diff --git a/changelogs/current.yaml b/changelogs/current.yaml index f0ff083bdd3d..e59477c5e4e9 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -21,6 +21,14 @@ minor_behavior_changes: change: | Added support for :ref:`connection_pool_per_downstream_connection ` flag in tcp connection pool. +- area: http3 + change: | + The ACCEPT_UNTRUSTED option now works more consistently for HTTP/3 requests. This change is + guarded by ``envoy.reloadable_features.extend_h3_accept_untrusted``. +- area: http3 + change: | + HTTP/3 alt-svc headers will now be respected from IP-address-based hostnames. This change is + guarded by runtime guard ``envoy.reloadable_features.allow_alt_svc_for_ips``. - area: lua change: | When Lua script executes httpCall, backpressure is exercised when receiving body from downstream client. This behavior can be reverted diff --git a/mobile/test/common/integration/test_server.cc b/mobile/test/common/integration/test_server.cc index c8727516a5eb..3b3ad20b88c3 100644 --- a/mobile/test/common/integration/test_server.cc +++ b/mobile/test/common/integration/test_server.cc @@ -29,7 +29,7 @@ namespace Envoy { namespace { -std::unique_ptr baseProxyConfig(bool http) { +std::unique_ptr baseProxyConfig(bool http, int port) { std::unique_ptr bootstrap = std::make_unique(); @@ -40,7 +40,7 @@ std::unique_ptr baseProxyConfig(bool ht auto* base_address = listener->mutable_address(); base_address->mutable_socket_address()->set_protocol(envoy::config::core::v3::SocketAddress::TCP); base_address->mutable_socket_address()->set_address("127.0.0.1"); - base_address->mutable_socket_address()->set_port_value(0); + base_address->mutable_socket_address()->set_port_value(port); envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager hcm; hcm.set_stat_prefix("remote hcm"); @@ -137,7 +137,8 @@ TestServer::TestServer() Envoy::ExtensionRegistry::registerFactories(); } -void TestServer::start(TestServerType type) { +void TestServer::start(TestServerType type, int port) { + port_ = port; ASSERT(!upstream_); // pre-setup: see https://github.com/envoyproxy/envoy/blob/main/test/test_runner.cc Logger::Context logging_state(spdlog::level::level_enum::err, @@ -180,7 +181,7 @@ void TestServer::start(TestServerType type) { test_server_ = IntegrationTestServer::create( "", Network::Address::IpVersion::v4, nullptr, nullptr, {}, time_system_, *api_, false, absl::nullopt, Server::FieldValidationConfig(), 1, std::chrono::seconds(1), - Server::DrainStrategy::Gradual, nullptr, false, false, baseProxyConfig(true)); + Server::DrainStrategy::Gradual, nullptr, false, false, baseProxyConfig(true, port_)); test_server_->waitUntilListenersReady(); ENVOY_LOG_MISC(debug, "Http proxy is now running"); return; @@ -195,7 +196,7 @@ void TestServer::start(TestServerType type) { test_server_ = IntegrationTestServer::create( "", Network::Address::IpVersion::v4, nullptr, nullptr, {}, time_system_, *api_, false, absl::nullopt, Server::FieldValidationConfig(), 1, std::chrono::seconds(1), - Server::DrainStrategy::Gradual, nullptr, false, false, baseProxyConfig(false)); + Server::DrainStrategy::Gradual, nullptr, false, false, baseProxyConfig(false, port_)); test_server_->waitUntilListenersReady(); ENVOY_LOG_MISC(debug, "Https proxy is now running"); return; diff --git a/mobile/test/common/integration/test_server.h b/mobile/test/common/integration/test_server.h index 68f6bbd59484..c4156aa667b8 100644 --- a/mobile/test/common/integration/test_server.h +++ b/mobile/test/common/integration/test_server.h @@ -31,7 +31,7 @@ class TestServer : public ListenerHooks { /** * Starts the test server. This function blocks until the test server is ready to accept requests. */ - void start(TestServerType type); + void start(TestServerType type, int port = 0); /** * Shutdowns the server server. This function blocks until all the resources have been freed. diff --git a/mobile/test/common/integration/test_server_interface.cc b/mobile/test/common/integration/test_server_interface.cc index e30ba593babd..91dc5f46e74f 100644 --- a/mobile/test/common/integration/test_server_interface.cc +++ b/mobile/test/common/integration/test_server_interface.cc @@ -12,7 +12,7 @@ void start_server(Envoy::TestServerType test_server_type) { weak_test_server_ = strong_test_server_; if (auto server = test_server()) { - server->start(test_server_type); + server->start(test_server_type, 0); } } diff --git a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/HttpTestServerFactory.java b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/HttpTestServerFactory.java index 7e1599f06203..ee4e7f84d5a5 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/HttpTestServerFactory.java +++ b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/HttpTestServerFactory.java @@ -53,8 +53,8 @@ private HttpTestServer(long handle, String ipAddress, int port, String address) * @param trailers the response headers * @return the `HttpTestServer` instance */ - public static native HttpTestServer start(int type, Map headers, String body, - Map trailers); + public static native HttpTestServer start(int type, int port, Map headers, + String body, Map trailers); /** * A convenience method to start the server with an empty response headers, body, and trailers. @@ -64,6 +64,6 @@ public static native HttpTestServer start(int type, Map headers, * @return the `HttpTestServer` instance */ public static HttpTestServer start(int type) { - return start(type, Collections.emptyMap(), "", Collections.emptyMap()); + return start(type, 0, Collections.emptyMap(), "", Collections.emptyMap()); } } diff --git a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/QuicTestServerTest.java b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/QuicTestServerTest.java index 654fd86e3d94..67d92c274089 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/QuicTestServerTest.java +++ b/mobile/test/java/io/envoyproxy/envoymobile/engine/testing/QuicTestServerTest.java @@ -41,7 +41,7 @@ public void setUpEngine() throws Exception { Map headers = new HashMap<>(); headers.put("Cache-Control", "max-age=0"); headers.put("Content-Type", "text/plain"); - httpTestServer = HttpTestServerFactory.start(HttpTestServerFactory.Type.HTTP3, headers, + httpTestServer = HttpTestServerFactory.start(HttpTestServerFactory.Type.HTTP3, 0, headers, "This is a simple text file served by QUIC.\n", Collections.emptyMap()); diff --git a/mobile/test/java/org/chromium/net/BUILD b/mobile/test/java/org/chromium/net/BUILD index 19773583241e..aefe1cb0152d 100644 --- a/mobile/test/java/org/chromium/net/BUILD +++ b/mobile/test/java/org/chromium/net/BUILD @@ -221,6 +221,33 @@ envoy_mobile_android_test( ], ) +envoy_mobile_android_test( + name = "cronet_http3_test", + srcs = [ + "CronetHttp3Test.java", + ], + native_deps = [ + "//test/jni:libenvoy_jni_with_test_extensions.so", + ] + select({ + "@platforms//os:macos": [ + "//test/jni:libenvoy_jni_with_test_extensions_jnilib", + ], + "//conditions:default": [], + }), + native_lib_name = "envoy_jni_with_test_extensions", + test_class = "org.chromium.net.CronetHttp3Test", + deps = [ + "//library/java/io/envoyproxy/envoymobile/engine:envoy_base_engine_lib", + "//library/java/io/envoyproxy/envoymobile/engine:envoy_engine_lib", + "//library/java/org/chromium/net", + "//library/java/org/chromium/net/impl:cronvoy", + "//library/kotlin/io/envoyproxy/envoymobile:envoy_interfaces_lib", + "//library/kotlin/io/envoyproxy/envoymobile:envoy_lib", + "//test/java/io/envoyproxy/envoymobile/engine/testing:http_test_server_factory_lib", + "//test/java/org/chromium/net/testing", + ], +) + envoy_mobile_android_test( name = "cronet_url_request_context_test", srcs = [ diff --git a/mobile/test/java/org/chromium/net/CronetHttp3Test.java b/mobile/test/java/org/chromium/net/CronetHttp3Test.java new file mode 100644 index 000000000000..9b0800b18e67 --- /dev/null +++ b/mobile/test/java/org/chromium/net/CronetHttp3Test.java @@ -0,0 +1,139 @@ +package org.chromium.net; + +import static org.chromium.net.testing.CronetTestRule.getContext; +import static org.junit.Assert.assertEquals; + +import org.chromium.net.impl.CronvoyUrlRequestContext; +import io.envoyproxy.envoymobile.engine.EnvoyEngine; +import org.chromium.net.impl.CronvoyLogger; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.filters.SmallTest; +import org.chromium.net.impl.CronvoyUrlRequestContext; +import org.chromium.net.impl.NativeCronvoyEngineBuilderImpl; +import org.chromium.net.testing.CronetTestRule; +import org.chromium.net.testing.CronetTestRule.CronetTestFramework; +import org.chromium.net.testing.CronetTestRule.RequiresMinApi; +import org.chromium.net.testing.Feature; +import org.chromium.net.testing.TestUrlRequestCallback; +import org.chromium.net.testing.TestUrlRequestCallback.ResponseStep; +import io.envoyproxy.envoymobile.engine.JniLibrary; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import io.envoyproxy.envoymobile.engine.testing.HttpTestServerFactory; +import java.util.HashMap; +import java.util.Map; +import java.util.Collections; + +/** + * Test CronetEngine with production HTTP/3 logic + */ +@RunWith(RobolectricTestRunner.class) +public class CronetHttp3Test { + @Rule public final CronetTestRule mTestRule = new CronetTestRule(); + + private static final String TAG = CronetHttp3Test.class.getSimpleName(); + + // URLs used for tests. + + // If true, dump envoy logs on test completion. + // Ideally we could override this from the command line but that's TBD. + private boolean printEnvoyLogs = false; + // The HTTP/2 server, set up to alt-svc to the HTTP/3 server + private HttpTestServerFactory.HttpTestServer http2TestServer; + // The HTTP/3 server + private HttpTestServerFactory.HttpTestServer http3TestServer; + // An optional CronvoyLogger, set up if printEnvoyLogs is true. + private CronvoyLogger logger; + // The engine for this test. + private CronvoyUrlRequestContext cronvoyEngine; + + @BeforeClass + public static void loadJniLibrary() { + JniLibrary.loadTestLibrary(); + } + + public void setUp(boolean setUpLogging) throws Exception { + // Set up the HTTP/3 server + Map headers = new HashMap<>(); + http3TestServer = HttpTestServerFactory.start(HttpTestServerFactory.Type.HTTP3, 0, headers, + "This is a simple text file served by QUIC.\n", + Collections.emptyMap()); + // Next set up the HTTP/2 server, advertising HTTP/3 support for the HTTP/3 server + String altSvc = "h3=\":" + http3TestServer.getPort() + "\"; ma=86400"; + headers.put("alt-svc", altSvc); + // Note that the HTTP/2 server must start on the same port as Envoy currently does not accept + // alt-svc with differing ports. This may cause problems if this UDP port is in use at which + // point listening on 127.0.0.N where N!=1 may improve flakiness. + http2TestServer = HttpTestServerFactory.start( + HttpTestServerFactory.Type.HTTP2_WITH_TLS, http3TestServer.getPort(), headers, + "This is a simple text file served by QUIC.\n", Collections.emptyMap()); + + // Optionally, set up logging. This will slow down the tests a bit but make debugging much + // easier. + if (setUpLogging) { + logger = new CronvoyLogger() { + @Override + public void log(int logLevel, String message) { + System.out.print(message); + } + }; + } + } + + @After + public void tearDown() throws Exception { + // Shut down Envoy and the test servers. + cronvoyEngine.shutdown(); + http2TestServer.shutdown(); + http3TestServer.shutdown(); + } + + @Test + @SmallTest + @Feature({"Cronet"}) + public void testInitEngineAndStartRequest() throws Exception { + // Ideally we could override this from the command line but that's TBD. + setUp(printEnvoyLogs); + + // Set up the Envoy engine. + NativeCronvoyEngineBuilderImpl nativeCronetEngineBuilder = + new NativeCronvoyEngineBuilderImpl(ApplicationProvider.getApplicationContext()); + if (printEnvoyLogs) { + nativeCronetEngineBuilder.setLogger(logger); + nativeCronetEngineBuilder.setLogLevel(EnvoyEngine.LogLevel.TRACE); + } + // Make sure the handshake will work despite lack of real certs. + nativeCronetEngineBuilder.setMockCertVerifierForTesting(); + cronvoyEngine = new CronvoyUrlRequestContext(nativeCronetEngineBuilder); + + // Do a request to https://127.0.0.1:test_server_port/ + TestUrlRequestCallback callback1 = new TestUrlRequestCallback(); + String newUrl = "https://" + http2TestServer.getAddress() + "/"; + UrlRequest.Builder urlRequestBuilder = + cronvoyEngine.newUrlRequestBuilder(newUrl, callback1, callback1.getExecutor()); + urlRequestBuilder.build().start(); + callback1.blockForDone(); + + // Make sure the request succeeded. It should go out over HTTP/2 as it's the first + // request and HTTP/3 support is not established. + assertEquals(200, callback1.mResponseInfo.getHttpStatusCode()); + assertEquals("h2", callback1.mResponseInfo.getNegotiatedProtocol()); + + // Set up a second request, which will hopefully go out over HTTP/3 due to alt-svc + // advertisement. + TestUrlRequestCallback callback2 = new TestUrlRequestCallback(); + UrlRequest.Builder urlRequestBuilder2 = + cronvoyEngine.newUrlRequestBuilder(newUrl, callback2, callback2.getExecutor()); + urlRequestBuilder2.build().start(); + callback2.blockForDone(); + + // Verify the second request used HTTP/3 + assertEquals(200, callback2.mResponseInfo.getHttpStatusCode()); + assertEquals("h3", callback2.mResponseInfo.getNegotiatedProtocol()); + } +} diff --git a/mobile/test/jni/jni_http_proxy_test_server_factory.cc b/mobile/test/jni/jni_http_proxy_test_server_factory.cc index 2314d0868397..877dfa81dcfb 100644 --- a/mobile/test/jni/jni_http_proxy_test_server_factory.cc +++ b/mobile/test/jni/jni_http_proxy_test_server_factory.cc @@ -22,7 +22,7 @@ Java_io_envoyproxy_envoymobile_engine_testing_HttpProxyTestServerFactory_start(J Envoy::ExtensionRegistry::registerFactories(); Envoy::TestServer* test_server = new Envoy::TestServer(); - test_server->start(static_cast(type)); + test_server->start(static_cast(type), 0); jclass java_http_proxy_server_factory_class = jni_helper.findClass( "io/envoyproxy/envoymobile/engine/testing/HttpProxyTestServerFactory$HttpProxyTestServer"); diff --git a/mobile/test/jni/jni_http_test_server_factory.cc b/mobile/test/jni/jni_http_test_server_factory.cc index dd6409b038ab..eb68d597007d 100644 --- a/mobile/test/jni/jni_http_test_server_factory.cc +++ b/mobile/test/jni/jni_http_test_server_factory.cc @@ -18,12 +18,13 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /* reserved */) { extern "C" JNIEXPORT jobject JNICALL Java_io_envoyproxy_envoymobile_engine_testing_HttpTestServerFactory_start( - JNIEnv* env, jclass, jint type, jobject headers, jstring body, jobject trailers) { + JNIEnv* env, jclass, jint type, jint requested_port, jobject headers, jstring body, + jobject trailers) { Envoy::JNI::JniHelper jni_helper(env); Envoy::ExtensionRegistry::registerFactories(); Envoy::TestServer* test_server = new Envoy::TestServer(); - test_server->start(static_cast(type)); + test_server->start(static_cast(type), requested_port); auto cpp_headers = Envoy::JNI::javaMapToCppMap(jni_helper, headers); auto cpp_body = Envoy::JNI::javaStringToCppString(jni_helper, body); diff --git a/mobile/test/kotlin/integration/ReceiveDataTest.kt b/mobile/test/kotlin/integration/ReceiveDataTest.kt index bab47e1cc2f6..630c55d1c766 100644 --- a/mobile/test/kotlin/integration/ReceiveDataTest.kt +++ b/mobile/test/kotlin/integration/ReceiveDataTest.kt @@ -31,6 +31,7 @@ class ReceiveDataTest { httpTestServer = HttpTestServerFactory.start( HttpTestServerFactory.Type.HTTP2_WITH_TLS, + 0, mapOf(), "data", mapOf() diff --git a/mobile/test/kotlin/integration/ReceiveTrailersTest.kt b/mobile/test/kotlin/integration/ReceiveTrailersTest.kt index abb176bc905d..6d43cad5c61a 100644 --- a/mobile/test/kotlin/integration/ReceiveTrailersTest.kt +++ b/mobile/test/kotlin/integration/ReceiveTrailersTest.kt @@ -35,6 +35,7 @@ class ReceiveTrailersTest { httpTestServer = HttpTestServerFactory.start( HttpTestServerFactory.Type.HTTP2_WITH_TLS, + 0, mapOf(), "data", mapOf(TRAILER_NAME to TRAILER_VALUE) diff --git a/mobile/test/kotlin/integration/SendDataTest.kt b/mobile/test/kotlin/integration/SendDataTest.kt index 094db7c32a26..05d5c1533bfe 100644 --- a/mobile/test/kotlin/integration/SendDataTest.kt +++ b/mobile/test/kotlin/integration/SendDataTest.kt @@ -36,6 +36,7 @@ class SendDataTest { httpTestServer = HttpTestServerFactory.start( HttpTestServerFactory.Type.HTTP2_WITH_TLS, + 0, mapOf(), "data", mapOf() diff --git a/source/common/http/conn_pool_grid.cc b/source/common/http/conn_pool_grid.cc index 8605f2dbc9c2..21dfc6a0927b 100644 --- a/source/common/http/conn_pool_grid.cc +++ b/source/common/http/conn_pool_grid.cc @@ -26,12 +26,19 @@ absl::string_view describePool(const ConnectionPool::Instance& pool) { static constexpr uint32_t kDefaultTimeoutMs = 300; -std::string getSni(const Network::TransportSocketOptionsConstSharedPtr& options, - Network::UpstreamTransportSocketFactory& transport_socket_factory) { +std::string getTargetHostname(const Network::TransportSocketOptionsConstSharedPtr& options, + Upstream::HostConstSharedPtr& host) { if (options && options->serverNameOverride().has_value()) { return options->serverNameOverride().value(); } - return std::string(transport_socket_factory.defaultServerNameIndication()); + std::string default_sni = + std::string(host->transportSocketFactory().defaultServerNameIndication()); + if (!default_sni.empty() || + !Runtime::runtimeFeatureEnabled("envoy.reloadable_features.allow_alt_svc_for_ips")) { + return default_sni; + } + // If there's no configured SNI the hostname is probably an IP address. Return it here. + return host->hostname(); } } // namespace @@ -297,7 +304,7 @@ ConnectivityGrid::ConnectivityGrid( time_source_(time_source), alternate_protocols_(alternate_protocols), quic_stat_names_(quic_stat_names), scope_(scope), // TODO(RyanTheOptimist): Figure out how scheme gets plumbed in here. - origin_("https", getSni(transport_socket_options, host_->transportSocketFactory()), + origin_("https", getTargetHostname(transport_socket_options, host_), host_->address()->ip()->port()), quic_info_(quic_info), priority_(priority) { // ProdClusterManagerFactory::allocateConnPool verifies the protocols are HTTP/1, HTTP/2 and diff --git a/source/common/quic/envoy_quic_proof_verifier.cc b/source/common/quic/envoy_quic_proof_verifier.cc index 0ae8f324eb8b..330da858d75a 100644 --- a/source/common/quic/envoy_quic_proof_verifier.cc +++ b/source/common/quic/envoy_quic_proof_verifier.cc @@ -36,9 +36,10 @@ class QuicValidateResultCallback : public Ssl::ValidateResultCallback { public: QuicValidateResultCallback(Event::Dispatcher& dispatcher, std::unique_ptr&& quic_callback, - const std::string& hostname, const std::string& leaf_cert) + const std::string& hostname, const std::string& leaf_cert, + bool accept_untrusted) : dispatcher_(dispatcher), quic_callback_(std::move(quic_callback)), hostname_(hostname), - leaf_cert_(leaf_cert) {} + leaf_cert_(leaf_cert), accept_untrusted_(accept_untrusted) {} Event::Dispatcher& dispatcher() override { return dispatcher_; } @@ -47,7 +48,8 @@ class QuicValidateResultCallback : public Ssl::ValidateResultCallback { std::string error; if (!succeeded) { error = error_details; - } else { + } else if (!accept_untrusted_ || !Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.extend_h3_accept_untrusted")) { std::unique_ptr cert_view = quic::CertificateView::ParseSingleCertificate(leaf_cert_); succeeded = verifyLeafCertMatchesHostname(*cert_view, hostname_, &error); @@ -63,6 +65,7 @@ class QuicValidateResultCallback : public Ssl::ValidateResultCallback { const std::string hostname_; // Leaf cert needs to be retained in case of asynchronous validation. std::string leaf_cert_; + const bool accept_untrusted_; }; } // namespace @@ -101,7 +104,7 @@ quic::QuicAsyncStatus EnvoyQuicProofVerifier::VerifyCertChain( } auto envoy_callback = std::make_unique( - verify_context->dispatcher(), std::move(callback), hostname, certs[0]); + verify_context->dispatcher(), std::move(callback), hostname, certs[0], accept_untrusted_); ASSERT(dynamic_cast(context_.get()) != nullptr); // We down cast rather than add customVerifyCertChainForQuic to Envoy::Ssl::Context because diff --git a/source/common/quic/envoy_quic_proof_verifier.h b/source/common/quic/envoy_quic_proof_verifier.h index b42e9841a709..b5af84ae3c4f 100644 --- a/source/common/quic/envoy_quic_proof_verifier.h +++ b/source/common/quic/envoy_quic_proof_verifier.h @@ -39,8 +39,9 @@ using EnvoyQuicProofVerifyContextPtr = std::unique_ptr QuicClientTransportSocketFactory:: ThreadLocalQuicConfig& tls_config = *tls_slot_; if (tls_config.client_context_ != context) { + bool accept_untrusted = + clientContextConfig() && clientContextConfig()->certificateValidationContext() && + clientContextConfig()->certificateValidationContext()->trustChainVerification() == + envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext:: + ACCEPT_UNTRUSTED; // If the context has been updated, update the crypto config. tls_config.client_context_ = context; tls_config.crypto_config_ = std::make_shared( - std::make_unique(std::move(context)), + std::make_unique(std::move(context), accept_untrusted), std::make_unique()); } // Return the latest crypto config. diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 57c8c7b90657..25cd64d30ded 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -29,6 +29,7 @@ // If issues are found that require a runtime feature to be disabled, it should be reported // ASAP by filing a bug on github. Overriding non-buggy code is strongly discouraged to avoid the // problem of the bugs being found after the old code path has been removed. +RUNTIME_GUARD(envoy_reloadable_features_allow_alt_svc_for_ips); RUNTIME_GUARD(envoy_reloadable_features_check_switch_protocol_websocket_handshake); RUNTIME_GUARD(envoy_reloadable_features_conn_pool_delete_when_idle); RUNTIME_GUARD(envoy_reloadable_features_consistent_header_validation); @@ -44,6 +45,7 @@ RUNTIME_GUARD(envoy_reloadable_features_enable_compression_bomb_protection); RUNTIME_GUARD(envoy_reloadable_features_enable_include_histograms); RUNTIME_GUARD(envoy_reloadable_features_exclude_host_in_eds_status_draining); RUNTIME_GUARD(envoy_reloadable_features_ext_proc_timeout_error); +RUNTIME_GUARD(envoy_reloadable_features_extend_h3_accept_untrusted); RUNTIME_GUARD(envoy_reloadable_features_gcp_authn_use_fixed_url); RUNTIME_GUARD(envoy_reloadable_features_grpc_side_stream_flow_control); RUNTIME_GUARD(envoy_reloadable_features_http1_balsa_delay_reset); diff --git a/test/common/http/conn_pool_grid_test.cc b/test/common/http/conn_pool_grid_test.cc index 476e6730b9dc..dd14534da4a6 100644 --- a/test/common/http/conn_pool_grid_test.cc +++ b/test/common/http/conn_pool_grid_test.cc @@ -4,6 +4,7 @@ #include "source/common/http/conn_pool_grid.h" #include "source/common/http/http_server_properties_cache_impl.h" +#include "source/common/upstream/transport_socket_match_impl.h" #include "test/common/http/common.h" #include "test/common/upstream/utility.h" @@ -51,6 +52,8 @@ class ConnectivityGridForTest : public ConnectivityGrid { return grid.getOrCreateHttp2Pool(); } + std::string getOriginHostname() { return origin_.hostname_; } + ConnectionPool::InstancePtr createHttp3Pool(bool alternate) override { if (!alternate) { return createMockPool("http3"); @@ -177,8 +180,8 @@ class ConnectivityGridTest : public Event::TestUsingSimulatedTime, public testin std::make_unique(); #endif host_ = std::make_shared( - cluster_, "hostname", *Network::Utility::resolveUrl("tcp://127.0.0.1:9000"), nullptr, - nullptr, 1, envoy::config::core::v3::Locality(), + cluster_, host_impl_hostname_, *Network::Utility::resolveUrl("tcp://127.0.0.1:9000"), + nullptr, nullptr, 1, envoy::config::core::v3::Locality(), envoy::config::endpoint::v3::Endpoint::HealthCheckConfig::default_instance(), 0, envoy::config::core::v3::UNKNOWN, simTime(), address_list_); @@ -213,7 +216,7 @@ class ConnectivityGridTest : public Event::TestUsingSimulatedTime, public testin HttpServerPropertiesCacheImpl::Origin origin_{"https", "hostname", 9000}; const Network::ConnectionSocket::OptionsSharedPtr socket_options_; - const Network::TransportSocketOptionsConstSharedPtr transport_socket_options_; + Network::TransportSocketOptionsConstSharedPtr transport_socket_options_; ConnectivityGrid::ConnectivityOptions options_; Upstream::ClusterConnectivityState state_; std::shared_ptr cluster_{new NiceMock()}; @@ -236,11 +239,40 @@ class ConnectivityGridTest : public Event::TestUsingSimulatedTime, public testin testing::NiceMock thread_local_; NiceMock dispatcher_; std::unique_ptr grid_; + std::string host_impl_hostname_ = "hostname"; }; +TEST_F(ConnectivityGridTest, HostnameFromTransportSocketFactory) { + Network::MockTransportSocketFactory factory; + Upstream::MockTransportSocketMatcher* transport_socket_matcher = + dynamic_cast( + cluster_->transport_socket_matcher_.get()); + EXPECT_CALL(*transport_socket_matcher, resolve(_, _)) + .WillOnce(Return(Upstream::TransportSocketMatcher::MatchData( + factory, transport_socket_matcher->stats_, "test"))); + EXPECT_CALL(factory, defaultServerNameIndication) + .WillRepeatedly(Return("transport_socket_hostname")); + host_impl_hostname_ = "custom_hostname"; + transport_socket_options_ = std::make_shared(); + initialize(); + // Without "hostname" in the TransportSocketOptionsImpl, this fails over to + // the host name in HostNameImpl + EXPECT_EQ("transport_socket_hostname", grid_->getOriginHostname()); +} + +TEST_F(ConnectivityGridTest, NoServerNameOverride) { + host_impl_hostname_ = "custom_hostname"; + transport_socket_options_ = std::make_shared(); + initialize(); + // Without "hostname" in the TransportSocketOptionsImpl, this fails over to + // the host name in HostNameImpl + EXPECT_EQ(host_impl_hostname_, grid_->getOriginHostname()); +} + // Test the first pool successfully connecting. TEST_F(ConnectivityGridTest, Success) { initialize(); + EXPECT_EQ("hostname", grid_->getOriginHostname()); addHttp3AlternateProtocol(); EXPECT_EQ(grid_->http3Pool(), nullptr); EXPECT_NE(grid_->newStream(decoder_, callbacks_, From f93d9077e5e9e83e4e338e6ddd8a147d197d05ee Mon Sep 17 00:00:00 2001 From: phlax Date: Tue, 6 Aug 2024 16:29:46 +0100 Subject: [PATCH 040/130] ci: Split build tests from release job (#35580) Signed-off-by: Ryan Northey --- .azure-pipelines/stage/linux.yml | 93 ---------------------------- .azure-pipelines/stage/prechecks.yml | 64 +++++++++++++++++-- .azure-pipelines/stage/publish.yml | 62 ++++++++++++++++++- .azure-pipelines/stages.yml | 40 ++---------- ci/do_ci.sh | 36 +++++++---- 5 files changed, 147 insertions(+), 148 deletions(-) delete mode 100644 .azure-pipelines/stage/linux.yml diff --git a/.azure-pipelines/stage/linux.yml b/.azure-pipelines/stage/linux.yml deleted file mode 100644 index ea465a768ae6..000000000000 --- a/.azure-pipelines/stage/linux.yml +++ /dev/null @@ -1,93 +0,0 @@ -parameters: -- name: cacheTestResults - displayName: "Cache test results" - type: boolean - default: true -- name: pool - displayName: "Agent pool" - type: string - default: envoy-x64-small -- name: artifactSuffix - displayName: "Artifact suffix" - type: string - default: -- name: runTests - displayName: "Run release tests" - type: string - default: true -- name: rbe - displayName: "Use RBE" - type: boolean - default: true -- name: timeoutBuild - displayName: "Build timeout" - type: number - default: 120 -- name: bazelBuildExtraOptions - type: string - default: "" -- name: bazelConfigRBE - type: string - default: --config=remote-ci --config=rbe-google --jobs=$(RbeJobs) - -- name: managedAgent - type: boolean - default: false -- name: tmpfsDockerDisabled - type: string - default: '' - - -- name: runBuild - displayName: "Run build" - type: string - default: true - -jobs: -- job: release - displayName: Build and test - condition: | - and(not(canceled()), - eq(${{ parameters.runBuild }}, 'true')) - timeoutInMinutes: ${{ parameters.timeoutBuild }} - pool: ${{ parameters.pool }} - steps: - - bash: | - if [[ "${{ parameters.runTests }}" == "false" ]]; then - CI_TARGET="release.server_only" - else - CI_TARGET="release" - fi - echo "${CI_TARGET}" - echo "##vso[task.setvariable variable=value;isoutput=true]${CI_TARGET}" - name: target - - template: ../ci.yml - parameters: - artifactName: release - managedAgent: ${{ parameters.managedAgent }} - ciTarget: $(target.value) - cacheName: "release" - bazelConfigRBE: ${{ parameters.bazelConfigRBE }} - bazelBuildExtraOptions: ${{ parameters.bazelBuildExtraOptions }} - cacheTestResults: ${{ parameters.cacheTestResults }} - cacheVersion: $(cacheKeyBazel) - artifactSuffix: ${{ parameters.artifactSuffix }} - rbe: ${{ parameters.rbe }} - tmpfsDockerDisabled: ${{ parameters.tmpfsDockerDisabled }} - -- job: released - displayName: Complete - dependsOn: ["release"] - pool: - vmImage: $(agentUbuntu) - # This condition ensures that this (required) job passes if all of - # the preceeding jobs either pass or are skipped - # adapted from: - # https://learn.microsoft.com/en-us/azure/devops/pipelines/process/expressions?view=azure-devops#job-to-job-dependencies-within-one-stage - condition: | - and(eq(variables['Build.Reason'], 'PullRequest'), - in(dependencies.release.result, 'Succeeded', 'SucceededWithIssues', 'Skipped')) - steps: - - checkout: none - - bash: | - echo "linux_x64 released" diff --git a/.azure-pipelines/stage/prechecks.yml b/.azure-pipelines/stage/prechecks.yml index b8bfaed62ab7..c4e2d779a6ba 100644 --- a/.azure-pipelines/stage/prechecks.yml +++ b/.azure-pipelines/stage/prechecks.yml @@ -25,6 +25,15 @@ parameters: type: string default: "" +- name: runBuild + displayName: "Run build" + type: string + default: true +- name: runPrechecks + displayName: "Run prechecks" + type: string + default: true + # Timeout/s - name: timeoutPrechecks type: number @@ -32,10 +41,10 @@ parameters: # a lot of change - eg protobuf changed, or a primitve proto changed. default: 40 -- name: runPrechecks - displayName: "Run prechecks" +- name: bazelConfigRBE type: string - default: true + default: --config=remote-ci --config=rbe-google --jobs=$(RbeJobs) + jobs: - job: prechecks @@ -53,17 +62,21 @@ jobs: matrix: format: CI_TARGET: "format" + CI_CACHE: format protobuf: CI_TARGET: "check_and_fix_proto_format" + CI_CACHE: check_and_fix_proto_format ${{ if eq(variables['Build.Reason'], 'PullRequest') }}: publishing: CI_TARGET: docs + CI_CACHE: docs + steps: - template: ../ci.yml parameters: bazelBuildExtraOptions: --config=docs-ci ciTarget: $(CI_TARGET) - cacheName: $(CI_TARGET) + cacheName: $(CI_CACHE) cacheTestResults: ${{ parameters.cacheTestResults }} cacheVersion: $(cacheKeyBazel) publishEnvoy: false @@ -157,9 +170,47 @@ jobs: GCS_ARTIFACT_BUCKET: ${{ parameters.bucketGCP }} condition: eq(variables['CI_TARGET'], 'docs') +- job: precheck_release_x64 + displayName: Precheck release (x64) + condition: | + and(not(canceled()), + eq(${{ parameters.runBuild }}, 'true')) + timeoutInMinutes: 180 + pool: envoy-x64-large + steps: + - template: ../ci.yml + parameters: + artifactName: release + ciTarget: release.test_only + cacheName: release-test-only + bazelConfigRBE: ${{ parameters.bazelConfigRBE }} + cacheTestResults: ${{ parameters.cacheTestResults }} + cacheVersion: $(cacheKeyBazel) + rbe: true + +- job: precheck_release_arm64 + displayName: Precheck release (arm64) + condition: | + and(not(canceled()), + eq(${{ parameters.runBuild }}, 'true')) + timeoutInMinutes: 180 + pool: envoy-arm-large + steps: + - template: ../ci.yml + parameters: + artifactName: release + ciTarget: release.test_only + cacheName: release-test-only + bazelConfigRBE: ${{ parameters.bazelConfigRBE }} + bazelBuildExtraOptions: "--sandbox_base=/tmp/sandbox_base" + cacheTestResults: ${{ parameters.cacheTestResults }} + cacheVersion: $(cacheKeyBazel) + artifactSuffix: .arm64 + rbe: false + - job: prechecked displayName: Prechecked - dependsOn: ["prechecks"] + dependsOn: ["prechecks", "precheck_release_arm64", "precheck_release_x64"] pool: vmImage: $(agentUbuntu) # This condition ensures that this (required) job passes if all of @@ -169,7 +220,8 @@ jobs: condition: | and( eq(variables['Build.Reason'], 'PullRequest'), - in(dependencies.prechecks.result, 'Succeeded', 'SucceededWithIssues', 'Skipped')) + in(dependencies.prechecks.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'), + in(dependencies.precheck_release_arm64.result, 'Succeeded', 'SucceededWithIssues', 'Skipped')) steps: - checkout: none - bash: | diff --git a/.azure-pipelines/stage/publish.yml b/.azure-pipelines/stage/publish.yml index 477c951b16e3..dc1f811cdf63 100644 --- a/.azure-pipelines/stage/publish.yml +++ b/.azure-pipelines/stage/publish.yml @@ -45,6 +45,14 @@ parameters: type: string default: "" +- name: bazelConfigRBE + type: string + default: --config=remote-ci --config=rbe-google --jobs=$(RbeJobs) + +- name: runBuild + displayName: "Run Build" + type: string + default: true - name: runDocker displayName: "Run Docker" type: string @@ -64,8 +72,58 @@ parameters: default: false jobs: + +- job: release_x64 + displayName: Binary release + condition: | + and(not(canceled()), + eq(${{ parameters.runBuild }}, 'true')) + timeoutInMinutes: 120 + pool: + vmImage: $(agentUbuntu) + steps: + - template: ../ci.yml + parameters: + artifactName: release + ciTarget: release.server_only + cacheName: release-server-only + bazelConfigRBE: ${{ parameters.bazelConfigRBE }} + cacheVersion: $(cacheKeyBazel) + cacheTestResults: false + publishTestResults: false + rbe: true + +- job: release_arm64 + displayName: Binary release (arm64) + condition: | + and(not(canceled()), + eq(${{ parameters.runBuild }}, 'true')) + timeoutInMinutes: 180 + pool: envoy-arm-large + steps: + - bash: | + CI_TARGET="release.server_only" + echo "${CI_TARGET}" + echo "##vso[task.setvariable variable=value;isoutput=true]${CI_TARGET}" + name: target + - template: ../ci.yml + parameters: + artifactName: release + ciTarget: release.server_only + cacheName: release-server-only + bazelConfigRBE: ${{ parameters.bazelConfigRBE }} + bazelBuildExtraOptions: "--sandbox_base=/tmp/sandbox_base" + cacheVersion: $(cacheKeyBazel) + cacheTestResults: false + artifactSuffix: .arm64 + publishTestResults: false + rbe: false + - job: docker displayName: "Docker (Linux multi arch)" + dependsOn: + - release_x64 + - release_arm64 condition: | and(not(canceled()), eq(${{ parameters.runDocker }}, 'true')) @@ -128,7 +186,7 @@ jobs: - job: package_x64 displayName: Linux debs (x64) - dependsOn: [] + dependsOn: ["release_x64"] condition: | and(not(canceled()), eq(${{ parameters.runPackaging }}, 'true')) @@ -162,7 +220,7 @@ jobs: - job: package_arm64 displayName: Linux debs (arm64) - dependsOn: [] + dependsOn: ["release_arm64"] condition: | and(not(canceled()), eq(${{ parameters.runPackaging }}, 'true')) diff --git a/.azure-pipelines/stages.yml b/.azure-pipelines/stages.yml index f4ab9607aa06..a0fc0c9cbc1b 100644 --- a/.azure-pipelines/stages.yml +++ b/.azure-pipelines/stages.yml @@ -12,7 +12,7 @@ parameters: type: object default: - env - - linux_x64 + - prechecks - name: concurrencyChecks displayName: "Check concurrency" type: number @@ -59,40 +59,6 @@ stages: bucketGCP: $(GcsArtifactBucket) runPrechecks: stageDependencies.env.repo.outputs['run.releaseTests'] -- stage: linux_x64 - displayName: Linux x64 - dependsOn: ${{ parameters.buildStageDeps }} - variables: - RUN_BUILD: $[stageDependencies.env.repo.outputs['run.build']] - RUN_TESTS: $[stageDependencies.env.repo.outputs['run.releaseTests']] - jobs: - - template: stage/linux.yml - parameters: - cacheTestResults: ${{ parameters.cacheTestResults }} - bazelConfigRBE: --config=remote-ci --config=rbe-google --jobs=200 - pool: envoy-x64-large - # these are parsed differently and _must_ be expressed in this way - runBuild: variables['RUN_BUILD'] - runTests: $(RUN_TESTS) - -- stage: linux_arm64 - displayName: Linux arm64 - dependsOn: ${{ parameters.buildStageDeps }} - variables: - RUN_BUILD: $[stageDependencies.env.repo.outputs['run.build']] - RUN_TESTS: $[stageDependencies.env.repo.outputs['run.releaseTests']] - jobs: - - template: stage/linux.yml - parameters: - cacheTestResults: ${{ parameters.cacheTestResults }} - rbe: false - artifactSuffix: .arm64 - timeoutBuild: 180 - pool: envoy-arm-large - runBuild: variables['RUN_BUILD'] - runTests: $(RUN_TESTS) - bazelBuildExtraOptions: "--sandbox_base=/tmp/sandbox_base" - - stage: check displayName: Checks (Linux x64) dependsOn: ${{ parameters.checkStageDeps }} @@ -110,8 +76,9 @@ stages: - stage: publish displayName: Publish - dependsOn: ["env", "linux_x64", "linux_arm64"] + dependsOn: ["env", "prechecks"] variables: + RUN_BUILD: $[stageDependencies.env.repo.outputs['run.build']] RUN_DOCKER: $[stageDependencies.env.repo.outputs['run.docker']] RUN_PACKAGING: $[stageDependencies.env.repo.outputs['run.packaging']] PUBLISH_GITHUB_RELEASE: $[stageDependencies.env.repo.outputs['publish.githubRelease']] @@ -131,6 +98,7 @@ stages: bucketGCP: $(GcsArtifactBucket) timeoutDockerBuild: ${{ parameters.timeoutDockerBuild }} timeoutDockerPublish: ${{ parameters.timeoutDockerPublish }} + runBuild: variables['RUN_BUILD'] runDocker: variables['RUN_DOCKER'] runPackaging: variables['RUN_PACKAGING'] publishDockerhub: variables['PUBLISH_DOCKERHUB'] diff --git a/ci/do_ci.sh b/ci/do_ci.sh index 8b634a2048fa..f0f4687323e1 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -258,7 +258,7 @@ if [[ $# -ge 1 ]]; then else # Coverage test will add QUICHE tests by itself. COVERAGE_TEST_TARGETS=("//test/...") - if [[ "${CI_TARGET}" == "release" ]]; then + if [[ "${CI_TARGET}" == "release" || "${CI_TARGET}" == "release.test_only" ]]; then # We test contrib on release only. COVERAGE_TEST_TARGETS=("${COVERAGE_TEST_TARGETS[@]}" "//contrib/...") elif [[ "${CI_TARGET}" == "msan" ]]; then @@ -714,11 +714,15 @@ case $CI_TARGET in fetch-gcc) targets=("${FETCH_GCC_TARGETS[@]}") ;; - fetch-release) + fetch-release|fetch-release.test_only) targets=( "${FETCH_BUILD_TARGETS[@]}" "${FETCH_ALL_TEST_TARGETS[@]}") ;; + fetch-release.server_only) + targets=( + "${FETCH_BUILD_TARGETS[@]}") + ;; fetch-*coverage) targets=("${FETCH_TEST_TARGETS[@]}") ;; @@ -828,8 +832,8 @@ case $CI_TARGET in "${PUBLISH_ARGS[@]}" ;; - release|release.server_only) - if [[ "$CI_TARGET" == "release" ]]; then + release|release.server_only|release.test_only) + if [[ "$CI_TARGET" == "release" || "$CI_TARGET" == "release.test_only" ]]; then # When testing memory consumption, we want to test against exact byte-counts # where possible. As these differ between platforms and compile options, we # define the 'release' builds as canonical and test them only in CI, so the @@ -841,19 +845,13 @@ case $CI_TARGET in fi fi setup_clang_toolchain - ENVOY_BINARY_DIR="${ENVOY_BUILD_DIR}/bin" - if [[ -e "${ENVOY_BINARY_DIR}" ]]; then - echo "Existing output directory found (${ENVOY_BINARY_DIR}), removing ..." - rm -rf "${ENVOY_BINARY_DIR}" - fi - mkdir -p "$ENVOY_BINARY_DIR" # As the binary build package enforces compiler options, adding here to ensure the tests and distribution build # reuse settings and any already compiled artefacts, the bundle itself will always be compiled # `--stripopt=--strip-all -c opt` BAZEL_RELEASE_OPTIONS=( --stripopt=--strip-all -c opt) - if [[ "$CI_TARGET" == "release" ]]; then + if [[ "$CI_TARGET" == "release" || "$CI_TARGET" == "release.test_only" ]]; then # Run release tests echo "Testing with:" echo " targets: ${TEST_TARGETS[*]}" @@ -865,6 +863,22 @@ case $CI_TARGET in "${BAZEL_RELEASE_OPTIONS[@]}" \ "${TEST_TARGETS[@]}" fi + + if [[ "$CI_TARGET" == "release.test_only" ]]; then + exit 0 + fi + + ENVOY_BINARY_DIR="${ENVOY_BUILD_DIR}/bin" + if [[ -e "${ENVOY_BINARY_DIR}" ]]; then + echo "Existing output directory found (${ENVOY_BINARY_DIR}), removing ..." + rm -rf "${ENVOY_BINARY_DIR}" + fi + mkdir -p "$ENVOY_BINARY_DIR" + + # Build + echo "Building with:" + echo " release options: ${BAZEL_RELEASE_OPTIONS[*]}" + # Build release binaries bazel build "${BAZEL_BUILD_OPTIONS[@]}" \ "${BAZEL_RELEASE_OPTIONS[@]}" \ From 793379603974968afa8b8b89f59be564e84ebae0 Mon Sep 17 00:00:00 2001 From: Kuo-Chung Hsu Date: Tue, 6 Aug 2024 08:48:41 -0700 Subject: [PATCH 041/130] report imcomplete metadata in complete events for thrift_to_metadata filter (#35574) Periodically http rq/resp with no `end_stream` filter events nor trailers event occur, which breaks the some assumptions. Hence, move checks to `decodeComplete` and `encodeComplete` Risk Level: low Testing: new unit Docs Changes: n/a Release Notes: n/a Platform Specific Features: n/a Signed-off-by: kuochunghsu --- .../filters/http/thrift_to_metadata/filter.cc | 16 ++--- .../filters/http/thrift_to_metadata/filter.h | 4 +- .../http/thrift_to_metadata/filter_test.cc | 67 +++++++++++++++++++ 3 files changed, 75 insertions(+), 12 deletions(-) diff --git a/source/extensions/filters/http/thrift_to_metadata/filter.cc b/source/extensions/filters/http/thrift_to_metadata/filter.cc index 6c4c0e858825..23753568d326 100644 --- a/source/extensions/filters/http/thrift_to_metadata/filter.cc +++ b/source/extensions/filters/http/thrift_to_metadata/filter.cc @@ -182,19 +182,17 @@ Http::FilterDataStatus Filter::decodeData(Buffer::Instance& data, bool end_strea : Http::FilterDataStatus::StopIterationAndBuffer; } -Http::FilterTrailersStatus Filter::decodeTrailers(Http::RequestTrailerMap&) { +void Filter::decodeComplete() { if (!config_->shouldParseRequestMetadata() || request_processing_finished_) { - return Http::FilterTrailersStatus::Continue; + return; } ENVOY_LOG(trace, - "thrift to metadata filter decodeTrailers: handle incomplete request thrift message"); + "thrift to metadata filter decodeComplete: handle incomplete request thrift message"); // Handle incomplete request thrift message while we reach here. handleAllOnMissing(config_->requestRules(), *decoder_callbacks_, request_processing_finished_); config_->rqstats().invalid_thrift_body_.inc(); - - return Http::FilterTrailersStatus::Continue; } Http::FilterHeadersStatus Filter::encodeHeaders(Http::ResponseHeaderMap& headers, bool end_stream) { @@ -246,19 +244,17 @@ Http::FilterDataStatus Filter::encodeData(Buffer::Instance& data, bool end_strea : Http::FilterDataStatus::StopIterationAndBuffer; } -Http::FilterTrailersStatus Filter::encodeTrailers(Http::ResponseTrailerMap&) { +void Filter::encodeComplete() { if (!config_->shouldParseResponseMetadata() || response_processing_finished_) { - return Http::FilterTrailersStatus::Continue; + return; } ENVOY_LOG(trace, - "thrift to metadata filter encodeTrailers: handle incomplete response thrift message"); + "thrift to metadata filter encodeComplete: handle incomplete response thrift message"); // Handle incomplete response thrift message while we reach here. handleAllOnMissing(config_->responseRules(), *encoder_callbacks_, response_processing_finished_); config_->respstats().invalid_thrift_body_.inc(); - - return Http::FilterTrailersStatus::Continue; } bool Filter::processData(Buffer::Instance& incoming_data, Buffer::Instance& buffer, diff --git a/source/extensions/filters/http/thrift_to_metadata/filter.h b/source/extensions/filters/http/thrift_to_metadata/filter.h index 18d89b78621d..fe28ce3767c7 100644 --- a/source/extensions/filters/http/thrift_to_metadata/filter.h +++ b/source/extensions/filters/http/thrift_to_metadata/filter.h @@ -181,11 +181,11 @@ class Filter : public Http::PassThroughFilter, Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap& headers, bool end_stream) override; Http::FilterDataStatus decodeData(Buffer::Instance& data, bool end_stream) override; - Http::FilterTrailersStatus decodeTrailers(Http::RequestTrailerMap& trailers) override; + void decodeComplete() override; Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap& headers, bool end_stream) override; Http::FilterDataStatus encodeData(Buffer::Instance& data, bool end_stream) override; - Http::FilterTrailersStatus encodeTrailers(Http::ResponseTrailerMap& trailers) override; + void encodeComplete() override; // PassThroughDecoderEventHandler FilterStatus messageBegin(MessageMetadataSharedPtr metadata) override; diff --git a/test/extensions/filters/http/thrift_to_metadata/filter_test.cc b/test/extensions/filters/http/thrift_to_metadata/filter_test.cc index 3153e5920b9c..c24be7992947 100644 --- a/test/extensions/filters/http/thrift_to_metadata/filter_test.cc +++ b/test/extensions/filters/http/thrift_to_metadata/filter_test.cc @@ -485,6 +485,39 @@ TEST_P(FilterTest, IncompleteRequestWithTrailer) { Http::TestRequestTrailerMapImpl trailers{{"some", "trailer"}}; EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(trailers)); + filter_->decodeComplete(); + + EXPECT_EQ(getCounterValue("thrift_to_metadata.rq.success"), 0); + EXPECT_EQ(getCounterValue("thrift_to_metadata.rq.mismatched_content_type"), 0); + EXPECT_EQ(getCounterValue("thrift_to_metadata.rq.no_body"), 0); + EXPECT_EQ(getCounterValue("thrift_to_metadata.rq.invalid_thrift_body"), 1); +} + +TEST_P(FilterTest, IncompleteRequestWithEarlyComplete) { + const auto [transport_type, protocol_type] = GetParam(); + MessageType message_type = MessageType::Call; + + initializeFilter(config_yaml_); + const std::map& expected_metadata = { + {"protocol", "unknown"}, + {"transport", "unknown"}, + {"request_message_type", "unknown"}, + {"method_name", "unknown"}}; + + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->decodeHeaders(request_headers_, false)); + EXPECT_CALL(decoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(stream_info_)); + EXPECT_CALL(stream_info_, setDynamicMetadata("envoy.lb", MapEq(expected_metadata))); + + Buffer::OwnedImpl whole_message; + writeMessage(whole_message, transport_type, protocol_type, message_type); + Buffer::OwnedImpl buffer; + // incomplete message + buffer.move(whole_message, whole_message.length() / 2); + EXPECT_EQ(Http::FilterDataStatus::StopIterationAndBuffer, filter_->decodeData(buffer, false)); + + filter_->decodeComplete(); + EXPECT_EQ(getCounterValue("thrift_to_metadata.rq.success"), 0); EXPECT_EQ(getCounterValue("thrift_to_metadata.rq.mismatched_content_type"), 0); EXPECT_EQ(getCounterValue("thrift_to_metadata.rq.no_body"), 0); @@ -518,6 +551,40 @@ TEST_P(FilterTest, IncompleteResponseWithTrailer) { Http::TestResponseTrailerMapImpl trailers{{"some", "trailer"}}; EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(trailers)); + filter_->encodeComplete(); + + EXPECT_EQ(getCounterValue("thrift_to_metadata.resp.success"), 0); + EXPECT_EQ(getCounterValue("thrift_to_metadata.resp.mismatched_content_type"), 0); + EXPECT_EQ(getCounterValue("thrift_to_metadata.resp.no_body"), 0); + EXPECT_EQ(getCounterValue("thrift_to_metadata.resp.invalid_thrift_body"), 1); +} + +TEST_P(FilterTest, IncompleteResponseEarlyComplete) { + const auto [transport_type, protocol_type] = GetParam(); + MessageType message_type = MessageType::Reply; + + initializeFilter(config_yaml_); + const std::map& expected_metadata = { + {"protocol", "unknown"}, + {"transport", "unknown"}, + {"response_message_type", "unknown"}, + {"method_name", "unknown"}, + {"response_reply_type", "unknown"}}; + + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->encodeHeaders(response_headers_, false)); + EXPECT_CALL(encoder_callbacks_, streamInfo()).WillRepeatedly(ReturnRef(stream_info_)); + EXPECT_CALL(stream_info_, setDynamicMetadata(HttpFilterNames::get().ThriftToMetadata, + MapEq(expected_metadata))); + + Buffer::OwnedImpl whole_message; + writeMessage(whole_message, transport_type, protocol_type, message_type, ReplyType::Success); + Buffer::OwnedImpl buffer; + // incomplete message + buffer.move(whole_message, whole_message.length() / 2); + EXPECT_EQ(Http::FilterDataStatus::StopIterationAndBuffer, filter_->encodeData(buffer, false)); + + filter_->encodeComplete(); EXPECT_EQ(getCounterValue("thrift_to_metadata.resp.success"), 0); EXPECT_EQ(getCounterValue("thrift_to_metadata.resp.mismatched_content_type"), 0); From 56be4f40fc5c90b6dd15a5242470c1fcf7ee4222 Mon Sep 17 00:00:00 2001 From: phlax Date: Tue, 6 Aug 2024 17:10:04 +0100 Subject: [PATCH 042/130] repo: Move examples to own repo (#35471) fix #32737 --------- Signed-off-by: Ryan Northey --- .github/dependabot.yml | 243 - .github/workflows/_stage_verify.yml | 9 +- .github/workflows/verify-requirements.in | 1 - BUILD | 7 - bazel/repositories.bzl | 2 + bazel/repository_locations.bzl | 14 + ci/do_ci.sh | 18 +- ci/verify_examples.sh | 84 - configs/BUILD | 29 +- docs/BUILD | 2 +- examples/BUILD | 114 - examples/DEVELOPER.md | 323 -- examples/_extra_certs/README.md | 7 - examples/_extra_certs/domain1.crt.pem | 21 - examples/_extra_certs/domain1.key.pem | 28 - examples/_extra_certs/domain2.crt.pem | 21 - examples/_extra_certs/domain2.key.pem | 28 - examples/_extra_certs/domain3.crt.pem | 21 - examples/_extra_certs/domain3.key.pem | 28 - examples/brotli/README.md | 2 - examples/brotli/docker-compose.yaml | 21 - examples/brotli/envoy.yaml | 238 - examples/brotli/example.rst | 95 - examples/brotli/verify.sh | 38 - examples/cache/README.md | 2 - examples/cache/ci-responses.yaml | 13 - examples/cache/docker-compose.yaml | 33 - examples/cache/envoy.yaml | 63 - examples/cache/example.rst | 245 - examples/cache/requirements.in | 1 - examples/cache/requirements.txt | 58 - examples/cache/responses.yaml | 13 - examples/cache/service.py | 84 - examples/cache/verify.sh | 98 - examples/cors/README.md | 2 - examples/cors/backend/docker-compose.yaml | 19 - examples/cors/backend/envoy.yaml | 97 - examples/cors/backend/service.py | 14 - examples/cors/example.rst | 101 - examples/cors/frontend/docker-compose.yaml | 19 - examples/cors/frontend/envoy.yaml | 48 - examples/cors/frontend/index.html | 73 - examples/cors/frontend/service.py | 18 - examples/cors/verify.sh | 56 - examples/csrf/README.md | 2 - examples/csrf/crosssite/docker-compose.yml | 19 - examples/csrf/crosssite/envoy.yaml | 45 - examples/csrf/crosssite/service.py | 18 - examples/csrf/example.rst | 110 - examples/csrf/index.html | 79 - examples/csrf/samesite/docker-compose.yml | 20 - examples/csrf/samesite/envoy.yaml | 123 - examples/csrf/samesite/service.py | 23 - examples/csrf/start_service.sh | 3 - examples/csrf/verify.sh | 78 - examples/double-proxy/README.md | 2 - examples/double-proxy/docker-compose.yaml | 84 - examples/double-proxy/envoy-backend.yaml | 49 - examples/double-proxy/envoy-frontend.yaml | 44 - examples/double-proxy/envoy.yaml | 42 - examples/double-proxy/example.rst | 190 - examples/double-proxy/requirements.in | 1 - examples/double-proxy/service.py | 22 - examples/double-proxy/verify.sh | 57 - examples/dynamic-config-cp/README.md | 2 - ...sponse-config-active-clusters-updated.json | 32 - .../response-config-active-clusters.json | 32 - .../_include/response-config-cluster.json | 31 - .../dynamic-config-cp/docker-compose.yaml | 31 - examples/dynamic-config-cp/envoy.yaml | 39 - examples/dynamic-config-cp/example.rst | 223 - examples/dynamic-config-cp/resource.go | 186 - examples/dynamic-config-cp/verify.sh | 82 - examples/dynamic-config-fs/README.md | 2 - ...sponse-config-active-clusters-updated.json | 31 - .../response-config-active-clusters.json | 31 - examples/dynamic-config-fs/configs/cds.yaml | 13 - examples/dynamic-config-fs/configs/lds.yaml | 28 - .../dynamic-config-fs/docker-compose.yaml | 23 - examples/dynamic-config-fs/envoy.yaml | 17 - examples/dynamic-config-fs/example.rst | 137 - examples/dynamic-config-fs/verify.sh | 33 - examples/ext_authz/.env | 1 - examples/ext_authz/Dockerfile-opa | 1 - examples/ext_authz/README.md | 2 - examples/ext_authz/auth/grpc-service/Makefile | 8 - examples/ext_authz/auth/grpc-service/go.mod | 21 - examples/ext_authz/auth/grpc-service/go.sum | 22 - examples/ext_authz/auth/grpc-service/main.go | 38 - .../auth/grpc-service/pkg/auth/users.go | 32 - .../auth/grpc-service/pkg/auth/v3/auth.go | 68 - .../ext_authz/auth/http-service/server.js | 29 - examples/ext_authz/auth/users.json | 5 - .../ext_authz/config/grpc-service/v3.yaml | 67 - examples/ext_authz/config/http-service.yaml | 67 - .../ext_authz/config/opa-service/policy.rego | 13 - examples/ext_authz/config/opa-service/v3.yaml | 67 - examples/ext_authz/docker-compose.yaml | 55 - examples/ext_authz/example.rst | 234 - examples/ext_authz/run_envoy.sh | 3 - .../ext_authz/upstream/service/service.py | 14 - examples/ext_authz/users.json | 5 - examples/ext_authz/verify.sh | 56 - examples/fault-injection/.gitignore | 1 - examples/fault-injection/Dockerfile-backend | 1 - examples/fault-injection/README.md | 2 - .../disable_abort_fault_injection.sh | 9 - .../disable_delay_fault_injection.sh | 9 - examples/fault-injection/docker-compose.yaml | 17 - .../enable_abort_fault_injection.sh | 10 - .../enable_delay_fault_injection.sh | 10 - examples/fault-injection/envoy.yaml | 64 - examples/fault-injection/example.rst | 118 - examples/fault-injection/send_request.sh | 7 - examples/fault-injection/verify.sh | 60 - examples/front-proxy/README.md | 2 - examples/front-proxy/docker-compose.yaml | 54 - examples/front-proxy/envoy.yaml | 168 - examples/front-proxy/example.rst | 333 -- examples/front-proxy/service-envoy-2.yaml | 47 - examples/front-proxy/service-envoy.yaml | 47 - examples/front-proxy/verify.sh | 44 - examples/go.mod | 0 examples/golang-http/README.md | 2 - examples/golang-http/docker-compose-go.yaml | 17 - examples/golang-http/docker-compose.yaml | 19 - examples/golang-http/envoy.yaml | 51 - examples/golang-http/example.rst | 92 - examples/golang-http/simple/config.go | 74 - examples/golang-http/simple/filter.go | 139 - examples/golang-http/simple/go.mod | 23 - examples/golang-http/simple/go.sum | 18 - examples/golang-http/verify.sh | 33 - examples/golang-network/Dockerfile-echo | 1 - examples/golang-network/README.md | 2 - .../golang-network/docker-compose-go.yaml | 17 - examples/golang-network/docker-compose.yaml | 19 - examples/golang-network/envoy.yaml | 25 - examples/golang-network/example.rst | 78 - examples/golang-network/simple/BUILD | 21 - examples/golang-network/simple/filter.go | 132 - examples/golang-network/simple/go.mod | 21 - examples/golang-network/simple/go.sum | 18 - examples/golang-network/verify.sh | 21 - examples/grpc-bridge/.gitignore | 3 - examples/grpc-bridge/Dockerfile-grpc-go | 1 - examples/grpc-bridge/Dockerfile-grpc-python | 1 - examples/grpc-bridge/README.md | 131 - examples/grpc-bridge/client/client.py | 85 - examples/grpc-bridge/client/envoy-proxy.yaml | 58 - examples/grpc-bridge/client/kv/__init__.py | 0 examples/grpc-bridge/client/requirements.in | 4 - examples/grpc-bridge/client/requirements.txt | 233 - .../grpc-bridge/docker-compose-protos.yaml | 22 - examples/grpc-bridge/docker-compose.yaml | 34 - examples/grpc-bridge/example.rst | 139 - examples/grpc-bridge/protos/kv.proto | 25 - examples/grpc-bridge/server/envoy-proxy.yaml | 51 - examples/grpc-bridge/server/go.mod | 10 - examples/grpc-bridge/server/go.sum | 2642 ---------- examples/grpc-bridge/server/kv/empty.go | 8 - examples/grpc-bridge/server/service.go | 64 - examples/grpc-bridge/verify.sh | 23 - examples/gzip/README.md | 2 - examples/gzip/docker-compose.yaml | 18 - examples/gzip/envoy.yaml | 129 - examples/gzip/example.rst | 124 - examples/gzip/verify.sh | 41 - examples/jaeger-tracing/README.md | 2 - examples/jaeger-tracing/docker-compose.yaml | 43 - examples/jaeger-tracing/envoy.yaml | 70 - examples/jaeger-tracing/example.rst | 98 - .../jaeger-tracing/service1-envoy-jaeger.yaml | 120 - .../jaeger-tracing/service2-envoy-jaeger.yaml | 70 - examples/jaeger-tracing/verify.sh | 20 - examples/kafka/Dockerfile-kafka | 1 - examples/kafka/Dockerfile-zookeeper | 1 - examples/kafka/README.md | 2 - examples/kafka/docker-compose.yaml | 54 - examples/kafka/envoy.yaml | 38 - examples/kafka/example.rst | 132 - examples/kafka/verify.sh | 81 - examples/load-reporting-service/README.md | 2 - .../docker-compose.yaml | 27 - examples/load-reporting-service/envoy.yaml | 69 - examples/load-reporting-service/example.rst | 90 - examples/load-reporting-service/go.mod | 21 - examples/load-reporting-service/go.sum | 22 - examples/load-reporting-service/main.go | 29 - .../load-reporting-service/send_requests.sh | 15 - .../server/lrs_server.go | 77 - examples/load-reporting-service/verify.sh | 35 - examples/local_ratelimit/Dockerfile-nginx | 1 - examples/local_ratelimit/README.md | 2 - examples/local_ratelimit/docker-compose.yaml | 14 - examples/local_ratelimit/envoy.yaml | 131 - examples/local_ratelimit/example.rst | 95 - examples/local_ratelimit/verify.sh | 82 - examples/locality-load-balancing/client.py | 20 - .../docker-compose.yaml | 52 - examples/locality-load-balancing/envoy.yaml | 108 - examples/locality-load-balancing/example.rst | 160 - examples/locality-load-balancing/service.py | 37 - examples/locality-load-balancing/verify.sh | 102 - examples/lua-cluster-specifier/README.md | 2 - .../lua-cluster-specifier/docker-compose.yaml | 8 - examples/lua-cluster-specifier/envoy.yaml | 56 - examples/lua-cluster-specifier/example.rst | 69 - examples/lua-cluster-specifier/verify.sh | 19 - examples/lua/README.md | 2 - examples/lua/docker-compose.yaml | 15 - examples/lua/envoy.yaml | 68 - examples/lua/example.rst | 75 - examples/lua/lib/mylibrary.lua | 7 - examples/lua/verify.sh | 21 - examples/mysql/Dockerfile-mysql | 1 - examples/mysql/README.md | 2 - examples/mysql/docker-compose.yaml | 20 - examples/mysql/envoy.yaml | 37 - examples/mysql/example.rst | 134 - examples/mysql/verify.sh | 37 - .../opentelemetry/Dockerfile-opentelemetry | 11 - examples/opentelemetry/README.md | 2 - examples/opentelemetry/docker-compose.yaml | 71 - examples/opentelemetry/envoy-1.yaml | 128 - examples/opentelemetry/envoy-2.yaml | 74 - examples/opentelemetry/envoy-front-proxy.yaml | 96 - examples/opentelemetry/example.rst | 121 - .../opentelemetry/otel-collector-config.yaml | 38 - examples/opentelemetry/verify.sh | 23 - examples/postgres/README.md | 2 - examples/postgres/docker-compose.yaml | 21 - examples/postgres/envoy.yaml | 37 - examples/postgres/example.rst | 148 - examples/postgres/verify.sh | 52 - examples/rbac/README.md | 2 - examples/rbac/docker-compose.yaml | 12 - examples/rbac/envoy.yaml | 79 - examples/rbac/example.rst | 100 - examples/rbac/verify.sh | 17 - examples/redis/Dockerfile-redis | 1 - examples/redis/README.md | 2 - examples/redis/docker-compose.yaml | 14 - examples/redis/envoy.yaml | 36 - examples/redis/example.rst | 72 - examples/redis/verify.sh | 31 - examples/route-mirror/README.md | 2 - examples/route-mirror/docker-compose.yaml | 45 - examples/route-mirror/envoy.yaml | 90 - examples/route-mirror/example.rst | 188 - examples/route-mirror/verify.sh | 36 - examples/shared/build/Dockerfile | 7 - examples/shared/build/build-entrypoint.sh | 13 - examples/shared/echo/Dockerfile | 1 - examples/shared/echo2/Dockerfile | 1 - examples/shared/envoy/Dockerfile | 96 - examples/shared/golang/Dockerfile | 77 - examples/shared/jaeger/Dockerfile | 7 - examples/shared/node/Dockerfile | 35 - examples/shared/node/routes.jq | 49 - examples/shared/node/scripts/build-routed.sh | 11 - examples/shared/node/scripts/build.sh | 4 - examples/shared/node/scripts/dev.sh | 4 - examples/shared/node/scripts/entrypoint.sh | 20 - examples/shared/postgres/Dockerfile | 3 - .../shared/postgres/docker-healthcheck.sh | 24 - examples/shared/python/Dockerfile | 82 - .../shared/python/aiohttp/requirements.in | 2 - .../shared/python/aiohttp/requirements.txt | 367 -- examples/shared/python/data-service.py | 30 - .../shared/python/postgres/requirements.in | 1 - .../shared/python/postgres/requirements.txt | 77 - examples/shared/python/service.py | 14 - examples/shared/python/tracing/service.py | 26 - examples/shared/python/tracing/service2.py | 60 - .../shared/python/tracing/start_service.sh | 3 - examples/shared/websocket/Dockerfile | 24 - examples/single-page-app/_github-clusters.yml | 34 - .../single-page-app/_static/spa-cookies.png | Bin 56881 -> 0 bytes .../_static/spa-github-oauth.png | Bin 22205 -> 0 bytes .../_static/spa-login-github.png | Bin 13084 -> 0 bytes .../single-page-app/_static/spa-login.png | Bin 14972 -> 0 bytes .../single-page-app/_static/spa-resources.png | Bin 38435 -> 0 bytes examples/single-page-app/docker-compose.yml | 82 - examples/single-page-app/envoy.yml | 325 -- examples/single-page-app/example.rst | 650 --- examples/single-page-app/hmac-secret.tmpl.yml | 6 - examples/single-page-app/myhub/api.py | 88 - examples/single-page-app/myhub/data.yml | 40 - .../myhub/images/users/envoy.svg | 1 - .../myhub/images/users/user0.png | Bin 298364 -> 0 bytes .../myhub/images/users/user1.png | Bin 336915 -> 0 bytes .../myhub/images/users/user2.png | Bin 233429 -> 0 bytes .../myhub/images/users/user3.png | Bin 378384 -> 0 bytes examples/single-page-app/myhub/myhub.py | 105 - examples/single-page-app/myhub/shared.py | 67 - .../secrets/myhub-token-secret.yml | 6 - .../single-page-app/token-secret.tmpl.yml | 6 - examples/single-page-app/ui/.env | 1 - examples/single-page-app/ui/.env.development | 1 - examples/single-page-app/ui/.env.production | 1 - examples/single-page-app/ui/.eslintrc.yml | 29 - examples/single-page-app/ui/index.html | 13 - examples/single-page-app/ui/package.json | 40 - examples/single-page-app/ui/public/envoy.svg | 1 - examples/single-page-app/ui/public/myhub.svg | 496 -- .../single-page-app/ui/src/@types/app.d.ts | 136 - examples/single-page-app/ui/src/App.tsx | 60 - .../ui/src/components/Auth.tsx | 22 - .../ui/src/components/Header.tsx | 38 - .../ui/src/components/Home.tsx | 66 - .../ui/src/components/Layout.tsx | 17 - .../ui/src/components/Login.tsx | 49 - .../ui/src/components/Logout.tsx | 7 - .../ui/src/components/Repos.tsx | 35 - .../ui/src/components/Resources.tsx | 96 - .../ui/src/components/User.tsx | 152 - .../ui/src/components/Users.tsx | 44 - examples/single-page-app/ui/src/hoc.tsx | 35 - examples/single-page-app/ui/src/main.tsx | 6 - examples/single-page-app/ui/src/myhub.tsx | 14 - examples/single-page-app/ui/src/providers.tsx | 13 - .../ui/src/store/reducer/index.tsx | 82 - examples/single-page-app/ui/src/vite-env.d.ts | 1 - examples/single-page-app/ui/tsconfig.json | 25 - .../single-page-app/ui/tsconfig.node.json | 10 - examples/single-page-app/ui/vite.config.ts | 10 - examples/single-page-app/ui/yarn.lock | 4382 ----------------- examples/single-page-app/verify.sh | 304 -- examples/single-page-app/xds/lds.tmpl.yml | 28 - examples/skywalking/Dockerfile-elasticsearch | 1 - examples/skywalking/Dockerfile-skywalking-oap | 1 - examples/skywalking/Dockerfile-skywalking-ui | 1 - examples/skywalking/README.md | 2 - .../_static/skywalking-services.png | Bin 248896 -> 0 bytes .../_static/skywalking-topology.png | Bin 327278 -> 0 bytes .../skywalking/_static/skywalking-trace.png | Bin 321182 -> 0 bytes examples/skywalking/docker-compose.yaml | 130 - examples/skywalking/envoy-1.yaml | 136 - examples/skywalking/envoy-2.yaml | 79 - examples/skywalking/envoy-front-proxy.yaml | 101 - examples/skywalking/example.rst | 131 - examples/skywalking/verify.sh | 61 - examples/tls-inspector/README.md | 2 - examples/tls-inspector/docker-compose.yaml | 40 - examples/tls-inspector/envoy.yaml | 79 - examples/tls-inspector/example.rst | 108 - examples/tls-inspector/verify.sh | 25 - examples/tls-sni/README.md | 2 - examples/tls-sni/docker-compose.yaml | 43 - examples/tls-sni/envoy-client.yaml | 91 - examples/tls-sni/envoy.yaml | 130 - examples/tls-sni/example.rst | 175 - examples/tls-sni/verify.sh | 56 - examples/tls/README.md | 2 - examples/tls/docker-compose.yaml | 53 - examples/tls/envoy-http-https.yaml | 46 - examples/tls/envoy-https-http.yaml | 106 - examples/tls/envoy-https-https.yaml | 110 - examples/tls/envoy-https-passthrough.yaml | 27 - examples/tls/example.rst | 179 - examples/tls/verify.sh | 37 - examples/udp/Dockerfile-udp | 1 - examples/udp/README.md | 2 - examples/udp/docker-compose.yaml | 14 - examples/udp/envoy.yaml | 40 - examples/udp/example.rst | 91 - examples/udp/verify.sh | 19 - examples/verify-common.sh | 252 - examples/vrp-litmus/Dockerfile-vrp | 1 - examples/vrp-litmus/README.md | 3 - examples/vrp-litmus/docker-compose.yaml | 11 - examples/vrp-litmus/verify.sh | 14 - examples/vrp-local/Dockerfile-vrp | 1 - examples/vrp-local/README.md | 3 - examples/vrp-local/docker-compose.yaml | 11 - examples/vrp-local/verify.sh | 75 - examples/wasm-cc/BUILD | 52 - examples/wasm-cc/Dockerfile-proxy | 5 - examples/wasm-cc/README.md | 2 - examples/wasm-cc/docker-compose-wasm.yaml | 38 - examples/wasm-cc/docker-compose.yaml | 17 - examples/wasm-cc/envoy.yaml | 128 - .../wasm-cc/envoy_filter_http_wasm_example.cc | 91 - .../envoy_filter_http_wasm_updated_example.cc | 91 - examples/wasm-cc/example.rst | 184 - .../lib/envoy_filter_http_wasm_example.wasm | Bin 59641 -> 0 bytes examples/wasm-cc/verify.sh | 60 - examples/websocket/README.md | 2 - examples/websocket/docker-compose.yaml | 63 - examples/websocket/envoy-ws-route.yaml | 49 - examples/websocket/envoy-ws.yaml | 43 - examples/websocket/envoy-wss-passthrough.yaml | 27 - examples/websocket/envoy-wss.yaml | 111 - examples/websocket/example.rst | 196 - examples/websocket/interact.sh | 30 - examples/websocket/verify.sh | 39 - .../win32-front-proxy/Dockerfile-frontenvoy | 6 - examples/win32-front-proxy/Dockerfile-service | 12 - examples/win32-front-proxy/README.md | 2 - .../win32-front-proxy/docker-compose.yaml | 24 - examples/win32-front-proxy/front-envoy.yaml | 170 - examples/win32-front-proxy/service-envoy.yaml | 47 - examples/win32-front-proxy/service.py | 31 - examples/win32-front-proxy/setup_python.ps1 | 54 - examples/win32-front-proxy/start_envoy.ps1 | 1 - examples/win32-front-proxy/start_service.ps1 | 4 - examples/zipkin/Dockerfile-zipkin | 1 - examples/zipkin/README.md | 2 - .../zipkin/_static/zipkin-ui-dependency.png | Bin 122831 -> 0 bytes examples/zipkin/_static/zipkin-ui.png | Bin 233848 -> 0 bytes examples/zipkin/docker-compose.yaml | 64 - examples/zipkin/envoy-1.yaml | 123 - examples/zipkin/envoy-2.yaml | 71 - examples/zipkin/envoy-front-proxy.yaml | 100 - examples/zipkin/example.rst | 127 - examples/zipkin/verify.sh | 23 - examples/zstd/README.md | 2 - examples/zstd/docker-compose.yaml | 15 - examples/zstd/envoy.yaml | 238 - examples/zstd/example.rst | 95 - examples/zstd/verify.sh | 33 - source/extensions/extensions_build_config.bzl | 4 +- 423 files changed, 45 insertions(+), 28550 deletions(-) delete mode 100644 .github/workflows/verify-requirements.in delete mode 100755 ci/verify_examples.sh delete mode 100644 examples/BUILD delete mode 100644 examples/DEVELOPER.md delete mode 100644 examples/_extra_certs/README.md delete mode 100644 examples/_extra_certs/domain1.crt.pem delete mode 100644 examples/_extra_certs/domain1.key.pem delete mode 100644 examples/_extra_certs/domain2.crt.pem delete mode 100644 examples/_extra_certs/domain2.key.pem delete mode 100644 examples/_extra_certs/domain3.crt.pem delete mode 100644 examples/_extra_certs/domain3.key.pem delete mode 100644 examples/brotli/README.md delete mode 100644 examples/brotli/docker-compose.yaml delete mode 100644 examples/brotli/envoy.yaml delete mode 100644 examples/brotli/example.rst delete mode 100755 examples/brotli/verify.sh delete mode 100644 examples/cache/README.md delete mode 100644 examples/cache/ci-responses.yaml delete mode 100644 examples/cache/docker-compose.yaml delete mode 100644 examples/cache/envoy.yaml delete mode 100644 examples/cache/example.rst delete mode 100644 examples/cache/requirements.in delete mode 100644 examples/cache/requirements.txt delete mode 100644 examples/cache/responses.yaml delete mode 100644 examples/cache/service.py delete mode 100755 examples/cache/verify.sh delete mode 100644 examples/cors/README.md delete mode 100644 examples/cors/backend/docker-compose.yaml delete mode 100644 examples/cors/backend/envoy.yaml delete mode 100644 examples/cors/backend/service.py delete mode 100644 examples/cors/example.rst delete mode 100644 examples/cors/frontend/docker-compose.yaml delete mode 100644 examples/cors/frontend/envoy.yaml delete mode 100644 examples/cors/frontend/index.html delete mode 100644 examples/cors/frontend/service.py delete mode 100755 examples/cors/verify.sh delete mode 100644 examples/csrf/README.md delete mode 100644 examples/csrf/crosssite/docker-compose.yml delete mode 100644 examples/csrf/crosssite/envoy.yaml delete mode 100644 examples/csrf/crosssite/service.py delete mode 100644 examples/csrf/example.rst delete mode 100644 examples/csrf/index.html delete mode 100644 examples/csrf/samesite/docker-compose.yml delete mode 100644 examples/csrf/samesite/envoy.yaml delete mode 100644 examples/csrf/samesite/service.py delete mode 100644 examples/csrf/start_service.sh delete mode 100755 examples/csrf/verify.sh delete mode 100644 examples/double-proxy/README.md delete mode 100644 examples/double-proxy/docker-compose.yaml delete mode 100644 examples/double-proxy/envoy-backend.yaml delete mode 100644 examples/double-proxy/envoy-frontend.yaml delete mode 100644 examples/double-proxy/envoy.yaml delete mode 100644 examples/double-proxy/example.rst delete mode 100644 examples/double-proxy/requirements.in delete mode 100644 examples/double-proxy/service.py delete mode 100755 examples/double-proxy/verify.sh delete mode 100644 examples/dynamic-config-cp/README.md delete mode 100644 examples/dynamic-config-cp/_include/response-config-active-clusters-updated.json delete mode 100644 examples/dynamic-config-cp/_include/response-config-active-clusters.json delete mode 100644 examples/dynamic-config-cp/_include/response-config-cluster.json delete mode 100644 examples/dynamic-config-cp/docker-compose.yaml delete mode 100644 examples/dynamic-config-cp/envoy.yaml delete mode 100644 examples/dynamic-config-cp/example.rst delete mode 100644 examples/dynamic-config-cp/resource.go delete mode 100755 examples/dynamic-config-cp/verify.sh delete mode 100644 examples/dynamic-config-fs/README.md delete mode 100644 examples/dynamic-config-fs/_include/response-config-active-clusters-updated.json delete mode 100644 examples/dynamic-config-fs/_include/response-config-active-clusters.json delete mode 100644 examples/dynamic-config-fs/configs/cds.yaml delete mode 100644 examples/dynamic-config-fs/configs/lds.yaml delete mode 100644 examples/dynamic-config-fs/docker-compose.yaml delete mode 100644 examples/dynamic-config-fs/envoy.yaml delete mode 100644 examples/dynamic-config-fs/example.rst delete mode 100755 examples/dynamic-config-fs/verify.sh delete mode 100644 examples/ext_authz/.env delete mode 100644 examples/ext_authz/Dockerfile-opa delete mode 100644 examples/ext_authz/README.md delete mode 100644 examples/ext_authz/auth/grpc-service/Makefile delete mode 100644 examples/ext_authz/auth/grpc-service/go.mod delete mode 100644 examples/ext_authz/auth/grpc-service/go.sum delete mode 100644 examples/ext_authz/auth/grpc-service/main.go delete mode 100644 examples/ext_authz/auth/grpc-service/pkg/auth/users.go delete mode 100644 examples/ext_authz/auth/grpc-service/pkg/auth/v3/auth.go delete mode 100644 examples/ext_authz/auth/http-service/server.js delete mode 100644 examples/ext_authz/auth/users.json delete mode 100644 examples/ext_authz/config/grpc-service/v3.yaml delete mode 100644 examples/ext_authz/config/http-service.yaml delete mode 100644 examples/ext_authz/config/opa-service/policy.rego delete mode 100644 examples/ext_authz/config/opa-service/v3.yaml delete mode 100644 examples/ext_authz/docker-compose.yaml delete mode 100644 examples/ext_authz/example.rst delete mode 100755 examples/ext_authz/run_envoy.sh delete mode 100644 examples/ext_authz/upstream/service/service.py delete mode 100644 examples/ext_authz/users.json delete mode 100755 examples/ext_authz/verify.sh delete mode 100644 examples/fault-injection/.gitignore delete mode 100644 examples/fault-injection/Dockerfile-backend delete mode 100644 examples/fault-injection/README.md delete mode 100755 examples/fault-injection/disable_abort_fault_injection.sh delete mode 100755 examples/fault-injection/disable_delay_fault_injection.sh delete mode 100644 examples/fault-injection/docker-compose.yaml delete mode 100755 examples/fault-injection/enable_abort_fault_injection.sh delete mode 100755 examples/fault-injection/enable_delay_fault_injection.sh delete mode 100644 examples/fault-injection/envoy.yaml delete mode 100644 examples/fault-injection/example.rst delete mode 100755 examples/fault-injection/send_request.sh delete mode 100755 examples/fault-injection/verify.sh delete mode 100644 examples/front-proxy/README.md delete mode 100644 examples/front-proxy/docker-compose.yaml delete mode 100644 examples/front-proxy/envoy.yaml delete mode 100644 examples/front-proxy/example.rst delete mode 100644 examples/front-proxy/service-envoy-2.yaml delete mode 100644 examples/front-proxy/service-envoy.yaml delete mode 100755 examples/front-proxy/verify.sh delete mode 100644 examples/go.mod delete mode 100644 examples/golang-http/README.md delete mode 100644 examples/golang-http/docker-compose-go.yaml delete mode 100644 examples/golang-http/docker-compose.yaml delete mode 100644 examples/golang-http/envoy.yaml delete mode 100644 examples/golang-http/example.rst delete mode 100644 examples/golang-http/simple/config.go delete mode 100644 examples/golang-http/simple/filter.go delete mode 100644 examples/golang-http/simple/go.mod delete mode 100644 examples/golang-http/simple/go.sum delete mode 100755 examples/golang-http/verify.sh delete mode 100644 examples/golang-network/Dockerfile-echo delete mode 100644 examples/golang-network/README.md delete mode 100644 examples/golang-network/docker-compose-go.yaml delete mode 100644 examples/golang-network/docker-compose.yaml delete mode 100644 examples/golang-network/envoy.yaml delete mode 100644 examples/golang-network/example.rst delete mode 100644 examples/golang-network/simple/BUILD delete mode 100644 examples/golang-network/simple/filter.go delete mode 100644 examples/golang-network/simple/go.mod delete mode 100644 examples/golang-network/simple/go.sum delete mode 100755 examples/golang-network/verify.sh delete mode 100644 examples/grpc-bridge/.gitignore delete mode 100644 examples/grpc-bridge/Dockerfile-grpc-go delete mode 100644 examples/grpc-bridge/Dockerfile-grpc-python delete mode 100644 examples/grpc-bridge/README.md delete mode 100755 examples/grpc-bridge/client/client.py delete mode 100644 examples/grpc-bridge/client/envoy-proxy.yaml delete mode 100644 examples/grpc-bridge/client/kv/__init__.py delete mode 100644 examples/grpc-bridge/client/requirements.in delete mode 100644 examples/grpc-bridge/client/requirements.txt delete mode 100644 examples/grpc-bridge/docker-compose-protos.yaml delete mode 100644 examples/grpc-bridge/docker-compose.yaml delete mode 100644 examples/grpc-bridge/example.rst delete mode 100644 examples/grpc-bridge/protos/kv.proto delete mode 100644 examples/grpc-bridge/server/envoy-proxy.yaml delete mode 100644 examples/grpc-bridge/server/go.mod delete mode 100644 examples/grpc-bridge/server/go.sum delete mode 100644 examples/grpc-bridge/server/kv/empty.go delete mode 100644 examples/grpc-bridge/server/service.go delete mode 100755 examples/grpc-bridge/verify.sh delete mode 100644 examples/gzip/README.md delete mode 100644 examples/gzip/docker-compose.yaml delete mode 100644 examples/gzip/envoy.yaml delete mode 100644 examples/gzip/example.rst delete mode 100755 examples/gzip/verify.sh delete mode 100644 examples/jaeger-tracing/README.md delete mode 100644 examples/jaeger-tracing/docker-compose.yaml delete mode 100644 examples/jaeger-tracing/envoy.yaml delete mode 100644 examples/jaeger-tracing/example.rst delete mode 100644 examples/jaeger-tracing/service1-envoy-jaeger.yaml delete mode 100644 examples/jaeger-tracing/service2-envoy-jaeger.yaml delete mode 100755 examples/jaeger-tracing/verify.sh delete mode 100644 examples/kafka/Dockerfile-kafka delete mode 100644 examples/kafka/Dockerfile-zookeeper delete mode 100644 examples/kafka/README.md delete mode 100644 examples/kafka/docker-compose.yaml delete mode 100644 examples/kafka/envoy.yaml delete mode 100644 examples/kafka/example.rst delete mode 100755 examples/kafka/verify.sh delete mode 100644 examples/load-reporting-service/README.md delete mode 100644 examples/load-reporting-service/docker-compose.yaml delete mode 100644 examples/load-reporting-service/envoy.yaml delete mode 100644 examples/load-reporting-service/example.rst delete mode 100644 examples/load-reporting-service/go.mod delete mode 100644 examples/load-reporting-service/go.sum delete mode 100644 examples/load-reporting-service/main.go delete mode 100644 examples/load-reporting-service/send_requests.sh delete mode 100644 examples/load-reporting-service/server/lrs_server.go delete mode 100755 examples/load-reporting-service/verify.sh delete mode 100644 examples/local_ratelimit/Dockerfile-nginx delete mode 100644 examples/local_ratelimit/README.md delete mode 100644 examples/local_ratelimit/docker-compose.yaml delete mode 100644 examples/local_ratelimit/envoy.yaml delete mode 100644 examples/local_ratelimit/example.rst delete mode 100755 examples/local_ratelimit/verify.sh delete mode 100644 examples/locality-load-balancing/client.py delete mode 100644 examples/locality-load-balancing/docker-compose.yaml delete mode 100644 examples/locality-load-balancing/envoy.yaml delete mode 100644 examples/locality-load-balancing/example.rst delete mode 100644 examples/locality-load-balancing/service.py delete mode 100755 examples/locality-load-balancing/verify.sh delete mode 100644 examples/lua-cluster-specifier/README.md delete mode 100644 examples/lua-cluster-specifier/docker-compose.yaml delete mode 100644 examples/lua-cluster-specifier/envoy.yaml delete mode 100644 examples/lua-cluster-specifier/example.rst delete mode 100755 examples/lua-cluster-specifier/verify.sh delete mode 100644 examples/lua/README.md delete mode 100644 examples/lua/docker-compose.yaml delete mode 100644 examples/lua/envoy.yaml delete mode 100644 examples/lua/example.rst delete mode 100644 examples/lua/lib/mylibrary.lua delete mode 100755 examples/lua/verify.sh delete mode 100644 examples/mysql/Dockerfile-mysql delete mode 100644 examples/mysql/README.md delete mode 100644 examples/mysql/docker-compose.yaml delete mode 100644 examples/mysql/envoy.yaml delete mode 100644 examples/mysql/example.rst delete mode 100755 examples/mysql/verify.sh delete mode 100644 examples/opentelemetry/Dockerfile-opentelemetry delete mode 100644 examples/opentelemetry/README.md delete mode 100644 examples/opentelemetry/docker-compose.yaml delete mode 100644 examples/opentelemetry/envoy-1.yaml delete mode 100644 examples/opentelemetry/envoy-2.yaml delete mode 100644 examples/opentelemetry/envoy-front-proxy.yaml delete mode 100644 examples/opentelemetry/example.rst delete mode 100755 examples/opentelemetry/otel-collector-config.yaml delete mode 100755 examples/opentelemetry/verify.sh delete mode 100644 examples/postgres/README.md delete mode 100644 examples/postgres/docker-compose.yaml delete mode 100644 examples/postgres/envoy.yaml delete mode 100644 examples/postgres/example.rst delete mode 100755 examples/postgres/verify.sh delete mode 100644 examples/rbac/README.md delete mode 100644 examples/rbac/docker-compose.yaml delete mode 100644 examples/rbac/envoy.yaml delete mode 100644 examples/rbac/example.rst delete mode 100755 examples/rbac/verify.sh delete mode 100644 examples/redis/Dockerfile-redis delete mode 100644 examples/redis/README.md delete mode 100644 examples/redis/docker-compose.yaml delete mode 100644 examples/redis/envoy.yaml delete mode 100644 examples/redis/example.rst delete mode 100755 examples/redis/verify.sh delete mode 100644 examples/route-mirror/README.md delete mode 100644 examples/route-mirror/docker-compose.yaml delete mode 100644 examples/route-mirror/envoy.yaml delete mode 100644 examples/route-mirror/example.rst delete mode 100755 examples/route-mirror/verify.sh delete mode 100644 examples/shared/build/Dockerfile delete mode 100755 examples/shared/build/build-entrypoint.sh delete mode 100644 examples/shared/echo/Dockerfile delete mode 100644 examples/shared/echo2/Dockerfile delete mode 100644 examples/shared/envoy/Dockerfile delete mode 100644 examples/shared/golang/Dockerfile delete mode 100644 examples/shared/jaeger/Dockerfile delete mode 100644 examples/shared/node/Dockerfile delete mode 100644 examples/shared/node/routes.jq delete mode 100755 examples/shared/node/scripts/build-routed.sh delete mode 100644 examples/shared/node/scripts/build.sh delete mode 100755 examples/shared/node/scripts/dev.sh delete mode 100755 examples/shared/node/scripts/entrypoint.sh delete mode 100644 examples/shared/postgres/Dockerfile delete mode 100755 examples/shared/postgres/docker-healthcheck.sh delete mode 100644 examples/shared/python/Dockerfile delete mode 100644 examples/shared/python/aiohttp/requirements.in delete mode 100644 examples/shared/python/aiohttp/requirements.txt delete mode 100644 examples/shared/python/data-service.py delete mode 100644 examples/shared/python/postgres/requirements.in delete mode 100644 examples/shared/python/postgres/requirements.txt delete mode 100644 examples/shared/python/service.py delete mode 100644 examples/shared/python/tracing/service.py delete mode 100644 examples/shared/python/tracing/service2.py delete mode 100644 examples/shared/python/tracing/start_service.sh delete mode 100644 examples/shared/websocket/Dockerfile delete mode 100644 examples/single-page-app/_github-clusters.yml delete mode 100644 examples/single-page-app/_static/spa-cookies.png delete mode 100644 examples/single-page-app/_static/spa-github-oauth.png delete mode 100644 examples/single-page-app/_static/spa-login-github.png delete mode 100644 examples/single-page-app/_static/spa-login.png delete mode 100644 examples/single-page-app/_static/spa-resources.png delete mode 100644 examples/single-page-app/docker-compose.yml delete mode 100644 examples/single-page-app/envoy.yml delete mode 100644 examples/single-page-app/example.rst delete mode 100644 examples/single-page-app/hmac-secret.tmpl.yml delete mode 100755 examples/single-page-app/myhub/api.py delete mode 100644 examples/single-page-app/myhub/data.yml delete mode 100644 examples/single-page-app/myhub/images/users/envoy.svg delete mode 100644 examples/single-page-app/myhub/images/users/user0.png delete mode 100644 examples/single-page-app/myhub/images/users/user1.png delete mode 100644 examples/single-page-app/myhub/images/users/user2.png delete mode 100644 examples/single-page-app/myhub/images/users/user3.png delete mode 100755 examples/single-page-app/myhub/myhub.py delete mode 100644 examples/single-page-app/myhub/shared.py delete mode 100644 examples/single-page-app/secrets/myhub-token-secret.yml delete mode 100644 examples/single-page-app/token-secret.tmpl.yml delete mode 100644 examples/single-page-app/ui/.env delete mode 100644 examples/single-page-app/ui/.env.development delete mode 100644 examples/single-page-app/ui/.env.production delete mode 100644 examples/single-page-app/ui/.eslintrc.yml delete mode 100644 examples/single-page-app/ui/index.html delete mode 100644 examples/single-page-app/ui/package.json delete mode 100644 examples/single-page-app/ui/public/envoy.svg delete mode 100644 examples/single-page-app/ui/public/myhub.svg delete mode 100644 examples/single-page-app/ui/src/@types/app.d.ts delete mode 100644 examples/single-page-app/ui/src/App.tsx delete mode 100644 examples/single-page-app/ui/src/components/Auth.tsx delete mode 100644 examples/single-page-app/ui/src/components/Header.tsx delete mode 100644 examples/single-page-app/ui/src/components/Home.tsx delete mode 100644 examples/single-page-app/ui/src/components/Layout.tsx delete mode 100644 examples/single-page-app/ui/src/components/Login.tsx delete mode 100644 examples/single-page-app/ui/src/components/Logout.tsx delete mode 100644 examples/single-page-app/ui/src/components/Repos.tsx delete mode 100644 examples/single-page-app/ui/src/components/Resources.tsx delete mode 100644 examples/single-page-app/ui/src/components/User.tsx delete mode 100644 examples/single-page-app/ui/src/components/Users.tsx delete mode 100644 examples/single-page-app/ui/src/hoc.tsx delete mode 100644 examples/single-page-app/ui/src/main.tsx delete mode 100644 examples/single-page-app/ui/src/myhub.tsx delete mode 100644 examples/single-page-app/ui/src/providers.tsx delete mode 100644 examples/single-page-app/ui/src/store/reducer/index.tsx delete mode 100644 examples/single-page-app/ui/src/vite-env.d.ts delete mode 100644 examples/single-page-app/ui/tsconfig.json delete mode 100644 examples/single-page-app/ui/tsconfig.node.json delete mode 100644 examples/single-page-app/ui/vite.config.ts delete mode 100644 examples/single-page-app/ui/yarn.lock delete mode 100755 examples/single-page-app/verify.sh delete mode 100644 examples/single-page-app/xds/lds.tmpl.yml delete mode 100644 examples/skywalking/Dockerfile-elasticsearch delete mode 100644 examples/skywalking/Dockerfile-skywalking-oap delete mode 100644 examples/skywalking/Dockerfile-skywalking-ui delete mode 100644 examples/skywalking/README.md delete mode 100644 examples/skywalking/_static/skywalking-services.png delete mode 100644 examples/skywalking/_static/skywalking-topology.png delete mode 100644 examples/skywalking/_static/skywalking-trace.png delete mode 100644 examples/skywalking/docker-compose.yaml delete mode 100644 examples/skywalking/envoy-1.yaml delete mode 100644 examples/skywalking/envoy-2.yaml delete mode 100644 examples/skywalking/envoy-front-proxy.yaml delete mode 100644 examples/skywalking/example.rst delete mode 100755 examples/skywalking/verify.sh delete mode 100644 examples/tls-inspector/README.md delete mode 100644 examples/tls-inspector/docker-compose.yaml delete mode 100644 examples/tls-inspector/envoy.yaml delete mode 100644 examples/tls-inspector/example.rst delete mode 100755 examples/tls-inspector/verify.sh delete mode 100644 examples/tls-sni/README.md delete mode 100644 examples/tls-sni/docker-compose.yaml delete mode 100644 examples/tls-sni/envoy-client.yaml delete mode 100644 examples/tls-sni/envoy.yaml delete mode 100644 examples/tls-sni/example.rst delete mode 100755 examples/tls-sni/verify.sh delete mode 100644 examples/tls/README.md delete mode 100644 examples/tls/docker-compose.yaml delete mode 100644 examples/tls/envoy-http-https.yaml delete mode 100644 examples/tls/envoy-https-http.yaml delete mode 100644 examples/tls/envoy-https-https.yaml delete mode 100644 examples/tls/envoy-https-passthrough.yaml delete mode 100644 examples/tls/example.rst delete mode 100755 examples/tls/verify.sh delete mode 100644 examples/udp/Dockerfile-udp delete mode 100644 examples/udp/README.md delete mode 100644 examples/udp/docker-compose.yaml delete mode 100644 examples/udp/envoy.yaml delete mode 100644 examples/udp/example.rst delete mode 100755 examples/udp/verify.sh delete mode 100644 examples/verify-common.sh delete mode 100644 examples/vrp-litmus/Dockerfile-vrp delete mode 100644 examples/vrp-litmus/README.md delete mode 100644 examples/vrp-litmus/docker-compose.yaml delete mode 100755 examples/vrp-litmus/verify.sh delete mode 100644 examples/vrp-local/Dockerfile-vrp delete mode 100644 examples/vrp-local/README.md delete mode 100644 examples/vrp-local/docker-compose.yaml delete mode 100755 examples/vrp-local/verify.sh delete mode 100644 examples/wasm-cc/BUILD delete mode 100644 examples/wasm-cc/Dockerfile-proxy delete mode 100644 examples/wasm-cc/README.md delete mode 100644 examples/wasm-cc/docker-compose-wasm.yaml delete mode 100644 examples/wasm-cc/docker-compose.yaml delete mode 100644 examples/wasm-cc/envoy.yaml delete mode 100644 examples/wasm-cc/envoy_filter_http_wasm_example.cc delete mode 100644 examples/wasm-cc/envoy_filter_http_wasm_updated_example.cc delete mode 100644 examples/wasm-cc/example.rst delete mode 100755 examples/wasm-cc/lib/envoy_filter_http_wasm_example.wasm delete mode 100755 examples/wasm-cc/verify.sh delete mode 100644 examples/websocket/README.md delete mode 100644 examples/websocket/docker-compose.yaml delete mode 100644 examples/websocket/envoy-ws-route.yaml delete mode 100644 examples/websocket/envoy-ws.yaml delete mode 100644 examples/websocket/envoy-wss-passthrough.yaml delete mode 100644 examples/websocket/envoy-wss.yaml delete mode 100644 examples/websocket/example.rst delete mode 100644 examples/websocket/interact.sh delete mode 100755 examples/websocket/verify.sh delete mode 100644 examples/win32-front-proxy/Dockerfile-frontenvoy delete mode 100644 examples/win32-front-proxy/Dockerfile-service delete mode 100644 examples/win32-front-proxy/README.md delete mode 100644 examples/win32-front-proxy/docker-compose.yaml delete mode 100644 examples/win32-front-proxy/front-envoy.yaml delete mode 100644 examples/win32-front-proxy/service-envoy.yaml delete mode 100644 examples/win32-front-proxy/service.py delete mode 100644 examples/win32-front-proxy/setup_python.ps1 delete mode 100644 examples/win32-front-proxy/start_envoy.ps1 delete mode 100644 examples/win32-front-proxy/start_service.ps1 delete mode 100644 examples/zipkin/Dockerfile-zipkin delete mode 100644 examples/zipkin/README.md delete mode 100644 examples/zipkin/_static/zipkin-ui-dependency.png delete mode 100644 examples/zipkin/_static/zipkin-ui.png delete mode 100644 examples/zipkin/docker-compose.yaml delete mode 100644 examples/zipkin/envoy-1.yaml delete mode 100644 examples/zipkin/envoy-2.yaml delete mode 100644 examples/zipkin/envoy-front-proxy.yaml delete mode 100644 examples/zipkin/example.rst delete mode 100755 examples/zipkin/verify.sh delete mode 100644 examples/zstd/README.md delete mode 100644 examples/zstd/docker-compose.yaml delete mode 100644 examples/zstd/envoy.yaml delete mode 100644 examples/zstd/example.rst delete mode 100755 examples/zstd/verify.sh diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c9d1d9ac09c1..eaebf612ac58 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,46 +9,6 @@ updates: # # Please ensure any new ones are added here, and any that are removed are removed here also. -- package-ecosystem: "pip" - directory: "/examples/grpc-bridge/client" - groups: - examples-grpc-bridge: - patterns: - - "*" - schedule: - interval: "daily" - time: "06:00" - -- package-ecosystem: "pip" - directory: "/examples/cache" - groups: - examples-cache: - patterns: - - "*" - schedule: - interval: "daily" - time: "06:00" - -- package-ecosystem: "pip" - directory: "/examples/shared/python/aiohttp" - groups: - examples-shared-python: - patterns: - - "*" - schedule: - interval: "daily" - time: "06:00" - -- package-ecosystem: "pip" - directory: "/examples/shared/python/postgres" - groups: - examples-postgres: - patterns: - - "*" - schedule: - interval: "daily" - time: "06:00" - - package-ecosystem: "pip" directory: "/tools/base" open-pull-requests-limit: 20 @@ -68,153 +28,6 @@ updates: interval: daily time: "06:00" -- package-ecosystem: "docker" - directory: "/examples/ext_authz" - groups: - examples-ext-authz: - patterns: - - "*" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/fault-injection" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/golang-network" - groups: - examples-golang-network: - patterns: - - "*" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/grpc-bridge" - groups: - examples-grpc-bridge: - patterns: - - "*" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/kafka" - groups: - examples-kafka: - patterns: - - "*" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/local_ratelimit" - groups: - examples-local-ratelimit: - patterns: - - "*" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/mysql" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/opentelemetry" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/redis" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/shared/build" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/shared/echo" - schedule: - interval: daily - time: "06:00" - -# TODO(phlax): just use above -- package-ecosystem: "docker" - directory: "/examples/shared/echo2" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/shared/golang" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/shared/jaeger" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/shared/node" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/shared/postgres" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/shared/python" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/shared/websocket" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/skywalking" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/udp" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "docker" - directory: "/examples/zipkin" - schedule: - interval: daily - time: "06:00" - - package-ecosystem: "github-actions" directory: "/" schedule: @@ -348,59 +161,3 @@ updates: schedule: interval: daily time: "06:00" - -- package-ecosystem: "gomod" - directory: "/examples/ext_authz/auth/grpc-service" - groups: - examples-ext-authz: - patterns: - - "*" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "gomod" - directory: "/examples/load-reporting-service" - groups: - examples-load-reporting: - patterns: - - "*" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "gomod" - directory: "/examples/grpc-bridge/server" - groups: - examples-grpc-bridge: - patterns: - - "*" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "gomod" - directory: "/examples/golang-http/simple" - groups: - examples-golang-http: - patterns: - - "*" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "gomod" - directory: "/examples/golang-network/simple" - groups: - examples-golang-network: - patterns: - - "*" - schedule: - interval: daily - time: "06:00" - -- package-ecosystem: "npm" - directory: "/examples/single-page-app/ui" - schedule: - interval: daily - time: "06:00" diff --git a/.github/workflows/_stage_verify.yml b/.github/workflows/_stage_verify.yml index c6de8a7c1bf6..1377a5cb6cfb 100644 --- a/.github/workflows/_stage_verify.yml +++ b/.github/workflows/_stage_verify.yml @@ -34,7 +34,7 @@ jobs: container-command: rbe: ${{ matrix.rbe }} request: ${{ inputs.request }} - runs-on: envoy-x64-small + runs-on: ubuntu-24.04 steps-pre: ${{ matrix.steps-pre }} source: ${{ matrix.source }} target: ${{ matrix.target }} @@ -62,8 +62,8 @@ jobs: > /dev/null sudo apt-get update sudo apt-get install -y -qq --allow-downgrades \ - docker-ce=5:24.0.9-1~ubuntu.22.04~jammy \ - docker-ce-cli=5:24.0.9-1~ubuntu.22.04~jammy + docker-ce=5:24.0.9-1~ubuntu.24.04~noble \ + docker-ce-cli=5:24.0.9-1~ubuntu.24.04~noble sudo systemctl restart docker sudo docker --version shell: bash @@ -102,6 +102,5 @@ jobs: # Install expected host packages export DEBIAN_FRONTEND=noninteractive sudo apt-get -qq update -y - sudo apt-get -qq install -y --no-install-recommends expect gettext whois - pip install -r ./.github/workflows/verify-requirements.txt + sudo apt-get -qq install -y --no-install-recommends expect gettext yq whois shell: bash diff --git a/.github/workflows/verify-requirements.in b/.github/workflows/verify-requirements.in deleted file mode 100644 index 87de2e955af3..000000000000 --- a/.github/workflows/verify-requirements.in +++ /dev/null @@ -1 +0,0 @@ -yq diff --git a/BUILD b/BUILD index 903253c8601c..c7b88cdece13 100644 --- a/BUILD +++ b/BUILD @@ -69,13 +69,6 @@ package_group( ], ) -package_group( - name = "examples_library", - packages = [ - "//examples/...", - ], -) - package_group( name = "mobile_library", packages = [ diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index e54d1161e99d..c7ac92f05e98 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -361,6 +361,8 @@ def envoy_dependencies(skip_targets = []): external_http_archive("bazel_toolchains") external_http_archive("bazel_compdb") external_http_archive("envoy_build_tools") + external_http_archive(name = "envoy_examples") + _com_github_maxmind_libmaxminddb() external_http_archive("rules_pkg") diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index da722fb42bea..1e2ea67ec6d9 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -92,6 +92,20 @@ REPOSITORY_LOCATIONS_SPEC = dict( urls = ["https://github.com/bazelbuild/buildtools/archive/v{version}.tar.gz"], use_category = ["test_only"], ), + envoy_examples = dict( + project_name = "envoy_examples", + project_desc = "Envoy proxy examples", + project_url = "https://github.com/envoyproxy/examples", + version = "6aa9c3730c91eddaf0c69cc7cbe5294d3da74310", + sha256 = "9bb97f256bedf68cc6696eb7bd8dbb9d0776c137fb88d028e5aa674cfc8ab467", + strip_prefix = "examples-{version}", + urls = ["https://github.com/envoyproxy/examples/archive/{version}.tar.gz"], + use_category = ["test_only"], + release_date = "2024-08-02", + cpe = "N/A", + license = "Apache-2.0", + license_url = "https://github.com/envoyproxy/examples/blob/{version}/LICENSE", + ), rules_fuzzing = dict( project_name = "Fuzzing Rules for Bazel", project_desc = "Bazel rules for fuzz tests", diff --git a/ci/do_ci.sh b/ci/do_ci.sh index f0f4687323e1..8f3db2df7474 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -233,16 +233,6 @@ function bazel_contrib_binary_build() { bazel_binary_build "$1" "${ENVOY_CONTRIB_BUILD_TARGET}" "${ENVOY_CONTRIB_BUILD_DEBUG_INFORMATION}" envoy-contrib } -function run_ci_verify () { - export DOCKER_NO_PULL=1 - export DOCKER_RMI_CLEANUP=1 - # This is set to simulate an environment where users have shared home drives protected - # by a strong umask (ie only group readable by default). - umask 027 - chmod -R o-rwx examples/ - "${ENVOY_SRCDIR}/ci/verify_examples.sh" "${@}" -} - CI_TARGET=$1 shift @@ -974,7 +964,13 @@ case $CI_TARGET in ;; verify_examples) - run_ci_verify "*" "win32-front-proxy|shared" + DEV_CONTAINER_ID=$(docker inspect --format='{{.Id}}' envoyproxy/envoy:dev) + bazel run --config=ci \ + --action_env="DEV_CONTAINER_ID=${DEV_CONTAINER_ID}" \ + --host_action_env="DEV_CONTAINER_ID=${DEV_CONTAINER_ID}" \ + --sandbox_writable_path="${HOME}/.docker/" \ + --sandbox_writable_path="$HOME" \ + @envoy_examples//:verify_examples ;; verify.trigger) diff --git a/ci/verify_examples.sh b/ci/verify_examples.sh deleted file mode 100755 index 9e551197f2e9..000000000000 --- a/ci/verify_examples.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env bash - -set -E - -TESTFILTER="${1:-*}" -TESTEXCLUDES="${2}" -FAILED=() -SRCDIR="${SRCDIR:-$(pwd)}" -WARNINGS=() - -# Sandboxes listed here should be regarded as broken(/untested) until whatever -# is causing them to flake is resolved!!! -FLAKY_SANDBOXES=( - # https://github.com/envoyproxy/envoy/issues/28542 - double-proxy - # https://github.com/envoyproxy/envoy/issues/31347 - local_ratelimit - # https://github.com/envoyproxy/envoy/issues/31333 - locality-load-balancing - # https://github.com/envoyproxy/envoy/issues/33533 - lua-cluster-specifier - # https://github.com/envoyproxy/envoy/issues/28541 - wasm-cc - # https://github.com/envoyproxy/envoy/issues/28546 - websocket) - - -trap_errors () { - local frame=0 command line sub file - for flake in "${FLAKY_SANDBOXES[@]}"; do - if [[ "$example" == "./${flake}" ]]; then - WARNINGS+=("FAILED (${flake})") - return - fi - done - if [[ -n "$example" ]]; then - command=" (${example})" - fi - set +v - while read -r line sub file < <(caller "$frame"); do - if [[ "$frame" -ne "0" ]]; then - FAILED+=(" > ${sub}@ ${file} :${line}") - else - FAILED+=("${sub}@ ${file} :${line}${command}") - fi - ((frame++)) - done - set -v -} - -trap trap_errors ERR -trap exit 1 INT - - -run_examples () { - local examples example - cd "${SRCDIR}/examples" || exit 1 - - examples=$(find . -mindepth 1 -maxdepth 1 -type d -name "$TESTFILTER" ! -iname "_*" | sort) - if [[ -n "$TESTEXCLUDES" ]]; then - examples=$(echo "$examples" | grep -Ev "$TESTEXCLUDES") - fi - for example in $examples; do - pushd "$example" > /dev/null || return 1 - ./verify.sh - popd > /dev/null || return 1 - done -} - -run_examples - -if [[ "${#WARNINGS[@]}" -ne "0" ]]; then - for warning in "${WARNINGS[@]}"; do - echo "WARNING: $warning" >&2 - done -fi - -if [[ "${#FAILED[@]}" -ne "0" ]]; then - echo "TESTS FAILED:" - for failed in "${FAILED[@]}"; do - echo "$failed" >&2 - done - exit 1 -fi diff --git a/configs/BUILD b/configs/BUILD index ab37531ddba9..b0bf82c893ed 100644 --- a/configs/BUILD +++ b/configs/BUILD @@ -53,10 +53,11 @@ genrule( name = "example_configs", srcs = [ ":configs", - "//examples:configs", - "//examples:certs", - "//examples:lua", - "//examples/wasm-cc:configs", + "@envoy_examples//:configs", + "@envoy_examples//:certs", + "@envoy_examples//:lua", + # TODO(phlax): re-enable once wasm example is fixed + # "@envoy_examples//wasm-cc:configs", "//docs:configs", "//docs:proto_examples", "//test/config/integration/certs", @@ -65,10 +66,10 @@ genrule( cmd = ( "$(location configgen.sh) $(location configgen) example_configs.tar $(@D) " + "$(locations :configs) " + - "$(locations //examples:configs) " + - "$(locations //examples:certs) " + - "$(locations //examples:lua) " + - "$(locations //examples/wasm-cc:configs) " + + "$(locations @envoy_examples//:configs) " + + "$(locations @envoy_examples//:certs) " + + "$(locations @envoy_examples//:lua) " + + # "$(locations @envoy_examples//wasm-cc:configs) " + "$(locations //docs:configs) " + "$(locations //docs:proto_examples) " + "$(locations //test/config/integration/certs)" @@ -84,8 +85,8 @@ genrule( srcs = [ "//docs:contrib_configs", "//contrib:configs", - "//examples:contrib_configs", - "//examples:certs", + "@envoy_examples//:contrib_configs", + "@envoy_examples//:certs", "//test/config/integration/certs", ], outs = ["example_contrib_configs.tar"], @@ -93,8 +94,8 @@ genrule( "$(location configgen.sh) NO_CONFIGGEN example_contrib_configs.tar $(@D) " + "$(locations //contrib:configs) " + "$(locations //docs:contrib_configs) " + - "$(locations //examples:contrib_configs) " + - "$(locations //examples:certs) " + + "$(locations @envoy_examples//:contrib_configs) " + + "$(locations @envoy_examples//:certs) " + "$(locations //test/config/integration/certs)" ), tools = [ @@ -109,14 +110,14 @@ py_binary( args = ( "--descriptor_path=$(location @envoy_api//:v3_proto_set)", "$(locations :configs) ", - "$(locations //examples:configs) ", + "$(locations @envoy_examples//:configs) ", "$(locations //docs:configs) ", ), data = [ ":configs", "//docs:configs", - "//examples:configs", "@envoy_api//:v3_proto_set", + "@envoy_examples//:configs", ], deps = [requirement("envoy.base.utils")], ) diff --git a/docs/BUILD b/docs/BUILD index 46df13e77e19..7ad25e7b18c4 100644 --- a/docs/BUILD +++ b/docs/BUILD @@ -236,7 +236,7 @@ pkg_tar( ":extensions_security_rst", ":external_deps_rst", ":version_history_rst", - "//examples:docs", + "@envoy_examples//:docs", ], ) diff --git a/examples/BUILD b/examples/BUILD deleted file mode 100644 index 941806731c4d..000000000000 --- a/examples/BUILD +++ /dev/null @@ -1,114 +0,0 @@ -load("@rules_pkg//pkg:mappings.bzl", "pkg_files") -load("@rules_pkg//pkg:pkg.bzl", "pkg_tar") -load( - "//bazel:envoy_build_system.bzl", - "envoy_package", -) - -licenses(["notice"]) # Apache 2 - -envoy_package() - -filegroup( - name = "configs", - srcs = glob( - [ - "**/*.yaml", - ], - exclude = [ - "cache/ci-responses.yaml", - "cache/responses.yaml", - "dynamic-config-fs/**/*", - "jaeger-native-tracing/*", - "opentelemetry/otel-collector-config.yaml", - "**/*docker-compose*.yaml", - # Contrib extensions tested over in contrib. - "golang-http/*.yaml", - "golang-network/*.yaml", - "mysql/*", - "postgres/*", - "kafka/*.yaml", - ], - ), -) - -filegroup( - name = "contrib_configs", - srcs = glob( - [ - "golang-http/*.yaml", - "golang-network/*.yaml", - "mysql/*.yaml", - "postgres/*.yaml", - "kafka/*.yaml", - ], - exclude = [ - "**/*docker-compose*.yaml", - ], - ), -) - -filegroup( - name = "certs", - srcs = glob(["_extra_certs/*.pem"]), -) - -filegroup( - name = "docs_rst", - srcs = glob(["**/example.rst"]) + ["//examples/wasm-cc:example.rst"], -) - -pkg_files( - name = "examples_files", - srcs = [":files"], - prefix = "_include", - strip_prefix = "/examples", -) - -genrule( - name = "examples_docs", - srcs = [":docs_rst"], - outs = ["examples_docs.tar.gz"], - cmd = """ - TEMP=$$(mktemp -d) - for location in $(locations :docs_rst); do - example=$$(echo $$location | sed -e 's#^external/[^/]*/##' | cut -d/ -f2) - cp -a $$location "$${TEMP}/$${example}.rst" - echo " $${example}" >> "$${TEMP}/_toctree.rst" - done - echo ".. toctree::" > "$${TEMP}/toctree.rst" - echo " :maxdepth: 1" >> "$${TEMP}/toctree.rst" - echo "" >> "$${TEMP}/toctree.rst" - cat "$${TEMP}/_toctree.rst" | sort >> "$${TEMP}/toctree.rst" - rm "$${TEMP}/_toctree.rst" - tar czf $@ -C $${TEMP} . - """, -) - -filegroup( - name = "lua", - srcs = glob(["**/*.lua"]), -) - -filegroup( - name = "files", - srcs = glob( - [ - "**/*", - ], - exclude = [ - "**/node_modules/**", - "**/*.rst", - ], - ) + [ - "//examples/wasm-cc:files", - ], -) - -pkg_tar( - name = "docs", - srcs = [":examples_files"], - extension = "tar.gz", - package_dir = "start/sandboxes", - deps = [":examples_docs"], -) diff --git a/examples/DEVELOPER.md b/examples/DEVELOPER.md deleted file mode 100644 index f89bdfb37b95..000000000000 --- a/examples/DEVELOPER.md +++ /dev/null @@ -1,323 +0,0 @@ -# Adding a sandbox example - -## Add a `verify.sh` test to your sandbox - -Sandboxes are tested as part of the continuous integration process, which expects -each sandbox to have a `verify.sh` script containing tests for the example. - -### Basic layout of the `verify.sh` script - -At a minimum the `verify.sh` script should include the necessary parts to start -and stop your sandbox. - -Given a sandbox with a single `docker` composition, adding the following -to `verify.sh` will test that the sandbox can be started and stopped. - -```bash -#!/bin/bash -e - -export NAME=example-sandbox - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -# add example tests here... - -``` - -The `$NAME` variable is used for logging when testing the example, and will -often be the same as the directory name. - -### Log before running each test - -There is a utility function `run_log` that can be used to indicate in test logs what -is being executed and why, for example: - -```bash -run_log "Checking foo.txt was created" -ls foo.txt - -run_log "Checking bar.txt was created" -ls bar.txt -``` - -### Add tests reflecting the documented examples - -The tests should follow the steps laid out in the documentation. - -For example, if the documentation provides a series of `bash` commands to execute, add these in order to `verify.sh`. - -You may wish to grep the responses, or check return codes to ensure the commands respond as expected. - -Likewise, if the documentation asks the user to browse to a page - for example http://localhost:8000 - -then you should add a test to ensure that the given URL responds as expected. - -If an example web page is also expected to make further JavaScript `HTTP` requests in order to function, then add -tests for requests that mimick this interaction. - -A number of utility functions have been added to simplify browser testing. - -#### Utility functions: `responds_with` - -The `responds_with` function can be used to ensure a request to a given URL responds with -expected `HTTP` content. - -It follows the form `responds_with []` - -For example, a simple `GET` request: - -```bash -responds_with \ - "Hello, world" \ - http://localhost:8000 -``` - -This is a more complicated example that uses an `HTTPS` `POST` request and sends some -additional headers: - -```bash -responds_with \ - "Hello, postie" \ - https://localhost:8000/some-endpoint \ - -k \ - -X POST \ - -d 'data=hello,rcpt=service' \ - -H 'Origin: https://example-service.com' -``` - -#### Utility functions: `responds_without` - -You can also check that a request *does not* respond with given `HTTP` content: - -```bash -responds_without \ - "Anything unexpected" \ - "http://localhost:8000" -``` - -`responds_without` can accept additional curl arguments like `responds_with` - -#### Utility functions: `responds_with_header` - -You can check that a request responds with an expected header as follows: - -```bash -responds_with_header \ - "HTTP/1.1 403 Forbidden" \ - "http://localhost:8000/?name=notdown" -``` - -`responds_with_header` can accept additional curl arguments like `responds_with` - -#### Utility functions: `responds_without_header` - -You can also check that a request *does not* respond with a given header: - -```bash -responds_without_header \ - "X-Secret: treasure" \ - "http://localhost:8000" -``` - -`responds_without_header` can accept additional curl arguments like `responds_with` - -#### Utility functions: `wait_for` - -You can wait for some amount of time (specified in seconds) for a command to return `0`. - -The following example will wait for 20 seconds for a service ``my-service`` to become healthy. - -```bash -wait_for 20 sh -c "docker-compose ps my-service | grep healthy | grep -v unhealthy" -``` - -### Slow starting `docker` compositions - -Unless your example provides a way for ensuring that all containers are healthy by -the time `docker-compose up -d` returns, you may need to add a `DELAY` before running -the steps in your `verify.sh` - -For example, to wait 10 seconds after `docker-compose up -d` has been called, set the -following: - -```bash -#!/bin/bash -e - -export NAME=example-sandbox -export DELAY=10 - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -# add example tests here... -``` - -### Examples with multiple `docker` compositions - -For your example to work it may need more than one `docker` composition to be run. - -You can set where to find the `docker-compose.yaml` files with the `PATHS` argument. - -By default `PATHS=.`, but you can change this to a comma-separated list of paths. - -For example a sandbox containing `frontend/docker-compose.yaml` and `backend/docker-compose.yaml`, -might use a `verify.sh` with: - -```bash -#!/bin/bash -e - -export NAME=example-sandbox -export PATHS=frontend,backend - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -# add example tests here... -``` - -### Bringing stacks up manually - -You may need to bring up the stack manually, in order to run some steps beforehand. - -Sourcing `verify-common.sh` will always leave you in the sandbox directory, and from there -you can use the `bring_up_example` function. - -For example: - -```bash -#!/bin/bash -e - -export NAME=example-sandbox -export MANUAL=true - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Creating bar.txt before starting containers" -echo foo > bar.txt - -bring_up_example - -# add example tests here... -``` - -If your sandbox has multiple compositions, and uses the `$PATHS` env var described above, -`bring_up_example` will bring all of your compositions up. - -### Additional arguments to `docker-compose up -d` - -If you need to pass additional arguments to compose you can set the `UPARGS` -env var. - -For example, to scale a composition with a service named `http_service`, you -should add the following: - -```bash -#!/bin/bash -e - -export NAME=example-sandbox -export UPARGS="--scale http_service=2" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -# add example tests here... -``` - -### Running commands inside `docker` containers - -If your example asks the user to run commands inside containers, you can -mimick this using `docker-compose exec -T`. The `-T` flag is necessary as the -tests do not have access to a `tty` in the CI pipeline. - -### Note on permissions and configuration - -The sandbox tests are run with a `umask` setting of `027` to ensure they will run in environments -where this is the case. - -As the Envoy containers run as non-root, it is essential that any configurations required -by the daemon are included in the relevant example `Dockerfile` rather than mounted in -any `docker-compose.yaml` files. - -The Docker recipe should also ensure that added configurations are world-readable. - -For example, with an added configuration file named `front-envoy.yaml`, you should add -the following in the Docker recipe: - -``` -RUN chmod go+r /etc/front-envoy.yaml -``` - -### Additional `pip` dependencies - -Pip dependencies should be added to a `requirements.in` file, and compiled with `pip-compile`. - -Please see existing examples for further information. - -The `requirements.txt` will also need to be added to `.github/dependabot.yaml`. - -### Shared Docker images and service definitions - -There are shared `Dockerfile` resources available for use in building an example. - -Please see the `examples/shared` folder. - -## Sandbox configuration tests - -Example configuration files are tested to ensure they are valid and well-formed, and do -not contain deprecated features. - -### Exclude configs from example configuration tests - -The CI script searches for all files in the examples folders with a `yaml` or `lua` extension. - -These files are bundled into a test and the `yaml` files are used to try to start an Envoy server. - -If your example includes `yaml` files that are either not Envoy configuration, or for some reason -cannot be tested in this way, you should add the files to the `exclude` list in the `filegroup.srcs` -section of the `examples/BUILD` file. - -The `exclude` patterns are evaluated as `globs` in the context of the `examples` folder. - - -## Verifying your sandbox - -Once you have built your sandbox, and added the `verify.sh` script you can run it directly in the -sandbox folder. - -For example: - -``` -cd examples/example-sandbox -./verify.sh - -``` - -You should see the docker composition brought up, your tests run, and the composition brought down again. - -The script should exit with `0` for the tests to pass. - - -## Verifying multiple/all sandboxes - -In continuous integration, all of the sandboxes are checked using the `ci/verify-examples.sh`. - -This can also be called with a filter argument, which is a `glob` evaluated in the context of the `examples` folder. - -For example, to run all sandboxes with names beginning `jaeger`: - -``` -./ci/verify-examples.sh jaeger* -``` - ---- - -**NOTE** - -You can use this script locally to test the sandboxes on your platform, but you should be aware that it requires -a lot of resources as it downloads and builds many Docker images, and then runs them in turn. - ---- - -One way to run the tests in an isolated environment is to mount the `envoy` source into a `docker-in-docker` container -or similar, and then run the script from inside that container. diff --git a/examples/_extra_certs/README.md b/examples/_extra_certs/README.md deleted file mode 100644 index 3e4b69997626..000000000000 --- a/examples/_extra_certs/README.md +++ /dev/null @@ -1,7 +0,0 @@ -Extra certificates for config validation testing -================================================ - -This folder contains certs that are referenced in the sandbox examples, that end users are -expected to create themselves. - -In order to test the related configs we need to provide the certs to CI. diff --git a/examples/_extra_certs/domain1.crt.pem b/examples/_extra_certs/domain1.crt.pem deleted file mode 100644 index 33be12e8b02c..000000000000 --- a/examples/_extra_certs/domain1.crt.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDhDCCAmygAwIBAgITAJlvbEs3wtayr3rx+TyuBtu0mTANBgkqhkiG9w0BAQsF -ADBSMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExGDAWBgNVBAoMD015RXhhbXBs -ZSwgSW5jLjEcMBoGA1UEAwwTZG9tYWluMS5leGFtcGxlLmNvbTAeFw0yMDExMTIx -MTA3MDdaFw0yMTExMTIxMTA3MDdaMFIxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJD -QTEYMBYGA1UECgwPTXlFeGFtcGxlLCBJbmMuMRwwGgYDVQQDDBNkb21haW4xLmV4 -YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA15Q63skf -pc5o2mhBE0dOcJaTqLS+nmIO5jK8QUKctpbOQz2p7j9zi9ZUh++4N84yjF56GQEw -/KqPvPHNA/tJKpkDugWHq4IFPU+o1k2AJKLVEvN3wXpbiae77eqgUCg0aS6kWDaT -LrCie/laxnSpnfRGDo1xsLRqNLzZxF3CPvA/WbgpR1JXYUAnoXZGHISrnXLzyI1O -DaDdDoi8Nn54neZ9jXtkeDWfuO5NkXK/U1dNnCez9a7EGO+h8ZF0Uc12UqPiX86L -frK0v25n94lPTGq5SOgswATMSOfN6g4pGaUFofZIyenHamUngzqm55M+/tMeiaF7 -Pwf4wcTyXEaXcQIDAQABo1MwUTAdBgNVHQ4EFgQUTSuIMFANakAWSPIUiqdMUrFq -66YwHwYDVR0jBBgwFoAUTSuIMFANakAWSPIUiqdMUrFq66YwDwYDVR0TAQH/BAUw -AwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAdbdIBKAomEsGtcuWWc8vI4r0l+AMegGK -yg86byKm9WRHtpYnO+iZ+SopTLTFhgLsGfEMoN+HGeUIexUvwDzb384EJ4kLPr3E -Yqt5uNz9YMuFpkhuFTL+V5RczcPKfir5hzAgvAtj6eaRf9WPlObF+Rr0t8pJZG0k -9dEtBqE87XVUDvj6waMCpTFxwv22E/xjRJ5nSDjfk9y8LDpIF5SOunncVMRVfcjg -Qp0Q9KpZpbxXFMYVBfMxp4Z/KQd0W5nVWZlwg/D03n0IkS0e8irUyrerFLdOTwxf -G5M/n/VeCwC2GPlT8Eo/3BUa+SeX2iHl93/osqfWNQAY3riaN0y+FA== ------END CERTIFICATE----- diff --git a/examples/_extra_certs/domain1.key.pem b/examples/_extra_certs/domain1.key.pem deleted file mode 100644 index eefcca26453f..000000000000 --- a/examples/_extra_certs/domain1.key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDXlDreyR+lzmja -aEETR05wlpOotL6eYg7mMrxBQpy2ls5DPanuP3OL1lSH77g3zjKMXnoZATD8qo+8 -8c0D+0kqmQO6BYerggU9T6jWTYAkotUS83fBeluJp7vt6qBQKDRpLqRYNpMusKJ7 -+VrGdKmd9EYOjXGwtGo0vNnEXcI+8D9ZuClHUldhQCehdkYchKudcvPIjU4NoN0O -iLw2fnid5n2Ne2R4NZ+47k2Rcr9TV02cJ7P1rsQY76HxkXRRzXZSo+Jfzot+srS/ -bmf3iU9MarlI6CzABMxI583qDikZpQWh9kjJ6cdqZSeDOqbnkz7+0x6JoXs/B/jB -xPJcRpdxAgMBAAECggEBALvIi+tKaH3mqaEuVRk08NfT4jV/k9ek9POCWEfEfXvz -KyKZUS+OJ9k0TpfGscIypAdvuI2VYxWEgQaF3h7MwfQQK5XbgU1dSbEZdamPAsNm -75G9cKChM6FZ8bKRwSlxjA3fKhsJFvYBuNei4naiYqmLgYbloJXa4fSkWFDblvt4 -cmsP9iEZL7tBJ4bIGmugpPR83PPlfu/EQY2w8T+Rw8/JAXDd80V1egCucYpwOx94 -esXVpWzTA3xZyPTlQrFmOe9NEb2C5oqOx1s/zmfQpytKPjF7YojnHnYeHrDL5Y+j -sVP753celaYncWoANfAyV4FOxEsOa1OCKbF5OOuWPI0CgYEA/KyTK4NyRHqXrhuO -J/rRDhhZBomP4LkY2uTMdOH+n0hvy0m5eV5+88CQF6atSfKQ8h+zbhJVNeVilZDK -NhjAEm+x2vME41Wp1vqsALpPtuGFQm3EcwKDvTgyvm04X2RfZSZ4MT0993M/g07u -x+VQiZu127PjcNibDXXoDwM5p/MCgYEA2mqoRnTr/DCrO8u678ccv8MYHhh0ISOs -Tbmh83qROWehdoB6kRQ/i+kefbL6Rw0bY5+3rlvQ+3B3MvVoYLWUWuyhtYK2pt3i -R071WPCuR3PIVOEK+wuHi85peiGSHxfEDiUb3AvNnd8dZGBFnHe8mZObccc7b4uy -jT4VLJ56IAsCgYEAlg3GuKivS4uiWHt0yLljPYOoGwHGuCY0ZIpMAX3UwLM78PYv -d6xuqENLT0Bk2O18ts2suUmZ4RAAo+IAtG+uYUSD0wtPc9KDsm/bhfMfM/RqNzEI -4WQ06EJfoEcsmzn4jRFzf4pnKnT+2vQdSgkc8xvNvFPwVivMqQnEbmXz75ECgYBr -BTfOzhuTRoWglwLR2k5L59w5YuIEGuaibwLbuoLODekfl3R3AeThOSinjrrzdYim -F+x4kqSjj0fYwEaUnGRE6Q2TUqkMukvVhOrS2ZuLhz/x1xL6T3vrFQi5vxlKAusd -wzETcPUfFePg+wsgz8qptZnE9ko5LcofSvw1ELHmYQKBgBfT1GtRlYCEMbuSJY20 -AtoOg5vN2b6s2nqQGff7J8UOywPDk9hyboL4ByS9Udemap0USisGAZiEfq+VbA+2 -lPhV/gmBFDidCCYRXKi6qfcDG9ssJ5Gylg/8XaaMKAQ7vp73sQYTDlcgUcPx90ue -GMITMZWQr8Qs/u9zl22tnxAb ------END PRIVATE KEY----- diff --git a/examples/_extra_certs/domain2.crt.pem b/examples/_extra_certs/domain2.crt.pem deleted file mode 100644 index 9f983682ae6b..000000000000 --- a/examples/_extra_certs/domain2.crt.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDhTCCAm2gAwIBAgIUXBZV/SLVGAdsb1mJrpuahYegPLEwDQYJKoZIhvcNAQEL -BQAwUjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRgwFgYDVQQKDA9NeUV4YW1w -bGUsIEluYy4xHDAaBgNVBAMME2RvbWFpbjIuZXhhbXBsZS5jb20wHhcNMjAxMTEy -MTEwNzA3WhcNMjExMTEyMTEwNzA3WjBSMQswCQYDVQQGEwJVUzELMAkGA1UECAwC -Q0ExGDAWBgNVBAoMD015RXhhbXBsZSwgSW5jLjEcMBoGA1UEAwwTZG9tYWluMi5l -eGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJfPje1r -nUfVnHobHRKReisGdGWHQhngJlp3II7sPk0vRuvOLsyLp0AKA/pS0isW1WFUh+Kz -BpOVrU/LPqi1ZW38mzCgGr91fc999vtsZyXOCEh7Vi3UVfzVtSNwnijlG3wovjGK -DqLlne03/lPFT1x9coIy7XvA1ZICfX8EfP5ajt60+UWYXAKN2FLas9K+3lzzbHx5 -F/iI134A695ozLNHT1qe+IOA6NwW5LoTwzoHRVMoJz2cvSRr0vCVkt0IvO5ARyyr -400Nx0vKkxhf0Z+yXGSowWVN8VtSPiRSeC4vGmPRl6O6XoiPwjus2jlXrJifcIyg -hNDrOQnYbYK5dA8CAwEAAaNTMFEwHQYDVR0OBBYEFIQfS5xxWYX7pWgc59p+h6y8 -sQMFMB8GA1UdIwQYMBaAFIQfS5xxWYX7pWgc59p+h6y8sQMFMA8GA1UdEwEB/wQF -MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIHDjt+wxYuOJkI8VncVR6VpGy9hqntd -rnNxupReenhocPN/QIl1TQva/gGq4gz1vNWhHz1B5bxPoyPESed5+QQvJMo3/5Ub -OyDIKwspwRy6PUoyJDjhC/z7B2FhZPmxVmbHfhL0wiQjI7j/u+/c8Jq9YDr8ZsZs -whXjvSOl9+I0xWZFRN0O+cFszTnoucmLRdFVl648ghUlW3m6/YNWF+mLucleZVt3 -wFUKGwq88Z4sU6kqcXXG4GykZYmSwB3BmaaamQKq06v+k9Qjrj5gJD8S1Ygznc6/ -Z+ZzAb/FfHXHV6QbY5/35wVFO3OMk6NHy9oLZrfPxBn/C5brUz+dXnI= ------END CERTIFICATE----- diff --git a/examples/_extra_certs/domain2.key.pem b/examples/_extra_certs/domain2.key.pem deleted file mode 100644 index 12602841945f..000000000000 --- a/examples/_extra_certs/domain2.key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCXz43ta51H1Zx6 -Gx0SkXorBnRlh0IZ4CZadyCO7D5NL0brzi7Mi6dACgP6UtIrFtVhVIfiswaTla1P -yz6otWVt/JswoBq/dX3Pffb7bGclzghIe1Yt1FX81bUjcJ4o5Rt8KL4xig6i5Z3t -N/5TxU9cfXKCMu17wNWSAn1/BHz+Wo7etPlFmFwCjdhS2rPSvt5c82x8eRf4iNd+ -AOveaMyzR09anviDgOjcFuS6E8M6B0VTKCc9nL0ka9LwlZLdCLzuQEcsq+NNDcdL -ypMYX9GfslxkqMFlTfFbUj4kUnguLxpj0Zejul6Ij8I7rNo5V6yYn3CMoITQ6zkJ -2G2CuXQPAgMBAAECggEAJ089Rv8YqOMtM4kVzBsTcVSoiyms+hpKlB5ItfmCYGYf -jSvEfn6i/jgZs5YCidnNwvgqf48v4sNdL05HmVPvQb2pSbwLcQwxWasaaxw00Vs6 -VdpqBE/5PBDyaIzex2Qb69h490byZ0fhzu0y0+pBlId/QSuCxwq1wqsWZ+93ljzi -rDDoPSWcty0R3QWrMUmbihi6i8v8fbFz174jxGH1TRJ0+a9IuGZ9Yap1mSg9YC/0 -oM20lsvmQvczXYdJMUhg0CYJ3weThO9pK4fnDa7pvkBgxgOTOY2TZh3PUs7DmYa7 -YCE5xp8CviKZywaAqvowjfdWj8yCU5ZFzN4LdDmhMQKBgQDHL/dUg61+viWR7GyD -Kb9E9FjktwSu52Ec1jfcjFe8UkRJa7JiruagVXuekAVvA+R7ZifSb0Yfs1QaHwA3 -NPvVpwb3omW02gfbXR9AJ/eEJfgkcliPrJsL0QqbLu+w+5CFaj/iCN5SxE822WLx -3dGNDOrQEr0A+K3Hmj5SCtCmbQKBgQDDHEwxzjq6jw2n+K0ArIvpXa1/hteT6h1q -7Qcg9nEaSiVbYAfp+1qgQqoCMe7aSNJe/RuGP59mwlIgt1rn+QFgb8K9IoZYvb4N -jwLmuOx1tLWtbRLHHFdYA14XlwKOl79NwjJPepCZIU8eOzaDLBUUeH0DkgJgwSZ2 -TnJOmp6m6wKBgHL0tNpa0INoPAiWmR2tt0yVdMQy+An1UW+yFjU77dqq4+w3spEP -fdyk2R5u4iPq7C9niq4BOEhNV8lngNlbw8fPiM7cM7SHbKdmfAWry0bCHw7xyzjI -Fgdg0q0zDnRnC0ZkRpAuLBk6YLk4BsmuCiVMgiwp1Fi+LJUY6MSypy6VAoGAUFun -RhwaNBwXE8dn+Y8XUNY0TwHKaDFUTGWzOfBGRP2kxS2YFNZhTQAn5R+LsHutqVG1 -tGUf0cLW8IKT/lagKofdPOirTIFZdVwhZcVkHlZ/PR5fTYJutuEsL6sScogtUmlZ -L0LbqzX80AazPPM6+2NkmcPZFuB2ZuOIULd+AGECgYBt40PiCf1WmAQK9J3pylOD -s7kWwzapIAKf92JBJmo5sOushYsPXAnqUJaZERpBBCmsdtYyjQV8VYxg/CrU5CkE -0zdFmFcfw7swwaE+aJLwueV1qR7lKi89DYZ9OFI+Z0JoekQc/TPeJLg1MctK76qA -OMswum0oXiu/zZJGGtCegg== ------END PRIVATE KEY----- diff --git a/examples/_extra_certs/domain3.crt.pem b/examples/_extra_certs/domain3.crt.pem deleted file mode 100644 index fdfbd07e9321..000000000000 --- a/examples/_extra_certs/domain3.crt.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDhTCCAm2gAwIBAgIUI/iTc/yKhX+HkiDCKAuCa25fxQwwDQYJKoZIhvcNAQEL -BQAwUjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRgwFgYDVQQKDA9NeUV4YW1w -bGUsIEluYy4xHDAaBgNVBAMME2RvbWFpbjMuZXhhbXBsZS5jb20wHhcNMjAxMTEy -MTEwNzA3WhcNMjExMTEyMTEwNzA3WjBSMQswCQYDVQQGEwJVUzELMAkGA1UECAwC -Q0ExGDAWBgNVBAoMD015RXhhbXBsZSwgSW5jLjEcMBoGA1UEAwwTZG9tYWluMy5l -eGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMOO0k18 -6k3164UpYGVOC1oj8jbmdZeeg57mkamYE9CUk0W9KKgHOCSNMZXUxPtnqtqJgJ92 -ccawTe0WrbOKQA3ARK7WePbX3HepJfTFZgCC5d5njRPDfIgsk4MP89nc2p8qO5Vv -SvCTae8/ykTyfz5fKwaGMYwvrSGUpuhFD8OFMOAsvnoIXZd+ixLkDATupP/IaV0a -6tHs6BnC3vxn+baC30fuHKErfOh7Jlo3FDqXNMwfes6MJ7/u8odeFfuGOaaO4eRT -EAhy4VQBJkCtS3yCFEv3kCRXjmgEBDSQ9jDjtnykOqViO5euibeKxnz+7xjRWVGY -bT5Z+s6eUHkiraMCAwEAAaNTMFEwHQYDVR0OBBYEFDVYD/F+NzsKgfYsRM6XfMuB -qXmqMB8GA1UdIwQYMBaAFDVYD/F+NzsKgfYsRM6XfMuBqXmqMA8GA1UdEwEB/wQF -MAMBAf8wDQYJKoZIhvcNAQELBQADggEBABwiF5AVDmQTLYE4wuHxq245qOj/vKfi -1L2lNgZ7G2Luobbvli2SQo7g8UYMSrwNF3Y1TDoEryeYMKYr2udb8WvdzhlL3z/J -a/qBElwWsATnpRfBAqxeWkx0x0E0C4nrjXM7PbAEjvEZ2AQKc5zmvii1Ek4h/+Sa -h2+Tmm5zg0Lo410CqujRmGtHU2AtkqguOhNrvJcRxEH4iLDB87WfUlLW6JrN+CLB -qIxkyLlhMUNMa200mpsfwQQRdImTjdn+VgpFR9BeZYU2gPZxqdxKcyrGfYXim1oJ -dC34TKistMWFs0C3l+Xs7unqSkqk5s1Nkdh6vnMF39PkwFoVP3Nn2wY= ------END CERTIFICATE----- diff --git a/examples/_extra_certs/domain3.key.pem b/examples/_extra_certs/domain3.key.pem deleted file mode 100644 index 84f259bd0a8a..000000000000 --- a/examples/_extra_certs/domain3.key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDDjtJNfOpN9euF -KWBlTgtaI/I25nWXnoOe5pGpmBPQlJNFvSioBzgkjTGV1MT7Z6raiYCfdnHGsE3t -Fq2zikANwESu1nj219x3qSX0xWYAguXeZ40Tw3yILJODD/PZ3NqfKjuVb0rwk2nv -P8pE8n8+XysGhjGML60hlKboRQ/DhTDgLL56CF2XfosS5AwE7qT/yGldGurR7OgZ -wt78Z/m2gt9H7hyhK3zoeyZaNxQ6lzTMH3rOjCe/7vKHXhX7hjmmjuHkUxAIcuFU -ASZArUt8ghRL95AkV45oBAQ0kPYw47Z8pDqlYjuXrom3isZ8/u8Y0VlRmG0+WfrO -nlB5Iq2jAgMBAAECggEBALe2MUzIP9kTYLlNIJiq07FPuZjnsarJKD8bvdWD34GA -QkYuqMYJWi3EUsO+CXtgbTo2GJY1kDcmo15Kgs3636fLavqQ0zyZlyz2w4iJ9QQf -9FCWGQtrB09qCP4D+4I8n0kNRMJithUBd3BiDePtp6nxf5r2cA+RLmUwoAft8Rws -GoZF5vm93iHF29H/nbP7KNTyBAoRq95y+SiXvcLOb9pN5IwAiqkAcVF8YpUCqsQL -PRTJZupSRU3Cg0Asq9nvqxeTBrySeOkoRid9b/5/MKP2CXNkGoOl7TCiHJ1SFYCd -waN+516Nd6Hu8WpH7n3eqcc/JhmP7jLv3vqLYAWpJ/ECgYEA9XigdnEiGFuP/SAE -l8InIGfNxGegEb9AMifZdir4lI1BKC+Ke84TD0V5tgHS5zsk8SOAIERHrclOTtHR -WPEx+GDrsukl3QDisdFE6sU1ktZ9S0uim9hYGaAtxME99U3EhDTBLApNyhxe2REC -yZoCuObqz7OVCu+SZ6/etw5mk+kCgYEAy/IgXJxCD3ais1Jy0DOfrzSExXulOi4X -+EWtMNcBkcYxE0Jl1mpsgd7GZNnJCf8ThajetREPFYzMCYiI6KOtOJBi+Bil6hpI -N2U29LD/dxIotHzdIau4ESFODJdgP2agzFx72JDKEBxGsuc7x5bd7P8hex+XURTW -KVuawJPXOasCgYEAoYXCcK149fYqBTGwU/vZqyUi7P4TAhqKr3YxTeRwta9NFJhT -06uCNyZMNEt279inMlVd1d2YHO69rHe7/X6YlwuPjKaF16rhgIhnhORHoFurDoSy -d0IglpwkAbf2gRevHB9qjQQqs7d/Ye4jm2zQJcMs94b/p7aE6915+5JqRSECgYAo -uC4n73btGXXAsfyEf1oppCXCPD6wEBXvFxJORw9kKJsRylcE6XjCsVURO759BXXD -YQUeR8qoNdVjLeSP9mYWfhWUjW9K/3ZdwRKo5lILVw/TgX6xQ1Tb7rdjojGwVvBR -/UEo6ze84bhn7e0sm32x3Pq1V4hhwvRDi6upOZtmQwKBgQDbjiPYdqKkbrC7YFsH -4m9VenSmnOppugcYU5h3zLORERfuTBT4cT2TEZT4zlz1vUZFjoQ5ML97xSGeCKzN -0Y0uh6zRgoy2zAQzwmtrNi80GuFEv1CJq7qxzz3aDU2Y/qYFmnl2cTwLNC+MxpbJ -lpvH9Ufkaj4vu/Iuw/Dnc2BPQg== ------END PRIVATE KEY----- diff --git a/examples/brotli/README.md b/examples/brotli/README.md deleted file mode 100644 index 575b73903e7b..000000000000 --- a/examples/brotli/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/brotli.html) diff --git a/examples/brotli/docker-compose.yaml b/examples/brotli/docker-compose.yaml deleted file mode 100644 index 78ec88551f44..000000000000 --- a/examples/brotli/docker-compose.yaml +++ /dev/null @@ -1,21 +0,0 @@ -services: - - envoy-stats: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - target: envoy-admin - args: - ENVOY_ADMIN_PORT: 9901 - depends_on: - service: - condition: service_healthy - ports: - - "${PORT_PROXY:-10000}:10000" - - "${PORT_STATS0:-9901}:9901" - - "${PORT_STATS1:-9902}:9902" - - service: - build: - context: ../shared/python - target: aiohttp-data-service diff --git a/examples/brotli/envoy.yaml b/examples/brotli/envoy.yaml deleted file mode 100644 index 703af847b73b..000000000000 --- a/examples/brotli/envoy.yaml +++ /dev/null @@ -1,238 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service - http_filters: - - name: envoy.filters.http.compressor - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor - response_direction_config: - common_config: - min_content_length: 100 - content_type: - - application/json - disable_on_etag_header: true - compressor_library: - name: text_optimized - typed_config: - "@type": type.googleapis.com/envoy.extensions.compression.brotli.compressor.v3.Brotli - window_bits: 10 - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext - common_tls_context: - tls_certificates: - # The following self-signed certificate pair is generated using: - # $ openssl req -x509 -newkey rsa:2048 -keyout a/brotli-key.pem -out a/brotli-crt.pem -days 3650 -nodes -subj '/CN=brotli' - # - # Instead of feeding it as an inline_string, certificate pair can also be fed to Envoy - # via filename. Reference: https://envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#config-core-v3-datasource. - # - # Or in a dynamic configuration scenario, certificate pair can be fetched remotely via - # Secret Discovery Service (SDS). Reference: https://envoyproxy.io/docs/envoy/latest/configuration/security/secret. - - certificate_chain: - inline_string: | - -----BEGIN CERTIFICATE----- - MIICqDCCAZACCQCquzpHNpqBcDANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtm - cm9udC1lbnZveTAeFw0yMDA3MDgwMTMxNDZaFw0zMDA3MDYwMTMxNDZaMBYxFDAS - BgNVBAMMC2Zyb250LWVudm95MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC - AQEAthnYkqVQBX+Wg7aQWyCCb87hBce1hAFhbRM8Y9dQTqxoMXZiA2n8G089hUou - oQpEdJgitXVS6YMFPFUUWfwcqxYAynLK4X5im26Yfa1eO8La8sZUS+4Bjao1gF5/ - VJxSEo2yZ7fFBo8M4E44ZehIIocipCRS+YZehFs6dmHoq/MGvh2eAHIa+O9xssPt - ofFcQMR8rwBHVbKy484O10tNCouX4yUkyQXqCRy6HRu7kSjOjNKSGtjfG+h5M8bh - 10W7ZrsJ1hWhzBulSaMZaUY3vh5ngpws1JATQVSK1Jm/dmMRciwlTK7KfzgxHlSX - 58ENpS7yPTISkEICcLbXkkKGEQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCmj6Hg - vwOxWz0xu+6fSfRL6PGJUGq6wghCfUvjfwZ7zppDUqU47fk+yqPIOzuGZMdAqi7N - v1DXkeO4A3hnMD22Rlqt25vfogAaZVToBeQxCPd/ALBLFrvLUFYuSlS3zXSBpQqQ - Ny2IKFYsMllz5RSROONHBjaJOn5OwqenJ91MPmTAG7ujXKN6INSBM0PjX9Jy4Xb9 - zT+I85jRDQHnTFce1WICBDCYidTIvJtdSSokGSuy4/xyxAAc/BpZAfOjBQ4G1QRe - 9XwOi790LyNUYFJVyeOvNJwveloWuPLHb9idmY5YABwikUY6QNcXwyHTbRCkPB2I - m+/R4XnmL4cKQ+5Z - -----END CERTIFICATE----- - private_key: - inline_string: | - -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2GdiSpVAFf5aD - tpBbIIJvzuEFx7WEAWFtEzxj11BOrGgxdmIDafwbTz2FSi6hCkR0mCK1dVLpgwU8 - VRRZ/ByrFgDKcsrhfmKbbph9rV47wtryxlRL7gGNqjWAXn9UnFISjbJnt8UGjwzg - Tjhl6EgihyKkJFL5hl6EWzp2Yeir8wa+HZ4Achr473Gyw+2h8VxAxHyvAEdVsrLj - zg7XS00Ki5fjJSTJBeoJHLodG7uRKM6M0pIa2N8b6HkzxuHXRbtmuwnWFaHMG6VJ - oxlpRje+HmeCnCzUkBNBVIrUmb92YxFyLCVMrsp/ODEeVJfnwQ2lLvI9MhKQQgJw - tteSQoYRAgMBAAECggEAeDGdEkYNCGQLe8pvg8Z0ccoSGpeTxpqGrNEKhjfi6NrB - NwyVav10iq4FxEmPd3nobzDPkAftfvWc6hKaCT7vyTkPspCMOsQJ39/ixOk+jqFx - lNa1YxyoZ9IV2DIHR1iaj2Z5gB367PZUoGTgstrbafbaNY9IOSyojCIO935ubbcx - DWwL24XAf51ez6sXnI8V5tXmrFlNXhbhJdH8iIxNyM45HrnlUlOk0lCK4gmLJjy9 - 10IS2H2Wh3M5zsTpihH1JvM56oAH1ahrhMXs/rVFXXkg50yD1KV+HQiEbglYKUxO - eMYtfaY9i2CuLwhDnWp3oxP3HfgQQhD09OEN3e0IlQKBgQDZ/3poG9TiMZSjfKqL - xnCABMXGVQsfFWNC8THoW6RRx5Rqi8q08yJrmhCu32YKvccsOljDQJQQJdQO1g09 - e/adJmCnTrqxNtjPkX9txV23Lp6Ak7emjiQ5ICu7iWxrcO3zf7hmKtj7z+av8sjO - mDI7NkX5vnlE74nztBEjp3eC0wKBgQDV2GeJV028RW3b/QyP3Gwmax2+cKLR9PKR - nJnmO5bxAT0nQ3xuJEAqMIss/Rfb/macWc2N/6CWJCRT6a2vgy6xBW+bqG6RdQMB - xEZXFZl+sSKhXPkc5Wjb4lQ14YWyRPrTjMlwez3k4UolIJhJmwl+D7OkMRrOUERO - EtUvc7odCwKBgBi+nhdZKWXveM7B5N3uzXBKmmRz3MpPdC/yDtcwJ8u8msUpTv4R - JxQNrd0bsIqBli0YBmFLYEMg+BwjAee7vXeDFq+HCTv6XMva2RsNryCO4yD3I359 - XfE6DJzB8ZOUgv4Dvluie3TB2Y6ZQV/p+LGt7G13yG4hvofyJYvlg3RPAoGAcjDg - +OH5zLN2eqah8qBN0CYa9/rFt0AJ19+7/smLTJ7QvQq4g0gwS1couplcCEnNGWiK - 72y1n/ckvvplmPeAE19HveMvR9UoCeV5ej86fACy8V/oVpnaaLBvL2aCMjPLjPP9 - DWeCIZp8MV86cvOrGfngf6kJG2qZTueXl4NAuwkCgYEArKkhlZVXjwBoVvtHYmN2 - o+F6cGMlRJTLhNc391WApsgDZfTZSdeJsBsvvzS/Nc0burrufJg0wYioTlpReSy4 - ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU - q5sGxGrC1RECGB5Zwx2S2ZY= - -----END PRIVATE KEY----- - - address: - socket_address: - address: 0.0.0.0 - port_value: 9902 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/stats/prometheus" - route: - cluster: envoy-stats - http_filters: - - name: envoy.filters.http.compressor - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor - response_direction_config: - common_config: - min_content_length: 100 - content_type: - - text/plain - disable_on_etag_header: true - compressor_library: - name: text_optimized - typed_config: - "@type": type.googleapis.com/envoy.extensions.compression.brotli.compressor.v3.Brotli - window_bits: 10 - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext - common_tls_context: - tls_certificates: - # The following self-signed certificate pair is generated using: - # $ openssl req -x509 -newkey rsa:2048 -keyout a/brotli.pem -out a/brotli-crt.pem -days 3650 -nodes -subj '/CN=brotli' - # - # Instead of feeding it as an inline_string, certificate pair can also be fed to Envoy - # via filename. Reference: https://envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#config-core-v3-datasource. - # - # Or in a dynamic configuration scenario, certificate pair can be fetched remotely via - # Secret Discovery Service (SDS). Reference: https://envoyproxy.io/docs/envoy/latest/configuration/security/secret. - - certificate_chain: - inline_string: | - -----BEGIN CERTIFICATE----- - MIICqDCCAZACCQCquzpHNpqBcDANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtm - cm9udC1lbnZveTAeFw0yMDA3MDgwMTMxNDZaFw0zMDA3MDYwMTMxNDZaMBYxFDAS - BgNVBAMMC2Zyb250LWVudm95MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC - AQEAthnYkqVQBX+Wg7aQWyCCb87hBce1hAFhbRM8Y9dQTqxoMXZiA2n8G089hUou - oQpEdJgitXVS6YMFPFUUWfwcqxYAynLK4X5im26Yfa1eO8La8sZUS+4Bjao1gF5/ - VJxSEo2yZ7fFBo8M4E44ZehIIocipCRS+YZehFs6dmHoq/MGvh2eAHIa+O9xssPt - ofFcQMR8rwBHVbKy484O10tNCouX4yUkyQXqCRy6HRu7kSjOjNKSGtjfG+h5M8bh - 10W7ZrsJ1hWhzBulSaMZaUY3vh5ngpws1JATQVSK1Jm/dmMRciwlTK7KfzgxHlSX - 58ENpS7yPTISkEICcLbXkkKGEQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCmj6Hg - vwOxWz0xu+6fSfRL6PGJUGq6wghCfUvjfwZ7zppDUqU47fk+yqPIOzuGZMdAqi7N - v1DXkeO4A3hnMD22Rlqt25vfogAaZVToBeQxCPd/ALBLFrvLUFYuSlS3zXSBpQqQ - Ny2IKFYsMllz5RSROONHBjaJOn5OwqenJ91MPmTAG7ujXKN6INSBM0PjX9Jy4Xb9 - zT+I85jRDQHnTFce1WICBDCYidTIvJtdSSokGSuy4/xyxAAc/BpZAfOjBQ4G1QRe - 9XwOi790LyNUYFJVyeOvNJwveloWuPLHb9idmY5YABwikUY6QNcXwyHTbRCkPB2I - m+/R4XnmL4cKQ+5Z - -----END CERTIFICATE----- - private_key: - inline_string: | - -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2GdiSpVAFf5aD - tpBbIIJvzuEFx7WEAWFtEzxj11BOrGgxdmIDafwbTz2FSi6hCkR0mCK1dVLpgwU8 - VRRZ/ByrFgDKcsrhfmKbbph9rV47wtryxlRL7gGNqjWAXn9UnFISjbJnt8UGjwzg - Tjhl6EgihyKkJFL5hl6EWzp2Yeir8wa+HZ4Achr473Gyw+2h8VxAxHyvAEdVsrLj - zg7XS00Ki5fjJSTJBeoJHLodG7uRKM6M0pIa2N8b6HkzxuHXRbtmuwnWFaHMG6VJ - oxlpRje+HmeCnCzUkBNBVIrUmb92YxFyLCVMrsp/ODEeVJfnwQ2lLvI9MhKQQgJw - tteSQoYRAgMBAAECggEAeDGdEkYNCGQLe8pvg8Z0ccoSGpeTxpqGrNEKhjfi6NrB - NwyVav10iq4FxEmPd3nobzDPkAftfvWc6hKaCT7vyTkPspCMOsQJ39/ixOk+jqFx - lNa1YxyoZ9IV2DIHR1iaj2Z5gB367PZUoGTgstrbafbaNY9IOSyojCIO935ubbcx - DWwL24XAf51ez6sXnI8V5tXmrFlNXhbhJdH8iIxNyM45HrnlUlOk0lCK4gmLJjy9 - 10IS2H2Wh3M5zsTpihH1JvM56oAH1ahrhMXs/rVFXXkg50yD1KV+HQiEbglYKUxO - eMYtfaY9i2CuLwhDnWp3oxP3HfgQQhD09OEN3e0IlQKBgQDZ/3poG9TiMZSjfKqL - xnCABMXGVQsfFWNC8THoW6RRx5Rqi8q08yJrmhCu32YKvccsOljDQJQQJdQO1g09 - e/adJmCnTrqxNtjPkX9txV23Lp6Ak7emjiQ5ICu7iWxrcO3zf7hmKtj7z+av8sjO - mDI7NkX5vnlE74nztBEjp3eC0wKBgQDV2GeJV028RW3b/QyP3Gwmax2+cKLR9PKR - nJnmO5bxAT0nQ3xuJEAqMIss/Rfb/macWc2N/6CWJCRT6a2vgy6xBW+bqG6RdQMB - xEZXFZl+sSKhXPkc5Wjb4lQ14YWyRPrTjMlwez3k4UolIJhJmwl+D7OkMRrOUERO - EtUvc7odCwKBgBi+nhdZKWXveM7B5N3uzXBKmmRz3MpPdC/yDtcwJ8u8msUpTv4R - JxQNrd0bsIqBli0YBmFLYEMg+BwjAee7vXeDFq+HCTv6XMva2RsNryCO4yD3I359 - XfE6DJzB8ZOUgv4Dvluie3TB2Y6ZQV/p+LGt7G13yG4hvofyJYvlg3RPAoGAcjDg - +OH5zLN2eqah8qBN0CYa9/rFt0AJ19+7/smLTJ7QvQq4g0gwS1couplcCEnNGWiK - 72y1n/ckvvplmPeAE19HveMvR9UoCeV5ej86fACy8V/oVpnaaLBvL2aCMjPLjPP9 - DWeCIZp8MV86cvOrGfngf6kJG2qZTueXl4NAuwkCgYEArKkhlZVXjwBoVvtHYmN2 - o+F6cGMlRJTLhNc391WApsgDZfTZSdeJsBsvvzS/Nc0burrufJg0wYioTlpReSy4 - ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU - q5sGxGrC1RECGB5Zwx2S2ZY= - -----END PRIVATE KEY----- - - clusters: - - name: envoy-stats - connect_timeout: 0.25s - type: STATIC - load_assignment: - cluster_name: envoy-stats - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: 127.0.0.1 - port_value: 9901 - - name: service - connect_timeout: 0.25s - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service - port_value: 8080 -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 9901 diff --git a/examples/brotli/example.rst b/examples/brotli/example.rst deleted file mode 100644 index 62e9e0246b16..000000000000 --- a/examples/brotli/example.rst +++ /dev/null @@ -1,95 +0,0 @@ -.. _install_sandboxes_brotli: - -Brotli -====== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -By enabling compression in Envoy you can save some network bandwidth, at the expense of increased processor usage. - -Envoy supports compression and decompression for both requests and responses. - -This sandbox provides an example of response compression served over ``HTTPS``. - -The sandbox covers two scenarios: - -- compression of files from an upstream server -- compression of Envoy's own statistics - -Step 1: Start all of our containers -*********************************** - -Change to the ``examples/brotli`` directory and bring up the docker composition. - -.. code-block:: console - - $ pwd - envoy/examples/brotli - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - Name Command State Ports - --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - brotli_envoy-stats_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp,:::10000->10000/tcp, 0.0.0.0:9901->9901/tcp,:::9901->9901/tcp, 0.0.0.0:9902->9902/tcp,:::9902->9902/tcp - brotli_service_1 python3 /code/service.py Up (healthy) - -Step 2: Test Envoy’s compression of upstream files -************************************************** - -The sandbox is configured with two endpoints on port ``10000`` for serving upstream files: - -- ``/file.txt`` -- ``/file.json`` - -Only ``/file.json`` is configured to be compressed. - -Use ``curl`` to check that the response from requesting ``file.json`` contains the ``content-encoding: br`` header. - -You will need to add an ``accept-encoding: br`` request header. - -.. code-block:: console - - $ curl -ski -H "Accept-Encoding: br" https://localhost:10000/file.json | grep "content-encoding" - content-encoding: br - -As only files with a content-type of ``application/json`` are configured to be compressed, the response from requesting ``file.txt`` should not contain the ``content-encoding: br`` header, and the file will not be compressed: - -.. code-block:: console - - $ curl -ski -H "Accept-Encoding: br" https://localhost:10000/file.txt | grep "content-encoding" - -Step 3: Test compression of Envoy’s statistics -********************************************** - -The sandbox is configured with two ports serving Envoy’s admin and statistics interface: - -- ``9901`` exposes the standard admin interface without tls -- ``9902`` exposes a compressed version of the admin interface with tls - -Use ``curl`` to make a request for uncompressed statistics on port ``9901``, it should not contain the ``content-encoding`` header in the response: - -.. code-block:: console - - $ curl -ski -H "Accept-Encoding: br" http://localhost:9901/stats/prometheus | grep "content-encoding" - -Now, use ``curl`` to make a request for the compressed statistics: - -.. code-block:: console - - $ curl -ski -H "Accept-Encoding: br" https://localhost:9902/stats/prometheus | grep "content-encoding" - content-encoding: br - -.. seealso:: - :ref:`Brotli API ` - API and configuration reference for Envoy's brotli compression. - - :ref:`Compression configuration ` - Reference documentation for Envoy's compressor filter. - - :ref:`Envoy admin quick start guide ` - Quick start guide to the Envoy admin interface. diff --git a/examples/brotli/verify.sh b/examples/brotli/verify.sh deleted file mode 100755 index 7aca2b3e590a..000000000000 --- a/examples/brotli/verify.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -e - -export NAME=brotli -export PORT_PROXY="${BROTLI_PORT_PROXY:-10200}" -export PORT_STATS0="${BROTLI_PORT_PROXY:-10201}" -export PORT_STATS1="${BROTLI_PORT_PROXY:-10202}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -wait_for 10 bash -c "\ - responds_with_header \ - 'content-encoding: br' \ - https://localhost:${PORT_PROXY}/file.json -ki -H 'Accept-Encoding: br'" - -run_log "Test service: localhost:${PORT_PROXY}/file.json with compression" -responds_with_header \ - "content-encoding: br" \ - "https://localhost:${PORT_PROXY}/file.json" \ - -ki -H "Accept-Encoding: br" - -run_log "Test service: localhost:${PORT_PROXY}/file.txt without compression" -responds_without_header \ - "content-encoding: br" \ - "https://localhost:${PORT_PROXY}/file.txt" \ - -ki -H "Accept-Encoding: br" - -run_log "Test service: localhost:${PORT_STATS0}/stats/prometheus without compression" -responds_without_header \ - "content-encoding: br" \ - "http://localhost:${PORT_STATS0}/stats/prometheus" \ - -ki -H "Accept-Encoding: br" - -run_log "Test service: localhost:${PORT_STATS1}/stats/prometheus with compression" -responds_with_header \ - "content-encoding: br" \ - "https://localhost:${PORT_STATS1}/stats/prometheus" \ - -ki -H "Accept-Encoding: br" diff --git a/examples/cache/README.md b/examples/cache/README.md deleted file mode 100644 index 4ef02a1844d1..000000000000 --- a/examples/cache/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/cache.html) diff --git a/examples/cache/ci-responses.yaml b/examples/cache/ci-responses.yaml deleted file mode 100644 index be571a8338c7..000000000000 --- a/examples/cache/ci-responses.yaml +++ /dev/null @@ -1,13 +0,0 @@ -valid-for-minute: - body: This response will stay fresh for one minute - headers: - cache-control: max-age=4 - custom-header: any value -private: - body: This is a private response, it will not be cached by Envoy - headers: - cache-control: private -no-cache: - body: This response can be cached, but it has to be validated on each request - headers: - cache-control: max-age=0, no-cache diff --git a/examples/cache/docker-compose.yaml b/examples/cache/docker-compose.yaml deleted file mode 100644 index 674c73d193b3..000000000000 --- a/examples/cache/docker-compose.yaml +++ /dev/null @@ -1,33 +0,0 @@ -services: - - front-envoy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - depends_on: - service1: - condition: service_healthy - service2: - condition: service_healthy - ports: - - "${PORT_PROXY:-8000}:8000" - - service1: - build: - context: ../shared/python - target: aiohttp-service - volumes: - - "${CACHE_RESPONSES_YAML:-./responses.yaml}:/etc/responses.yaml" - - ./service.py:/code/service.py - environment: - - SERVICE_NAME=1 - - service2: - build: - context: ../shared/python - target: aiohttp-service - volumes: - - "${CACHE_RESPONSES_YAML:-./responses.yaml}:/etc/responses.yaml" - - ./service.py:/code/service.py - environment: - - SERVICE_NAME=2 diff --git a/examples/cache/envoy.yaml b/examples/cache/envoy.yaml deleted file mode 100644 index b854214b565f..000000000000 --- a/examples/cache/envoy.yaml +++ /dev/null @@ -1,63 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 8000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/service/1" - route: - cluster: service1 - - match: - prefix: "/service/2" - route: - cluster: service2 - http_filters: - - name: "envoy.filters.http.cache" - typed_config: - "@type": "type.googleapis.com/envoy.extensions.filters.http.cache.v3.CacheConfig" - typed_config: - "@type": "type.googleapis.com/envoy.extensions.http.cache.simple_http_cache.v3.SimpleHttpCacheConfig" - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - clusters: - - name: service1 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service1 - port_value: 8080 - - name: service2 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service2 - port_value: 8080 diff --git a/examples/cache/example.rst b/examples/cache/example.rst deleted file mode 100644 index 2fe26289dc5b..000000000000 --- a/examples/cache/example.rst +++ /dev/null @@ -1,245 +0,0 @@ -.. _install_sandboxes_cache_filter: - -Cache filter -============ -.. TODO(yosrym93): When a documentation is written for a production-ready Cache Filter, link to it through this doc. - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -In this example, we demonstrate how HTTP caching can be utilized in Envoy by using the Cache Filter. -The setup of this sandbox is based on the setup of the :ref:`Front Proxy sandbox `. - -All incoming requests are routed via the front Envoy, which acts as a reverse proxy sitting on -the edge of the ``envoymesh`` network. - -Port ``8000`` is exposed by :download:`docker-compose.yaml <_include/cache/docker-compose.yaml>` to handle ``HTTP`` calls -to the services. Two backend services are deployed behind the front Envoy, each with a sidecar Envoy. - -The front Envoy is configured to run the Cache Filter, which stores cacheable responses in an in-memory cache, -and serves it to subsequent requests. - -In this demo, the responses that are served by the deployed services are stored in :download:`responses.yaml <_include/cache/responses.yaml>`. - -This file is mounted to both services' containers, so any changes made to the stored responses while the services are -running should be instantly effective (no need to rebuild or rerun). - -For the purposes of the demo, a response's date of creation is appended to its body before being served. -An Etag is computed for every response for validation purposes, which only depends on the response body in the yaml file (i.e. the appended date is not taken into account). -Cached responses can be identified by having an ``age`` header. Validated responses can be identified by having a generation date older than the ``date`` header; -as when a response is validated the ``date`` header is updated, while the body stays the same. Validated responses do not have an ``age`` header. -Responses served from the backend service have no ``age`` header, and their ``date`` header is the same as their generation date. - -Step 1: Start all of our containers -*********************************** - -Change to the ``examples/cache`` directory. - -.. code-block:: console - - $ pwd - envoy/examples/cache - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - --------------------------------------------------------------------------------------------------- - cache_front-envoy_1 /docker-entrypoint.sh /bin ... Up 10000/tcp, 0.0.0.0:8000->8000/tcp - cache_service1_1 python3 /code/service.py Up (healthy) - cache_service2_1 python3 /code/service.py Up (healthy) - -Step 2: Test Envoy's HTTP caching capabilities -********************************************** - -You can now send a request to both services via the ``front-envoy``. Note that since the two services have different routes, -identical requests to different services have different cache entries (i.e. a request sent to service 2 will not be served by a cached -response produced by service 1). - -To send a request: - -``curl -i localhost:8000/service//`` - -``service_no``: The service to send the request to, 1 or 2. - -``response``: The response that is being requested. The responses are found in -:download:`responses.yaml <_include/cache/responses.yaml>`. - - -The provided example responses are: - -- ``valid-for-minute`` - This response remains fresh in the cache for a minute. After which, the response gets validated by the backend service before being served from the cache. - If found to be updated, the new response is served (and cached). Otherwise, the cached response is served and refreshed. - -- ``private`` - This response is private; it cannot be stored by shared caches (such as proxies). It will always be served from the backend service. - -- ``no-cache`` - This response has to be validated every time before being served. - -You can change the responses' headers and bodies (or add new ones) while the sandbox is running to experiment. - -Example responses ------------------ - -1. valid-for-minute -^^^^^^^^^^^^^^^^^^^ - -.. code-block:: console - - $ curl -i localhost:8000/service/1/valid-for-minute - HTTP/1.1 200 OK - content-type: text/html; charset=utf-8 - content-length: 103 - cache-control: max-age=60 - custom-header: any value - etag: "172ae25df822c3299cf2248694b4ce23" - date: Fri, 11 Sep 2020 03:20:40 GMT - server: envoy - x-envoy-upstream-service-time: 11 - - This response will stay fresh for one minute - Response body generated at: Fri, 11 Sep 2020 03:20:40 GMT - -Naturally, response ``date`` header is the same time as the generated time. -Sending the same request after 30 seconds gives the same exact response with the same generation date, -but with an ``age`` header as it was served from cache: - -.. code-block:: console - - $ curl -i localhost:8000/service/1/valid-for-minute - HTTP/1.1 200 OK - content-type: text/html; charset=utf-8 - content-length: 103 - cache-control: max-age=60 - custom-header: any value - etag: "172ae25df822c3299cf2248694b4ce23" - date: Fri, 11 Sep 2020 03:20:40 GMT - server: envoy - x-envoy-upstream-service-time: 11 - age: 30 - - This response will stay fresh for one minute - Response body generated at: Fri, 11 Sep 2020 03:20:40 GMT - -After 1 minute and 1 second: - -.. code-block:: console - - $ curl -i localhost:8000/service/1/valid-for-minute - HTTP/1.1 200 OK - cache-control: max-age=60 - custom-header: any value - etag: "172ae25df822c3299cf2248694b4ce23" - date: Fri, 11 Sep 2020 03:21:41 GMT - server: envoy - x-envoy-upstream-service-time: 8 - content-length: 103 - content-type: text/html; charset=utf-8 - - This response will stay fresh for one minute - Response body generated at: Fri, 11 Sep 2020 03:20:40 GMT - -The same response was served after being validated with the backend service. -You can verify this as the response generation time is the same, -but the response ``date`` header was updated with the validation response date. -Also, no ``age`` header. - -Every time the response is validated, it stays fresh for another minute. -If the response body changes while the cached response is still fresh, -the cached response will still be served. The cached response will only be updated when it is no longer fresh. - -2. private -^^^^^^^^^^ - -.. code-block:: console - - $ curl -i localhost:8000/service/1/private - HTTP/1.1 200 OK - content-type: text/html; charset=utf-8 - content-length: 117 - cache-control: private - etag: "6bd80b59b2722606abf2b8d83ed2126d" - date: Fri, 11 Sep 2020 03:22:28 GMT - server: envoy - x-envoy-upstream-service-time: 7 - - This is a private response, it will not be cached by Envoy - Response body generated at: Fri, 11 Sep 2020 03:22:28 GMT - -No matter how many times you make this request, you will always receive a new response; -new date of generation, new ``date`` header, and no ``age`` header. - -3. no-cache -^^^^^^^^^^^ - -.. code-block:: console - - $ curl -i localhost:8000/service/1/no-cache - HTTP/1.1 200 OK - content-type: text/html; charset=utf-8 - content-length: 130 - cache-control: max-age=0, no-cache - etag: "ce39a53bd6bb8abdb2488a5a375397e4" - date: Fri, 11 Sep 2020 03:23:07 GMT - server: envoy - x-envoy-upstream-service-time: 7 - - This response can be cached, but it has to be validated on each request - Response body generated at: Fri, 11 Sep 2020 03:23:07 GMT - -After a few seconds: - -.. code-block:: console - - $ curl -i localhost:8000/service/1/no-cache - HTTP/1.1 200 OK - cache-control: max-age=0, no-cache - etag: "ce39a53bd6bb8abdb2488a5a375397e4" - date: Fri, 11 Sep 2020 03:23:12 GMT - server: envoy - x-envoy-upstream-service-time: 7 - content-length: 130 - content-type: text/html; charset=utf-8 - - This response can be cached, but it has to be validated on each request - Response body generated at: Fri, 11 Sep 2020 03:23:07 GMT - -You will receive a cached response that has the same generation time. -However, the ``date`` header will always be updated as this response will always be validated first. -Also, no ``age`` header. - -If you change the response body in the yaml file: - -.. code-block:: console - - $ curl -i localhost:8000/service/1/no-cache - HTTP/1.1 200 OK - content-type: text/html; charset=utf-8 - content-length: 133 - cache-control: max-age=0, no-cache - etag: "f4768af0ac9f6f54f88169a1f3ecc9f3" - date: Fri, 11 Sep 2020 03:24:10 GMT - server: envoy - x-envoy-upstream-service-time: 7 - - This response can be cached, but it has to be validated on each request!!! - Response body generated at: Fri, 11 Sep 2020 03:24:10 GMT - -You will receive a new response that's served from the backend service. -The new response will be cached for subsequent requests. - -You can also add new responses to the yaml file with different ``cache-control`` headers and start experimenting! - -.. seealso:: - - :ref:`Envoy Cache filter configuration ` - Learn more about configuring the Envoy Cache filter. - - `MDN Web Docs `_. - Learn more about caching and ``cache-control`` on the web. diff --git a/examples/cache/requirements.in b/examples/cache/requirements.in deleted file mode 100644 index c3726e8bfeee..000000000000 --- a/examples/cache/requirements.in +++ /dev/null @@ -1 +0,0 @@ -pyyaml diff --git a/examples/cache/requirements.txt b/examples/cache/requirements.txt deleted file mode 100644 index c160459ca0cf..000000000000 --- a/examples/cache/requirements.txt +++ /dev/null @@ -1,58 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile --allow-unsafe --generate-hashes requirements.in -# -pyyaml==6.0.1 \ - --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \ - --hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \ - --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \ - --hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \ - --hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \ - --hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \ - --hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \ - --hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \ - --hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \ - --hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \ - --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \ - --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \ - --hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \ - --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \ - --hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \ - --hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \ - --hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \ - --hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \ - --hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \ - --hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \ - --hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \ - --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \ - --hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \ - --hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \ - --hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \ - --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \ - --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \ - --hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \ - --hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \ - --hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \ - --hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \ - --hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \ - --hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \ - --hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \ - --hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \ - --hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \ - --hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \ - --hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \ - --hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \ - --hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \ - --hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \ - --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \ - --hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \ - --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \ - --hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \ - --hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \ - --hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \ - --hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \ - --hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \ - --hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f - # via -r requirements.in diff --git a/examples/cache/responses.yaml b/examples/cache/responses.yaml deleted file mode 100644 index 1b20ac58f6a1..000000000000 --- a/examples/cache/responses.yaml +++ /dev/null @@ -1,13 +0,0 @@ -valid-for-minute: - body: This response will stay fresh for one minute - headers: - cache-control: max-age=60 - custom-header: any value -private: - body: This is a private response, it will not be cached by Envoy - headers: - cache-control: private -no-cache: - body: This response can be cached, but it has to be validated on each request - headers: - cache-control: max-age=0, no-cache diff --git a/examples/cache/service.py b/examples/cache/service.py deleted file mode 100644 index 4e85ec595261..000000000000 --- a/examples/cache/service.py +++ /dev/null @@ -1,84 +0,0 @@ -import hashlib -import os -import re -import datetime -from typing import Optional - -import yaml - -from aiohttp import web - -routes = web.RouteTableDef() - -# TOOD(phlax): move this to pytooling - -# Etag fun lifted from https://github.com/zhangkaizhao/aiohttp-etag - - -def _check_etag_header(request, computed_etag) -> bool: - # Find all weak and strong etag values from If-None-Match header - # because RFC 7232 allows multiple etag values in a single header. - etags = re.findall(r'\*|(?:W/)?"[^"]*"', request.headers.get("If-None-Match", "")) - if not etags: - return False - - match = False - if etags[0] == "*": - match = True - else: - # Use a weak comparison when comparing entity-tags. - def val(x: str) -> str: - return x[2:] if x.startswith("W/") else x - - for etag in etags: - if val(etag) == val(computed_etag): - match = True - break - return match - - -def _compute_etag(body) -> str: - hasher = hashlib.sha1() - hasher.update(body.encode()) - return f'"{hasher.hexdigest()}"' - - -def _set_etag_header(response, computed_etag) -> None: - response.headers["Etag"] = computed_etag - - -@routes.get("/service/{service_number}/{response_id}") -async def get(request): - service_number = request.match_info["service_number"] - response_id = request.match_info["response_id"] - stored_response = yaml.safe_load(open('/etc/responses.yaml', 'r')).get(response_id) - - if stored_response is None: - raise web.HTTPNotFound(reason="No response found with the given id") - - # Etag is computed for every response, which only depends on the response body in - # the yaml file (i.e. the appended date is not taken into account). - body = stored_response.get('body') - computed_etag = _compute_etag(body) - - if _check_etag_header(request, computed_etag): - return web.HTTPNotModified(headers={'ETag': computed_etag}) - - request_date = datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S GMT") - response = web.Response(text=f"{body}\nResponse generated at: {request_date}\n") - - if stored_response.get('headers'): - response.headers.update(stored_response.get('headers')) - - _set_etag_header(response, computed_etag) - - return response - - -if __name__ == "__main__": - if not os.path.isfile('/etc/responses.yaml'): - print('Responses file not found at /etc/responses.yaml') - exit(1) - app = web.Application() - app.add_routes(routes) - web.run_app(app, host='0.0.0.0', port=8080) diff --git a/examples/cache/verify.sh b/examples/cache/verify.sh deleted file mode 100755 index b8993be6bb24..000000000000 --- a/examples/cache/verify.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash -e - -export NAME=cache - -export PORT_PROXY="${CACHE_PORT_PROXY:-10300}" -export CACHE_RESPONSES_YAML=./ci-responses.yaml - - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -check_validated() { - # Get the date header and the response generation timestamp - local _dates dates httpCode - _dates=$(grep -oP '\d\d:\d\d:\d\d' <<< "$1") - httpCode=$(echo "$response" | head -n 1 | cut -d ' ' -f 2) - while read -r line; do dates+=("$line"); done \ - <<< "$_dates" - - # Make sure it succeeds - if [[ $httpCode != "200" ]]; then - echo "ERROR: HTTP response code should be 200, but it was $httpCode" >&2 - return 1 - fi - - # Make sure they are different - if [[ ${dates[0]} == "${dates[1]}" ]]; then - echo "ERROR: validated responses should have a date AFTER the generation timestamp" >&2 - return 1 - fi - # Make sure there is no age header - if grep -q "age:" <<< "$1"; then - echo "ERROR: validated responses should not have an age header" >&2 - return 1 - fi -} - -check_cached() { - # Make sure there is an age header - if ! grep -q "age:" <<< "$1"; then - echo "ERROR: cached responses should have an age header" >&2 - return 1 - fi -} - -check_from_origin() { - # Get the date header and the response generation timestamp - local _dates dates - _dates=$(grep -oP '\d\d:\d\d:\d\d' <<< "$1") - while read -r line; do dates+=("$line"); done \ - <<< "$_dates" - # Make sure they are equal - if [[ ${dates[0]} != "${dates[1]}" ]]; then - echo "ERROR: responses from origin should have a date equal to the generation timestamp" >&2 - return 1 - fi - # Make sure there is no age header - if grep -q "age:" <<< "$1" ; then - echo "ERROR: responses from origin should not have an age header" >&2 - return 1 - fi -} - - -run_log "Valid-for-minute: First request should be served by the origin" -response=$(curl -si "localhost:${PORT_PROXY}/service/1/valid-for-minute") -check_from_origin "$response" - -run_log "Snooze for 2 seconds" -sleep 2 - -run_log "Valid-for-minute: Second request should be served from cache" -response=$(curl -si "localhost:${PORT_PROXY}/service/1/valid-for-minute") -check_cached "$response" - -run_log "Snooze for 3 more seconds" -sleep 3 - -run_log "Valid-for-minute: More than a minute has passed, this request should get a validated response" -response=$(curl -si "localhost:${PORT_PROXY}/service/1/valid-for-minute") -check_validated "$response" - -run_log "Private: Make 4 requests make sure they are all served by the origin" -for _ in {0..3}; do - response=$(curl -si "localhost:${PORT_PROXY}/service/1/private") - check_from_origin "$response" -done - -run_log "No-cache: First request should be served by the origin" -response=$(curl -si "localhost:${PORT_PROXY}/service/1/no-cache") -check_from_origin "$response" - -run_log "No-cache: Make 4 more requests and make sure they are all validated before being served from cache" -for _ in {0..3}; do - sleep 1 - response=$(curl -si "localhost:${PORT_PROXY}/service/1/no-cache") - check_validated "$response" -done diff --git a/examples/cors/README.md b/examples/cors/README.md deleted file mode 100644 index 1908b6455bf1..000000000000 --- a/examples/cors/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/cors.html). diff --git a/examples/cors/backend/docker-compose.yaml b/examples/cors/backend/docker-compose.yaml deleted file mode 100644 index 262f09fa74d0..000000000000 --- a/examples/cors/backend/docker-compose.yaml +++ /dev/null @@ -1,19 +0,0 @@ -services: - - front-envoy: - build: - context: . - dockerfile: ../../shared/envoy/Dockerfile - depends_on: - backend-service: - condition: service_healthy - ports: - - "${PORT_BACKEND:-8002}:10000" - - "${PORT_STATS:-8003}:8001" - - backend-service: - build: - context: ../../shared/python - target: aiohttp-service - volumes: - - ./service.py:/code/service.py diff --git a/examples/cors/backend/envoy.yaml b/examples/cors/backend/envoy.yaml deleted file mode 100644 index 5feebc5e903c..000000000000 --- a/examples/cors/backend/envoy.yaml +++ /dev/null @@ -1,97 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - access_log: - - name: envoy.access_loggers.stdout - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog - route_config: - name: local_route - virtual_hosts: - - name: www - domains: - - "*" - typed_per_filter_config: - envoy.filters.http.cors: - "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.CorsPolicy - allow_origin_string_match: - - safe_regex: - regex: \* - allow_methods: "GET" - filter_enabled: - default_value: - numerator: 100 - denominator: HUNDRED - runtime_key: cors.www.enabled - shadow_enabled: - default_value: - numerator: 0 - denominator: HUNDRED - runtime_key: cors.www.shadow_enabled - routes: - - match: - prefix: "/cors/open" - route: - cluster: backend_service - - match: - prefix: "/cors/disabled" - route: - cluster: backend_service - typed_per_filter_config: - envoy.filters.http.cors: - "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.CorsPolicy - filter_enabled: - default_value: - numerator: 0 - denominator: HUNDRED - - match: - prefix: "/cors/restricted" - route: - cluster: backend_service - typed_per_filter_config: - envoy.filters.http.cors: - "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.CorsPolicy - allow_origin_string_match: - - safe_regex: - regex: .*\.envoyproxy\.io - allow_methods: "GET" - - match: - prefix: "/" - route: - cluster: backend_service - http_filters: - - name: envoy.filters.http.cors - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: backend_service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: backend_service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: backend-service - port_value: 8080 - -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 8001 diff --git a/examples/cors/backend/service.py b/examples/cors/backend/service.py deleted file mode 100644 index 29ec77ade4a8..000000000000 --- a/examples/cors/backend/service.py +++ /dev/null @@ -1,14 +0,0 @@ -from aiohttp import web - -routes = web.RouteTableDef() - - -@routes.get("/cors/{status}") -async def get(request): - return web.Response(text="Success!") - - -if __name__ == "__main__": - app = web.Application() - app.add_routes(routes) - web.run_app(app, host='0.0.0.0', port=8080) diff --git a/examples/cors/example.rst b/examples/cors/example.rst deleted file mode 100644 index 616401b1a3e4..000000000000 --- a/examples/cors/example.rst +++ /dev/null @@ -1,101 +0,0 @@ -.. _install_sandboxes_cors: - -CORS filter -=========== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - -Cross-Origin Resource Sharing (CORS) is a method of enforcing client-side -access controls on resources by specifying external domains that are able to -access certain or all routes of your domain. Browsers use the presence of HTTP -headers to determine if a response from a different origin is allowed. - -To help demonstrate how front-envoy can enforce CORS policies, we are -releasing a set of `docker compose `_ sandboxes -that deploy a frontend and backend service on different origins, both behind -front-envoy. - -The frontend service has a field to input the remote domain of your backend -service along with radio buttons to select the remote domain's CORS enforcement. -The CORS enforcement choices are: - - * Disabled: CORS is disabled on the route requested. This will result in a - client-side CORS error since the required headers to be considered a - valid CORS request are not present. - * Open: CORS is enabled on the route requested but the allowed origin is set - to ``*``. This is a very permissive policy and means that origin can request - data from this endpoint. - * Restricted: CORS is enabled on the route requested and the only allowed - origin is ``envoyproxy.io``. This will result in a client-side CORS error. - -Step 1: Start all of our containers -*********************************** - -Change to the ``examples/cors/frontend`` directory, and start the containers: - -.. code-block:: console - - $ pwd - envoy/examples/cors/frontend - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ----------------------------------------------------------------------------------------------------------- - frontend_front-envoy_1 /docker-entrypoint.sh /bin ... Up 10000/tcp, 0.0.0.0:8000->8000/tcp - frontend_frontend-service_1 python3 /code/service.py ... Up (healthy) - -Now, switch to the ``backend`` directory in the ``cors`` example, and start the containers: - -.. code-block:: console - - $ pwd - envoy/examples/cors/backend - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ----------------------------------------------------------------------------------------------------------------------------------- - backend_backend-service_1 python3 /code/service.py ... Up (healthy) - backend_front-envoy_1 /docker-entrypoint.sh /bin ... Up 10000/tcp, 0.0.0.0:8002->8000/tcp, 0.0.0.0:8003->8001/tcp - -Step 2: Test Envoy's CORS capabilities -************************************** - -You can now open a browser to view your frontend service at http://localhost:8000. - -Results of the cross-origin request will be shown on the page under *Request Results*. - -Your browser's ``CORS`` enforcement logs can be found in the browser console. - -For example: - -.. code-block:: console - - Access to XMLHttpRequest at 'http://192.168.99.100:8002/cors/disabled' from origin 'http://192.168.99.101:8000' - has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. - -Step 3: Check stats of backend via admin -**************************************** - -When Envoy runs, it can listen to ``admin`` requests if a port is configured. - -In the example configs, the backend admin is bound to port ``8003``. - -If you browse to http://localhost:8003/stats you will be able to view -all of the Envoy stats for the backend. You should see the ``CORS`` stats for -invalid and valid origins increment as you make requests from the frontend cluster. - -.. code-block:: none - - http.ingress_http.cors.origin_invalid: 2 - http.ingress_http.cors.origin_valid: 7 - -.. seealso:: - - :ref:`Envoy admin quick start guide ` - Quick start guide to the Envoy admin interface. diff --git a/examples/cors/frontend/docker-compose.yaml b/examples/cors/frontend/docker-compose.yaml deleted file mode 100644 index 6546e87369ee..000000000000 --- a/examples/cors/frontend/docker-compose.yaml +++ /dev/null @@ -1,19 +0,0 @@ -services: - - front-envoy: - build: - context: . - dockerfile: ../../shared/envoy/Dockerfile - depends_on: - frontend-service: - condition: service_healthy - ports: - - "${PORT_PROXY:-8000}:10000" - - frontend-service: - build: - context: ../../shared/python - target: aiohttp-service - volumes: - - ./service.py:/code/service.py - - ./index.html:/code/index.html diff --git a/examples/cors/frontend/envoy.yaml b/examples/cors/frontend/envoy.yaml deleted file mode 100644 index e42bd5813c0f..000000000000 --- a/examples/cors/frontend/envoy.yaml +++ /dev/null @@ -1,48 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - access_log: - - name: envoy.access_loggers.stdout - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog - route_config: - name: local_route - virtual_hosts: - - name: services - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: frontend_service - http_filters: - - name: envoy.filters.http.cors - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: frontend_service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: frontend_service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: frontend-service - port_value: 8080 diff --git a/examples/cors/frontend/index.html b/examples/cors/frontend/index.html deleted file mode 100644 index 30e31d51c51b..000000000000 --- a/examples/cors/frontend/index.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - Envoy CORS Webpage - - - - -

- Envoy CORS Demo -

-

- This page requests an asset from another domain via cross-site XMLHttpRequest mitigated by Access Control.
- This scenario demonstrates a simple method.
- It does NOT dispatch a preflight request. -

-

- Enter the IP address of backend Docker container. As we are running Docker Compose this should just be localhost.
-

-
- -
-
-
CORS Enforcement
- Disabled
- Open
- Restricted
-
-
-
-

Request Results

-

-
-
- - - diff --git a/examples/cors/frontend/service.py b/examples/cors/frontend/service.py deleted file mode 100644 index 623b46dda882..000000000000 --- a/examples/cors/frontend/service.py +++ /dev/null @@ -1,18 +0,0 @@ -import os - -from aiohttp import web - -routes = web.RouteTableDef() - - -@routes.get("/") -async def get(request): - file_dir = os.path.dirname(os.path.realpath(__file__)) - with open(f"{file_dir}/index.html") as f: - return web.Response(text=f.read(), content_type='text/html') - - -if __name__ == "__main__": - app = web.Application() - app.add_routes(routes) - web.run_app(app, host='0.0.0.0', port=8080) diff --git a/examples/cors/verify.sh b/examples/cors/verify.sh deleted file mode 100755 index fc37386f265f..000000000000 --- a/examples/cors/verify.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash -e - -export NAME=cors -export PATHS=frontend,backend - -export PORT_PROXY="${CORS_PORT_PROXY:-10310}" -export PORT_BACKEND="${CORS_PORT_BACKEND:-10311}" -export PORT_STATS="${CORS_PORT_STATS:-10312}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - - -run_log "Test service" -responds_with \ - "Envoy CORS Webpage" \ - "http://localhost:${PORT_PROXY}" - -run_log "Test cors server: disabled" -responds_with \ - Success \ - -H "Origin: http://example.com" \ - "http://localhost:${PORT_BACKEND}/cors/disabled" -responds_without_header \ - access-control-allow-origin \ - -H "Origin: http://example.com" \ - "http://localhost:${PORT_BACKEND}/cors/disabled" - -run_log "Test cors server: open" -responds_with \ - Success \ - -H 'Origin: http://example.com' \ - "http://localhost:${PORT_BACKEND}/cors/open" -responds_with_header \ - "access-control-allow-origin: http://example.com" \ - -H "Origin: http://example.com" \ - "http://localhost:${PORT_BACKEND}/cors/open" - -run_log "Test cors server: restricted" -responds_with \ - Success \ - -H "Origin: http://example.com" \ - "http://localhost:${PORT_BACKEND}/cors/restricted" -responds_without_header \ - access-control-allow-origin \ - -H "Origin: http://example.com" \ - "http://localhost:${PORT_BACKEND}/cors/restricted" -responds_with_header \ - "access-control-allow-origin: http://foo.envoyproxy.io" \ - -H "Origin: http://foo.envoyproxy.io" \ - "http://localhost:${PORT_BACKEND}/cors/restricted" - -run_log "Check admin ingress stats" -responds_with \ - ingress_http.cors \ - "http://localhost:${PORT_STATS}/stats?filter=ingress_http" diff --git a/examples/csrf/README.md b/examples/csrf/README.md deleted file mode 100644 index fe8d62602b6b..000000000000 --- a/examples/csrf/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/csrf.html). diff --git a/examples/csrf/crosssite/docker-compose.yml b/examples/csrf/crosssite/docker-compose.yml deleted file mode 100644 index 96a5586e76a8..000000000000 --- a/examples/csrf/crosssite/docker-compose.yml +++ /dev/null @@ -1,19 +0,0 @@ -services: - - front-envoy: - build: - context: . - dockerfile: ../../shared/envoy/Dockerfile - depends_on: - service: - condition: service_healthy - ports: - - "${PORT_CROSS:-8002}:10000" - - service: - build: - context: ../../shared/python - target: aiohttp-service - volumes: - - ./service.py:/code/service.py - - ../index.html:/code/index.html diff --git a/examples/csrf/crosssite/envoy.yaml b/examples/csrf/crosssite/envoy.yaml deleted file mode 100644 index 47cb74b3acb3..000000000000 --- a/examples/csrf/crosssite/envoy.yaml +++ /dev/null @@ -1,45 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - access_log: - - name: envoy.access_loggers.stdout - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog - route_config: - name: local_route - virtual_hosts: - - name: www - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: generic_service - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: generic_service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: generic_service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service - port_value: 8080 diff --git a/examples/csrf/crosssite/service.py b/examples/csrf/crosssite/service.py deleted file mode 100644 index 13220f175371..000000000000 --- a/examples/csrf/crosssite/service.py +++ /dev/null @@ -1,18 +0,0 @@ -import os - -from aiohttp import web - -routes = web.RouteTableDef() - - -@routes.get("/") -async def get(request): - file_dir = os.path.dirname(os.path.realpath(__file__)) - with open(f"{file_dir}/index.html") as f: - return web.Response(text=f.read()) - - -if __name__ == "__main__": - app = web.Application() - app.add_routes(routes) - web.run_app(app, host='0.0.0.0', port=8080) diff --git a/examples/csrf/example.rst b/examples/csrf/example.rst deleted file mode 100644 index 12223e76a06c..000000000000 --- a/examples/csrf/example.rst +++ /dev/null @@ -1,110 +0,0 @@ -.. _install_sandboxes_csrf: - -CSRF filter -=========== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - -Cross-Site Request Forgery (CSRF) is an attack that occurs when a malicious -third-party website exploits a vulnerability that allows them to submit an -undesired request on a user's behalf. To mitigate this attack this filter -checks where a request is coming from to determine if the request's origin -is the same as it's destination. - -To help demonstrate how front-envoy can enforce CSRF policies, we are releasing -a `docker compose `_ sandbox that -deploys a service with both a frontend and backed. This service will be started -on two different virtual machines with different origins. - -The frontend has a field to input the remote domain of where you would like to -send POST requests along with radio buttons to select the remote domain's CSRF -enforcement. The CSRF enforcement choices are: - - * Disabled: CSRF is disabled on the requested route. This will result in a - successful request since there is no CSRF enforcement. - * Shadow Mode: CSRF is not enforced on the requested route but will record - if the request contains a valid source origin. - * Enabled: CSRF is enabled and will return a 403 (Forbidden) status code when - a request is made from a different origin. - * Ignored: CSRF is enabled but the request type is a GET. This should bypass - the CSRF filter and return successfully. - -Step 1: Start all of our containers -*********************************** - -Change to the ``examples/csrf/samesite`` directory, and start the containers: - -.. code-block:: console - - $ pwd - envoy/examples/csrf/samesite - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - --------------------------------------------------------------------------------------------------------------------------------- - samesite_front-envoy_1 /docker-entrypoint.sh /bin ... Up 10000/tcp, 0.0.0.0:8000->8000/tcp, 0.0.0.0:8001->8001/tcp - samesite_service_1 python3 /code/service.py ... Up (healthy) - -Now, switch to the ``crosssite`` directory in the ``csrf`` example, and start the containers: - -.. code-block:: console - - $ pwd - envoy/examples/csrf/crosssite - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ---------------------------------------------------------------------------------------------------------- - crosssite_front-envoy_1 /bin/sh -c /usr/local/bin/ ... Up 10000/tcp, 0.0.0.0:8002->8000/tcp - crosssite_service_1 python3 /code/service.py ... Up (healthy) - -Step 2: Test Envoy's CSRF capabilities -************************************** - -You can now open a browser at http://localhost:8002 to view your ``crosssite`` frontend service. - -Enter the IP of the ``samesite`` machine to demonstrate cross-site requests. Requests -with the enabled enforcement will fail. By default this field will be populated -with ``localhost``. - -To demonstrate same-site requests open the frontend service for ``samesite`` at http://localhost:8000 -and enter the IP address of the ``samesite`` machine as the destination. - -Results of the cross-site request will be shown on the page under *Request Results*. -Your browser's ``CSRF`` enforcement logs can be found in the browser console and in the -network tab. - -For example: - -.. code-block:: console - - Failed to load resource: the server responded with a status of 403 (Forbidden) - -If you change the destination to be the same as one displaying the website and -set the ``CSRF`` enforcement to enabled the request will go through successfully. - -Step 3: Check stats of backend via admin -**************************************** - -When Envoy runs, it can listen to ``admin`` requests if a port is configured. In -the example configs, the backend admin is bound to port ``8001``. - -If you browse to http://localhost:8001/stats you will be able to view -all of the Envoy stats for the backend. You should see the CORS stats for -invalid and valid origins increment as you make requests from the frontend cluster. - -.. code-block:: none - - http.ingress_http.csrf.missing_source_origin: 0 - http.ingress_http.csrf.request_invalid: 1 - http.ingress_http.csrf.request_valid: 0 - -.. seealso:: - - :ref:`Envoy admin quick start guide ` - Quick start guide to the Envoy admin interface. diff --git a/examples/csrf/index.html b/examples/csrf/index.html deleted file mode 100644 index e39a5cb61659..000000000000 --- a/examples/csrf/index.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - Envoy CSRF Wepage - - - - -

- Envoy CSRF Demo -

-

- This page demonstrates a few scenarios for CSRF. -

-

- Enter the IP address of the destination Docker container.
-

-
- -
-
-
CSRF Enforcement
- Disabled
- Shadow Mode
- Enabled
- Ignored
- Additional Origin
-
-
-
-

Request Results

-

-
-
- - - diff --git a/examples/csrf/samesite/docker-compose.yml b/examples/csrf/samesite/docker-compose.yml deleted file mode 100644 index feabf37afc92..000000000000 --- a/examples/csrf/samesite/docker-compose.yml +++ /dev/null @@ -1,20 +0,0 @@ -services: - - front-envoy: - build: - context: . - dockerfile: ../../shared/envoy/Dockerfile - depends_on: - service: - condition: service_healthy - ports: - - "${PORT_SAME:-8000}:10000" - - "${PORT_STATS:-8001}:8001" - - service: - build: - context: ../../shared/python - target: aiohttp-service - volumes: - - ./service.py:/code/service.py - - ../index.html:/code/index.html diff --git a/examples/csrf/samesite/envoy.yaml b/examples/csrf/samesite/envoy.yaml deleted file mode 100644 index cffeea09c790..000000000000 --- a/examples/csrf/samesite/envoy.yaml +++ /dev/null @@ -1,123 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - access_log: - - name: envoy.access_loggers.stdout - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog - route_config: - name: local_route - virtual_hosts: - - name: www - domains: - - "*" - typed_per_filter_config: - envoy.filters.http.cors: - "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.CorsPolicy - allow_origin_string_match: - - safe_regex: - regex: \* - filter_enabled: - default_value: - numerator: 100 - denominator: HUNDRED - envoy.filters.http.csrf: - "@type": type.googleapis.com/envoy.extensions.filters.http.csrf.v3.CsrfPolicy - filter_enabled: - default_value: - numerator: 100 - denominator: HUNDRED - runtime_key: csrf.www.enabled - shadow_enabled: - default_value: - numerator: 0 - denominator: HUNDRED - runtime_key: csrf.www.shadow_enabled - routes: - - match: - prefix: "/csrf/disabled" - route: - cluster: generic_service - typed_per_filter_config: - envoy.filters.http.csrf: - "@type": type.googleapis.com/envoy.extensions.filters.http.csrf.v3.CsrfPolicy - filter_enabled: - default_value: - numerator: 0 - denominator: HUNDRED - - match: - prefix: "/csrf/shadow" - route: - cluster: generic_service - typed_per_filter_config: - envoy.filters.http.csrf: - "@type": type.googleapis.com/envoy.extensions.filters.http.csrf.v3.CsrfPolicy - filter_enabled: - default_value: - numerator: 0 - denominator: HUNDRED - shadow_enabled: - default_value: - numerator: 100 - denominator: HUNDRED - - match: - prefix: "/csrf/additional_origin" - route: - cluster: generic_service - typed_per_filter_config: - envoy.filters.http.csrf: - "@type": type.googleapis.com/envoy.extensions.filters.http.csrf.v3.CsrfPolicy - filter_enabled: - default_value: - numerator: 100 - denominator: HUNDRED - additional_origins: - - safe_regex: - regex: .* - - match: - prefix: "/" - route: - cluster: generic_service - http_filters: - - name: envoy.filters.http.cors - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors - - name: envoy.filters.http.csrf - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.csrf.v3.CsrfPolicy - filter_enabled: - default_value: - numerator: 0 - denominator: HUNDRED - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: generic_service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: generic_service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service - port_value: 8080 - -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 8001 diff --git a/examples/csrf/samesite/service.py b/examples/csrf/samesite/service.py deleted file mode 100644 index 1f9dde7b1a20..000000000000 --- a/examples/csrf/samesite/service.py +++ /dev/null @@ -1,23 +0,0 @@ -import os - -from aiohttp import web - -routes = web.RouteTableDef() - - -@routes.post("/csrf/{status}") -async def csrf_with_status(request): - return web.Response(text="Success!") - - -@routes.get("/") -async def get(request): - file_dir = os.path.dirname(os.path.realpath(__file__)) - with open(f"{file_dir}/index.html") as f: - return web.Response(text=f.read()) - - -if __name__ == "__main__": - app = web.Application() - app.add_routes(routes) - web.run_app(app, host='0.0.0.0', port=8080) diff --git a/examples/csrf/start_service.sh b/examples/csrf/start_service.sh deleted file mode 100644 index 856194b13590..000000000000 --- a/examples/csrf/start_service.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -python3 /code/service.py & -envoy -c /etc/service-envoy.yaml --service-cluster service diff --git a/examples/csrf/verify.sh b/examples/csrf/verify.sh deleted file mode 100755 index f2188a5e7815..000000000000 --- a/examples/csrf/verify.sh +++ /dev/null @@ -1,78 +0,0 @@ -#!/bin/bash -e - -export NAME=csrf -export PATHS=samesite,crosssite - -export PORT_SAME="${CSRF_PORT_SAME:-10320}" -export PORT_STATS="${CSRF_PORT_STATS:-10321}" -export PORT_CROSS="${CSRF_PORT_CROSS:-10322}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - - -run_log "Test services" -responds_with \ - "Envoy CSRF Demo" \ - "http://localhost:${PORT_CROSS}" -responds_with \ - "Envoy CSRF Demo" \ - "http://localhost:${PORT_SAME}" - -run_log "Test stats server" -responds_with \ - ":" \ - "http://localhost:${PORT_STATS}/stats" - -run_log "Test csrf server: disabled" -responds_with \ - Success \ - -X POST \ - -H "Origin: http://example.com" \ - "http://localhost:${PORT_SAME}/csrf/disabled" -responds_with_header \ - "access-control-allow-origin: http://example.com" \ - -X POST \ - -H "Origin: http://example.com" \ - "http://localhost:${PORT_SAME}/csrf/disabled" - -run_log "Test csrf server: shadow" -responds_with \ - Success \ - -X POST \ - -H "Origin: http://example.com" \ - "http://localhost:${PORT_SAME}/csrf/shadow" -responds_with_header \ - "access-control-allow-origin: http://example.com" \ - -X POST \ - -H "Origin: http://example.com" \ - "http://localhost:${PORT_SAME}/csrf/shadow" - -run_log "Test csrf server: enabled" -responds_with \ - "Invalid origin" \ - -X POST \ - -H "Origin: http://example.com" \ - "http://localhost:${PORT_SAME}/csrf/enabled" -responds_with_header \ - "HTTP/1.1 403 Forbidden" \ - -X POST \ - -H "Origin: http://example.com" \ - "http://localhost:${PORT_SAME}/csrf/enabled" - -run_log "Test csrf server: additional_origin" -responds_with \ - Success \ - -X POST \ - -H "Origin: http://example.com" \ - "http://localhost:${PORT_SAME}/csrf/additional_origin" -responds_with_header \ - "access-control-allow-origin: http://example.com" \ - -X POST \ - -H "Origin: http://example.com" \ - "http://localhost:${PORT_SAME}/csrf/additional_origin" - -run_log "Check admin ingress stats" -responds_with \ - ingress_http.csrf \ - "http://localhost:${PORT_STATS}/stats?filter=ingress_http" diff --git a/examples/double-proxy/README.md b/examples/double-proxy/README.md deleted file mode 100644 index 42ac1ea14aab..000000000000 --- a/examples/double-proxy/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/double-proxy.html). diff --git a/examples/double-proxy/docker-compose.yaml b/examples/double-proxy/docker-compose.yaml deleted file mode 100644 index 23e3a7c0a482..000000000000 --- a/examples/double-proxy/docker-compose.yaml +++ /dev/null @@ -1,84 +0,0 @@ -services: - - proxy-frontend: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - networks: - edge: - depends_on: - app: - condition: service_healthy - ports: - - "10000:10000" - - app: - build: - context: ../shared/python - target: aiohttp-postgres-service - depends_on: - proxy-postgres-frontend: - condition: service_started - networks: - edge: - postgres-frontend: - volumes: - - ./service.py:/code/service.py - - proxy-postgres-frontend: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - target: envoy-double-proxy-frontend - args: - ENVOY_CONFIG: envoy-frontend.yaml - depends_on: - proxy-postgres-backend: - condition: service_started - networks: - postgres-frontend: - aliases: - - postgres - postgres-in-between: - - proxy-postgres-backend: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - target: envoy-double-proxy-backend - args: - ENVOY_CONFIG: envoy-backend.yaml - depends_on: - postgres: - condition: service_healthy - networks: - postgres-backend: - postgres-in-between: - aliases: - - proxy-postgres-backend.example.com - - postgres: - build: - context: ../shared/postgres - networks: - postgres-backend: - environment: - # WARNING! Do not use it on production environments because this will - # allow anyone with access to the Postgres port to access your - # database without a password, even if POSTGRES_PASSWORD is set. - # See PostgreSQL documentation about "trust": - # https://www.postgresql.org/docs/current/auth-trust.html - POSTGRES_HOST_AUTH_METHOD: trust - -networks: - edge: - name: edge - - postgres-backend: - name: postgres-backend - - postgres-frontend: - name: postgres-frontend - - postgres-in-between: - name: postgres-in-between diff --git a/examples/double-proxy/envoy-backend.yaml b/examples/double-proxy/envoy-backend.yaml deleted file mode 100644 index 49c8306f35d0..000000000000 --- a/examples/double-proxy/envoy-backend.yaml +++ /dev/null @@ -1,49 +0,0 @@ -static_resources: - listeners: - - name: postgres_listener - address: - socket_address: - address: 0.0.0.0 - port_value: 5432 - listener_filters: - - name: "envoy.filters.listener.tls_inspector" - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector - filter_chains: - - filters: - - name: envoy.filters.network.tcp_proxy - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - stat_prefix: postgres_tcp - cluster: postgres_cluster - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext - require_client_certificate: true - common_tls_context: - tls_certificates: - - certificate_chain: - filename: certs/servercert.pem - private_key: - filename: certs/serverkey.pem - validation_context: - match_typed_subject_alt_names: - - san_type: DNS - matcher: - exact: proxy-postgres-frontend.example.com - trusted_ca: - filename: certs/cacert.pem - - clusters: - - name: postgres_cluster - type: STRICT_DNS - load_assignment: - cluster_name: postgres_cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: postgres - port_value: 5432 diff --git a/examples/double-proxy/envoy-frontend.yaml b/examples/double-proxy/envoy-frontend.yaml deleted file mode 100644 index 21fa643e62ed..000000000000 --- a/examples/double-proxy/envoy-frontend.yaml +++ /dev/null @@ -1,44 +0,0 @@ -static_resources: - listeners: - - name: postgres_listener - address: - socket_address: - address: 0.0.0.0 - port_value: 5432 - filter_chains: - - filters: - - name: envoy.filters.network.tcp_proxy - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - stat_prefix: postgres_tcp - cluster: postgres_cluster - - clusters: - - name: postgres_cluster - type: STRICT_DNS - load_assignment: - cluster_name: postgres_cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: proxy-postgres-backend.example.com - port_value: 5432 - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - common_tls_context: - tls_certificates: - - certificate_chain: - filename: certs/clientcert.pem - private_key: - filename: certs/clientkey.pem - validation_context: - match_typed_subject_alt_names: - - san_type: DNS - matcher: - exact: proxy-postgres-backend.example.com - trusted_ca: - filename: certs/cacert.pem diff --git a/examples/double-proxy/envoy.yaml b/examples/double-proxy/envoy.yaml deleted file mode 100644 index f3e77fc147cf..000000000000 --- a/examples/double-proxy/envoy.yaml +++ /dev/null @@ -1,42 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: app - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service1 - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - clusters: - - name: service1 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: app - port_value: 8080 diff --git a/examples/double-proxy/example.rst b/examples/double-proxy/example.rst deleted file mode 100644 index 3089089190e0..000000000000 --- a/examples/double-proxy/example.rst +++ /dev/null @@ -1,190 +0,0 @@ -.. _install_sandboxes_double_proxy: - -Double proxy (with ``mTLS`` encryption) -======================================= - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - - :ref:`openssl ` - Generate ``SSL`` keys and certificates. - -This sandbox demonstrates a basic "double proxy" configuration, in which a simple ``aiohttp`` app -connects to a PostgreSQL database, with two Envoy proxies in between. - -``Envoy (front)`` -> ``aiohttp`` -> ``Envoy (postgres-front)`` -> ``Envoy (postgres-back)`` -> ``PostgreSQL`` - -This type of setup is common in a service mesh where Envoy acts as a "sidecar" between individual services. - -It can also be useful as a way of providing access for application servers to upstream services or -databases that may be in a different location or subnet, outside of a service mesh or sidecar-based setup. - -Another common use case is with Envoy configured to provide "Points of presence" at the edges of the cloud, -and to relay requests to upstream servers and services. - -This example encrypts the transmission of data between the two middle proxies and provides mutual authentication -using ``mTLS``. - -This can be useful if the proxies are physically separated or transmit data over untrusted networks. - -In order to use the sandbox you will first need to generate the necessary ``SSL`` keys and certificates. - -This example walks through creating a certificate authority, and using it to create a domain key and sign -certificates for the proxies. - -Change to the ``examples/double-proxy`` directory. - -Step 1: Create a certificate authority -************************************** - -First create a key for the certificate authority: - -.. code-block:: console - - $ pwd - envoy/examples/double-proxy - $ mkdir -p certs - $ openssl genrsa -out certs/ca.key 4096 - Generating RSA private key, 4096 bit long modulus (2 primes) - ..........++++ - ..........................................................................................................++++ - e is 65537 (0x010001) - -Now use the key to generate a certificate authority certificate. - -If you wish, you can interactively alter the fields in the certificate. - -For the purpose of this example, the defaults should be sufficient. - -.. code-block:: console - - $ openssl req -x509 -new -nodes -key certs/ca.key -sha256 -days 1024 -out certs/ca.crt - - You are about to be asked to enter information that will be incorporated - into your certificate request. - What you are about to enter is what is called a Distinguished Name or a DN. - There are quite a few fields but you can leave some blank - For some fields there will be a default value, - If you enter '.', the field will be left blank. - ----- - Country Name (2 letter code) [AU]: - State or Province Name (full name) [Some-State]: - Locality Name (eg, city) []: - Organization Name (eg, company) [Internet Widgits Pty Ltd]: - Organizational Unit Name (eg, section) []: - Common Name (e.g. server FQDN or YOUR name) []: - Email Address []: - -Step 2: Create a domain key -*************************** - -Create a key for the example domain: - -.. code-block:: console - - $ openssl genrsa -out certs/example.com.key 2048 - Generating RSA private key, 2048 bit long modulus (2 primes) - ..+++++ - .................................................+++++ - e is 65537 (0x010001) - -Step 3: Generate certificate signing requests for the proxies -************************************************************* - -Use the domain key to generate certificate signing requests for each of the proxies: - -.. code-block:: console - - $ openssl req -new -sha256 \ - -key certs/example.com.key \ - -subj "/C=US/ST=CA/O=MyExample, Inc./CN=proxy-postgres-frontend.example.com" \ - -out certs/proxy-postgres-frontend.example.com.csr - $ openssl req -new -sha256 \ - -key certs/example.com.key \ - -subj "/C=US/ST=CA/O=MyExample, Inc./CN=proxy-postgres-backend.example.com" \ - -out certs/proxy-postgres-backend.example.com.csr - -Step 4: Sign the proxy certificates -*********************************** - -You can now use the certificate authority that you created to sign the certificate requests. - -Note the ``subjectAltName``. This is used for reciprocally matching and validating the certificates. - -.. code-block:: console - - $ openssl x509 -req \ - -in certs/proxy-postgres-frontend.example.com.csr \ - -CA certs/ca.crt \ - -CAkey certs/ca.key \ - -CAcreateserial \ - -extfile <(printf "subjectAltName=DNS:proxy-postgres-frontend.example.com") \ - -out certs/postgres-frontend.example.com.crt \ - -days 500 \ - -sha256 - Signature ok - subject=C = US, ST = CA, O = "MyExample, Inc.", CN = proxy-postgres-frontend.example.com - Getting CA Private Key - - $ openssl x509 -req \ - -in certs/proxy-postgres-backend.example.com.csr \ - -CA certs/ca.crt \ - -CAkey certs/ca.key \ - -CAcreateserial \ - -extfile <(printf "subjectAltName=DNS:proxy-postgres-backend.example.com") \ - -out certs/postgres-backend.example.com.crt \ - -days 500 \ - -sha256 - Signature ok - subject=C = US, ST = CA, O = "MyExample, Inc.", CN = proxy-postgres-backend.example.com - Getting CA Private Key - -At this point you should have the necessary keys and certificates to secure the connection between -the proxies. - -The keys and certificates are stored in the ``certs/`` directory. - -Step 5: Start all of our containers -*********************************** - -Build and start the containers. - -This will load the required keys and certificates into the frontend and backend proxies. - -.. code-block:: console - - $ pwd - envoy/examples/double-proxy - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ------------------------------------------------------------------------------------------------------------- - double-proxy_app_1 python3 /code/service.py Up (healthy) - double-proxy_postgres_1 docker-entrypoint.sh postgres Up 5432/tcp - double-proxy_proxy-frontend_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp - double-proxy_proxy-postgres-backend_1 /docker-entrypoint.sh /usr ... Up 10000/tcp - double-proxy_proxy-postgres-frontend_1 /docker-entrypoint.sh /usr ... Up 10000/tcp - -Step 6: Check the ``aiohttp`` app can connect to the database -************************************************************* - -Checking the response at http://localhost:10000, you should see the output from the ``aiohttp`` app: - -.. code-block:: console - - $ curl -s http://localhost:10000 - Connected to Postgres, version: PostgreSQL 13.0 (Debian 13.0-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit - -.. seealso:: - - :ref:`Securing Envoy quick start guide ` - Outline of key concepts for securing Envoy. - - :ref:`TLS sandbox ` - Examples of various ``TLS`` termination patterns with Envoy. diff --git a/examples/double-proxy/requirements.in b/examples/double-proxy/requirements.in deleted file mode 100644 index 37ec460f84e8..000000000000 --- a/examples/double-proxy/requirements.in +++ /dev/null @@ -1 +0,0 @@ -psycopg2-binary diff --git a/examples/double-proxy/service.py b/examples/double-proxy/service.py deleted file mode 100644 index 6e166755e7f2..000000000000 --- a/examples/double-proxy/service.py +++ /dev/null @@ -1,22 +0,0 @@ -from aiohttp import web - -# TODO(phlax): shift to aiopg -import psycopg2 - -routes = web.RouteTableDef() - - -@routes.get("/") -async def get(request): - conn = psycopg2.connect("host=postgres user=postgres") - cur = conn.cursor() - cur.execute('SELECT version()') - msg = 'Connected to Postgres, version: %s' % cur.fetchone() - cur.close() - return web.Response(text=msg) - - -if __name__ == "__main__": - app = web.Application() - app.add_routes(routes) - web.run_app(app, host='0.0.0.0', port=8080) diff --git a/examples/double-proxy/verify.sh b/examples/double-proxy/verify.sh deleted file mode 100755 index 6dab851b9300..000000000000 --- a/examples/double-proxy/verify.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash -e - -export NAME=double-proxy -export MANUAL=true - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -mkdir -p certs - -# TODO(phlax): remove openssl bug workaround when openssl/ubuntu are updated -# see #15555 for more info -touch ~/.rnd - -run_log "Create a cert authority" -openssl genrsa -out certs/ca.key 4096 -openssl req -batch -x509 -new -nodes -key certs/ca.key -sha256 -days 1024 -out certs/ca.crt - -run_log "Create a domain key" -openssl genrsa -out certs/example.com.key 2048 - -run_log "Generate signing requests for each proxy" -openssl req -new -sha256 \ - -key certs/example.com.key \ - -subj "/C=US/ST=CA/O=MyExample, Inc./CN=proxy-postgres-frontend.example.com" \ - -out certs/proxy-postgres-frontend.example.com.csr -openssl req -new -sha256 \ - -key certs/example.com.key \ - -subj "/C=US/ST=CA/O=MyExample, Inc./CN=proxy-postgres-backend.example.com" \ - -out certs/proxy-postgres-backend.example.com.csr - -run_log "Generate certificates for each proxy" -openssl x509 -req \ - -in certs/proxy-postgres-frontend.example.com.csr \ - -CA certs/ca.crt \ - -CAkey certs/ca.key \ - -CAcreateserial \ - -extfile <(printf "subjectAltName=DNS:proxy-postgres-frontend.example.com") \ - -out certs/postgres-frontend.example.com.crt \ - -days 500 \ - -sha256 -openssl x509 -req \ - -in certs/proxy-postgres-backend.example.com.csr \ - -CA certs/ca.crt \ - -CAkey certs/ca.key \ - -CAcreateserial \ - -extfile <(printf "subjectAltName=DNS:proxy-postgres-backend.example.com") \ - -out certs/postgres-backend.example.com.crt \ - -days 500 \ - -sha256 - -bring_up_example - -run_log "Test app/db connection" -responds_with \ - "Connected to Postgres, version: PostgreSQL" \ - http://localhost:10000 diff --git a/examples/dynamic-config-cp/README.md b/examples/dynamic-config-cp/README.md deleted file mode 100644 index 936e6dd00f7a..000000000000 --- a/examples/dynamic-config-cp/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/dynamic-configuration-control-plane.html). diff --git a/examples/dynamic-config-cp/_include/response-config-active-clusters-updated.json b/examples/dynamic-config-cp/_include/response-config-active-clusters-updated.json deleted file mode 100644 index 4ff0f03a2c1a..000000000000 --- a/examples/dynamic-config-cp/_include/response-config-active-clusters-updated.json +++ /dev/null @@ -1,32 +0,0 @@ -[ - { - "version_info": "2", - "cluster": { - "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", - "name": "example_proxy_cluster", - "type": "LOGICAL_DNS", - "connect_timeout": "5s", - "dns_lookup_family": "V4_ONLY", - "load_assignment": { - "cluster_name": "example_proxy_cluster", - "endpoints": [ - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "socket_address": { - "address": "service2", - "port_value": 8080 - } - } - } - } - ] - } - ] - } - }, - "last_updated": "2020-10-26T14:35:17.360Z" - } -] diff --git a/examples/dynamic-config-cp/_include/response-config-active-clusters.json b/examples/dynamic-config-cp/_include/response-config-active-clusters.json deleted file mode 100644 index f51d9df5e527..000000000000 --- a/examples/dynamic-config-cp/_include/response-config-active-clusters.json +++ /dev/null @@ -1,32 +0,0 @@ -[ - { - "version_info": "1", - "cluster": { - "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", - "name": "example_proxy_cluster", - "type": "LOGICAL_DNS", - "connect_timeout": "5s", - "dns_lookup_family": "V4_ONLY", - "load_assignment": { - "cluster_name": "example_proxy_cluster", - "endpoints": [ - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "socket_address": { - "address": "service1", - "port_value": 8080 - } - } - } - } - ] - } - ] - } - }, - "last_updated": "2020-10-25T20:37:05.838Z" - } -] diff --git a/examples/dynamic-config-cp/_include/response-config-cluster.json b/examples/dynamic-config-cp/_include/response-config-cluster.json deleted file mode 100644 index 9df9306e0f45..000000000000 --- a/examples/dynamic-config-cp/_include/response-config-cluster.json +++ /dev/null @@ -1,31 +0,0 @@ -[ - { - "cluster": { - "@type": "type.googleapis.com/envoy.api.v2.Cluster", - "name": "xds_cluster", - "type": "STRICT_DNS", - "connect_timeout": "1s", - "http2_protocol_options": {}, - "load_assignment": { - "cluster_name": "xds_cluster", - "endpoints": [ - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "socket_address": { - "address": "go-control-plane", - "port_value": 18000 - } - } - } - } - ] - } - ] - } - }, - "last_updated": "2020-10-25T20:20:54.897Z" - } -] diff --git a/examples/dynamic-config-cp/docker-compose.yaml b/examples/dynamic-config-cp/docker-compose.yaml deleted file mode 100644 index b83883f42b21..000000000000 --- a/examples/dynamic-config-cp/docker-compose.yaml +++ /dev/null @@ -1,31 +0,0 @@ -services: - - proxy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - depends_on: - - service1 - - service2 - ports: - - 10000:10000 - - 19000:19000 - - service1: - build: - context: ../shared/echo - hostname: service1 - - service2: - build: - context: ../shared/echo - hostname: service2 - - go-control-plane: - build: - context: . - dockerfile: ../shared/golang/Dockerfile - target: golang-control-plane - command: /usr/local/bin/example - healthcheck: - test: nc -zv localhost 18000 diff --git a/examples/dynamic-config-cp/envoy.yaml b/examples/dynamic-config-cp/envoy.yaml deleted file mode 100644 index 4fa89ba5852b..000000000000 --- a/examples/dynamic-config-cp/envoy.yaml +++ /dev/null @@ -1,39 +0,0 @@ -node: - cluster: test-cluster - id: test-id - -dynamic_resources: - ads_config: - api_type: GRPC - grpc_services: - - envoy_grpc: - cluster_name: xds_cluster - cds_config: - ads: {} - lds_config: - ads: {} - -static_resources: - clusters: - - type: STRICT_DNS - typed_extension_protocol_options: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicit_http_config: - http2_protocol_options: {} - name: xds_cluster - load_assignment: - cluster_name: xds_cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: go-control-plane - port_value: 18000 - -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 19000 diff --git a/examples/dynamic-config-cp/example.rst b/examples/dynamic-config-cp/example.rst deleted file mode 100644 index 53968bee3c50..000000000000 --- a/examples/dynamic-config-cp/example.rst +++ /dev/null @@ -1,223 +0,0 @@ -.. _install_sandboxes_dynamic_config_cp: - -Dynamic configuration (control plane) -===================================== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - - :ref:`jq ` - Parse ``json`` output from the upstream echo servers. - -This example walks through configuring Envoy using the `Go Control Plane `_ -reference implementation. - -It demonstrates how configuration provided to Envoy persists, even when the control plane is not available, -and provides a trivial example of how to update Envoy's configuration dynamically. - -Step 1: Start the proxy container -********************************* - -Change directory to ``examples/dynamic-config-cp`` in the Envoy repository. - -First build the containers and start the ``proxy`` container. - -This should also start two upstream ``HTTP`` echo servers, ``service1`` and ``service2``. - -The control plane has not yet been started. - -.. code-block:: console - - $ pwd - envoy/examples/dynamic-config-cp - $ docker compose pull - $ docker compose up --build -d proxy - $ docker compose ps - - Name Command State Ports - ------------------------------------------------------------------------------------------------------------------------ - dynamic-config-cp_proxy_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp, 0.0.0.0:19000->19000/tcp - dynamic-config-cp_service1_1 /bin/echo-server Up 8080/tcp - dynamic-config-cp_service2_1 /bin/echo-server Up 8080/tcp - -Step 2: Check initial config and web response -********************************************* - -As we have not yet started the control plane, nothing should be responding on port ``10000``. - -.. code-block:: console - - $ curl http://localhost:10000 - curl: (56) Recv failure: Connection reset by peer - -Dump the proxy's :ref:`static_clusters ` -configuration and you should see the cluster named ``xds_cluster`` configured for the control plane: - -.. code-block:: console - - $ curl -s http://localhost:19000/config_dump | jq '.configs[1].static_clusters' - -.. literalinclude:: /start/sandboxes/_include/dynamic-config-cp/_include/response-config-cluster.json - :language: json - :emphasize-lines: 10, 18-19 - -No :ref:`dynamic_active_clusters ` -have been configured yet: - -.. code-block:: console - - $ curl -s http://localhost:19000/config_dump | jq '.configs[1].dynamic_active_clusters' - null - -Step 3: Start the control plane -******************************* - -Start up the ``go-control-plane`` service. - -You may need to wait a moment or two for it to become ``healthy``. - -.. code-block:: console - - $ docker compose up --build -d go-control-plane - $ docker compose ps - - Name Command State Ports - ------------------------------------------------------------------------------------------------------------------------------------- - dynamic-config-cp_go-control-plane_1 bin/example -debug Up (healthy) - dynamic-config-cp_proxy_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp, 0.0.0.0:19000->19000/tcp - dynamic-config-cp_service1_1 /bin/echo-server Up 8080/tcp - dynamic-config-cp_service2_1 /bin/echo-server Up 8080/tcp - -Step 4: Query the proxy -*********************** - -Once the control plane has started and is ``healthy``, you should be able to make a request to port ``10000``, -which will be served by ``service1``. - -.. code-block:: console - - $ curl http://localhost:10000 - Request served by service1 - - HTTP/1.1 GET / - - Host: localhost:10000 - Accept: */* - X-Forwarded-Proto: http - X-Request-Id: 1d93050e-f39c-4602-90f8-a124d6e78d26 - X-Envoy-Expected-Rq-Timeout-Ms: 15000 - Content-Length: 0 - User-Agent: curl/7.72.0 - -Step 5: Dump Envoy's ``dynamic_active_clusters`` config -******************************************************* - -If you now dump the proxy's :ref:`dynamic_active_clusters ` -configuration, you should see it is configured with the ``example_proxy_cluster`` pointing to ``service1``, and a version of ``1``. - -.. code-block:: console - - $ curl -s http://localhost:19000/config_dump | jq '.configs[1].dynamic_active_clusters' - -.. literalinclude:: /start/sandboxes/_include/dynamic-config-cp/_include/response-config-active-clusters.json - :language: json - :emphasize-lines: 3, 11, 19-20 - -Step 6: Stop the control plane -****************************** - -Stop the ``go-control-plane`` service: - -.. code-block:: console - - $ docker compose stop go-control-plane - -The Envoy proxy should continue proxying responses from ``service1``. - -.. code-block:: console - - $ curl http://localhost:10000 | grep "served by" - Request served by service1 - -Step 7: Edit ``go`` file and restart the control plane -****************************************************** - -The example setup starts the ``go-control-plane`` -service with a custom :download:`resource.go ` file which -specifies the configuration provided to Envoy. - -Update this to have Envoy proxy instead to ``service2``. - -Edit ``resource.go`` in the dynamic configuration example folder and change the ``UpstreamHost`` -from ``service1`` to ``service2``: - -.. literalinclude:: /start/sandboxes/_include/dynamic-config-cp/resource.go - :language: go - :lines: 34-43 - :lineno-start: 35 - :emphasize-lines: 6 - :linenos: - -Further down in this file you must also change the configuration snapshot version number from -``"1"`` to ``"2"`` to ensure Envoy sees the configuration as newer: - -.. literalinclude:: /start/sandboxes/_include/dynamic-config-cp/resource.go - :language: go - :lineno-start: 175 - :lines: 174-186 - :emphasize-lines: 3 - :linenos: - -Now rebuild and restart the control plane: - -.. code-block:: console - - $ docker compose up --build -d go-control-plane - -You may need to wait a moment or two for the ``go-control-plane`` service to become ``healthy`` again. - -Step 8: Check Envoy uses the updated configuration -************************************************** - -Now when you make a request to the proxy it should be served by the ``service2`` upstream service. - -.. code-block:: console - - $ curl http://localhost:10000 | grep "served by" - Request served by service2 - -Dumping the :ref:`dynamic_active_clusters ` -you should see the cluster configuration now has a version of ``2``, and ``example_proxy_cluster`` -is configured to proxy to ``service2``: - -.. code-block:: console - - $ curl -s http://localhost:19000/config_dump | jq '.configs[1].dynamic_active_clusters' - -.. literalinclude:: /start/sandboxes/_include/dynamic-config-cp/_include/response-config-active-clusters-updated.json - :language: json - :emphasize-lines: 3, 11, 19-20 - -.. note:: - In this example we increment the version for simplicity. - - Any change to the version will trigger an update in Envoy, and ordering is not significant - (see :ref:`xDS protocol docs for further information about updates `). - -.. seealso:: - - :ref:`Dynamic configuration (control plane) quick start guide ` - Quick start guide to dynamic configuration of Envoy with a control plane. - - :ref:`Envoy admin quick start guide ` - Quick start guide to the Envoy admin interface. - - :ref:`Dynamic configuration (filesystem) sandbox ` - Configure Envoy using filesystem-based dynamic configuration. - - `Go control plane `_ - Reference implementation of Envoy control plane written in ``go``. diff --git a/examples/dynamic-config-cp/resource.go b/examples/dynamic-config-cp/resource.go deleted file mode 100644 index d9c5aa6288f3..000000000000 --- a/examples/dynamic-config-cp/resource.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2020 Envoyproxy Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package example - -import ( - "time" - - "github.com/golang/protobuf/ptypes" - - cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" - listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - router "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" - hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - - "github.com/envoyproxy/go-control-plane/pkg/cache/types" - cache "github.com/envoyproxy/go-control-plane/pkg/cache/v3" - resource "github.com/envoyproxy/go-control-plane/pkg/resource/v3" - "github.com/envoyproxy/go-control-plane/pkg/wellknown" -) - -const ( - ClusterName = "example_proxy_cluster" - RouteName = "local_route" - ListenerName = "listener_0" - ListenerPort = 10000 - UpstreamHost = "service1" - UpstreamPort = 8080 -) - -func makeCluster(clusterName string) *cluster.Cluster { - return &cluster.Cluster{ - Name: clusterName, - ConnectTimeout: ptypes.DurationProto(5 * time.Second), - ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_LOGICAL_DNS}, - LbPolicy: cluster.Cluster_ROUND_ROBIN, - LoadAssignment: makeEndpoint(clusterName), - DnsLookupFamily: cluster.Cluster_V4_ONLY, - } -} - -func makeEndpoint(clusterName string) *endpoint.ClusterLoadAssignment { - return &endpoint.ClusterLoadAssignment{ - ClusterName: clusterName, - Endpoints: []*endpoint.LocalityLbEndpoints{{ - LbEndpoints: []*endpoint.LbEndpoint{{ - HostIdentifier: &endpoint.LbEndpoint_Endpoint{ - Endpoint: &endpoint.Endpoint{ - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - Protocol: core.SocketAddress_TCP, - Address: UpstreamHost, - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: UpstreamPort, - }, - }, - }, - }, - }, - }, - }}, - }}, - } -} - -func makeRoute(routeName string, clusterName string) *route.RouteConfiguration { - return &route.RouteConfiguration{ - Name: routeName, - VirtualHosts: []*route.VirtualHost{{ - Name: "local_service", - Domains: []string{"*"}, - Routes: []*route.Route{{ - Match: &route.RouteMatch{ - PathSpecifier: &route.RouteMatch_Prefix{ - Prefix: "/", - }, - }, - Action: &route.Route_Route{ - Route: &route.RouteAction{ - ClusterSpecifier: &route.RouteAction_Cluster{ - Cluster: clusterName, - }, - }, - }, - }}, - }}, - } -} - -func makeHTTPListener(listenerName string, route string) *listener.Listener { - router := &router.Router{} - routerpb, err := ptypes.MarshalAny(router) - if err != nil { - panic(err) - } - - // HTTP filter configuration - manager := &hcm.HttpConnectionManager{ - CodecType: hcm.HttpConnectionManager_AUTO, - StatPrefix: "http", - RouteSpecifier: &hcm.HttpConnectionManager_Rds{ - Rds: &hcm.Rds{ - ConfigSource: makeConfigSource(), - RouteConfigName: route, - }, - }, - HttpFilters: []*hcm.HttpFilter{{ - Name: wellknown.Router, - ConfigType: &hcm.HttpFilter_TypedConfig{ - TypedConfig: routerpb, - }, - }}, - } - pbst, err := ptypes.MarshalAny(manager) - if err != nil { - panic(err) - } - - return &listener.Listener{ - Name: listenerName, - Address: &core.Address{ - Address: &core.Address_SocketAddress{ - SocketAddress: &core.SocketAddress{ - Protocol: core.SocketAddress_TCP, - Address: "0.0.0.0", - PortSpecifier: &core.SocketAddress_PortValue{ - PortValue: ListenerPort, - }, - }, - }, - }, - FilterChains: []*listener.FilterChain{{ - Filters: []*listener.Filter{{ - Name: wellknown.HTTPConnectionManager, - ConfigType: &listener.Filter_TypedConfig{ - TypedConfig: pbst, - }, - }}, - }}, - } -} - -func makeConfigSource() *core.ConfigSource { - source := &core.ConfigSource{} - source.ResourceApiVersion = resource.DefaultAPIVersion - source.ConfigSourceSpecifier = &core.ConfigSource_ApiConfigSource{ - ApiConfigSource: &core.ApiConfigSource{ - TransportApiVersion: resource.DefaultAPIVersion, - ApiType: core.ApiConfigSource_GRPC, - SetNodeOnFirstMessageOnly: true, - GrpcServices: []*core.GrpcService{{ - TargetSpecifier: &core.GrpcService_EnvoyGrpc_{ - EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: "xds_cluster"}, - }, - }}, - }, - } - return source -} - -func GenerateSnapshot() cache.Snapshot { - return cache.NewSnapshot( - "1", - []types.Resource{}, // endpoints - []types.Resource{makeCluster(ClusterName)}, - []types.Resource{makeRoute(RouteName, ClusterName)}, - []types.Resource{makeHTTPListener(ListenerName, RouteName)}, - []types.Resource{}, // runtimes - []types.Resource{}, // secrets - []types.Resource{}, // extensions configs - ) -} diff --git a/examples/dynamic-config-cp/verify.sh b/examples/dynamic-config-cp/verify.sh deleted file mode 100755 index ed6747c24a22..000000000000 --- a/examples/dynamic-config-cp/verify.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash -e - -export NAME=dynamic-config-cp -export UPARGS=" proxy" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Check port 10000 is not open (still shows as succeeded)" -nc -zv localhost 10000 |& grep -v open - -run_log "Check the static cluster" -curl -s http://localhost:19000/config_dump \ - | jq -r '.configs[1].static_clusters' \ - | grep 'go-control-plane' - -run_log "Check there is no config for dynamic clusters" -curl -s http://localhost:19000/config_dump \ - | jq -r '.configs[1].dynamic_active_clusters // "NO_CLUSTERS"' \ - | grep NO_CLUSTERS - -run_log "Bring up go-control-plane" -"${DOCKER_COMPOSE[@]}" up --build -d go-control-plane -wait_for 30 sh -c "${DOCKER_COMPOSE[*]} ps go-control-plane | grep healthy | grep -v unhealthy" -wait_for 10 bash -c "responds_with 'Request served by service1' http://localhost:10000" - -run_log "Check for response from service1 backend" -responds_with \ - "Request served by service1" \ - http://localhost:10000 - -run_log "Check config for active clusters" -curl -s http://localhost:19000/config_dump \ - | jq -r '.configs[1].dynamic_active_clusters' \ - | grep '"version_info": "1"' -curl -s http://localhost:19000/config_dump \ - | jq -r '.configs[1].dynamic_active_clusters' \ - | grep '"address": "service1"' - -run_log "Bring down the control plane" -"${DOCKER_COMPOSE[@]}" stop go-control-plane - -wait_for 10 sh -c "\ - curl -s http://localhost:19000/config_dump \ - | jq -r '.configs[1].dynamic_active_clusters' \ - | grep '\"version_info\": \"1\"'" - -run_log "Check for continued response from service1 backend" -responds_with \ - "Request served by service1" \ - http://localhost:10000 - -run_log "Check config for active clusters" -curl -s http://localhost:19000/config_dump \ - | jq -r '.configs[1].dynamic_active_clusters' \ - | grep '"version_info": "1"' -curl -s http://localhost:19000/config_dump \ - | jq -r '.configs[1].dynamic_active_clusters' \ - | grep '"address": "service1"' - -run_log "Edit resource.go" -sed -i'.bak' s/service1/service2/ resource.go -sed -i'.bak2' s/\"1\",/\"2\",/ resource.go - -run_log "Bring back up the control plane" -"${DOCKER_COMPOSE[@]}" up --build -d go-control-plane -wait_for 30 sh -c "${DOCKER_COMPOSE[*]} ps go-control-plane | grep healthy | grep -v unhealthy" - -run_log "Check for response from service2 backend" -wait_for 5 bash -c "responds_with \ - 'Request served by service2' \ - http://localhost:10000" - -run_log "Check config for active clusters pointing to service2" -curl -s http://localhost:19000/config_dump \ - | jq -r '.configs[1].dynamic_active_clusters' \ - | grep '"version_info": "2"' -curl -s http://localhost:19000/config_dump \ - | jq -r '.configs[1].dynamic_active_clusters' \ - | grep '"address": "service2"' - -mv resource.go.bak resource.go diff --git a/examples/dynamic-config-fs/README.md b/examples/dynamic-config-fs/README.md deleted file mode 100644 index 3cb1ed49d940..000000000000 --- a/examples/dynamic-config-fs/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/dynamic-configuration-filesystem.html). diff --git a/examples/dynamic-config-fs/_include/response-config-active-clusters-updated.json b/examples/dynamic-config-fs/_include/response-config-active-clusters-updated.json deleted file mode 100644 index 43b676e72d66..000000000000 --- a/examples/dynamic-config-fs/_include/response-config-active-clusters-updated.json +++ /dev/null @@ -1,31 +0,0 @@ -[ - { - "cluster": { - "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", - "name": "example_proxy_cluster", - "type": "LOGICAL_DNS", - "connect_timeout": "5s", - "dns_lookup_family": "V4_ONLY", - "load_assignment": { - "cluster_name": "example_proxy_cluster", - "endpoints": [ - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "socket_address": { - "address": "service2", - "port_value": 8080 - } - } - } - } - ] - } - ] - } - }, - "last_updated": "2020-10-25T20:37:05.838Z" - } -] diff --git a/examples/dynamic-config-fs/_include/response-config-active-clusters.json b/examples/dynamic-config-fs/_include/response-config-active-clusters.json deleted file mode 100644 index 3813a9a9c56c..000000000000 --- a/examples/dynamic-config-fs/_include/response-config-active-clusters.json +++ /dev/null @@ -1,31 +0,0 @@ -[ - { - "cluster": { - "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", - "name": "example_proxy_cluster", - "type": "LOGICAL_DNS", - "connect_timeout": "5s", - "dns_lookup_family": "V4_ONLY", - "load_assignment": { - "cluster_name": "example_proxy_cluster", - "endpoints": [ - { - "lb_endpoints": [ - { - "endpoint": { - "address": { - "socket_address": { - "address": "service1", - "port_value": 8080 - } - } - } - } - ] - } - ] - } - }, - "last_updated": "2020-10-25T20:37:05.838Z" - } -] diff --git a/examples/dynamic-config-fs/configs/cds.yaml b/examples/dynamic-config-fs/configs/cds.yaml deleted file mode 100644 index 8cc9e3c0b26c..000000000000 --- a/examples/dynamic-config-fs/configs/cds.yaml +++ /dev/null @@ -1,13 +0,0 @@ -resources: -- "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster - name: example_proxy_cluster - type: STRICT_DNS - load_assignment: - cluster_name: example_proxy_cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service1 - port_value: 8080 diff --git a/examples/dynamic-config-fs/configs/lds.yaml b/examples/dynamic-config-fs/configs/lds.yaml deleted file mode 100644 index ac12d349dffc..000000000000 --- a/examples/dynamic-config-fs/configs/lds.yaml +++ /dev/null @@ -1,28 +0,0 @@ -resources: -- "@type": type.googleapis.com/envoy.config.listener.v3.Listener - name: listener_0 - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_http - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - route_config: - name: local_route - virtual_hosts: - - name: local_service - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: example_proxy_cluster diff --git a/examples/dynamic-config-fs/docker-compose.yaml b/examples/dynamic-config-fs/docker-compose.yaml deleted file mode 100644 index 9734368cbf37..000000000000 --- a/examples/dynamic-config-fs/docker-compose.yaml +++ /dev/null @@ -1,23 +0,0 @@ -services: - - proxy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - target: envoy-dynamic-fs - depends_on: - - service1 - - service2 - ports: - - 10000:10000 - - 19000:19000 - - service1: - build: - context: ../shared/echo - hostname: service1 - - service2: - build: - context: ../shared/echo - hostname: service2 diff --git a/examples/dynamic-config-fs/envoy.yaml b/examples/dynamic-config-fs/envoy.yaml deleted file mode 100644 index d496dad71643..000000000000 --- a/examples/dynamic-config-fs/envoy.yaml +++ /dev/null @@ -1,17 +0,0 @@ -node: - id: id_1 - cluster: test - -dynamic_resources: - cds_config: - path_config_source: - path: /var/lib/envoy/cds.yaml - lds_config: - path_config_source: - path: /var/lib/envoy/lds.yaml - -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 19000 diff --git a/examples/dynamic-config-fs/example.rst b/examples/dynamic-config-fs/example.rst deleted file mode 100644 index 4935cd5bbe6e..000000000000 --- a/examples/dynamic-config-fs/example.rst +++ /dev/null @@ -1,137 +0,0 @@ -.. _install_sandboxes_dynamic_config_fs: - -Dynamic configuration (filesystem) -================================== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - - :ref:`jq ` - Parse ``json`` output from the upstream echo servers. - -This example walks through configuring Envoy using filesystem-based dynamic configuration. - -It demonstrates how configuration provided to Envoy dynamically can be updated without -restarting the server. - -Step 1: Start the proxy container -********************************* - -Change directory to ``examples/dynamic-config-fs`` in the Envoy repository. - -Build and start the containers. - -This should also start two upstream ``HTTP`` echo servers, ``service1`` and ``service2``. - -.. code-block:: console - - $ pwd - envoy/examples/dynamic-config-fs - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ------------------------------------------------------------------------------------------------------------------------ - dynamic-config-fs_proxy_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp, 0.0.0.0:19000->19000/tcp - dynamic-config-fs_service1_1 /bin/echo-server Up 8080/tcp - dynamic-config-fs_service2_1 /bin/echo-server Up 8080/tcp - -Step 2: Check web response -************************** - -You should be able to make a request to port ``10000``, which will be served by ``service1``. - -.. code-block:: console - - $ curl -s http://localhost:10000 - Request served by service1 - - HTTP/2.0 GET / - - Host: localhost:10000 - User-Agent: curl/7.72.0 - Accept: */* - X-Forwarded-Proto: http - X-Request-Id: 6672902d-56ca-456c-be6a-992a603cab9a - X-Envoy-Expected-Rq-Timeout-Ms: 15000 - -Step 3: Dump Envoy's ``dynamic_active_clusters`` config -******************************************************* - -If you now dump the proxy’s :ref:`dynamic_active_clusters ` -configuration, you should see it is configured with the ``example_proxy_cluster`` pointing to ``service1``. - -.. code-block:: console - - $ curl -s http://localhost:19000/config_dump | jq -r '.configs[1].dynamic_active_clusters' - -.. literalinclude:: /start/sandboxes/_include/dynamic-config-fs/_include/response-config-active-clusters.json - :language: json - :emphasize-lines: 10, 18-19 - -Step 4: Replace ``cds.yaml`` inside the container to update upstream cluster -**************************************************************************** - -The example setup provides Envoy with two dynamic configuration files: - -- :download:`configs/cds.yaml ` to provide a :ref:`Cluster - discovery service (CDS) `. -- :download:`configs/lds.yaml ` to provide a :ref:`Listener - discovery service (LDS) `. - -Edit ``cds.yaml`` inside the container and change the cluster address -from ``service1`` to ``service2``: - -.. literalinclude:: /start/sandboxes/_include/dynamic-config-fs/configs/cds.yaml - :language: yaml - :linenos: - :lines: 6-13 - :lineno-start: 6 - :emphasize-lines: 7 - -You can do this using ``sed`` inside the container: - -.. code-block:: console - - docker compose exec -T proxy sed -i s/service1/service2/ /var/lib/envoy/cds.yaml - -.. note:: - - The above example uses ``sed -i``, which works as an inplace edit as ``sed`` does copy, edit and move in order to do this. - -Step 5: Check Envoy uses updated configuration -********************************************** - -Checking the web response again, the request should now be handled by ``service2``: - -.. code-block:: console - - $ curl http://localhost:10000 | grep "served by" - Request served by service2 - -Dumping the :ref:`dynamic_active_clusters `, -the ``example_proxy_cluster`` should now be configured to proxy to ``service2``: - -.. code-block:: console - - $ curl -s http://localhost:19000/config_dump | jq -r '.configs[1].dynamic_active_clusters' - -.. literalinclude:: /start/sandboxes/_include/dynamic-config-fs/_include/response-config-active-clusters-updated.json - :language: json - :emphasize-lines: 10, 18-19 - -.. seealso:: - - :ref:`Dynamic configuration (filesystem) quick start guide ` - Quick start guide to filesystem-based dynamic configuration of Envoy. - - :ref:`Envoy admin quick start guide ` - Quick start guide to the Envoy admin interface. - - :ref:`Dynamic configuration (control plane) sandbox ` - Configure Envoy dynamically with the Go Control Plane. diff --git a/examples/dynamic-config-fs/verify.sh b/examples/dynamic-config-fs/verify.sh deleted file mode 100755 index 476454ba1ac6..000000000000 --- a/examples/dynamic-config-fs/verify.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -e - -export NAME=dynamic-config-fs - -chmod go+r configs/* -chmod go+rx configs - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Check for response comes from service1 upstream" -responds_with \ - "Request served by service1" \ - http://localhost:10000 - -run_log "Check config for active clusters pointing to service1" -curl -s http://localhost:19000/config_dump \ - | jq -r '.configs[1].dynamic_active_clusters' \ - | grep '"address": "service1"' - -run_log "Set upstream to service2" -"${DOCKER_COMPOSE[@]}" exec -T proxy sed -i s/service1/service2/ /var/lib/envoy/cds.yaml -wait_for 10 bash -c "responds_with 'Request served by service2' http://localhost:10000" - -run_log "Check for response comes from service2 upstream" -responds_with \ - "Request served by service2" \ - http://localhost:10000 - -run_log "Check config for active clusters pointing to service2" -curl -s http://localhost:19000/config_dump \ - | jq -r '.configs[1].dynamic_active_clusters' \ - | grep '"address": "service2"' diff --git a/examples/ext_authz/.env b/examples/ext_authz/.env deleted file mode 100644 index 0a7d4cb0eaf0..000000000000 --- a/examples/ext_authz/.env +++ /dev/null @@ -1 +0,0 @@ -FRONT_ENVOY_YAML=config/grpc-service/v3.yaml diff --git a/examples/ext_authz/Dockerfile-opa b/examples/ext_authz/Dockerfile-opa deleted file mode 100644 index 463bca657f15..000000000000 --- a/examples/ext_authz/Dockerfile-opa +++ /dev/null @@ -1 +0,0 @@ -FROM openpolicyagent/opa:0.67.0-istio@sha256:41512cb576ba71c2a7267a64958f9f866d549fd681792eac1679cccd3f8b0276 diff --git a/examples/ext_authz/README.md b/examples/ext_authz/README.md deleted file mode 100644 index c0a121144d07..000000000000 --- a/examples/ext_authz/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/ext_authz) diff --git a/examples/ext_authz/auth/grpc-service/Makefile b/examples/ext_authz/auth/grpc-service/Makefile deleted file mode 100644 index e9ee1e9581cf..000000000000 --- a/examples/ext_authz/auth/grpc-service/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -all: server - -server: - @CGO_ENABLED=0 GOOS=linux go build -a --ldflags '-extldflags "-static"' \ - -tags "netgo" -installsuffix netgo \ - -o server -clean: - @rm -fr server diff --git a/examples/ext_authz/auth/grpc-service/go.mod b/examples/ext_authz/auth/grpc-service/go.mod deleted file mode 100644 index ae97c30f021b..000000000000 --- a/examples/ext_authz/auth/grpc-service/go.mod +++ /dev/null @@ -1,21 +0,0 @@ -module github.com/envoyproxy/envoy/examples/ext_authz/auth/grpc-service - -go 1.21 - -toolchain go1.22.5 - -require ( - github.com/envoyproxy/go-control-plane v0.12.0 - github.com/golang/protobuf v1.5.4 - google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 - google.golang.org/grpc v1.65.0 -) - -require ( - github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect - github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect - google.golang.org/protobuf v1.34.1 // indirect -) diff --git a/examples/ext_authz/auth/grpc-service/go.sum b/examples/ext_authz/auth/grpc-service/go.sum deleted file mode 100644 index 0af3b63c8b48..000000000000 --- a/examples/ext_authz/auth/grpc-service/go.sum +++ /dev/null @@ -1,22 +0,0 @@ -github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw= -github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= -github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= -github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= -github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= -github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= diff --git a/examples/ext_authz/auth/grpc-service/main.go b/examples/ext_authz/auth/grpc-service/main.go deleted file mode 100644 index bbfaf9a989d6..000000000000 --- a/examples/ext_authz/auth/grpc-service/main.go +++ /dev/null @@ -1,38 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "log" - "net" - - envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" - "google.golang.org/grpc" - - "github.com/envoyproxy/envoy/examples/ext_authz/auth/grpc-service/pkg/auth" - auth_v3 "github.com/envoyproxy/envoy/examples/ext_authz/auth/grpc-service/pkg/auth/v3" -) - -func main() { - port := flag.Int("port", 9001, "gRPC port") - data := flag.String("users", "../../users.json", "users file") - - flag.Parse() - - lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) - if err != nil { - log.Fatalf("failed to listen to %d: %v", *port, err) - } - - users, err := auth.LoadUsers(*data) - if err != nil { - log.Fatalf("failed to load user data:%s %v", *data, err) - } - gs := grpc.NewServer() - - envoy_service_auth_v3.RegisterAuthorizationServer(gs, auth_v3.New(users)) - - log.Printf("starting gRPC server on: %d\n", *port) - - gs.Serve(lis) -} diff --git a/examples/ext_authz/auth/grpc-service/pkg/auth/users.go b/examples/ext_authz/auth/grpc-service/pkg/auth/users.go deleted file mode 100644 index 507c3560bdf1..000000000000 --- a/examples/ext_authz/auth/grpc-service/pkg/auth/users.go +++ /dev/null @@ -1,32 +0,0 @@ -package auth - -import ( - "encoding/json" - "io/ioutil" -) - -// Users holds a list of users. -type Users map[string]string - -// Check checks if a key could retrieve a user from a list of users. -func (u Users) Check(key string) (bool, string) { - value, ok := u[key] - if !ok { - return false, "" - } - return ok, value -} - -// LoadUsers load users data from a JSON file. -func LoadUsers(jsonFile string) (Users, error) { - var users Users - data, err := ioutil.ReadFile(jsonFile) - if err != nil { - return nil, err - } - - if err := json.Unmarshal(data, &users); err != nil { - return nil, err - } - return users, nil -} diff --git a/examples/ext_authz/auth/grpc-service/pkg/auth/v3/auth.go b/examples/ext_authz/auth/grpc-service/pkg/auth/v3/auth.go deleted file mode 100644 index 1cae7cbd8d43..000000000000 --- a/examples/ext_authz/auth/grpc-service/pkg/auth/v3/auth.go +++ /dev/null @@ -1,68 +0,0 @@ -package v3 - -import ( - "context" - "log" - "strings" - - envoy_api_v3_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" - "github.com/golang/protobuf/ptypes/wrappers" - "google.golang.org/genproto/googleapis/rpc/code" - "google.golang.org/genproto/googleapis/rpc/status" - - "github.com/envoyproxy/envoy/examples/ext_authz/auth/grpc-service/pkg/auth" -) - -type server struct { - users auth.Users -} - -var _ envoy_service_auth_v3.AuthorizationServer = &server{} - -// New creates a new authorization server. -func New(users auth.Users) envoy_service_auth_v3.AuthorizationServer { - return &server{users} -} - -// Check implements authorization's Check interface which performs authorization check based on the -// attributes associated with the incoming request. -func (s *server) Check( - ctx context.Context, - req *envoy_service_auth_v3.CheckRequest) (*envoy_service_auth_v3.CheckResponse, error) { - authorization := req.Attributes.Request.Http.Headers["authorization"] - log.Println(authorization) - - extracted := strings.Fields(authorization) - if len(extracted) == 2 && extracted[0] == "Bearer" { - valid, user := s.users.Check(extracted[1]) - if valid { - return &envoy_service_auth_v3.CheckResponse{ - HttpResponse: &envoy_service_auth_v3.CheckResponse_OkResponse{ - OkResponse: &envoy_service_auth_v3.OkHttpResponse{ - Headers: []*envoy_api_v3_core.HeaderValueOption{ - { - Append: &wrappers.BoolValue{Value: false}, - Header: &envoy_api_v3_core.HeaderValue{ - // For a successful request, the authorization server sets the - // x-current-user value. - Key: "x-current-user", - Value: user, - }, - }, - }, - }, - }, - Status: &status.Status{ - Code: int32(code.Code_OK), - }, - }, nil - } - } - - return &envoy_service_auth_v3.CheckResponse{ - Status: &status.Status{ - Code: int32(code.Code_PERMISSION_DENIED), - }, - }, nil -} diff --git a/examples/ext_authz/auth/http-service/server.js b/examples/ext_authz/auth/http-service/server.js deleted file mode 100644 index 9c890d75226a..000000000000 --- a/examples/ext_authz/auth/http-service/server.js +++ /dev/null @@ -1,29 +0,0 @@ -const Http = require("http"); -const path = require("path"); - -const tokens = require(process.env.USERS || - path.join(__dirname, "..", "users.json")); - -const server = new Http.Server((req, res) => { - const authorization = req.headers["authorization"] || ""; - const extracted = authorization.split(" "); - if (extracted.length === 2 && extracted[0] === "Bearer") { - const user = checkToken(extracted[1]); - if (user !== undefined) { - // The authorization server returns a response with "x-current-user" header for a successful - // request. - res.writeHead(200, { "x-current-user": user }); - return res.end(); - } - } - res.writeHead(403); - res.end(); -}); - -const port = process.env.PORT || 9002; -server.listen(port); -console.log(`starting HTTP server on: ${port}`); - -function checkToken(token) { - return tokens[token]; -} diff --git a/examples/ext_authz/auth/users.json b/examples/ext_authz/auth/users.json deleted file mode 100644 index 4068bcb7628e..000000000000 --- a/examples/ext_authz/auth/users.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "token1": "user1", - "token2": "user2", - "token3": "user3" -} diff --git a/examples/ext_authz/config/grpc-service/v3.yaml b/examples/ext_authz/config/grpc-service/v3.yaml deleted file mode 100644 index 166e501c689c..000000000000 --- a/examples/ext_authz/config/grpc-service/v3.yaml +++ /dev/null @@ -1,67 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 8000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: upstream - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: upstream-service - http_filters: - - name: envoy.filters.http.ext_authz - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz - grpc_service: - envoy_grpc: - cluster_name: ext_authz-grpc-service - timeout: 0.250s - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - clusters: - - name: upstream-service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: upstream-service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: upstream-service - port_value: 8080 - - - name: ext_authz-grpc-service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - typed_extension_protocol_options: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicit_http_config: - http2_protocol_options: {} - load_assignment: - cluster_name: ext_authz-grpc-service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: ext_authz-grpc-service - port_value: 9001 diff --git a/examples/ext_authz/config/http-service.yaml b/examples/ext_authz/config/http-service.yaml deleted file mode 100644 index a1b7f18c1406..000000000000 --- a/examples/ext_authz/config/http-service.yaml +++ /dev/null @@ -1,67 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 8000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: upstream - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: upstream-service - http_filters: - - name: envoy.filters.http.ext_authz - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz - http_service: - server_uri: - uri: ext_authz - cluster: ext_authz-http-service - timeout: 0.250s - authorization_response: - allowed_upstream_headers: - patterns: - - exact: x-current-user - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - clusters: - - name: upstream-service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: upstream-service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: upstream-service - port_value: 8080 - - - name: ext_authz-http-service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: ext_authz-http-service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: ext_authz-http-service - port_value: 9002 diff --git a/examples/ext_authz/config/opa-service/policy.rego b/examples/ext_authz/config/opa-service/policy.rego deleted file mode 100644 index 484f01923c22..000000000000 --- a/examples/ext_authz/config/opa-service/policy.rego +++ /dev/null @@ -1,13 +0,0 @@ -package envoy.authz - -import input.attributes.request.http as http_request - -default allow = false - -allow = response { - http_request.method == "GET" - response := { - "allowed": true, - "headers": {"x-current-user": "OPA"} - } -} diff --git a/examples/ext_authz/config/opa-service/v3.yaml b/examples/ext_authz/config/opa-service/v3.yaml deleted file mode 100644 index 824239d4fd5f..000000000000 --- a/examples/ext_authz/config/opa-service/v3.yaml +++ /dev/null @@ -1,67 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 8000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: upstream - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: upstream-service - http_filters: - - name: envoy.filters.http.ext_authz - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz - grpc_service: - envoy_grpc: - cluster_name: ext_authz-opa-service - timeout: 0.250s - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - clusters: - - name: upstream-service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: upstream-service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: upstream-service - port_value: 8080 - - - name: ext_authz-opa-service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - typed_extension_protocol_options: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicit_http_config: - http2_protocol_options: {} - load_assignment: - cluster_name: ext_authz-opa-service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: ext_authz-opa-service - port_value: 9002 diff --git a/examples/ext_authz/docker-compose.yaml b/examples/ext_authz/docker-compose.yaml deleted file mode 100644 index 3bf74828b5b8..000000000000 --- a/examples/ext_authz/docker-compose.yaml +++ /dev/null @@ -1,55 +0,0 @@ -services: - - front-envoy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - target: envoy-ext_authz - args: - ENVOY_CONFIG: config/http-service.yaml - depends_on: - upstream-service: - condition: service_healthy - environment: - - FRONT_ENVOY_YAML - ports: - - "${PORT_PROXY:-8000}:8000" - - ext_authz-http-service: - build: - context: ./auth - dockerfile: ../../shared/node/Dockerfile - target: node-http-auth - volumes: - - ./users.json:/etc/users.json - environment: - - USERS=/etc/users.json - - ext_authz-grpc-service: - build: - context: auth - dockerfile: ../../shared/golang/Dockerfile - target: golang-grpc-auth - volumes: - - ./users.json:/etc/users.json - - ext_authz-opa-service: - build: - context: . - dockerfile: Dockerfile-opa - volumes: - - ./config/opa-service/policy.rego:/etc/policy.rego - command: - - run - - --server - - --log-format=json-pretty - - --set=plugins.envoy_ext_authz_grpc.addr=:9002 - - --set=decision_logs.console=true - - /etc/policy.rego - - upstream-service: - build: - context: ../shared/python - target: aiohttp-service - volumes: - - ./upstream/service/service.py:/code/service.py diff --git a/examples/ext_authz/example.rst b/examples/ext_authz/example.rst deleted file mode 100644 index 81b6460234c2..000000000000 --- a/examples/ext_authz/example.rst +++ /dev/null @@ -1,234 +0,0 @@ -.. _install_sandboxes_ext_authz: - -External authorization (``ext_authz``) filter -============================================= - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -The External Authorization sandbox demonstrates Envoy's :ref:`ext_authz filter ` -capability to delegate authorization of incoming requests through Envoy to an external services. - -While ext_authz can also be employed as a network filter, this sandbox is limited to exhibit -ext_authz HTTP Filter, which supports to call HTTP or gRPC service. - -The setup of this sandbox is very similar to front-proxy deployment, however calls to upstream -service behind the proxy will be checked by an external HTTP or gRPC service. In this sandbox, -for every authorized call, the external authorization service adds additional ``x-current-user`` -header entry to the original request headers to be forwarded to the upstream service. - -Step 1: Start all of our containers -*********************************** - -Change to the ``examples/ext_authz`` directory. - -To build this sandbox example and start the example services, run the following commands: - -.. code-block:: console - - $ pwd - envoy/examples/ext_authz - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - -------------------------------------------------------------------------------------------------------------------- - ext_authz_ext_authz-grpc-service_1 /app/server -users /etc/us Up - ext_authz_ext_authz-http-service_1 docker-entrypoint.sh node Up - ext_authz_front-envoy_1 /docker-entrypoint.sh /bin Up 10000/tcp, 0.0.0.0:8000->8000/tcp - ext_authz_upstream-service_1 python3 /code/service.py Up (healthy) - -.. note:: - - This sandbox has multiple setup controlled by ``FRONT_ENVOY_YAML`` environment variable which - points to the effective Envoy configuration to be used. The default value of ``FRONT_ENVOY_YAML`` - can be defined in the ``.env`` file or provided inline when running the ``docker compose up`` - command. - - For more information, please take a look at - `environment variables in Compose documentation `_. - -By default, ``FRONT_ENVOY_YAML`` points to :download:`config/grpc-service/v3.yaml <_include/ext_authz/config/grpc-service/v3.yaml>` -file which bootstraps front-envoy with ext_authz HTTP filter with gRPC service ``V3`` (this is specified by -:ref:`transport_api_version field`). - -The possible values of ``FRONT_ENVOY_YAML`` can be found inside the ``config`` -directory. - -For example, to run Envoy with ext_authz HTTP filter with HTTP service will be: - -.. code-block:: console - - $ pwd - envoy/examples/ext_authz - $ docker compose pull - $ # Tearing down the currently running setup - $ docker compose down - $ FRONT_ENVOY_YAML=config/http-service.yaml docker compose up --build -d - $ # Or you can update the .env file with the above FRONT_ENVOY_YAML value, so you don't have to specify it when running the "up" command. - -Step 4: Access the upstream-service behind the Front Envoy -********************************************************** - -You can now try to send a request to upstream-service via the front-envoy as follows: - -.. code-block:: console - - $ curl -v localhost:8000/service - * Trying 127.0.0.1... - * TCP_NODELAY set - * Connected to localhost (127.0.0.1) port 8000 (#0) - > GET /service HTTP/1.1 - > Host: localhost:8000 - > User-Agent: curl/7.58.0 - > Accept: */* - > - < HTTP/1.1 403 Forbidden - < date: Fri, 19 Jun 2020 15:02:24 GMT - < server: envoy - < content-length: 0 - -As observed, the request failed with ``403 Forbidden`` status code. This happened since the ext_authz -filter employed by Envoy rejected the call. To let the request reach the upstream service, you need -to provide a ``Bearer`` token via the ``Authorization`` header. - -.. note:: - - A complete list of users is defined in :download:`auth/users.json <_include/ext_authz/auth/users.json>` - file. For example, the ``token1`` used in the below example is corresponding to ``user1``. - -An example of successful requests can be observed as follows: - -.. code-block:: console - - $ curl -v -H "Authorization: Bearer token1" localhost:8000/service - * Trying 127.0.0.1... - * TCP_NODELAY set - * Connected to localhost (127.0.0.1) port 8000 (#0) - > GET /service HTTP/1.1 - > Host: localhost:8000 - > User-Agent: curl/7.58.0 - > Accept: */* - > Authorization: Bearer token1 - > - < HTTP/1.1 200 OK - < content-type: text/html; charset=utf-8 - < content-length: 24 - < server: envoy - < date: Fri, 19 Jun 2020 15:04:29 GMT - < x-envoy-upstream-service-time: 2 - < - * Connection #0 to host localhost left intact - Hello user1 from behind Envoy! - -We can also employ `Open Policy Agent `_ server -(with `envoy_ext_authz_grpc `_ plugin enabled) -as the authorization server. To run this example: - -.. code-block:: console - - $ pwd - envoy/examples/ext_authz - $ docker compose pull - $ # Tearing down the currently running setup - $ docker compose down - $ FRONT_ENVOY_YAML=config/opa-service/v3.yaml docker compose up --build -d - -And sending a request to the upstream service (via the Front Envoy) gives: - -.. code-block:: console - - $ curl localhost:8000/service --verbose - * Trying ::1... - * TCP_NODELAY set - * Connected to localhost (::1) port 8000 (#0) - > GET /service HTTP/1.1 - > Host: localhost:8000 - > User-Agent: curl/7.64.1 - > Accept: */* - > - < HTTP/1.1 200 OK - < content-type: text/html; charset=utf-8 - < content-length: 28 - < server: envoy - < date: Thu, 02 Jul 2020 06:29:58 GMT - < x-envoy-upstream-service-time: 2 - < - * Connection #0 to host localhost left intact - Hello OPA from behind Envoy! - -From the logs, we can observe the policy decision message from the Open Policy Agent server (for -the above request against the defined policy in -:download:`config/opa-service/policy.rego <_include/ext_authz/config/opa-service/policy.rego>`): - -.. code-block:: console - - $ docker compose logs ext_authz-opa-service | grep decision_id -A 30 - ext_authz-opa-service_1 | "decision_id": "8143ca68-42d8-43e6-ade6-d1169bf69110", - ext_authz-opa-service_1 | "input": { - ext_authz-opa-service_1 | "attributes": { - ext_authz-opa-service_1 | "destination": { - ext_authz-opa-service_1 | "address": { - ext_authz-opa-service_1 | "Address": { - ext_authz-opa-service_1 | "SocketAddress": { - ext_authz-opa-service_1 | "PortSpecifier": { - ext_authz-opa-service_1 | "PortValue": 8000 - ext_authz-opa-service_1 | }, - ext_authz-opa-service_1 | "address": "172.28.0.6" - ext_authz-opa-service_1 | } - ext_authz-opa-service_1 | } - ext_authz-opa-service_1 | } - ext_authz-opa-service_1 | }, - ext_authz-opa-service_1 | "metadata_context": {}, - ext_authz-opa-service_1 | "request": { - ext_authz-opa-service_1 | "http": { - ext_authz-opa-service_1 | "headers": { - ext_authz-opa-service_1 | ":authority": "localhost:8000", - ext_authz-opa-service_1 | ":method": "GET", - ext_authz-opa-service_1 | ":path": "/service", - ext_authz-opa-service_1 | "accept": "*/*", - ext_authz-opa-service_1 | "user-agent": "curl/7.64.1", - ext_authz-opa-service_1 | "x-forwarded-proto": "http", - ext_authz-opa-service_1 | "x-request-id": "b77919c0-f1d4-4b06-b444-5a8b32d5daf4" - ext_authz-opa-service_1 | }, - ext_authz-opa-service_1 | "host": "localhost:8000", - ext_authz-opa-service_1 | "id": "16617514055874272263", - ext_authz-opa-service_1 | "method": "GET", - ext_authz-opa-service_1 | "path": "/service", - -Trying to send a request with method other than ``GET`` gives a rejection: - -.. code-block:: console - - $ curl -X POST localhost:8000/service --verbose - * Trying ::1... - * TCP_NODELAY set - * Connected to localhost (::1) port 8000 (#0) - > PUT /service HTTP/1.1 - > Host: localhost:8000 - > User-Agent: curl/7.64.1 - > Accept: */* - > - < HTTP/1.1 403 Forbidden - < date: Thu, 02 Jul 2020 06:46:13 GMT - < server: envoy - < content-length: 0 - -.. seealso:: - - :ref:`ext_authz filter ` - Learn more about using Envoy's ``ext_authz`` filter. - - `Open Policy Agent `_ - Policy-based control for cloud native environments. - - `envoy_ext_authz_grpc `_ - Open Policy Agent Envoy plugin. - - `environment variables in Compose documentation `_. - Further information about using env variables with Docker Compose. diff --git a/examples/ext_authz/run_envoy.sh b/examples/ext_authz/run_envoy.sh deleted file mode 100755 index c9bb7ca58b4d..000000000000 --- a/examples/ext_authz/run_envoy.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -/usr/local/bin/envoy -c "/etc/envoy-${FRONT_ENVOY_YAML}" --service-cluster front-proxy diff --git a/examples/ext_authz/upstream/service/service.py b/examples/ext_authz/upstream/service/service.py deleted file mode 100644 index 6bbf828f6cde..000000000000 --- a/examples/ext_authz/upstream/service/service.py +++ /dev/null @@ -1,14 +0,0 @@ -from aiohttp import web - -routes = web.RouteTableDef() - - -@routes.get("/service") -async def get(request): - return web.Response(text=f"Hello {request.headers.get('x-current-user')} from behind Envoy!") - - -if __name__ == "__main__": - app = web.Application() - app.add_routes(routes) - web.run_app(app, host='0.0.0.0', port=8080) diff --git a/examples/ext_authz/users.json b/examples/ext_authz/users.json deleted file mode 100644 index 4068bcb7628e..000000000000 --- a/examples/ext_authz/users.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "token1": "user1", - "token2": "user2", - "token3": "user3" -} diff --git a/examples/ext_authz/verify.sh b/examples/ext_authz/verify.sh deleted file mode 100755 index c2e8699f64c3..000000000000 --- a/examples/ext_authz/verify.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash -e - -export NAME=ext_authz -export PORT_PROXY="${EXT_AUTH_PORT_PROXY:-10500}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - - -run_log "Test services responds with 403" -responds_with_header \ - "HTTP/1.1 403 Forbidden"\ - "http://localhost:${PORT_PROXY}/service" - -run_log "Restart front-envoy with FRONT_ENVOY_YAML=config/http-service.yaml" -"${DOCKER_COMPOSE[@]}" down -FRONT_ENVOY_YAML=config/http-service.yaml "${DOCKER_COMPOSE[@]}" up -d - -wait_for 15 bash -c "\ - responds_with_header \ - 'HTTP/1.1 200 OK' \ - -H 'Authorization: Bearer token1' \ - http://localhost:${PORT_PROXY}/service" - -run_log "Test service responds with 403" -responds_with_header \ - "HTTP/1.1 403 Forbidden"\ - "http://localhost:${PORT_PROXY}/service" - -run_log "Test authenticated service responds with 200" -responds_with_header \ - "HTTP/1.1 200 OK" \ - -H "Authorization: Bearer token1" \ - "http://localhost:${PORT_PROXY}/service" - -run_log "Restart front-envoy with FRONT_ENVOY_YAML=config/opa-service/v3.yaml" -"${DOCKER_COMPOSE[@]}" down -FRONT_ENVOY_YAML=config/opa-service/v3.yaml "${DOCKER_COMPOSE[@]}" up -d -wait_for 15 bash -c "\ - responds_with_header \ - 'HTTP/1.1 200 OK' \ - http://localhost:${PORT_PROXY}/service" - -run_log "Test OPA service responds with 200" -responds_with_header \ - "HTTP/1.1 200 OK" \ - "http://localhost:${PORT_PROXY}/service" - -run_log "Check OPA logs" -"${DOCKER_COMPOSE[@]}" logs ext_authz-opa-service | grep decision_id -A 30 - -run_log "Check OPA service rejects POST" -responds_with_header \ - "HTTP/1.1 403 Forbidden" \ - -X POST \ - "http://localhost:${PORT_PROXY}/service" diff --git a/examples/fault-injection/.gitignore b/examples/fault-injection/.gitignore deleted file mode 100644 index a2ac47b60d6e..000000000000 --- a/examples/fault-injection/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/runtime/ diff --git a/examples/fault-injection/Dockerfile-backend b/examples/fault-injection/Dockerfile-backend deleted file mode 100644 index 0fdf28a41af1..000000000000 --- a/examples/fault-injection/Dockerfile-backend +++ /dev/null @@ -1 +0,0 @@ -FROM kennethreitz/httpbin@sha256:599fe5e5073102dbb0ee3dbb65f049dab44fa9fc251f6835c9990f8fb196a72b diff --git a/examples/fault-injection/README.md b/examples/fault-injection/README.md deleted file mode 100644 index bda89392fb87..000000000000 --- a/examples/fault-injection/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/fault_injection.html). diff --git a/examples/fault-injection/disable_abort_fault_injection.sh b/examples/fault-injection/disable_abort_fault_injection.sh deleted file mode 100755 index bfbc949eb8fa..000000000000 --- a/examples/fault-injection/disable_abort_fault_injection.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -set -ex - -rm /srv/runtime/v1/envoy/fault/http/abort/abort_percent -rm /srv/runtime/v1/envoy/fault/http/abort/http_status - -pushd /srv/runtime -ln -s /srv/runtime/v1 new && mv -Tf new current -popd diff --git a/examples/fault-injection/disable_delay_fault_injection.sh b/examples/fault-injection/disable_delay_fault_injection.sh deleted file mode 100755 index 72c84f90b71b..000000000000 --- a/examples/fault-injection/disable_delay_fault_injection.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -set -ex - -rm /srv/runtime/v1/envoy/fault/http/delay/fixed_delay_percent -rm /srv/runtime/v1/envoy/fault/http/delay/fixed_duration_ms - -pushd /srv/runtime -ln -s /srv/runtime/v1 new && mv -Tf new current -popd diff --git a/examples/fault-injection/docker-compose.yaml b/examples/fault-injection/docker-compose.yaml deleted file mode 100644 index f31ad11fcf6d..000000000000 --- a/examples/fault-injection/docker-compose.yaml +++ /dev/null @@ -1,17 +0,0 @@ -services: - envoy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - target: envoy-fault-injection - volumes: - - ./runtime:/srv/runtime - ports: - - 9211:9211 - - backend: - build: - context: . - dockerfile: Dockerfile-backend - ports: - - ${PORT_PROXY:-8080}:80 diff --git a/examples/fault-injection/enable_abort_fault_injection.sh b/examples/fault-injection/enable_abort_fault_injection.sh deleted file mode 100755 index ff587a093770..000000000000 --- a/examples/fault-injection/enable_abort_fault_injection.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -set -ex - -mkdir -p /srv/runtime/v1/envoy/fault/http/abort -echo '100' > /srv/runtime/v1/envoy/fault/http/abort/abort_percent -echo '503' > /srv/runtime/v1/envoy/fault/http/abort/http_status - -pushd /srv/runtime -ln -s /srv/runtime/v1 new && mv -Tf new current -popd diff --git a/examples/fault-injection/enable_delay_fault_injection.sh b/examples/fault-injection/enable_delay_fault_injection.sh deleted file mode 100755 index 09740d17fcb0..000000000000 --- a/examples/fault-injection/enable_delay_fault_injection.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -set -ex - -mkdir -p /srv/runtime/v1/envoy/fault/http/delay -echo '50' > /srv/runtime/v1/envoy/fault/http/delay/fixed_delay_percent -echo '3000' > /srv/runtime/v1/envoy/fault/http/delay/fixed_duration_ms - -pushd /srv/runtime -ln -s /srv/runtime/v1 new && mv -Tf new current -popd diff --git a/examples/fault-injection/envoy.yaml b/examples/fault-injection/envoy.yaml deleted file mode 100644 index 697fd2957960..000000000000 --- a/examples/fault-injection/envoy.yaml +++ /dev/null @@ -1,64 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 9211 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - access_log: - - name: envoy.access_loggers.stdout - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog - route_config: - name: local_route - virtual_hosts: - - name: service - domains: - - "*" - routes: - - match: - prefix: / - route: - cluster: local_service - http_filters: - - name: envoy.filters.http.fault - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault - abort: - http_status: 503 - percentage: - numerator: 0 - denominator: HUNDRED - delay: - fixed_delay: 3s - percentage: - numerator: 0 - denominator: HUNDRED - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: local_service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: local_service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: backend - port_value: 80 -layered_runtime: - layers: - - name: disk_layer_0 - disk_layer: - symlink_root: /srv/runtime/current - subdirectory: envoy diff --git a/examples/fault-injection/example.rst b/examples/fault-injection/example.rst deleted file mode 100644 index a30c96f856b1..000000000000 --- a/examples/fault-injection/example.rst +++ /dev/null @@ -1,118 +0,0 @@ -.. _install_sandboxes_fault_injection: - -Fault injection filter -====================== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - -This simple example demonstrates Envoy's :ref:`fault injection ` capability -using Envoy's :ref:`runtime support ` to control the feature. - -It demonstrates fault injection that cause the request to abort and fail, and also faults that simply delay the -response. - -Step 1: Start all of our containers -*********************************** - -Change to the ``examples/fault_injection`` directory. - -Terminal 1 - -.. code-block:: console - - $ pwd - envoy/examples/fault-injection - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ------------------------------------------------------------------------------------------------------ - fault-injection_backend_1 gunicorn -b 0.0.0.0:80 htt Up 0.0.0.0:8080->80/tcp - fault-injection_envoy_1 /docker-entrypoint.sh /usr Up 10000/tcp, 0.0.0.0:9211->9211/tcp - -Step 2: Start sending continuous stream of HTTP requests -******************************************************** - -Terminal 2 - -.. code-block:: console - - $ pwd - envoy/examples/fault-injection - $ docker compose exec envoy bash - $ bash send_request.sh - -The script above (:download:`send_request.sh <_include/fault-injection/send_request.sh>`) sends a continuous stream -of HTTP requests to Envoy, which in turn forwards the requests to the backend container. - -Fault injection is configured in Envoy but turned off (i.e. affects 0% of requests). - -Consequently, you should see a continuous sequence of ``HTTP 200`` response codes. - -Step 3: Test Envoy's abort fault injection -****************************************** - -Turn on *abort* fault injection via the runtime using the commands below. - -Terminal 3 - -.. code-block:: console - - $ docker compose exec envoy bash - $ bash enable_abort_fault_injection.sh - -The script above enables ``HTTP`` aborts for 100% of requests. - -You should now see a continuous sequence of ``HTTP 503`` responses for all requests. - -To disable the abort injection: - -Terminal 3 - -.. code-block:: console - - $ bash disable_abort_fault_injection.sh - -Step 4: Test Envoy's delay fault injection -****************************************** - -Turn on *delay* fault injection via the runtime using the commands below. - -Terminal 3 - -.. code-block:: console - - $ docker compose exec envoy bash - $ bash enable_delay_fault_injection.sh - -The script above will add a 3-second delay to 50% of ``HTTP`` requests. - -You should now see a continuous sequence of ``HTTP 200`` responses for all requests, but half of the requests -will take 3 seconds to complete. - -To disable the delay injection: - -Terminal 3 - -.. code-block:: console - - $ bash disable_delay_fault_injection.sh - -Step 5: Check the current runtime filesystem -******************************************** - -To see the current runtime filesystem overview: - -Terminal 3 - -.. code-block:: console - - $ tree /srv/runtime - -.. seealso:: - - :ref:`Fault injection ` - Learn more about Envoy's ``HTTP`` fault injection filter. diff --git a/examples/fault-injection/send_request.sh b/examples/fault-injection/send_request.sh deleted file mode 100755 index 9a10435664c2..000000000000 --- a/examples/fault-injection/send_request.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -set -ex - -while :; do - curl -v localhost:9211/status/200 - sleep 1 -done diff --git a/examples/fault-injection/verify.sh b/examples/fault-injection/verify.sh deleted file mode 100755 index a6af1cb1d420..000000000000 --- a/examples/fault-injection/verify.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash -e - -export NAME=fault-injection -export PORT_PROXY="${FAULT_INJECTION_PORT_PROXY:-10610}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - - -run_log "Send requests for 20 seconds" -"${DOCKER_COMPOSE[@]}" exec -T envoy bash -c \ - "bash send_request.sh & export pid=\$! && sleep 20 && kill \$pid" \ - &> /dev/null - -run_log "Check logs" -"${DOCKER_COMPOSE[@]}" logs | grep "HTTP/1.1\" 200" - - -_fault_injection_test () { - local action code existing_200s existing_codes - action="$1" - code="$2" - existing_codes=0 - - # enable fault injection and check for http hits of type $code - existing_codes=$("${DOCKER_COMPOSE[@]}" logs | grep -c "HTTP/1.1\" ${code}" || :) - run_log "Enable ${action} fault injection" - "${DOCKER_COMPOSE[@]}" exec -T envoy bash "enable_${action}_fault_injection.sh" - run_log "Send requests for 20 seconds" - "${DOCKER_COMPOSE[@]}" exec -T envoy bash -c \ - "bash send_request.sh & export pid=\$! && sleep 20 && kill \$pid" \ - &> /dev/null - run_log "Check logs again" - new_codes=$("${DOCKER_COMPOSE[@]}" logs | grep -c "HTTP/1.1\" ${code}") - if [[ "$new_codes" -le "$existing_codes" ]]; then - echo "ERROR: expected to find new logs with response code $code" >&2 - return 1 - fi - - # disable fault injection and check for http hits of type 200 - existing_200s=$("${DOCKER_COMPOSE[@]}" logs | grep -c "HTTP/1.1\" 200") - run_log "Disable ${action} fault injection" - "${DOCKER_COMPOSE[@]}" exec -T envoy bash "disable_${action}_fault_injection.sh" - run_log "Send requests for 20 seconds" - "${DOCKER_COMPOSE[@]}" exec -T envoy bash -c \ - "bash send_request.sh & export pid=\$! && sleep 20 && kill \$pid" \ - &> /dev/null - run_log "Check logs again" - new_200s=$("${DOCKER_COMPOSE[@]}" logs | grep -c "HTTP/1.1\" 200") - if [[ "$new_200s" -le "$existing_200s" ]]; then - echo "ERROR: expected to find new logs with response code 200" >&2 - return 1 - fi -} - -_fault_injection_test abort 503 -_fault_injection_test delay 200 - -run_log "Check tree" -"${DOCKER_COMPOSE[@]}" exec -T envoy tree /srv/runtime diff --git a/examples/front-proxy/README.md b/examples/front-proxy/README.md deleted file mode 100644 index 95dd5abd42bb..000000000000 --- a/examples/front-proxy/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/front_proxy.html) diff --git a/examples/front-proxy/docker-compose.yaml b/examples/front-proxy/docker-compose.yaml deleted file mode 100644 index 641097d9bbc5..000000000000 --- a/examples/front-proxy/docker-compose.yaml +++ /dev/null @@ -1,54 +0,0 @@ -services: - - front-envoy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - depends_on: - service-envoy-1: - condition: service_healthy - service-envoy-2: - condition: service_healthy - ports: - - "${PORT_PROXY:-8080}:8080" - - "${PORT_HTTPS:-8443}:8443" - - "${PORT_STATS:-8001}:8001" - - service-envoy-1: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - target: envoy-admin - args: - ENVOY_CONFIG: ./service-envoy.yaml - ENVOY_ADMIN_PORT: 8001 - depends_on: - service1: - condition: service_healthy - - service1: - build: - context: ../shared/python - target: aiohttp-tracing-service2 - environment: - - SERVICE_NAME=1 - - service-envoy-2: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - target: envoy-admin - args: - ENVOY_ADMIN_PORT: 8001 - ENVOY_CONFIG: ./service-envoy-2.yaml - - depends_on: - service2: - condition: service_healthy - - service2: - build: - context: ../shared/python - target: aiohttp-tracing-service2 - environment: - - SERVICE_NAME=2 diff --git a/examples/front-proxy/envoy.yaml b/examples/front-proxy/envoy.yaml deleted file mode 100644 index 3eb5d0e0b74d..000000000000 --- a/examples/front-proxy/envoy.yaml +++ /dev/null @@ -1,168 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 8080 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/service/1" - route: - cluster: service1-envoy - - match: - prefix: "/service/2" - route: - cluster: service2-envoy - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - - address: - socket_address: - address: 0.0.0.0 - port_value: 8443 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/service/1" - route: - cluster: service1-envoy - - match: - prefix: "/service/2" - route: - cluster: service2-envoy - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext - common_tls_context: - tls_certificates: - # The following self-signed certificate pair is generated using: - # $ openssl req -x509 -newkey rsa:2048 -keyout a/front-proxy-key.pem -out a/front-proxy-crt.pem \ - # -days 3650 -nodes -subj '/CN=front-envoy' - # - # Instead of feeding it as an inline_string, certificate pair can also be fed to Envoy - # via filename. Reference: https://envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#config-core-v3-datasource. - # - # Or in a dynamic configuration scenario, certificate pair can be fetched remotely via - # Secret Discovery Service (SDS). Reference: https://envoyproxy.io/docs/envoy/latest/configuration/security/secret. - - certificate_chain: - inline_string: | - -----BEGIN CERTIFICATE----- - MIICqDCCAZACCQCquzpHNpqBcDANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtm - cm9udC1lbnZveTAeFw0yMDA3MDgwMTMxNDZaFw0zMDA3MDYwMTMxNDZaMBYxFDAS - BgNVBAMMC2Zyb250LWVudm95MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC - AQEAthnYkqVQBX+Wg7aQWyCCb87hBce1hAFhbRM8Y9dQTqxoMXZiA2n8G089hUou - oQpEdJgitXVS6YMFPFUUWfwcqxYAynLK4X5im26Yfa1eO8La8sZUS+4Bjao1gF5/ - VJxSEo2yZ7fFBo8M4E44ZehIIocipCRS+YZehFs6dmHoq/MGvh2eAHIa+O9xssPt - ofFcQMR8rwBHVbKy484O10tNCouX4yUkyQXqCRy6HRu7kSjOjNKSGtjfG+h5M8bh - 10W7ZrsJ1hWhzBulSaMZaUY3vh5ngpws1JATQVSK1Jm/dmMRciwlTK7KfzgxHlSX - 58ENpS7yPTISkEICcLbXkkKGEQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCmj6Hg - vwOxWz0xu+6fSfRL6PGJUGq6wghCfUvjfwZ7zppDUqU47fk+yqPIOzuGZMdAqi7N - v1DXkeO4A3hnMD22Rlqt25vfogAaZVToBeQxCPd/ALBLFrvLUFYuSlS3zXSBpQqQ - Ny2IKFYsMllz5RSROONHBjaJOn5OwqenJ91MPmTAG7ujXKN6INSBM0PjX9Jy4Xb9 - zT+I85jRDQHnTFce1WICBDCYidTIvJtdSSokGSuy4/xyxAAc/BpZAfOjBQ4G1QRe - 9XwOi790LyNUYFJVyeOvNJwveloWuPLHb9idmY5YABwikUY6QNcXwyHTbRCkPB2I - m+/R4XnmL4cKQ+5Z - -----END CERTIFICATE----- - private_key: - inline_string: | - -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2GdiSpVAFf5aD - tpBbIIJvzuEFx7WEAWFtEzxj11BOrGgxdmIDafwbTz2FSi6hCkR0mCK1dVLpgwU8 - VRRZ/ByrFgDKcsrhfmKbbph9rV47wtryxlRL7gGNqjWAXn9UnFISjbJnt8UGjwzg - Tjhl6EgihyKkJFL5hl6EWzp2Yeir8wa+HZ4Achr473Gyw+2h8VxAxHyvAEdVsrLj - zg7XS00Ki5fjJSTJBeoJHLodG7uRKM6M0pIa2N8b6HkzxuHXRbtmuwnWFaHMG6VJ - oxlpRje+HmeCnCzUkBNBVIrUmb92YxFyLCVMrsp/ODEeVJfnwQ2lLvI9MhKQQgJw - tteSQoYRAgMBAAECggEAeDGdEkYNCGQLe8pvg8Z0ccoSGpeTxpqGrNEKhjfi6NrB - NwyVav10iq4FxEmPd3nobzDPkAftfvWc6hKaCT7vyTkPspCMOsQJ39/ixOk+jqFx - lNa1YxyoZ9IV2DIHR1iaj2Z5gB367PZUoGTgstrbafbaNY9IOSyojCIO935ubbcx - DWwL24XAf51ez6sXnI8V5tXmrFlNXhbhJdH8iIxNyM45HrnlUlOk0lCK4gmLJjy9 - 10IS2H2Wh3M5zsTpihH1JvM56oAH1ahrhMXs/rVFXXkg50yD1KV+HQiEbglYKUxO - eMYtfaY9i2CuLwhDnWp3oxP3HfgQQhD09OEN3e0IlQKBgQDZ/3poG9TiMZSjfKqL - xnCABMXGVQsfFWNC8THoW6RRx5Rqi8q08yJrmhCu32YKvccsOljDQJQQJdQO1g09 - e/adJmCnTrqxNtjPkX9txV23Lp6Ak7emjiQ5ICu7iWxrcO3zf7hmKtj7z+av8sjO - mDI7NkX5vnlE74nztBEjp3eC0wKBgQDV2GeJV028RW3b/QyP3Gwmax2+cKLR9PKR - nJnmO5bxAT0nQ3xuJEAqMIss/Rfb/macWc2N/6CWJCRT6a2vgy6xBW+bqG6RdQMB - xEZXFZl+sSKhXPkc5Wjb4lQ14YWyRPrTjMlwez3k4UolIJhJmwl+D7OkMRrOUERO - EtUvc7odCwKBgBi+nhdZKWXveM7B5N3uzXBKmmRz3MpPdC/yDtcwJ8u8msUpTv4R - JxQNrd0bsIqBli0YBmFLYEMg+BwjAee7vXeDFq+HCTv6XMva2RsNryCO4yD3I359 - XfE6DJzB8ZOUgv4Dvluie3TB2Y6ZQV/p+LGt7G13yG4hvofyJYvlg3RPAoGAcjDg - +OH5zLN2eqah8qBN0CYa9/rFt0AJ19+7/smLTJ7QvQq4g0gwS1couplcCEnNGWiK - 72y1n/ckvvplmPeAE19HveMvR9UoCeV5ej86fACy8V/oVpnaaLBvL2aCMjPLjPP9 - DWeCIZp8MV86cvOrGfngf6kJG2qZTueXl4NAuwkCgYEArKkhlZVXjwBoVvtHYmN2 - o+F6cGMlRJTLhNc391WApsgDZfTZSdeJsBsvvzS/Nc0burrufJg0wYioTlpReSy4 - ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU - q5sGxGrC1RECGB5Zwx2S2ZY= - -----END PRIVATE KEY----- - - clusters: - - name: service1-envoy - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service1-envoy - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-envoy-1 - port_value: 8000 - - name: service2-envoy - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service2-envoy - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-envoy-2 - port_value: 8000 -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 8001 -layered_runtime: - layers: - - name: static_layer_0 - static_layer: - envoy: - resource_limits: - listener: - example_listener_name: - connection_limit: 10000 diff --git a/examples/front-proxy/example.rst b/examples/front-proxy/example.rst deleted file mode 100644 index 5611ad5301c3..000000000000 --- a/examples/front-proxy/example.rst +++ /dev/null @@ -1,333 +0,0 @@ -.. _install_sandboxes_front_proxy: - -Front proxy -=========== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -To get a flavor of what Envoy has to offer as a front proxy, we are releasing a -`docker compose `_ sandbox that deploys a front Envoy and a -couple of services (simple ``aiohttp`` apps) colocated with a running service Envoy. - -The three containers will be deployed inside a virtual network called ``envoymesh``. - -Below you can see a graphic showing the docker compose deployment: - -.. image:: /_static/docker_compose_front_proxy.svg - :width: 100% - -All incoming requests are routed via the front Envoy, which is acting as a reverse proxy sitting on -the edge of the ``envoymesh`` network. Port ``8080``, ``8443``, and ``8001`` are exposed by docker -compose (see :download:`docker-compose.yaml <_include/front-proxy/docker-compose.yaml>`) to handle -``HTTP``, ``HTTPS`` calls to the services and requests to ``/admin`` respectively. - -Moreover, notice that all traffic routed by the front Envoy to the service containers is actually -routed to the service Envoys (routes setup in :download:`envoy.yaml <_include/front-proxy/envoy.yaml>`). - -In turn the service Envoys route the request to the ``aiohttp`` app via the loopback -address (routes setup in :download:`service-envoy.yaml <_include/front-proxy/service-envoy.yaml>`). This -setup illustrates the advantage of running service Envoys collocated with your services: all -requests are handled by the service Envoy, and efficiently routed to your services. - -Step 1: Start all of our containers -*********************************** - -Change to the ``examples/front-proxy`` directory. - -.. code-block:: console - - $ pwd - envoy/examples/front-proxy - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - --------------------------------------------------------------------------------------------------------------------------------------------------------- - front-proxy_front-envoy_1 /docker-entrypoint.sh /bin ... Up 10000/tcp, 0.0.0.0:8080->8080/tcp, 0.0.0.0:8001->8001/tcp, 0.0.0.0:8443->8443/tcp - front-proxy_service1_1 python3 /code/service.py ... Up (healthy) - front-proxy_service2_1 python3 /code/service.py ... Up (healthy) - -Step 2: Test Envoy's routing capabilities -***************************************** - -You can now send a request to both services via the ``front-envoy``. - -For ``service1``: - -.. code-block:: console - - $ curl -v localhost:8080/service/1 - * Trying ::1... - * TCP_NODELAY set - * Connected to localhost (::1) port 8080 (#0) - > GET /service/1 HTTP/1.1 - > Host: localhost:8080 - > User-Agent: curl/7.64.1 - > Accept: */* - > - < HTTP/1.1 200 OK - < content-type: text/html; charset=utf-8 - < content-length: 92 - < server: envoy - < date: Mon, 06 Jul 2020 06:20:00 GMT - < x-envoy-upstream-service-time: 2 - < - Hello from behind Envoy (service 1)! hostname: 36418bc3c824 resolvedhostname: 192.168.160.4 - -For ``service2``: - -.. code-block:: console - - $ curl -v localhost:8080/service/2 - * Trying ::1... - * TCP_NODELAY set - * Connected to localhost (::1) port 8080 (#0) - > GET /service/2 HTTP/1.1 - > Host: localhost:8080 - > User-Agent: curl/7.64.1 - > Accept: */* - > - < HTTP/1.1 200 OK - < content-type: text/html; charset=utf-8 - < content-length: 92 - < server: envoy - < date: Mon, 06 Jul 2020 06:23:13 GMT - < x-envoy-upstream-service-time: 2 - < - Hello from behind Envoy (service 2)! hostname: ea6165ee4fee resolvedhostname: 192.168.160.2 - -Notice that each request, while sent to the front Envoy, was correctly routed to the respective -application. - -We can also use ``HTTPS`` to call services behind the front Envoy. For example, calling ``service1``: - -.. code-block:: console - - $ curl https://localhost:8443/service/1 -k -v - * Trying ::1... - * TCP_NODELAY set - * Connected to localhost (::1) port 8443 (#0) - * ALPN, offering h2 - * ALPN, offering http/1.1 - * successfully set certificate verify locations: - * CAfile: /etc/ssl/cert.pem - CApath: none - * TLSv1.2 (OUT), TLS handshake, Client hello (1): - * TLSv1.2 (IN), TLS handshake, Server hello (2): - * TLSv1.2 (IN), TLS handshake, Certificate (11): - * TLSv1.2 (IN), TLS handshake, Server key exchange (12): - * TLSv1.2 (IN), TLS handshake, Server finished (14): - * TLSv1.2 (OUT), TLS handshake, Client key exchange (16): - * TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1): - * TLSv1.2 (OUT), TLS handshake, Finished (20): - * TLSv1.2 (IN), TLS change cipher, Change cipher spec (1): - * TLSv1.2 (IN), TLS handshake, Finished (20): - * SSL connection using TLSv1.2 / ECDHE-RSA-CHACHA20-POLY1305 - * ALPN, server did not agree to a protocol - * Server certificate: - * subject: CN=front-envoy - * start date: Jul 5 15:18:44 2020 GMT - * expire date: Jul 5 15:18:44 2021 GMT - * issuer: CN=front-envoy - * SSL certificate verify result: self signed certificate (18), continuing anyway. - > GET /service/1 HTTP/1.1 - > Host: localhost:8443 - > User-Agent: curl/7.64.1 - > Accept: */* - > - < HTTP/1.1 200 OK - < content-type: text/html; charset=utf-8 - < content-length: 92 - < server: envoy - < date: Mon, 06 Jul 2020 06:17:14 GMT - < x-envoy-upstream-service-time: 3 - < - Hello from behind Envoy (service 1)! hostname: 36418bc3c824 resolvedhostname: 192.168.160.4 - -Step 3: Test Envoy's load balancing capabilities -************************************************ - -Now let's scale up our ``service1`` nodes to demonstrate the load balancing abilities of Envoy: - -.. code-block:: console - - $ docker compose scale service1=3 - Creating and starting example_service1_2 ... done - Creating and starting example_service1_3 ... done - -Now if we send a request to ``service1`` multiple times, the front Envoy will load balance the -requests by doing a round robin of the three ``service1`` machines: - -.. code-block:: console - - $ curl -v localhost:8080/service/1 - * Trying ::1... - * TCP_NODELAY set - * Connected to localhost (::1) port 8080 (#0) - > GET /service/1 HTTP/1.1 - > Host: localhost:8080 - > User-Agent: curl/7.64.1 - > Accept: */* - > - < HTTP/1.1 200 OK - < content-type: text/html; charset=utf-8 - < content-length: 92 - < server: envoy - < date: Mon, 06 Jul 2020 06:21:47 GMT - < x-envoy-upstream-service-time: 6 - < - Hello from behind Envoy (service 1)! hostname: 3dc787578c23 resolvedhostname: 192.168.160.6 - - $ curl -v localhost:8080/service/1 - * Trying 192.168.99.100... - * Connected to 192.168.99.100 (192.168.99.100) port 8080 (#0) - > GET /service/1 HTTP/1.1 - > Host: 192.168.99.100:8080 - > User-Agent: curl/7.54.0 - > Accept: */* - > - < HTTP/1.1 200 OK - < content-type: text/html; charset=utf-8 - < content-length: 89 - < x-envoy-upstream-service-time: 1 - < server: envoy - < date: Fri, 26 Aug 2018 19:40:22 GMT - < - Hello from behind Envoy (service 1)! hostname: 3a93ece62129 resolvedhostname: 192.168.160.5 - - $ curl -v localhost:8080/service/1 - * Trying 192.168.99.100... - * Connected to 192.168.99.100 (192.168.99.100) port 8080 (#0) - > GET /service/1 HTTP/1.1 - > Host: 192.168.99.100:8080 - > User-Agent: curl/7.43.0 - > Accept: */* - > - < HTTP/1.1 200 OK - < content-type: text/html; charset=utf-8 - < content-length: 89 - < x-envoy-upstream-service-time: 1 - < server: envoy - < date: Fri, 26 Aug 2018 19:40:24 GMT - < x-envoy-protocol-version: HTTP/1.1 - < - Hello from behind Envoy (service 1)! hostname: 36418bc3c824 resolvedhostname: 192.168.160.4 - -Step 4: Enter containers and curl services -****************************************** - -In addition of using ``curl`` from your host machine, you can also enter the -containers themselves and ``curl`` from inside them. To enter a container you -can use ``docker compose exec /bin/bash``. For example we can -enter the ``front-envoy`` container, and ``curl`` for services locally: - -.. code-block:: console - - $ docker compose exec front-envoy /bin/bash - root@81288499f9d7:/# curl localhost:8080/service/1 - Hello from behind Envoy (service 1)! hostname: 85ac151715c6 resolvedhostname: 172.19.0.3 - root@81288499f9d7:/# curl localhost:8080/service/1 - Hello from behind Envoy (service 1)! hostname: 20da22cfc955 resolvedhostname: 172.19.0.5 - root@81288499f9d7:/# curl localhost:8080/service/1 - Hello from behind Envoy (service 1)! hostname: f26027f1ce28 resolvedhostname: 172.19.0.6 - root@81288499f9d7:/# curl localhost:8080/service/2 - Hello from behind Envoy (service 2)! hostname: 92f4a3737bbc resolvedhostname: 172.19.0.2 - -Step 5: Enter container and curl admin -************************************** - -When Envoy runs it also attaches an ``admin`` to your desired port. - -In the example configs the admin is bound to port ``8001``. - -We can ``curl`` it to gain useful information: - -- :ref:`/server_info ` provides information about the Envoy version you are running. -- :ref:`/stats ` provides statistics about the Envoy server. - -In the example we can enter the ``front-envoy`` container to query admin: - -.. code-block:: console - - $ docker compose exec front-envoy /bin/bash - root@e654c2c83277:/# curl localhost:8001/server_info - -.. code-block:: json - - { - "version": "093e2ffe046313242144d0431f1bb5cf18d82544/1.15.0-dev/Clean/RELEASE/BoringSSL", - "state": "LIVE", - "hot_restart_version": "11.104", - "command_line_options": { - "base_id": "0", - "use_dynamic_base_id": false, - "base_id_path": "", - "concurrency": 8, - "config_path": "/etc/envoy.yaml", - "config_yaml": "", - "allow_unknown_static_fields": false, - "reject_unknown_dynamic_fields": false, - "ignore_unknown_dynamic_fields": false, - "admin_address_path": "", - "local_address_ip_version": "v4", - "log_level": "info", - "component_log_level": "", - "log_format": "[%Y-%m-%d %T.%e][%t][%l][%n] [%g:%#] %v", - "log_format_escaped": false, - "log_path": "", - "service_cluster": "front-proxy", - "service_node": "", - "service_zone": "", - "drain_strategy": "Gradual", - "mode": "Serve", - "disable_hot_restart": false, - "enable_mutex_tracing": false, - "restart_epoch": 0, - "cpuset_threads": false, - "disabled_extensions": [], - "bootstrap_version": 0, - "hidden_envoy_deprecated_max_stats": "0", - "hidden_envoy_deprecated_max_obj_name_len": "0", - "file_flush_interval": "10s", - "drain_time": "600s", - "parent_shutdown_time": "900s" - }, - "uptime_current_epoch": "188s", - "uptime_all_epochs": "188s" - } - -.. code-block:: console - - root@e654c2c83277:/# curl localhost:8001/stats - cluster.service1.external.upstream_rq_200: 7 - ... - cluster.service1.membership_change: 2 - cluster.service1.membership_total: 3 - ... - cluster.service1.upstream_cx_http2_total: 3 - ... - cluster.service1.upstream_rq_total: 7 - ... - cluster.service2.external.upstream_rq_200: 2 - ... - cluster.service2.membership_change: 1 - cluster.service2.membership_total: 1 - ... - cluster.service2.upstream_cx_http2_total: 1 - ... - cluster.service2.upstream_rq_total: 2 - ... - -Notice that we can get the number of members of upstream clusters, number of requests fulfilled by -them, information about http ingress, and a plethora of other useful stats. - -.. seealso:: - - :ref:`Envoy admin quick start guide ` - Quick start guide to the Envoy admin interface. diff --git a/examples/front-proxy/service-envoy-2.yaml b/examples/front-proxy/service-envoy-2.yaml deleted file mode 100644 index 642b0cc468c1..000000000000 --- a/examples/front-proxy/service-envoy-2.yaml +++ /dev/null @@ -1,47 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 8000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: service_envoy_2 - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/service/2" - route: - cluster: service2 - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - clusters: - - name: service2 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service2 - port_value: 8080 -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 8001 diff --git a/examples/front-proxy/service-envoy.yaml b/examples/front-proxy/service-envoy.yaml deleted file mode 100644 index 14a61902856d..000000000000 --- a/examples/front-proxy/service-envoy.yaml +++ /dev/null @@ -1,47 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 8000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: service_envoy_1 - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/service/1" - route: - cluster: service1 - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - clusters: - - name: service1 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service1 - port_value: 8080 -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 8001 diff --git a/examples/front-proxy/verify.sh b/examples/front-proxy/verify.sh deleted file mode 100755 index 398e85161a7e..000000000000 --- a/examples/front-proxy/verify.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -e - -export NAME=front-proxy -export PORT_PROXY="${FRONT_PROXY_PORT_PROXY:-10610}" -export PORT_HTTPS="${FRONT_PROXY_PORT_HTTPS:-10611}" -export PORT_STATS="${FRONT_PROXY_PORT_STATS:-10612}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - - -run_log "Test service: localhost:${PORT_PROXY}/service/1" -responds_with \ - "Hello from behind Envoy (service 1)!" \ - "http://localhost:${PORT_PROXY}/service/1" - -run_log "Test service: localhost:${PORT_PROXY}/service/2" -responds_with \ - "Hello from behind Envoy (service 2)!" \ - "http://localhost:${PORT_PROXY}/service/2" - -run_log "Test service: https://localhost:${PORT_HTTPS}/service/1" -responds_with \ - "Hello from behind Envoy (service 1)!" \ - -k "https://localhost:${PORT_HTTPS}/service/1" - -run_log "Scale up docker service1=3" -"${DOCKER_COMPOSE[@]}" up --wait -d --scale service1=3 - -run_log "Test round-robin localhost:${PORT_PROXY}/service/1" -"${DOCKER_COMPOSE[@]}" exec -T front-envoy bash -c "\ - curl -s http://localhost:8080/service/1 \ - && curl -s http://localhost:8080/service/1 \ - && curl -s http://localhost:8080/service/1" \ - | grep Hello | grep "service 1" - -run_log "Test service inside front-envoy: localhost:${PORT_PROXY}/service/2" -"${DOCKER_COMPOSE[@]}" exec -T front-envoy curl -s "http://localhost:8080/service/2" | grep Hello | grep "service 2" - -run_log "Test service info: localhost:${PORT_STATS}/server_info" -"${DOCKER_COMPOSE[@]}" exec -T front-envoy curl -s "http://localhost:8001/server_info" | jq '.' - -run_log "Test service stats: localhost:${PORT_STATS}/stats" -"${DOCKER_COMPOSE[@]}" exec -T front-envoy curl -s "http://localhost:8001/stats" | grep ":" diff --git a/examples/go.mod b/examples/go.mod deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/examples/golang-http/README.md b/examples/golang-http/README.md deleted file mode 100644 index 73def7ce10f8..000000000000 --- a/examples/golang-http/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/golang.html). diff --git a/examples/golang-http/docker-compose-go.yaml b/examples/golang-http/docker-compose-go.yaml deleted file mode 100644 index 6d5d1e821486..000000000000 --- a/examples/golang-http/docker-compose-go.yaml +++ /dev/null @@ -1,17 +0,0 @@ -services: - go_plugin_compile: - build: - context: simple - dockerfile: ../../shared/golang/Dockerfile - target: golang-base - command: > - bash -c " - cd examples/golang-http/simple - && go build -o simple.so -buildmode=c-shared . - && cp ./simple.so /output" - working_dir: /source - environment: - - GOFLAGS=-buildvcs=false - volumes: - - ../..:/source - - ./lib:/output diff --git a/examples/golang-http/docker-compose.yaml b/examples/golang-http/docker-compose.yaml deleted file mode 100644 index 2b05743c1cd0..000000000000 --- a/examples/golang-http/docker-compose.yaml +++ /dev/null @@ -1,19 +0,0 @@ -services: - - proxy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - target: envoy-go - args: - ENVOY_VARIANT: contrib-dev - depends_on: - helloworld_service: - condition: service_healthy - ports: - - "${PORT_PROXY:-10000}:10000" - - helloworld_service: - build: - context: ../shared/python - target: aiohttp-hello-service diff --git a/examples/golang-http/envoy.yaml b/examples/golang-http/envoy.yaml deleted file mode 100644 index e4a2c630fb66..000000000000 --- a/examples/golang-http/envoy.yaml +++ /dev/null @@ -1,51 +0,0 @@ -# envoy demo with golang extension enabled -static_resources: - listeners: - - name: listener_0 - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_http - http_filters: - - name: envoy.filters.http.golang - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config - library_id: simple - library_path: "lib/simple.so" - plugin_name: simple - plugin_config: - "@type": type.googleapis.com/xds.type.v3.TypedStruct - value: - prefix_localreply_body: "Configured local reply from go" - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - route_config: - name: local_route - virtual_hosts: - - name: local_service - domains: ["*"] - routes: - - match: - prefix: "/" - route: - cluster: helloworld_service_cluster - clusters: - - name: helloworld_service_cluster - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: helloworld_service_cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: helloworld_service - port_value: 8080 diff --git a/examples/golang-http/example.rst b/examples/golang-http/example.rst deleted file mode 100644 index 43680611d71b..000000000000 --- a/examples/golang-http/example.rst +++ /dev/null @@ -1,92 +0,0 @@ -.. _install_sandboxes_golang_http: - -Golang HTTP filter -================== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -In this example, we show how the `Golang `_ filter can be used with the Envoy -proxy. - -The example demonstrates a Go plugin that can respond directly to requests and also update responses provided by an upstream server. - -It also shows how Go plugins can be independently configured at runtime. - -Step 1: Compile the go plugin library -************************************* - -Change to the ``examples/golang-http`` directory and build the go plugin library. - -.. code-block:: console - - $ pwd - envoy/examples/golang-http - $ docker compose -f docker-compose-go.yaml run --rm go_plugin_compile - -The compiled library should now be in the ``lib`` folder. - -.. code-block:: console - - $ ls lib - simple.so - -Step 2: Start all of our containers -*********************************** - -Start all the containers. - -.. code-block:: console - - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ----------------------------------------------------------------------------------------------------------------------- - golang_proxy_1 /docker-entrypoint.sh /usr ... Up 10000/tcp, 0.0.0.0:10000->10000/tcp,:::10000->10000/tcp - golang_web_service_1 /bin/echo-server Up 8080/tcp - -Step 3: Make a request handled by the Go plugin -*********************************************** - -The output from the ``curl`` command below should include the header added by the simple Go plugin. - -.. code-block:: console - - $ curl -v localhost:10000 2>&1 | grep rsp-header-from-go - < rsp-header-from-go: bar-test - -Step 4: Make a request handled upstream and updated by the Go plugin -******************************************************************** - -The output from the ``curl`` command below should include the body that has been updated by the simple Go plugin. - -.. code-block:: console - - $ curl localhost:10000/update_upstream_response 2>&1 | grep "updated" - upstream response body updated by the simple plugin - -Step 5: Make a request handled by the Go plugin using custom configuration -************************************************************************** - -The output from the ``curl`` command below should include the body that contains value of -``prefix_localreply_body`` by the simple Go plugin. - -.. code-block:: console - - $ curl localhost:10000/localreply_by_config 2>&1 | grep "localreply" - Configured local reply from go, path: /localreply_by_config - -.. seealso:: - - :ref:`Envoy Go filter ` - Further information about the Envoy Go filter. - :ref:`Go extension API ` - The Go extension filter API. - :repo:`Go plugin API ` - Overview of Envoy's Go plugin APIs. diff --git a/examples/golang-http/simple/config.go b/examples/golang-http/simple/config.go deleted file mode 100644 index 2c635d9f02bb..000000000000 --- a/examples/golang-http/simple/config.go +++ /dev/null @@ -1,74 +0,0 @@ -package main - -import ( - "errors" - "fmt" - - xds "github.com/cncf/xds/go/xds/type/v3" - "google.golang.org/protobuf/types/known/anypb" - - "github.com/envoyproxy/envoy/contrib/golang/common/go/api" - "github.com/envoyproxy/envoy/contrib/golang/filters/http/source/go/pkg/http" -) - -const Name = "simple" - -func init() { - http.RegisterHttpFilterFactoryAndConfigParser(Name, filterFactory, &parser{}) -} - -type config struct { - echoBody string - // other fields -} - -type parser struct { -} - -// Parse the filter configuration. We can call the ConfigCallbackHandler to control the filter's -// behavior -func (p *parser) Parse(any *anypb.Any, callbacks api.ConfigCallbackHandler) (interface{}, error) { - configStruct := &xds.TypedStruct{} - if err := any.UnmarshalTo(configStruct); err != nil { - return nil, err - } - - v := configStruct.Value - conf := &config{} - prefix, ok := v.AsMap()["prefix_localreply_body"] - if !ok { - return nil, errors.New("missing prefix_localreply_body") - } - if str, ok := prefix.(string); ok { - conf.echoBody = str - } else { - return nil, fmt.Errorf("prefix_localreply_body: expect string while got %T", prefix) - } - return conf, nil -} - -// Merge configuration from the inherited parent configuration -func (p *parser) Merge(parent interface{}, child interface{}) interface{} { - parentConfig := parent.(*config) - childConfig := child.(*config) - - // copy one, do not update parentConfig directly. - newConfig := *parentConfig - if childConfig.echoBody != "" { - newConfig.echoBody = childConfig.echoBody - } - return &newConfig -} - -func filterFactory(c interface{}, callbacks api.FilterCallbackHandler) api.StreamFilter { - conf, ok := c.(*config) - if !ok { - panic("unexpected config type") - } - return &filter{ - callbacks: callbacks, - config: conf, - } -} - -func main() {} diff --git a/examples/golang-http/simple/filter.go b/examples/golang-http/simple/filter.go deleted file mode 100644 index b5bc5542669f..000000000000 --- a/examples/golang-http/simple/filter.go +++ /dev/null @@ -1,139 +0,0 @@ -package main - -import ( - "fmt" - "strconv" - - "github.com/envoyproxy/envoy/contrib/golang/common/go/api" -) - -var UpdateUpstreamBody = "upstream response body updated by the simple plugin" - -// The callbacks in the filter, like `DecodeHeaders`, can be implemented on demand. -// Because api.PassThroughStreamFilter provides a default implementation. -type filter struct { - api.PassThroughStreamFilter - - callbacks api.FilterCallbackHandler - path string - config *config -} - -func (f *filter) sendLocalReplyInternal() api.StatusType { - body := fmt.Sprintf("%s, path: %s\r\n", f.config.echoBody, f.path) - f.callbacks.DecoderFilterCallbacks().SendLocalReply(200, body, nil, 0, "") - // Remember to return LocalReply when the request is replied locally - return api.LocalReply -} - -// Callbacks which are called in request path -// The endStream is true if the request doesn't have body -func (f *filter) DecodeHeaders(header api.RequestHeaderMap, endStream bool) api.StatusType { - f.path, _ = header.Get(":path") - api.LogDebugf("get path %s", f.path) - - if f.path == "/localreply_by_config" { - return f.sendLocalReplyInternal() - } - return api.Continue - /* - // If the code is time-consuming, to avoid blocking the Envoy, - // we need to run the code in a background goroutine - // and suspend & resume the filter - go func() { - defer f.callbacks.DecoderFilterCallbacks().RecoverPanic() - // do time-consuming jobs - - // resume the filter - f.callbacks.DecoderFilterCallbacks().Continue(status) - }() - - // suspend the filter - return api.Running - */ -} - -// DecodeData might be called multiple times during handling the request body. -// The endStream is true when handling the last piece of the body. -func (f *filter) DecodeData(buffer api.BufferInstance, endStream bool) api.StatusType { - // support suspending & resuming the filter in a background goroutine - return api.Continue -} - -func (f *filter) DecodeTrailers(trailers api.RequestTrailerMap) api.StatusType { - // support suspending & resuming the filter in a background goroutine - return api.Continue -} - -// Callbacks which are called in response path -// The endStream is true if the response doesn't have body -func (f *filter) EncodeHeaders(header api.ResponseHeaderMap, endStream bool) api.StatusType { - if f.path == "/update_upstream_response" { - header.Set("Content-Length", strconv.Itoa(len(UpdateUpstreamBody))) - } - header.Set("Rsp-Header-From-Go", "bar-test") - // support suspending & resuming the filter in a background goroutine - return api.Continue -} - -// EncodeData might be called multiple times during handling the response body. -// The endStream is true when handling the last piece of the body. -func (f *filter) EncodeData(buffer api.BufferInstance, endStream bool) api.StatusType { - if f.path == "/update_upstream_response" { - if endStream { - buffer.SetString(UpdateUpstreamBody) - } else { - buffer.Reset() - } - } - // support suspending & resuming the filter in a background goroutine - return api.Continue -} - -func (f *filter) EncodeTrailers(trailers api.ResponseTrailerMap) api.StatusType { - return api.Continue -} - -// OnLog is called when the HTTP stream is ended on HTTP Connection Manager filter. -func (f *filter) OnLog() { - code, _ := f.callbacks.StreamInfo().ResponseCode() - respCode := strconv.Itoa(int(code)) - api.LogDebug(respCode) - - /* - // It's possible to kick off a goroutine here. - // But it's unsafe to access the f.callbacks because the FilterCallbackHandler - // may be already released when the goroutine is scheduled. - go func() { - defer func() { - if p := recover(); p != nil { - const size = 64 << 10 - buf := make([]byte, size) - buf = buf[:runtime.Stack(buf, false)] - fmt.Printf("http: panic serving: %v\n%s", p, buf) - } - }() - - // do time-consuming jobs - }() - */ -} - -// OnLogDownstreamStart is called when HTTP Connection Manager filter receives a new HTTP request -// (required the corresponding access log type is enabled) -func (f *filter) OnLogDownstreamStart() { - // also support kicking off a goroutine here, like OnLog. -} - -// OnLogDownstreamPeriodic is called on any HTTP Connection Manager periodic log record -// (required the corresponding access log type is enabled) -func (f *filter) OnLogDownstreamPeriodic() { - // also support kicking off a goroutine here, like OnLog. -} - -func (f *filter) OnDestroy(reason api.DestroyReason) { - // One should not access f.callbacks here because the FilterCallbackHandler - // is released. But we can still access other Go fields in the filter f. - - // goroutine can be used everywhere. -} diff --git a/examples/golang-http/simple/go.mod b/examples/golang-http/simple/go.mod deleted file mode 100644 index ac19b6ad7afd..000000000000 --- a/examples/golang-http/simple/go.mod +++ /dev/null @@ -1,23 +0,0 @@ -module github.com/envoyproxy/envoy/examples/golang-http/simple - -// the version should >= 1.18 -go 1.20 - -// NOTICE: these lines could be generated automatically by "go mod tidy" -require ( - github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa - github.com/envoyproxy/envoy v1.24.0 - google.golang.org/protobuf v1.34.2 -) - -require ( - github.com/envoyproxy/protoc-gen-validate v1.0.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect -) - -// TODO: remove when #26173 lands. -// And check the "API compatibility" section in doc: -// https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/golang_filter#developing-a-go-plugin -replace github.com/envoyproxy/envoy => ../../.. diff --git a/examples/golang-http/simple/go.sum b/examples/golang-http/simple/go.sum deleted file mode 100644 index 93cdbfb59e6c..000000000000 --- a/examples/golang-http/simple/go.sum +++ /dev/null @@ -1,18 +0,0 @@ -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= -github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= -github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM= -google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= diff --git a/examples/golang-http/verify.sh b/examples/golang-http/verify.sh deleted file mode 100755 index 163efc9fb39c..000000000000 --- a/examples/golang-http/verify.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -e - -export NAME=golang -export UID -export MANUAL=true -export PORT_PROXY="${GOLANG_PORT_PROXY:-10710}" - - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Compile the go plugin library" -"${DOCKER_COMPOSE[@]}" -f docker-compose-go.yaml up --quiet-pull --remove-orphans go_plugin_compile - -run_log "Start all of our containers" -bring_up_example - -wait_for 10 bash -c "responds_with_header 'rsp-header-from-go: bar-test' http://localhost:${PORT_PROXY}" - -run_log "Make a request handled by the Go plugin" -responds_with_header \ - "rsp-header-from-go: bar-test" \ - "http://localhost:${PORT_PROXY}" - -run_log "Make a request handled upstream and updated by the Go plugin" -responds_with \ - "upstream response body updated by the simple plugin" \ - "http://localhost:${PORT_PROXY}/update_upstream_response" - -run_log "Make a request handled by the Go plugin using custom configuration" -responds_with \ - "Configured local reply from go, path: /localreply_by_config" \ - "http://localhost:${PORT_PROXY}/localreply_by_config" diff --git a/examples/golang-network/Dockerfile-echo b/examples/golang-network/Dockerfile-echo deleted file mode 100644 index 52c5b0ff12da..000000000000 --- a/examples/golang-network/Dockerfile-echo +++ /dev/null @@ -1 +0,0 @@ -FROM cjimti/go-echo@sha256:ff9ac5cb2051d7ec0ba7b12b805550bbcb87966e9d5c288d75c18f87d9d861e9 diff --git a/examples/golang-network/README.md b/examples/golang-network/README.md deleted file mode 100644 index ef68f96dce2b..000000000000 --- a/examples/golang-network/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/golang-network.html). diff --git a/examples/golang-network/docker-compose-go.yaml b/examples/golang-network/docker-compose-go.yaml deleted file mode 100644 index eeb68aa6dad4..000000000000 --- a/examples/golang-network/docker-compose-go.yaml +++ /dev/null @@ -1,17 +0,0 @@ -services: - go_plugin_compile: - build: - context: simple - dockerfile: ../../shared/golang/Dockerfile - target: golang-base - command: > - bash -c " - cd examples/golang-network/simple - && go build -o simple.so -buildmode=c-shared . - && cp ./simple.so /output" - working_dir: /source - environment: - - GOFLAGS=-buildvcs=false - volumes: - - ../..:/source - - ./lib:/output diff --git a/examples/golang-network/docker-compose.yaml b/examples/golang-network/docker-compose.yaml deleted file mode 100644 index 0835a7f8b01b..000000000000 --- a/examples/golang-network/docker-compose.yaml +++ /dev/null @@ -1,19 +0,0 @@ -services: - - proxy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - target: envoy-go - args: - ENVOY_VARIANT: contrib-dev - depends_on: - - echo_service - ports: - - "${PORT_PROXY:-10000}:10000" - - echo_service: - build: - context: . - dockerfile: Dockerfile-echo - hostname: echo_service diff --git a/examples/golang-network/envoy.yaml b/examples/golang-network/envoy.yaml deleted file mode 100644 index 4a96a9091f31..000000000000 --- a/examples/golang-network/envoy.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# envoy demo with golang extension enabled -static_resources: - listeners: - - name: listener_0 - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.golang - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.golang.v3alpha.Config - is_terminal_filter: true - library_id: simple - library_path: "/lib/simple.so" - plugin_name: simple - plugin_config: - "@type": type.googleapis.com/xds.type.v3.TypedStruct - value: - echo_server_addr: echo_service - clusters: - - name: plainText - type: ORIGINAL_DST - lb_policy: CLUSTER_PROVIDED diff --git a/examples/golang-network/example.rst b/examples/golang-network/example.rst deleted file mode 100644 index 1b5403ab1b20..000000000000 --- a/examples/golang-network/example.rst +++ /dev/null @@ -1,78 +0,0 @@ -.. _install_sandboxes_golang_network: - -Golang network filter -===================== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`netcat ` - Used to send TCP data. - -In this example, we show how the `Golang `_ network filter can be used with the Envoy proxy. We also show how the Go plugins can be independently configured at runtime. - -The example Go plugin adds a :literal:`hello, \ ` prefix to the requests received from its TCP connections. These modified requests are then proxied to an echo service that is retrieved from the configuration file. - -.. code-block:: yaml - :emphasize-lines: 4 - - plugin_config: - "@type": type.googleapis.com/xds.type.v3.TypedStruct - value: - echo_server_addr: echo_service - - -Step 1: Compile the go plugin library -************************************* - -Change to the ``examples/golang-network`` directory and build the go plugin library. - -.. code-block:: console - - $ pwd - envoy/examples/golang-network - $ docker compose -f docker-compose-go.yaml run --rm go_plugin_compile - -The compiled library should now be in the ``lib`` folder. - -.. code-block:: console - - $ ls lib - simple.so - -Step 2: Start all of our containers -*********************************** - -Start all the containers. - -.. code-block:: console - - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - NAME COMMAND SERVICE STATUS PORTS - golang-network-echo_service-1 "/tcp-echo" echo_service running - golang-network-proxy-1 "/docker-entrypoint.…" proxy running 0.0.0.0:10000->10000/tcp - -In this example, we start up two containers - an echo service which simply responds to what it has received from its TCP connections, and a proxy service that utilizes a Golang plugin to process and proxy data to the echo service. - -Step 3: Send some data to be handled by the Go plugin -***************************************************** - -The response from the ``nc`` command below should include the :literal:`hello, \ ` prefix which will be added by the example Go plugin. - -.. code-block:: console - - $ echo "world" | nc localhost 10000 2>&1 - < hello, world - -.. seealso:: - - :ref:`Envoy Go network filter ` - Further information about the Envoy Go network filter. - :ref:`Envoy Go HTTP filter ` - Further information about the Envoy Go HTTP filter. - :repo:`Go plugin API ` - Overview of Envoy's Go plugin APIs. diff --git a/examples/golang-network/simple/BUILD b/examples/golang-network/simple/BUILD deleted file mode 100644 index ac7b55fe0f5a..000000000000 --- a/examples/golang-network/simple/BUILD +++ /dev/null @@ -1,21 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary") - -licenses(["notice"]) # Apache 2 - -go_binary( - name = "simple.so", - srcs = [ - "filter.go", - ], - out = "simple.so", - cgo = True, - importpath = "github.com/envoyproxy/envoy/examples/golang-network/simple", - linkmode = "c-shared", - visibility = ["//visibility:public"], - deps = [ - "//contrib/golang/common/go/api", - "//contrib/golang/filters/network/source/go/pkg/network", - "@com_github_cncf_xds_go//xds/type/v3:type", - "@org_golang_google_protobuf//types/known/anypb", - ], -) diff --git a/examples/golang-network/simple/filter.go b/examples/golang-network/simple/filter.go deleted file mode 100644 index 615d3a0e6aae..000000000000 --- a/examples/golang-network/simple/filter.go +++ /dev/null @@ -1,132 +0,0 @@ -package main - -import ( - "fmt" - "net" - - xds "github.com/cncf/xds/go/xds/type/v3" - "google.golang.org/protobuf/types/known/anypb" - - "github.com/envoyproxy/envoy/contrib/golang/common/go/api" - "github.com/envoyproxy/envoy/contrib/golang/filters/network/source/go/pkg/network" -) - -func init() { - network.RegisterNetworkFilterConfigFactory("simple", cf) -} - -var cf = &configFactory{} - -type configFactory struct{} - -func (f *configFactory) CreateFactoryFromConfig(config interface{}) network.FilterFactory { - a := config.(*anypb.Any) - configStruct := &xds.TypedStruct{} - _ = a.UnmarshalTo(configStruct) - - v := configStruct.Value.AsMap()["echo_server_addr"] - addr, err := net.LookupHost(v.(string)) - if err != nil { - fmt.Printf("fail to resolve: %v, err: %v\n", v.(string), err) - return nil - } - upAddr := addr[0] + ":1025" - - return &filterFactory{ - upAddr: upAddr, - } -} - -type filterFactory struct { - upAddr string -} - -func (f *filterFactory) CreateFilter(cb api.ConnectionCallback) api.DownstreamFilter { - return &downFilter{ - upAddr: f.upAddr, - cb: cb, - } -} - -type downFilter struct { - api.EmptyDownstreamFilter - - cb api.ConnectionCallback - upAddr string - upFilter *upFilter -} - -func (f *downFilter) OnNewConnection() api.FilterStatus { - localAddr, _ := f.cb.StreamInfo().UpstreamLocalAddress() - remoteAddr, _ := f.cb.StreamInfo().UpstreamRemoteAddress() - fmt.Printf("OnNewConnection, local: %v, remote: %v, connect to: %v\n", localAddr, remoteAddr, f.upAddr) - f.upFilter = &upFilter{ - downFilter: f, - ch: make(chan []byte, 1), - } - network.CreateUpstreamConn(f.upAddr, f.upFilter) - return api.NetworkFilterContinue -} - -func (f *downFilter) OnData(buffer []byte, endOfStream bool) api.FilterStatus { - remoteAddr, _ := f.cb.StreamInfo().UpstreamRemoteAddress() - fmt.Printf("OnData, addr: %v, buffer: %v, endOfStream: %v\n", remoteAddr, string(buffer), endOfStream) - buffer = append([]byte("hello, "), buffer...) - f.upFilter.ch <- buffer - return api.NetworkFilterContinue -} - -func (f *downFilter) OnEvent(event api.ConnectionEvent) { - remoteAddr, _ := f.cb.StreamInfo().UpstreamRemoteAddress() - fmt.Printf("OnEvent, addr: %v, event: %v\n", remoteAddr, event) -} - -func (f *downFilter) OnWrite(buffer []byte, endOfStream bool) api.FilterStatus { - fmt.Printf("OnWrite, buffer: %v, endOfStream: %v\n", string(buffer), endOfStream) - return api.NetworkFilterContinue -} - -type upFilter struct { - api.EmptyUpstreamFilter - - cb api.ConnectionCallback - downFilter *downFilter - ch chan []byte -} - -func (f *upFilter) OnPoolReady(cb api.ConnectionCallback) { - f.cb = cb - f.cb.EnableHalfClose(false) - localAddr, _ := f.cb.StreamInfo().UpstreamLocalAddress() - remoteAddr, _ := f.cb.StreamInfo().UpstreamRemoteAddress() - fmt.Printf("OnPoolReady, local: %v, remote: %v\n", localAddr, remoteAddr) - go func() { - for { - buf, ok := <-f.ch - if !ok { - return - } - f.cb.Write(buf, false) - } - }() -} - -func (f *upFilter) OnPoolFailure(poolFailureReason api.PoolFailureReason, transportFailureReason string) { - fmt.Printf("OnPoolFailure, reason: %v, transportFailureReason: %v\n", poolFailureReason, transportFailureReason) -} - -func (f *upFilter) OnData(buffer []byte, endOfStream bool) { - remoteAddr, _ := f.cb.StreamInfo().UpstreamRemoteAddress() - fmt.Printf("OnData, addr: %v, buffer: %v, endOfStream: %v\n", remoteAddr, string(buffer), endOfStream) - f.downFilter.cb.Write(buffer, endOfStream) -} - -func (f *upFilter) OnEvent(event api.ConnectionEvent) { - remoteAddr, _ := f.cb.StreamInfo().UpstreamRemoteAddress() - fmt.Printf("OnEvent, addr: %v, event: %v\n", remoteAddr, event) - if event == api.LocalClose || event == api.RemoteClose { - close(f.ch) - } -} - -func main() {} diff --git a/examples/golang-network/simple/go.mod b/examples/golang-network/simple/go.mod deleted file mode 100644 index 385db640eeaa..000000000000 --- a/examples/golang-network/simple/go.mod +++ /dev/null @@ -1,21 +0,0 @@ -module github.com/envoyproxy/envoy/examples/golang-network/simple - -// the version should >= 1.18 -go 1.18 - -// NOTICE: these lines could be generated automatically by "go mod tidy" -require ( - github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa - github.com/envoyproxy/envoy v1.24.0 - google.golang.org/protobuf v1.34.2 -) - -require ( - github.com/envoyproxy/protoc-gen-validate v1.0.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect -) - -// TODO: remove when #26173 lands. -replace github.com/envoyproxy/envoy => ../../.. diff --git a/examples/golang-network/simple/go.sum b/examples/golang-network/simple/go.sum deleted file mode 100644 index 93cdbfb59e6c..000000000000 --- a/examples/golang-network/simple/go.sum +++ /dev/null @@ -1,18 +0,0 @@ -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= -github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= -github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM= -google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= diff --git a/examples/golang-network/verify.sh b/examples/golang-network/verify.sh deleted file mode 100755 index 276d6a7b16e6..000000000000 --- a/examples/golang-network/verify.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -e - -export NAME=golang-network -export UID -export MANUAL=true -export PORT_PROXY="${GOLANG_NETWORK_PORT_PROXY:-10720}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Compile the go plugin library" -"${DOCKER_COMPOSE[@]}" -f docker-compose-go.yaml up --quiet-pull --remove-orphans go_plugin_compile - -run_log "Start all of our containers" -bring_up_example - -run_log "Send tcp data handled by the Go plugin" -echo -n "world" | nc -w1 127.0.0.1 "${PORT_PROXY}" - -run_log "Check echo server log" -"${DOCKER_COMPOSE[@]}" logs echo_service | grep "hello, world" diff --git a/examples/grpc-bridge/.gitignore b/examples/grpc-bridge/.gitignore deleted file mode 100644 index 0e57261fc9a9..000000000000 --- a/examples/grpc-bridge/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.idea -server/kv/kv.pb.go -client/kv/kv_pb2.py diff --git a/examples/grpc-bridge/Dockerfile-grpc-go b/examples/grpc-bridge/Dockerfile-grpc-go deleted file mode 100644 index 8b74dbaddba4..000000000000 --- a/examples/grpc-bridge/Dockerfile-grpc-go +++ /dev/null @@ -1 +0,0 @@ -FROM grpc/go@sha256:0d3bb1fbfab306680ebaf751992bd2db2a0322106e4b389e85028a027242c2bc diff --git a/examples/grpc-bridge/Dockerfile-grpc-python b/examples/grpc-bridge/Dockerfile-grpc-python deleted file mode 100644 index 800c00915b81..000000000000 --- a/examples/grpc-bridge/Dockerfile-grpc-python +++ /dev/null @@ -1 +0,0 @@ -FROM grpc/python@sha256:6f0898ac290991eab0c262fa18453904f75286f17978d3185164d01400dc042e diff --git a/examples/grpc-bridge/README.md b/examples/grpc-bridge/README.md deleted file mode 100644 index e3d746fa0264..000000000000 --- a/examples/grpc-bridge/README.md +++ /dev/null @@ -1,131 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/grpc_bridge) - -# gRPC HTTP/1.1 to HTTP/2 bridge - -This is an example of a key-value store where a client CLI, written in Python, updates a remote store, written in Go, using the stubs generated for both languages. - -Running clients that uses gRPC stubs and sends messages through a proxy -that upgrades the HTTP requests from http/1.1 to http/2. This is a more detailed -implementation of the Envoy documentation at https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/grpc_bridge - -* Client: talks in python and sends HTTP/1.1 requests (gRPC stubs) - * Client-Proxy: Envoy setup that acts as an egress and converts the HTTP/1.1 call to HTTP/2. -* Server: talks in golang and receives HTTP/2 requests (gRPC stubs) - * Server-Proxy: Envoy setup that acts as an ingress and receives the HTTP/2 calls - -`[client](http/1.1) -> [client-egress-proxy](http/2) -> [server-ingress-proxy](http/2) -> [server]` - -# Running in 3 Steps - -* Generate Stubs: both the `client` and `server` stubs in `python` and `go` respectively to be used by each server. -* Start Both Client and Server Servers and Proxies: ` -* Use the Client CLI to make calls to the kv server. - -## Generate Stubs - -* Uses the `protos` dir and generates the stubs for both `client` and `server` -* Inspect the file `docker-compose-protos.yaml` with the gRPC protoc commands to generate the stubs. - -```console -$ docker-compose -f docker-compose-protos.yaml up --remove-orphans -Starting grpc-bridge_stubs_python_1 ... done -Starting grpc-bridge_stubs_go_1 ... done -Attaching to grpc-bridge_stubs_go_1, grpc-bridge_stubs_python_1 -grpc-bridge_stubs_go_1 exited with code 0 -grpc-bridge_stubs_python_1 exited with code 0 -``` - -* The files created were the `kv` modules for both the client and server respective dir. - * Note that both stubs are their respective languages. - * For each language, use its ways to include the stubs as an external module. - -```console -$ ls -la client/kv/kv_pb2.py --rw-r--r-- 1 mdesales CORP\Domain Users 9527 Nov 6 21:59 client/kv/kv_pb2.py - -$ ls -la server/kv/kv.pb.go --rw-r--r-- 1 mdesales CORP\Domain Users 9994 Nov 6 21:59 server/kv/kv.pb.go -``` - -## Start Both Client and Server and Proxies - -* After the stubs are in place, start the containers described in `docker-compose.yaml`. - -```console -$ docker-compose up --build -``` - -* Inspect the files `client/envoy-proxy.yaml` and `server/envoy-proxy.yaml`, as they define configs for their respective container, comparing port numbers and other specific settings. - -Notice that you will be interacting with the client container, which hosts -the client python CLI. The port numbers for the proxies and the containers are displayed -by the `docker-compose ps`, so it's easier to compare with the `\*/envoy-proxy.yaml` config files for each -of the containers how they match. - -Note that the client container to use is `grpc-bridge_grpc-client_1` and binds to no port -as it will use the `python` CLI. - -```console -$ docker-compose ps - Name Command State Ports ------------------------------------------------------------------------------------------------------------------------------------- -grpc-bridge_grpc-client-proxy_1 /docker-entrypoint.sh /usr ... Up 10000/tcp, 0.0.0.0:9911->9911/tcp, 0.0.0.0:9991->9991/tcp -grpc-bridge_grpc-client_1 /bin/sh -c tail -f /dev/null Up -grpc-bridge_grpc-server-proxy_1 /docker-entrypoint.sh /usr ... Up 10000/tcp, 0.0.0.0:8811->8811/tcp, 0.0.0.0:8881->8881/tcp -grpc-bridge_grpc-server_1 /bin/sh -c /bin/server Up 0.0.0.0:8081->8081/tcp -``` - -## Use the Client CLI - -* Since the containers are running, you can use the client container to interact with the gRPC server through the proxies -* The client has the methods `set key value` and `get key` to use the in-memory key-value store. - -```console -$ docker-compose exec grpc-client /client/grpc-kv-client.py set foo bar -setf foo to bar -``` - -> NOTE: You could also run docker instead of docker-compose `docker exec -ti grpc-bridge_grpc-client_1 /client/grpc-kv-client.py set foo bar` - -* The server will display the gRPC call received by the server, and then the access logs from the proxy for the SET method. - * Note that the proxy is propagating the headers of the request - -```console -grpc-server_1 | 2019/11/07 16:33:58 set: foo = bar -grpc-server-proxy_1 | [2019-11-07T16:33:58.856Z] "POST /kv.KV/Set HTTP/1.1" 200 - 15 7 3 1 "172.24.0.3" "python-requests/2.22.0" "c11cf735-0647-4e67-965c-5b1e362a5532" "grpc" "172.24.0.2:8081" -grpc-client-proxy_1 | [2019-11-07T16:33:58.855Z] "POST /kv.KV/Set HTTP/1.1" 200 - 15 7 5 3 "172.24.0.3" "python-requests/2.22.0" "c11cf735-0647-4e67-965c-5b1e362a5532" "grpc" "172.24.0.5:8811" -``` - -* Getting the value is no different - -```console -$ docker-compose exec grpc-client /client/grpc-kv-client.py get foo -bar -``` - -> NOTE: You could also run docker instead of docker-compose `docker exec -ti grpc-bridge_grpc-client_1 /client/grpc-kv-client.py get foo` - -* The logs in the server will show the same for the GET method. - * Note that again the request ID is proxied through - -```console -grpc-server_1 | 2019/11/07 16:34:50 get: foo -grpc-server-proxy_1 | [2019-11-07T16:34:50.456Z] "POST /kv.KV/Get HTTP/1.1" 200 - 10 10 2 1 "172.24.0.3" "python-requests/2.22.0" "727d4dcd-a276-4bb2-b4cc-494ae7119c24" "grpc" "172.24.0.2:8081" -grpc-client-proxy_1 | [2019-11-07T16:34:50.455Z] "POST /kv.KV/Get HTTP/1.1" 200 - 10 10 3 2 "172.24.0.3" "python-requests/2.22.0" "727d4dcd-a276-4bb2-b4cc-494ae7119c24" "grpc" "172.24.0.5:8811" -``` - -# Troubleshooting - -* Errors building the `client` or `server` are related to the missing gRPC stubs. -* Make sure to produce the stubs before building - * The error below is when the server is missing the stubs in the kv dir. - -```console -$ go build -o server -go: finding github.com/envoyproxy/envoy/examples/grpc-bridge latest -go: finding github.com/envoyproxy/envoy/examples latest -go: finding github.com/envoyproxy/envoy/examples/grpc-bridge/server/kv latest -go: finding github.com/envoyproxy/envoy/examples/grpc-bridge/server latest -build github.com/envoyproxy/envoy: cannot load github.com/envoyproxy/envoy/examples/grpc-bridge/server/kv: no matching versions for query "latest" -``` diff --git a/examples/grpc-bridge/client/client.py b/examples/grpc-bridge/client/client.py deleted file mode 100755 index e98607cbcfca..000000000000 --- a/examples/grpc-bridge/client/client.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python - -import requests, sys -import os - -# Stubs generated by protoc -from kv import kv_pb2 as kv - -from struct import pack - -HOST = os.getenv('CLIENT_PROXY', "http://localhost:9001") -HEADERS = {'content-type': 'application/grpc', 'Host': 'grpc'} -USAGE = """ -grpc-client usage [{host}]: - ./client.py set - sets the and - ./client.py get - gets the value for - - Set env var CLIENT_PROXY to change to a different host - """.format(host=HOST) - - -class KVClient: - - def get(self, key): - r = kv.GetRequest(key=key) - - # Build the gRPC frame - data = r.SerializeToString() - data = pack('!cI', b'\0', len(data)) + data - - resp = requests.post(HOST + "/kv.KV/Get", data=data, headers=HEADERS) - - return kv.GetResponse().FromString(resp.content[5:]) - - def set(self, key, value): - r = kv.SetRequest(key=key, value=value) - data = r.SerializeToString() - data = pack('!cI', b'\0', len(data)) + data - - return requests.post(HOST + "/kv.KV/Set", data=data, headers=HEADERS) - - -def main(): - if len(sys.argv) == 1: - print(USAGE) - - sys.exit(0) - - cmd = sys.argv[1] - - client = KVClient() - - if cmd == "get": - # ensure a key was provided - if len(sys.argv) != 3: - print(USAGE) - sys.exit(1) - - # get the key to fetch - key = sys.argv[2] - - # send the request to the server - response = client.get(key) - - print(response.value) - sys.exit(0) - - elif cmd == "set": - # ensure a key and value were provided - if len(sys.argv) < 4: - print(USAGE) - sys.exit(1) - - # get the key and the full text of value - key = sys.argv[2] - value = " ".join(sys.argv[3:]) - - # send the request to the server - response = client.set(key, value) - - print("setf %s to %s" % (key, value)) - - -if __name__ == '__main__': - main() diff --git a/examples/grpc-bridge/client/envoy-proxy.yaml b/examples/grpc-bridge/client/envoy-proxy.yaml deleted file mode 100644 index ff017b671c76..000000000000 --- a/examples/grpc-bridge/client/envoy-proxy.yaml +++ /dev/null @@ -1,58 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 9911 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - add_user_agent: true - access_log: - - name: envoy.access_loggers.stdout - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog - stat_prefix: egress_http - common_http_protocol_options: - idle_timeout: 0.840s - use_remote_address: true - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - grpc - routes: - - match: - prefix: "/" - route: - cluster: backend-proxy - http_filters: - - name: envoy.filters.http.grpc_http1_bridge - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_http1_bridge.v3.Config - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: backend-proxy - type: LOGICAL_DNS - dns_lookup_family: V4_ONLY - lb_policy: ROUND_ROBIN - typed_extension_protocol_options: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicit_http_config: - http_protocol_options: {} - load_assignment: - cluster_name: backend-proxy - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: grpc-server-proxy - port_value: 8811 diff --git a/examples/grpc-bridge/client/kv/__init__.py b/examples/grpc-bridge/client/kv/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/examples/grpc-bridge/client/requirements.in b/examples/grpc-bridge/client/requirements.in deleted file mode 100644 index 96b06d428c7e..000000000000 --- a/examples/grpc-bridge/client/requirements.in +++ /dev/null @@ -1,4 +0,0 @@ -requests>=2.22.0 -grpcio -grpcio-tools -protobuf>=3.18.0 diff --git a/examples/grpc-bridge/client/requirements.txt b/examples/grpc-bridge/client/requirements.txt deleted file mode 100644 index 620edcc4e603..000000000000 --- a/examples/grpc-bridge/client/requirements.txt +++ /dev/null @@ -1,233 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile --allow-unsafe --generate-hashes requirements.in -# -certifi==2024.7.4 \ - --hash=sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b \ - --hash=sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90 - # via requests -charset-normalizer==3.3.0 \ - --hash=sha256:02673e456dc5ab13659f85196c534dc596d4ef260e4d86e856c3b2773ce09843 \ - --hash=sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786 \ - --hash=sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e \ - --hash=sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8 \ - --hash=sha256:09c77f964f351a7369cc343911e0df63e762e42bac24cd7d18525961c81754f4 \ - --hash=sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa \ - --hash=sha256:1063da2c85b95f2d1a430f1c33b55c9c17ffaf5e612e10aeaad641c55a9e2b9d \ - --hash=sha256:12ebea541c44fdc88ccb794a13fe861cc5e35d64ed689513a5c03d05b53b7c82 \ - --hash=sha256:153e7b6e724761741e0974fc4dcd406d35ba70b92bfe3fedcb497226c93b9da7 \ - --hash=sha256:15b26ddf78d57f1d143bdf32e820fd8935d36abe8a25eb9ec0b5a71c82eb3895 \ - --hash=sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d \ - --hash=sha256:187d18082694a29005ba2944c882344b6748d5be69e3a89bf3cc9d878e548d5a \ - --hash=sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382 \ - --hash=sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678 \ - --hash=sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b \ - --hash=sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e \ - --hash=sha256:249c6470a2b60935bafd1d1d13cd613f8cd8388d53461c67397ee6a0f5dce741 \ - --hash=sha256:24a91a981f185721542a0b7c92e9054b7ab4fea0508a795846bc5b0abf8118d4 \ - --hash=sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596 \ - --hash=sha256:250c9eb0f4600361dd80d46112213dff2286231d92d3e52af1e5a6083d10cad9 \ - --hash=sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69 \ - --hash=sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c \ - --hash=sha256:2f4a0033ce9a76e391542c182f0d48d084855b5fcba5010f707c8e8c34663d77 \ - --hash=sha256:30a85aed0b864ac88309b7d94be09f6046c834ef60762a8833b660139cfbad13 \ - --hash=sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459 \ - --hash=sha256:3ae38d325b512f63f8da31f826e6cb6c367336f95e418137286ba362925c877e \ - --hash=sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7 \ - --hash=sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908 \ - --hash=sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a \ - --hash=sha256:468d2a840567b13a590e67dd276c570f8de00ed767ecc611994c301d0f8c014f \ - --hash=sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8 \ - --hash=sha256:542da1178c1c6af8873e143910e2269add130a299c9106eef2594e15dae5e482 \ - --hash=sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d \ - --hash=sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d \ - --hash=sha256:588245972aca710b5b68802c8cad9edaa98589b1b42ad2b53accd6910dad3545 \ - --hash=sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34 \ - --hash=sha256:619d1c96099be5823db34fe89e2582b336b5b074a7f47f819d6b3a57ff7bdb86 \ - --hash=sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6 \ - --hash=sha256:67b8cc9574bb518ec76dc8e705d4c39ae78bb96237cb533edac149352c1f39fe \ - --hash=sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e \ - --hash=sha256:70f1d09c0d7748b73290b29219e854b3207aea922f839437870d8cc2168e31cc \ - --hash=sha256:750b446b2ffce1739e8578576092179160f6d26bd5e23eb1789c4d64d5af7dc7 \ - --hash=sha256:7966951325782121e67c81299a031f4c115615e68046f79b85856b86ebffc4cd \ - --hash=sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c \ - --hash=sha256:7f5d10bae5d78e4551b7be7a9b29643a95aded9d0f602aa2ba584f0388e7a557 \ - --hash=sha256:805dfea4ca10411a5296bcc75638017215a93ffb584c9e344731eef0dcfb026a \ - --hash=sha256:81bf654678e575403736b85ba3a7867e31c2c30a69bc57fe88e3ace52fb17b89 \ - --hash=sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078 \ - --hash=sha256:85a32721ddde63c9df9ebb0d2045b9691d9750cb139c161c80e500d210f5e26e \ - --hash=sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4 \ - --hash=sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403 \ - --hash=sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0 \ - --hash=sha256:93aa7eef6ee71c629b51ef873991d6911b906d7312c6e8e99790c0f33c576f89 \ - --hash=sha256:96c2b49eb6a72c0e4991d62406e365d87067ca14c1a729a870d22354e6f68115 \ - --hash=sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9 \ - --hash=sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05 \ - --hash=sha256:a0ac5e7015a5920cfce654c06618ec40c33e12801711da6b4258af59a8eff00a \ - --hash=sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec \ - --hash=sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56 \ - --hash=sha256:aae32c93e0f64469f74ccc730a7cb21c7610af3a775157e50bbd38f816536b38 \ - --hash=sha256:aaf7b34c5bc56b38c931a54f7952f1ff0ae77a2e82496583b247f7c969eb1479 \ - --hash=sha256:abecce40dfebbfa6abf8e324e1860092eeca6f7375c8c4e655a8afb61af58f2c \ - --hash=sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e \ - --hash=sha256:ac71b2977fb90c35d41c9453116e283fac47bb9096ad917b8819ca8b943abecd \ - --hash=sha256:ada214c6fa40f8d800e575de6b91a40d0548139e5dc457d2ebb61470abf50186 \ - --hash=sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455 \ - --hash=sha256:b1121de0e9d6e6ca08289583d7491e7fcb18a439305b34a30b20d8215922d43c \ - --hash=sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65 \ - --hash=sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78 \ - --hash=sha256:b5bcf60a228acae568e9911f410f9d9e0d43197d030ae5799e20dca8df588287 \ - --hash=sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df \ - --hash=sha256:c2af80fb58f0f24b3f3adcb9148e6203fa67dd3f61c4af146ecad033024dde43 \ - --hash=sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1 \ - --hash=sha256:c5a74c359b2d47d26cdbbc7845e9662d6b08a1e915eb015d044729e92e7050b7 \ - --hash=sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989 \ - --hash=sha256:d47ecf253780c90ee181d4d871cd655a789da937454045b17b5798da9393901a \ - --hash=sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63 \ - --hash=sha256:d97d85fa63f315a8bdaba2af9a6a686e0eceab77b3089af45133252618e70884 \ - --hash=sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649 \ - --hash=sha256:dc45229747b67ffc441b3de2f3ae5e62877a282ea828a5bdb67883c4ee4a8810 \ - --hash=sha256:e0fc42822278451bc13a2e8626cf2218ba570f27856b536e00cfa53099724828 \ - --hash=sha256:e39c7eb31e3f5b1f88caff88bcff1b7f8334975b46f6ac6e9fc725d829bc35d4 \ - --hash=sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2 \ - --hash=sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd \ - --hash=sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5 \ - --hash=sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe \ - --hash=sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293 \ - --hash=sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e \ - --hash=sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e \ - --hash=sha256:fc52b79d83a3fe3a360902d3f5d79073a993597d48114c29485e9431092905d8 - # via requests -grpcio==1.65.1 \ - --hash=sha256:12e9bdf3b5fd48e5fbe5b3da382ad8f97c08b47969f3cca81dd9b36b86ed39e2 \ - --hash=sha256:1bceeec568372cbebf554eae1b436b06c2ff24cfaf04afade729fb9035408c6c \ - --hash=sha256:1faaf7355ceed07ceaef0b9dcefa4c98daf1dd8840ed75c2de128c3f4a4d859d \ - --hash=sha256:1fbd6331f18c3acd7e09d17fd840c096f56eaf0ef830fbd50af45ae9dc8dfd83 \ - --hash=sha256:27adee2338d697e71143ed147fe286c05810965d5d30ec14dd09c22479bfe48a \ - --hash=sha256:2ca684ba331fb249d8a1ce88db5394e70dbcd96e58d8c4b7e0d7b141a453dce9 \ - --hash=sha256:2f56b5a68fdcf17a0a1d524bf177218c3c69b3947cb239ea222c6f1867c3ab68 \ - --hash=sha256:3019fb50128b21a5e018d89569ffaaaa361680e1346c2f261bb84a91082eb3d3 \ - --hash=sha256:34966cf526ef0ea616e008d40d989463e3db157abb213b2f20c6ce0ae7928875 \ - --hash=sha256:3c492301988cd720cd145d84e17318d45af342e29ef93141228f9cd73222368b \ - --hash=sha256:3dc5f928815b8972fb83b78d8db5039559f39e004ec93ebac316403fe031a062 \ - --hash=sha256:4effc0562b6c65d4add6a873ca132e46ba5e5a46f07c93502c37a9ae7f043857 \ - --hash=sha256:54cb822e177374b318b233e54b6856c692c24cdbd5a3ba5335f18a47396bac8f \ - --hash=sha256:557de35bdfbe8bafea0a003dbd0f4da6d89223ac6c4c7549d78e20f92ead95d9 \ - --hash=sha256:5f096ffb881f37e8d4f958b63c74bfc400c7cebd7a944b027357cd2fb8d91a57 \ - --hash=sha256:5fd7337a823b890215f07d429f4f193d24b80d62a5485cf88ee06648591a0c57 \ - --hash=sha256:60f1f38eed830488ad2a1b11579ef0f345ff16fffdad1d24d9fbc97ba31804ff \ - --hash=sha256:6e71aed8835f8d9fbcb84babc93a9da95955d1685021cceb7089f4f1e717d719 \ - --hash=sha256:71a05fd814700dd9cb7d9a507f2f6a1ef85866733ccaf557eedacec32d65e4c2 \ - --hash=sha256:76e81a86424d6ca1ce7c16b15bdd6a964a42b40544bf796a48da241fdaf61153 \ - --hash=sha256:7ae15275ed98ea267f64ee9ddedf8ecd5306a5b5bb87972a48bfe24af24153e8 \ - --hash=sha256:7af64838b6e615fff0ec711960ed9b6ee83086edfa8c32670eafb736f169d719 \ - --hash=sha256:8333ca46053c35484c9f2f7e8d8ec98c1383a8675a449163cea31a2076d93de8 \ - --hash=sha256:8558f0083ddaf5de64a59c790bffd7568e353914c0c551eae2955f54ee4b857f \ - --hash=sha256:8bfd95ef3b097f0cc86ade54eafefa1c8ed623aa01a26fbbdcd1a3650494dd11 \ - --hash=sha256:8d8143a3e3966f85dce6c5cc45387ec36552174ba5712c5dc6fcc0898fb324c0 \ - --hash=sha256:941596d419b9736ab548aa0feb5bbba922f98872668847bf0720b42d1d227b9e \ - --hash=sha256:941c4869aa229d88706b78187d60d66aca77fe5c32518b79e3c3e03fc26109a2 \ - --hash=sha256:9a1c84560b3b2d34695c9ba53ab0264e2802721c530678a8f0a227951f453462 \ - --hash=sha256:9e6a8f3d6c41e6b642870afe6cafbaf7b61c57317f9ec66d0efdaf19db992b90 \ - --hash=sha256:a6c71575a2fedf259724981fd73a18906513d2f306169c46262a5bae956e6364 \ - --hash=sha256:a8422dc13ad93ec8caa2612b5032a2b9cd6421c13ed87f54db4a3a2c93afaf77 \ - --hash=sha256:aaf3c54419a28d45bd1681372029f40e5bfb58e5265e3882eaf21e4a5f81a119 \ - --hash=sha256:b12c1aa7b95abe73b3e04e052c8b362655b41c7798da69f1eaf8d186c7d204df \ - --hash=sha256:b590f1ad056294dfaeac0b7e1b71d3d5ace638d8dd1f1147ce4bd13458783ba8 \ - --hash=sha256:bbb46330cc643ecf10bd9bd4ca8e7419a14b6b9dedd05f671c90fb2c813c6037 \ - --hash=sha256:ca931de5dd6d9eb94ff19a2c9434b23923bce6f767179fef04dfa991f282eaad \ - --hash=sha256:cb5175f45c980ff418998723ea1b3869cce3766d2ab4e4916fbd3cedbc9d0ed3 \ - --hash=sha256:d827a6fb9215b961eb73459ad7977edb9e748b23e3407d21c845d1d8ef6597e5 \ - --hash=sha256:dbb64b4166362d9326f7efbf75b1c72106c1aa87f13a8c8b56a1224fac152f5c \ - --hash=sha256:de5b6be29116e094c5ef9d9e4252e7eb143e3d5f6bd6d50a78075553ab4930b0 \ - --hash=sha256:e4a3cdba62b2d6aeae6027ae65f350de6dc082b72e6215eccf82628e79efe9ba \ - --hash=sha256:e75acfa52daf5ea0712e8aa82f0003bba964de7ae22c26d208cbd7bc08500177 \ - --hash=sha256:f40cebe5edb518d78b8131e87cb83b3ee688984de38a232024b9b44e74ee53d3 \ - --hash=sha256:f62652ddcadc75d0e7aa629e96bb61658f85a993e748333715b4ab667192e4e8 \ - --hash=sha256:ff5a84907e51924973aa05ed8759210d8cdae7ffcf9e44fd17646cf4a902df59 - # via - # -r requirements.in - # grpcio-tools -grpcio-tools==1.65.1 \ - --hash=sha256:004232fa8ef82298eeb01b391d708b3a89317910e2f7c623b566aea0448c8dcc \ - --hash=sha256:013017df92d6165e1556a17c618cf22471ef131fb614b428683730968b54b46d \ - --hash=sha256:074fce3b96a5c59ed526bdd07c04c6243c07b13278388837a0540840ae10bf5b \ - --hash=sha256:16f2f49048c76a68a8171507c39652c8be9ed4e7408deb9877002813aea4c396 \ - --hash=sha256:196e12c18f0ebe5ac7f5446fc1daef8d9c69ba40a987a1f8379bfdf6c32e54af \ - --hash=sha256:1ab64a9af7ce0aeb639a77423fa99de91863a0b8ce0e43fc50f57fc460a0d30e \ - --hash=sha256:1d8671d82449206ef040756a14484b0c5189615a0aac5f4734ad3d023d07d4b1 \ - --hash=sha256:1ece34ebb677a869606200812653f274757844754f0b684e59d61244b194f002 \ - --hash=sha256:24cffe8bc90fb8237f0bcf240bd6c70304255fe27b69db32601499a043f871be \ - --hash=sha256:257decc1782b9adca422a2625663529be64018c056d7346d8bbc7c9bf0fe3b80 \ - --hash=sha256:2ec7e376f3f53e7ab90614d5a2404c19c7902750bcc5bed8219ab864f9bc1c4b \ - --hash=sha256:3135888461888dcc7b358c17d60f32654cb36daf02bb805c84c0f8ab550743eb \ - --hash=sha256:32fa16e64f4b1684ed634155af9b03fdeabdf641d484e53c453e592e0f574f03 \ - --hash=sha256:33e4c602221f91c1d87c4574c496621f11826d4f8867f31f4c4c2ff1b144a777 \ - --hash=sha256:4887af67ff130174fa7fb420ee985d38659a7c960053639de28980003fe710eb \ - --hash=sha256:4b0714458a6a3a1ed587271f3e7c301b735ccbdd7946071a1d85a6d0aabcb57a \ - --hash=sha256:4dde0d90f96e29670c58a08aeeac61da49792f71602cb7421943be8918857a2a \ - --hash=sha256:5c9b4d95d2623b8b9435103305c3d375f8b4a266ee6fbbf29b5f4a57a8405047 \ - --hash=sha256:68d14cbd135541366bbef18c1d463f5d560878629f1901cae03777dad87755d9 \ - --hash=sha256:6f75bf562057723818dff7bf4e05884c220653ead3db19effe5873ce88c7cfd2 \ - --hash=sha256:7813a67cb427847e1a88d4fd4cbabfd2ed272455bd78b4f417377361d3b8edbd \ - --hash=sha256:881ccc523a171235bb6b1d8e965c2f11e525b54eb1d66aeb8fea5a72f84d6e02 \ - --hash=sha256:8d5a12e0bd2a0f33af11e11d89f19cddea66568716b53b77f3f5dc605ceb32e0 \ - --hash=sha256:9b6dbddca8e399ad96d263b786d0803acc67194cb80d01117691a9f239ac8dc9 \ - --hash=sha256:9cc6f342b8e8a2aa801d2d1640290a47563d8bb1a802671191dc3fc218747da3 \ - --hash=sha256:a89203d864dd024c4a13032f2df792eb465c63c224f9b82460d53f0cf30a3d16 \ - --hash=sha256:a95fd13dc17b065a934f00a0b99078de7773d4743772312efc8e75521ab62f7b \ - --hash=sha256:ac8cc2684bcde43296cf5a350b80b73713610f0789ff912c88f898ef065a0b6c \ - --hash=sha256:b6e45377dbe50c7a737d81620841b8c3f3a1650c76cb56a87b5b0414d10f9987 \ - --hash=sha256:b8ef108fceabb12ed29f750f2cb4827d7bad5033dc13596ad0de092f015f5123 \ - --hash=sha256:b8fe0bd8e63a4dd84c022ccbb6057e9f3c338e036a1b95c2a6dbcc928c35b4f9 \ - --hash=sha256:bc55edf7a0af0ad7384887845b6498fdb1a75d3633d11807f953cac531a34588 \ - --hash=sha256:bfb1a5e429756cdc9ce7183cca24a90bd7e68626379c83ea065bb30125d7aca4 \ - --hash=sha256:c394cf5b77eb71ff5c0ab857877f59dfee080cc95fb24d47e97d3965aaaf3c64 \ - --hash=sha256:c50895d383d41a379f9a235ce6d14c6639f36d43bf71c7148bf8a114a8f0936a \ - --hash=sha256:d4afb3e74c7a567eabda3c447421eb8fb5c6cbf19bb9292319056beff4ab49a1 \ - --hash=sha256:d99945dc53daa7987ae8c33227f96697ccc4d0a4a1ca6c366e28fcc9fc1c55fb \ - --hash=sha256:dc904f0de72eecbd024c111caa3e3165522349ff3c89361e4cbf06035c93061a \ - --hash=sha256:e24819f8d11fc9e6bad1e13a1d7fddd6027ed2a1aad583f093cfe027852ff3f9 \ - --hash=sha256:e44c69c029614fc61da2701587299fe19e52031aa1fba2a69a02c2dd77f903fe \ - --hash=sha256:e859001e20d4199ac90979e11d0d0ecb83f6b0235b08f8bfae93c2bd1401795a \ - --hash=sha256:edb4731b4ad068c3c48d52bbfa1404236cbcdd2524eb01a655e8adfadc2f0034 \ - --hash=sha256:ee1353c741f8f2fcf4fcce8764d4570e2d7c3025cc4c918a0c6532c18b6cbac5 \ - --hash=sha256:f49acf17ae7b1a35b5e0e5907ed9b70c042b3e7ab8769ea9fd26f20b2b888743 \ - --hash=sha256:fabbc0698cf0c614059c3e103b06c74d07190e9c7518f457703e98617ed467c0 \ - --hash=sha256:fd2fe61d40e7421dc64c90912e29c05f1c419dd7a90452c84a1b456e06bd8530 - # via -r requirements.in -idna==3.7 \ - --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \ - --hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0 - # via requests -protobuf==5.27.2 \ - --hash=sha256:0e341109c609749d501986b835f667c6e1e24531096cff9d34ae411595e26505 \ - --hash=sha256:176c12b1f1c880bf7a76d9f7c75822b6a2bc3db2d28baa4d300e8ce4cde7409b \ - --hash=sha256:354d84fac2b0d76062e9b3221f4abbbacdfd2a4d8af36bab0474f3a0bb30ab38 \ - --hash=sha256:4fadd8d83e1992eed0248bc50a4a6361dc31bcccc84388c54c86e530b7f58863 \ - --hash=sha256:54330f07e4949d09614707c48b06d1a22f8ffb5763c159efd5c0928326a91470 \ - --hash=sha256:610e700f02469c4a997e58e328cac6f305f649826853813177e6290416e846c6 \ - --hash=sha256:7fc3add9e6003e026da5fc9e59b131b8f22b428b991ccd53e2af8071687b4fce \ - --hash=sha256:9e8f199bf7f97bd7ecebffcae45ebf9527603549b2b562df0fbc6d4d688f14ca \ - --hash=sha256:a109916aaac42bff84702fb5187f3edadbc7c97fc2c99c5ff81dd15dcce0d1e5 \ - --hash=sha256:b848dbe1d57ed7c191dfc4ea64b8b004a3f9ece4bf4d0d80a367b76df20bf36e \ - --hash=sha256:f3ecdef226b9af856075f28227ff2c90ce3a594d092c39bee5513573f25e2714 - # via - # -r requirements.in - # grpcio-tools -requests==2.32.3 \ - --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \ - --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6 - # via -r requirements.in -urllib3==2.2.2 \ - --hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \ - --hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168 - # via requests - -# The following packages are considered to be unsafe in a requirements file: -setuptools==70.0.0 \ - --hash=sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4 \ - --hash=sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0 - # via grpcio-tools diff --git a/examples/grpc-bridge/docker-compose-protos.yaml b/examples/grpc-bridge/docker-compose-protos.yaml deleted file mode 100644 index 77dae2cb7e36..000000000000 --- a/examples/grpc-bridge/docker-compose-protos.yaml +++ /dev/null @@ -1,22 +0,0 @@ -services: - - # $ docker run -ti -v $(pwd):/protos -v $(pwd)/stubs:/stubs grpc/go protoc --go_out=plugins=grpc:/stubs -I/protos /protos/kv.proto - stubs_go: - build: - context: . - dockerfile: Dockerfile-grpc-go - command: protoc --go_out=plugins=grpc:/stubs -I/protos /protos/kv.proto - volumes: - - ./protos:/protos - - ./server/kv:/stubs - - # $ docker run -ti -v $(pwd):/protos -v $(pwd)/stubs:/stubs grpc/python python -m grpc.tools.protoc \ - # --python_out=/stubs --grpc_python_out=/stubs -I/protos /protos/kv.proto - stubs_python: - build: - context: . - dockerfile: Dockerfile-grpc-python - command: python -m grpc.tools.protoc --python_out=/stubs --grpc_python_out=/stubs -I/protos /protos/kv.proto - volumes: - - ./protos:/protos - - ./client/kv:/stubs diff --git a/examples/grpc-bridge/docker-compose.yaml b/examples/grpc-bridge/docker-compose.yaml deleted file mode 100644 index 75f27fdee359..000000000000 --- a/examples/grpc-bridge/docker-compose.yaml +++ /dev/null @@ -1,34 +0,0 @@ -services: - - # Requires the build of the stubs first - grpc-server: - build: - context: server - dockerfile: ../../shared/golang/Dockerfile - target: golang-grpc-server - - grpc-server-proxy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: ./server/envoy-proxy.yaml - - # Requires the build of the stubs first - grpc-client: - build: - context: client - dockerfile: ../../shared/python/Dockerfile - target: python-grpc-client - args: - PYTHON_REQUIREMENTS_FILE: requirements.txt - environment: - CLIENT_PROXY: http://grpc-client-proxy:9911 - PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION: python - - grpc-client-proxy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: ./client/envoy-proxy.yaml diff --git a/examples/grpc-bridge/example.rst b/examples/grpc-bridge/example.rst deleted file mode 100644 index 54d2e7211173..000000000000 --- a/examples/grpc-bridge/example.rst +++ /dev/null @@ -1,139 +0,0 @@ -.. _install_sandboxes_grpc_bridge: - -gRPC bridge -=========== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - -The gRPC bridge sandbox is an example usage of Envoy's -:ref:`gRPC bridge filter `. - -This is an example of a key-value store where an ``http``-based client CLI, written in ``Python``, -updates a remote store, written in ``Go``, using the stubs generated for both languages. - -The client send messages through a proxy that upgrades the HTTP requests from ``http/1.1`` to ``http/2`` or -``http/3`` - -``[client](http/1.1) -> [client-egress-proxy](http/2) -> [server-ingress-proxy](http/2) -> [server]`` - -Another Envoy feature demonstrated in this example is Envoy's ability to do authority -base routing via its route configuration. - -Step 1: Generate the protocol stubs -*********************************** - -Change to the ``examples/grpc-bridge`` directory. - -A docker compose file is provided that generates the stubs for both ``client`` and ``server`` from the -specification in the ``protos`` directory. - -Inspecting the :download:`docker-compose-protos.yaml <_include/grpc-bridge/docker-compose-protos.yaml>` file, -you will see that it contains both the ``python`` and ``go`` gRPC protoc commands necessary for generating the -protocol stubs. - -Generate the stubs as follows: - -.. code-block:: console - - $ pwd - envoy/examples/grpc-bridge - $ docker compose -f docker-compose-protos.yaml up - Starting grpc-bridge_stubs_python_1 ... done - Starting grpc-bridge_stubs_go_1 ... done - Attaching to grpc-bridge_stubs_go_1, grpc-bridge_stubs_python_1 - grpc-bridge_stubs_go_1 exited with code 0 - grpc-bridge_stubs_python_1 exited with code 0 - -You may wish to clean up left over containers with the following command: - -.. code-block:: console - - $ docker container prune - -You can view the generated ``kv`` modules for both the client and server in their -respective directories: - -.. code-block:: console - - $ ls -la client/kv/kv_pb2.py - -rw-r--r-- 1 mdesales CORP\Domain Users 9527 Nov 6 21:59 client/kv/kv_pb2.py - - $ ls -la server/kv/kv.pb.go - -rw-r--r-- 1 mdesales CORP\Domain Users 9994 Nov 6 21:59 server/kv/kv.pb.go - -These generated ``python`` and ``go`` stubs can be included as external modules. - -Step 2: Start all of our containers -*********************************** - -To build this sandbox example and start the example services, run the following commands: - -.. code-block:: console - - $ pwd - envoy/examples/grpc-bridge - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - --------------------------------------------------------------------------------------------------------------- - grpc-bridge_grpc-client-proxy_1 /docker-entrypoint.sh /bin ... Up 10000/tcp, 0.0.0.0:9911->9911/tcp - grpc-bridge_grpc-client_1 /bin/sh -c tail -f /dev/null Up - grpc-bridge_grpc-server-proxy_1 /docker-entrypoint.sh /bin ... Up 10000/tcp, 0.0.0.0:8811->8811/tcp - grpc-bridge_grpc-server_1 /bin/sh -c /bin/server Up 0.0.0.0:8081->8081/tcp - -Step 3: Send requests to the Key/Value store -******************************************** - -To use the Python service and send gRPC requests: - -.. code-block:: console - - $ pwd - envoy/examples/grpc-bridge - -Set a key: - -.. code-block:: console - - $ docker compose exec grpc-client python /client/grpc-kv-client.py set foo bar - setf foo to bar - - -Get a key: - -.. code-block:: console - - $ docker compose exec grpc-client python /client/grpc-kv-client.py get foo - bar - -Modify an existing key: - -.. code-block:: console - - $ docker compose exec grpc-client python /client/grpc-kv-client.py set foo baz - setf foo to baz - -Get the modified key: - -.. code-block:: console - - $ docker compose exec grpc-client python /client/grpc-kv-client.py get foo - baz - -In the running docker compose container, you should see the gRPC service printing a record of its activity: - -.. code-block:: console - - $ docker compose logs grpc-server - grpc_1 | 2017/05/30 12:05:09 set: foo = bar - grpc_1 | 2017/05/30 12:05:12 get: foo - grpc_1 | 2017/05/30 12:05:18 set: foo = baz - -.. seealso:: - - :ref:`gRPC bridge filter `. - Learn more about configuring Envoy's gRPC bridge filter. diff --git a/examples/grpc-bridge/protos/kv.proto b/examples/grpc-bridge/protos/kv.proto deleted file mode 100644 index 584e89b4f271..000000000000 --- a/examples/grpc-bridge/protos/kv.proto +++ /dev/null @@ -1,25 +0,0 @@ -syntax = "proto3"; - -package kv; - -message GetRequest { - string key = 1; -} - -message GetResponse { - string value = 1; -} - -message SetRequest { - string key = 1; - string value = 2; -} - -message SetResponse { - bool ok = 1; -} - -service KV { - rpc Get(GetRequest) returns (GetResponse); - rpc Set(SetRequest) returns (SetResponse); -} diff --git a/examples/grpc-bridge/server/envoy-proxy.yaml b/examples/grpc-bridge/server/envoy-proxy.yaml deleted file mode 100644 index f007a85dc5e0..000000000000 --- a/examples/grpc-bridge/server/envoy-proxy.yaml +++ /dev/null @@ -1,51 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 8811 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - access_log: - - name: envoy.access_loggers.stdout - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/" - grpc: {} - route: - cluster: backend_grpc_service - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: backend_grpc_service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - typed_extension_protocol_options: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicit_http_config: - http2_protocol_options: {} - load_assignment: - cluster_name: backend_grpc_service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: grpc-server - port_value: 8081 diff --git a/examples/grpc-bridge/server/go.mod b/examples/grpc-bridge/server/go.mod deleted file mode 100644 index ef7577426982..000000000000 --- a/examples/grpc-bridge/server/go.mod +++ /dev/null @@ -1,10 +0,0 @@ -module github.com/envoyproxy/envoy/examples/grpc-bridge/server - -go 1.13 - -require ( - github.com/golang/protobuf v1.5.4 - golang.org/x/net v0.26.0 - google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/grpc v1.64.1 -) diff --git a/examples/grpc-bridge/server/go.sum b/examples/grpc-bridge/server/go.sum deleted file mode 100644 index 2edc00714b2c..000000000000 --- a/examples/grpc-bridge/server/go.sum +++ /dev/null @@ -1,2642 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= -cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= -cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= -cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= -cloud.google.com/go v0.110.9/go.mod h1:rpxevX/0Lqvlbc88b7Sc1SPNdyK1riNBTUU6JXhYNpM= -cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= -cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU= -cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= -cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= -cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= -cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= -cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= -cloud.google.com/go/accessapproval v1.7.1/go.mod h1:JYczztsHRMK7NTXb6Xw+dwbs/WnOJxbo/2mTI+Kgg68= -cloud.google.com/go/accessapproval v1.7.2/go.mod h1:/gShiq9/kK/h8T/eEn1BTzalDvk0mZxJlhfw0p+Xuc0= -cloud.google.com/go/accessapproval v1.7.3/go.mod h1:4l8+pwIxGTNqSf4T3ds8nLO94NQf0W/KnMNuQ9PbnP8= -cloud.google.com/go/accessapproval v1.7.4/go.mod h1:/aTEh45LzplQgFYdQdwPMR9YdX0UlhBmvB84uAmQKUc= -cloud.google.com/go/accessapproval v1.7.5/go.mod h1:g88i1ok5dvQ9XJsxpUInWWvUBrIZhyPDPbk4T01OoJ0= -cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= -cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= -cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= -cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= -cloud.google.com/go/accesscontextmanager v1.8.0/go.mod h1:uI+AI/r1oyWK99NN8cQ3UK76AMelMzgZCvJfsi2c+ps= -cloud.google.com/go/accesscontextmanager v1.8.1/go.mod h1:JFJHfvuaTC+++1iL1coPiG1eu5D24db2wXCDWDjIrxo= -cloud.google.com/go/accesscontextmanager v1.8.2/go.mod h1:E6/SCRM30elQJ2PKtFMs2YhfJpZSNcJyejhuzoId4Zk= -cloud.google.com/go/accesscontextmanager v1.8.3/go.mod h1:4i/JkF2JiFbhLnnpnfoTX5vRXfhf9ukhU1ANOTALTOQ= -cloud.google.com/go/accesscontextmanager v1.8.4/go.mod h1:ParU+WbMpD34s5JFEnGAnPBYAgUHozaTmDJU7aCU9+M= -cloud.google.com/go/accesscontextmanager v1.8.5/go.mod h1:TInEhcZ7V9jptGNqN3EzZ5XMhT6ijWxTGjzyETwmL0Q= -cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= -cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= -cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= -cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= -cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= -cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= -cloud.google.com/go/aiplatform v1.45.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= -cloud.google.com/go/aiplatform v1.48.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA= -cloud.google.com/go/aiplatform v1.50.0/go.mod h1:IRc2b8XAMTa9ZmfJV1BCCQbieWWvDnP1A8znyz5N7y4= -cloud.google.com/go/aiplatform v1.51.0/go.mod h1:IRc2b8XAMTa9ZmfJV1BCCQbieWWvDnP1A8znyz5N7y4= -cloud.google.com/go/aiplatform v1.51.1/go.mod h1:kY3nIMAVQOK2XDqDPHaOuD9e+FdMA6OOpfBjsvaFSOo= -cloud.google.com/go/aiplatform v1.51.2/go.mod h1:hCqVYB3mY45w99TmetEoe8eCQEwZEp9WHxeZdcv9phw= -cloud.google.com/go/aiplatform v1.52.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= -cloud.google.com/go/aiplatform v1.54.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= -cloud.google.com/go/aiplatform v1.57.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= -cloud.google.com/go/aiplatform v1.58.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= -cloud.google.com/go/aiplatform v1.58.2/go.mod h1:c3kCiVmb6UC1dHAjZjcpDj6ZS0bHQ2slL88ZjC2LtlA= -cloud.google.com/go/aiplatform v1.60.0/go.mod h1:eTlGuHOahHprZw3Hio5VKmtThIOak5/qy6pzdsqcQnM= -cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= -cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= -cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= -cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= -cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= -cloud.google.com/go/analytics v0.21.2/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= -cloud.google.com/go/analytics v0.21.3/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo= -cloud.google.com/go/analytics v0.21.4/go.mod h1:zZgNCxLCy8b2rKKVfC1YkC2vTrpfZmeRCySM3aUbskA= -cloud.google.com/go/analytics v0.21.5/go.mod h1:BQtOBHWTlJ96axpPPnw5CvGJ6i3Ve/qX2fTxR8qWyr8= -cloud.google.com/go/analytics v0.21.6/go.mod h1:eiROFQKosh4hMaNhF85Oc9WO97Cpa7RggD40e/RBy8w= -cloud.google.com/go/analytics v0.22.0/go.mod h1:eiROFQKosh4hMaNhF85Oc9WO97Cpa7RggD40e/RBy8w= -cloud.google.com/go/analytics v0.23.0/go.mod h1:YPd7Bvik3WS95KBok2gPXDqQPHy08TsCQG6CdUCb+u0= -cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= -cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= -cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= -cloud.google.com/go/apigateway v1.6.1/go.mod h1:ufAS3wpbRjqfZrzpvLC2oh0MFlpRJm2E/ts25yyqmXA= -cloud.google.com/go/apigateway v1.6.2/go.mod h1:CwMC90nnZElorCW63P2pAYm25AtQrHfuOkbRSHj0bT8= -cloud.google.com/go/apigateway v1.6.3/go.mod h1:k68PXWpEs6BVDTtnLQAyG606Q3mz8pshItwPXjgv44Y= -cloud.google.com/go/apigateway v1.6.4/go.mod h1:0EpJlVGH5HwAN4VF4Iec8TAzGN1aQgbxAWGJsnPCGGY= -cloud.google.com/go/apigateway v1.6.5/go.mod h1:6wCwvYRckRQogyDDltpANi3zsCDl6kWi0b4Je+w2UiI= -cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= -cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= -cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= -cloud.google.com/go/apigeeconnect v1.6.1/go.mod h1:C4awq7x0JpLtrlQCr8AzVIzAaYgngRqWf9S5Uhg+wWs= -cloud.google.com/go/apigeeconnect v1.6.2/go.mod h1:s6O0CgXT9RgAxlq3DLXvG8riw8PYYbU/v25jqP3Dy18= -cloud.google.com/go/apigeeconnect v1.6.3/go.mod h1:peG0HFQ0si2bN15M6QSjEW/W7Gy3NYkWGz7pFz13cbo= -cloud.google.com/go/apigeeconnect v1.6.4/go.mod h1:CapQCWZ8TCjnU0d7PobxhpOdVz/OVJ2Hr/Zcuu1xFx0= -cloud.google.com/go/apigeeconnect v1.6.5/go.mod h1:MEKm3AiT7s11PqTfKE3KZluZA9O91FNysvd3E6SJ6Ow= -cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= -cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= -cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= -cloud.google.com/go/apigeeregistry v0.7.1/go.mod h1:1XgyjZye4Mqtw7T9TsY4NW10U7BojBvG4RMD+vRDrIw= -cloud.google.com/go/apigeeregistry v0.7.2/go.mod h1:9CA2B2+TGsPKtfi3F7/1ncCCsL62NXBRfM6iPoGSM+8= -cloud.google.com/go/apigeeregistry v0.8.1/go.mod h1:MW4ig1N4JZQsXmBSwH4rwpgDonocz7FPBSw6XPGHmYw= -cloud.google.com/go/apigeeregistry v0.8.2/go.mod h1:h4v11TDGdeXJDJvImtgK2AFVvMIgGWjSb0HRnBSjcX8= -cloud.google.com/go/apigeeregistry v0.8.3/go.mod h1:aInOWnqF4yMQx8kTjDqHNXjZGh/mxeNlAf52YqtASUs= -cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= -cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= -cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= -cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= -cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= -cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= -cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= -cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= -cloud.google.com/go/appengine v1.8.1/go.mod h1:6NJXGLVhZCN9aQ/AEDvmfzKEfoYBlfB80/BHiKVputY= -cloud.google.com/go/appengine v1.8.2/go.mod h1:WMeJV9oZ51pvclqFN2PqHoGnys7rK0rz6s3Mp6yMvDo= -cloud.google.com/go/appengine v1.8.3/go.mod h1:2oUPZ1LVZ5EXi+AF1ihNAF+S8JrzQ3till5m9VQkrsk= -cloud.google.com/go/appengine v1.8.4/go.mod h1:TZ24v+wXBujtkK77CXCpjZbnuTvsFNT41MUaZ28D6vg= -cloud.google.com/go/appengine v1.8.5/go.mod h1:uHBgNoGLTS5di7BvU25NFDuKa82v0qQLjyMJLuPQrVo= -cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= -cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= -cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= -cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= -cloud.google.com/go/area120 v0.8.1/go.mod h1:BVfZpGpB7KFVNxPiQBuHkX6Ed0rS51xIgmGyjrAfzsg= -cloud.google.com/go/area120 v0.8.2/go.mod h1:a5qfo+x77SRLXnCynFWPUZhnZGeSgvQ+Y0v1kSItkh4= -cloud.google.com/go/area120 v0.8.3/go.mod h1:5zj6pMzVTH+SVHljdSKC35sriR/CVvQZzG/Icdyriw0= -cloud.google.com/go/area120 v0.8.4/go.mod h1:jfawXjxf29wyBXr48+W+GyX/f8fflxp642D/bb9v68M= -cloud.google.com/go/area120 v0.8.5/go.mod h1:BcoFCbDLZjsfe4EkCnEq1LKvHSK0Ew/zk5UFu6GMyA0= -cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= -cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= -cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= -cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= -cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= -cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= -cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= -cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= -cloud.google.com/go/artifactregistry v1.14.1/go.mod h1:nxVdG19jTaSTu7yA7+VbWL346r3rIdkZ142BSQqhn5E= -cloud.google.com/go/artifactregistry v1.14.2/go.mod h1:Xk+QbsKEb0ElmyeMfdHAey41B+qBq3q5R5f5xD4XT3U= -cloud.google.com/go/artifactregistry v1.14.3/go.mod h1:A2/E9GXnsyXl7GUvQ/2CjHA+mVRoWAXC0brg2os+kNI= -cloud.google.com/go/artifactregistry v1.14.4/go.mod h1:SJJcZTMv6ce0LDMUnihCN7WSrI+kBSFV0KIKo8S8aYU= -cloud.google.com/go/artifactregistry v1.14.6/go.mod h1:np9LSFotNWHcjnOgh8UVK0RFPCTUGbO0ve3384xyHfE= -cloud.google.com/go/artifactregistry v1.14.7/go.mod h1:0AUKhzWQzfmeTvT4SjfI4zjot72EMfrkvL9g9aRjnnM= -cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= -cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= -cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= -cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= -cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= -cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= -cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= -cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= -cloud.google.com/go/asset v1.14.1/go.mod h1:4bEJ3dnHCqWCDbWJ/6Vn7GVI9LerSi7Rfdi03hd+WTQ= -cloud.google.com/go/asset v1.15.0/go.mod h1:tpKafV6mEut3+vN9ScGvCHXHj7FALFVta+okxFECHcg= -cloud.google.com/go/asset v1.15.1/go.mod h1:yX/amTvFWRpp5rcFq6XbCxzKT8RJUam1UoboE179jU4= -cloud.google.com/go/asset v1.15.2/go.mod h1:B6H5tclkXvXz7PD22qCA2TDxSVQfasa3iDlM89O2NXs= -cloud.google.com/go/asset v1.15.3/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= -cloud.google.com/go/asset v1.16.0/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= -cloud.google.com/go/asset v1.17.0/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= -cloud.google.com/go/asset v1.17.1/go.mod h1:byvDw36UME5AzGNK7o4JnOnINkwOZ1yRrGrKIahHrng= -cloud.google.com/go/asset v1.17.2/go.mod h1:SVbzde67ehddSoKf5uebOD1sYw8Ab/jD/9EIeWg99q4= -cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= -cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= -cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= -cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= -cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= -cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= -cloud.google.com/go/assuredworkloads v1.11.1/go.mod h1:+F04I52Pgn5nmPG36CWFtxmav6+7Q+c5QyJoL18Lry0= -cloud.google.com/go/assuredworkloads v1.11.2/go.mod h1:O1dfr+oZJMlE6mw0Bp0P1KZSlj5SghMBvTpZqIcUAW4= -cloud.google.com/go/assuredworkloads v1.11.3/go.mod h1:vEjfTKYyRUaIeA0bsGJceFV2JKpVRgyG2op3jfa59Zs= -cloud.google.com/go/assuredworkloads v1.11.4/go.mod h1:4pwwGNwy1RP0m+y12ef3Q/8PaiWrIDQ6nD2E8kvWI9U= -cloud.google.com/go/assuredworkloads v1.11.5/go.mod h1:FKJ3g3ZvkL2D7qtqIGnDufFkHxwIpNM9vtmhvt+6wqk= -cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= -cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= -cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= -cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= -cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= -cloud.google.com/go/automl v1.13.1/go.mod h1:1aowgAHWYZU27MybSCFiukPO7xnyawv7pt3zK4bheQE= -cloud.google.com/go/automl v1.13.2/go.mod h1:gNY/fUmDEN40sP8amAX3MaXkxcqPIn7F1UIIPZpy4Mg= -cloud.google.com/go/automl v1.13.3/go.mod h1:Y8KwvyAZFOsMAPqUCfNu1AyclbC6ivCUF/MTwORymyY= -cloud.google.com/go/automl v1.13.4/go.mod h1:ULqwX/OLZ4hBVfKQaMtxMSTlPx0GqGbWN8uA/1EqCP8= -cloud.google.com/go/automl v1.13.5/go.mod h1:MDw3vLem3yh+SvmSgeYUmUKqyls6NzSumDm9OJ3xJ1Y= -cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= -cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= -cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= -cloud.google.com/go/baremetalsolution v1.1.1/go.mod h1:D1AV6xwOksJMV4OSlWHtWuFNZZYujJknMAP4Qa27QIA= -cloud.google.com/go/baremetalsolution v1.2.0/go.mod h1:68wi9AwPYkEWIUT4SvSGS9UJwKzNpshjHsH4lzk8iOw= -cloud.google.com/go/baremetalsolution v1.2.1/go.mod h1:3qKpKIw12RPXStwQXcbhfxVj1dqQGEvcmA+SX/mUR88= -cloud.google.com/go/baremetalsolution v1.2.2/go.mod h1:O5V6Uu1vzVelYahKfwEWRMaS3AbCkeYHy3145s1FkhM= -cloud.google.com/go/baremetalsolution v1.2.3/go.mod h1:/UAQ5xG3faDdy180rCUv47e0jvpp3BFxT+Cl0PFjw5g= -cloud.google.com/go/baremetalsolution v1.2.4/go.mod h1:BHCmxgpevw9IEryE99HbYEfxXkAEA3hkMJbYYsHtIuY= -cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= -cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= -cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= -cloud.google.com/go/batch v1.3.1/go.mod h1:VguXeQKXIYaeeIYbuozUmBR13AfL4SJP7IltNPS+A4A= -cloud.google.com/go/batch v1.4.1/go.mod h1:KdBmDD61K0ovcxoRHGrN6GmOBWeAOyCgKD0Mugx4Fkk= -cloud.google.com/go/batch v1.5.0/go.mod h1:KdBmDD61K0ovcxoRHGrN6GmOBWeAOyCgKD0Mugx4Fkk= -cloud.google.com/go/batch v1.5.1/go.mod h1:RpBuIYLkQu8+CWDk3dFD/t/jOCGuUpkpX+Y0n1Xccs8= -cloud.google.com/go/batch v1.6.1/go.mod h1:urdpD13zPe6YOK+6iZs/8/x2VBRofvblLpx0t57vM98= -cloud.google.com/go/batch v1.6.3/go.mod h1:J64gD4vsNSA2O5TtDB5AAux3nJ9iV8U3ilg3JDBYejU= -cloud.google.com/go/batch v1.7.0/go.mod h1:J64gD4vsNSA2O5TtDB5AAux3nJ9iV8U3ilg3JDBYejU= -cloud.google.com/go/batch v1.8.0/go.mod h1:k8V7f6VE2Suc0zUM4WtoibNrA6D3dqBpB+++e3vSGYc= -cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= -cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= -cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= -cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= -cloud.google.com/go/beyondcorp v0.6.1/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= -cloud.google.com/go/beyondcorp v1.0.0/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4= -cloud.google.com/go/beyondcorp v1.0.1/go.mod h1:zl/rWWAFVeV+kx+X2Javly7o1EIQThU4WlkynffL/lk= -cloud.google.com/go/beyondcorp v1.0.2/go.mod h1:m8cpG7caD+5su+1eZr+TSvF6r21NdLJk4f9u4SP2Ntc= -cloud.google.com/go/beyondcorp v1.0.3/go.mod h1:HcBvnEd7eYr+HGDd5ZbuVmBYX019C6CEXBonXbCVwJo= -cloud.google.com/go/beyondcorp v1.0.4/go.mod h1:Gx8/Rk2MxrvWfn4WIhHIG1NV7IBfg14pTKv1+EArVcc= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= -cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= -cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= -cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= -cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= -cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= -cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= -cloud.google.com/go/bigquery v1.52.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= -cloud.google.com/go/bigquery v1.53.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4= -cloud.google.com/go/bigquery v1.55.0/go.mod h1:9Y5I3PN9kQWuid6183JFhOGOW3GcirA5LpsKCUn+2ec= -cloud.google.com/go/bigquery v1.56.0/go.mod h1:KDcsploXTEY7XT3fDQzMUZlpQLHzE4itubHrnmhUrZA= -cloud.google.com/go/bigquery v1.57.1/go.mod h1:iYzC0tGVWt1jqSzBHqCr3lrRn0u13E8e+AqowBsDgug= -cloud.google.com/go/bigquery v1.58.0/go.mod h1:0eh4mWNY0KrBTjUzLjoYImapGORq9gEPT7MWjCy9lik= -cloud.google.com/go/bigquery v1.59.1/go.mod h1:VP1UJYgevyTwsV7desjzNzDND5p6hZB+Z8gZJN1GQUc= -cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= -cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= -cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= -cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= -cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= -cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= -cloud.google.com/go/billing v1.16.0/go.mod h1:y8vx09JSSJG02k5QxbycNRrN7FGZB6F3CAcgum7jvGA= -cloud.google.com/go/billing v1.17.0/go.mod h1:Z9+vZXEq+HwH7bhJkyI4OQcR6TSbeMrjlpEjO2vzY64= -cloud.google.com/go/billing v1.17.1/go.mod h1:Z9+vZXEq+HwH7bhJkyI4OQcR6TSbeMrjlpEjO2vzY64= -cloud.google.com/go/billing v1.17.2/go.mod h1:u/AdV/3wr3xoRBk5xvUzYMS1IawOAPwQMuHgHMdljDg= -cloud.google.com/go/billing v1.17.3/go.mod h1:z83AkoZ7mZwBGT3yTnt6rSGI1OOsHSIi6a5M3mJ8NaU= -cloud.google.com/go/billing v1.17.4/go.mod h1:5DOYQStCxquGprqfuid/7haD7th74kyMBHkjO/OvDtk= -cloud.google.com/go/billing v1.18.0/go.mod h1:5DOYQStCxquGprqfuid/7haD7th74kyMBHkjO/OvDtk= -cloud.google.com/go/billing v1.18.2/go.mod h1:PPIwVsOOQ7xzbADCwNe8nvK776QpfrOAUkvKjCUcpSE= -cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= -cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= -cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= -cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= -cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= -cloud.google.com/go/binaryauthorization v1.6.1/go.mod h1:TKt4pa8xhowwffiBmbrbcxijJRZED4zrqnwZ1lKH51U= -cloud.google.com/go/binaryauthorization v1.7.0/go.mod h1:Zn+S6QqTMn6odcMU1zDZCJxPjU2tZPV1oDl45lWY154= -cloud.google.com/go/binaryauthorization v1.7.1/go.mod h1:GTAyfRWYgcbsP3NJogpV3yeunbUIjx2T9xVeYovtURE= -cloud.google.com/go/binaryauthorization v1.7.2/go.mod h1:kFK5fQtxEp97m92ziy+hbu+uKocka1qRRL8MVJIgjv0= -cloud.google.com/go/binaryauthorization v1.7.3/go.mod h1:VQ/nUGRKhrStlGr+8GMS8f6/vznYLkdK5vaKfdCIpvU= -cloud.google.com/go/binaryauthorization v1.8.0/go.mod h1:VQ/nUGRKhrStlGr+8GMS8f6/vznYLkdK5vaKfdCIpvU= -cloud.google.com/go/binaryauthorization v1.8.1/go.mod h1:1HVRyBerREA/nhI7yLang4Zn7vfNVA3okoAR9qYQJAQ= -cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= -cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= -cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= -cloud.google.com/go/certificatemanager v1.7.1/go.mod h1:iW8J3nG6SaRYImIa+wXQ0g8IgoofDFRp5UMzaNk1UqI= -cloud.google.com/go/certificatemanager v1.7.2/go.mod h1:15SYTDQMd00kdoW0+XY5d9e+JbOPjp24AvF48D8BbcQ= -cloud.google.com/go/certificatemanager v1.7.3/go.mod h1:T/sZYuC30PTag0TLo28VedIRIj1KPGcOQzjWAptHa00= -cloud.google.com/go/certificatemanager v1.7.4/go.mod h1:FHAylPe/6IIKuaRmHbjbdLhGhVQ+CWHSD5Jq0k4+cCE= -cloud.google.com/go/certificatemanager v1.7.5/go.mod h1:uX+v7kWqy0Y3NG/ZhNvffh0kuqkKZIXdvlZRO7z0VtM= -cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= -cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= -cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= -cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= -cloud.google.com/go/channel v1.16.0/go.mod h1:eN/q1PFSl5gyu0dYdmxNXscY/4Fi7ABmeHCJNf/oHmc= -cloud.google.com/go/channel v1.17.0/go.mod h1:RpbhJsGi/lXWAUM1eF4IbQGbsfVlg2o8Iiy2/YLfVT0= -cloud.google.com/go/channel v1.17.1/go.mod h1:xqfzcOZAcP4b/hUDH0GkGg1Sd5to6di1HOJn/pi5uBQ= -cloud.google.com/go/channel v1.17.2/go.mod h1:aT2LhnftnyfQceFql5I/mP8mIbiiJS4lWqgXA815zMk= -cloud.google.com/go/channel v1.17.3/go.mod h1:QcEBuZLGGrUMm7kNj9IbU1ZfmJq2apotsV83hbxX7eE= -cloud.google.com/go/channel v1.17.4/go.mod h1:QcEBuZLGGrUMm7kNj9IbU1ZfmJq2apotsV83hbxX7eE= -cloud.google.com/go/channel v1.17.5/go.mod h1:FlpaOSINDAXgEext0KMaBq/vwpLMkkPAw9b2mApQeHc= -cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= -cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= -cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= -cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= -cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= -cloud.google.com/go/cloudbuild v1.10.1/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.13.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.14.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU= -cloud.google.com/go/cloudbuild v1.14.1/go.mod h1:K7wGc/3zfvmYWOWwYTgF/d/UVJhS4pu+HAy7PL7mCsU= -cloud.google.com/go/cloudbuild v1.14.2/go.mod h1:Bn6RO0mBYk8Vlrt+8NLrru7WXlQ9/RDWz2uo5KG1/sg= -cloud.google.com/go/cloudbuild v1.14.3/go.mod h1:eIXYWmRt3UtggLnFGx4JvXcMj4kShhVzGndL1LwleEM= -cloud.google.com/go/cloudbuild v1.15.0/go.mod h1:eIXYWmRt3UtggLnFGx4JvXcMj4kShhVzGndL1LwleEM= -cloud.google.com/go/cloudbuild v1.15.1/go.mod h1:gIofXZSu+XD2Uy+qkOrGKEx45zd7s28u/k8f99qKals= -cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= -cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= -cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= -cloud.google.com/go/clouddms v1.6.1/go.mod h1:Ygo1vL52Ov4TBZQquhz5fiw2CQ58gvu+PlS6PVXCpZI= -cloud.google.com/go/clouddms v1.7.0/go.mod h1:MW1dC6SOtI/tPNCciTsXtsGNEM0i0OccykPvv3hiYeM= -cloud.google.com/go/clouddms v1.7.1/go.mod h1:o4SR8U95+P7gZ/TX+YbJxehOCsM+fe6/brlrFquiszk= -cloud.google.com/go/clouddms v1.7.2/go.mod h1:Rk32TmWmHo64XqDvW7jgkFQet1tUKNVzs7oajtJT3jU= -cloud.google.com/go/clouddms v1.7.3/go.mod h1:fkN2HQQNUYInAU3NQ3vRLkV2iWs8lIdmBKOx4nrL6Hc= -cloud.google.com/go/clouddms v1.7.4/go.mod h1:RdrVqoFG9RWI5AvZ81SxJ/xvxPdtcRhFotwdE79DieY= -cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= -cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= -cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= -cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= -cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= -cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= -cloud.google.com/go/cloudtasks v1.11.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= -cloud.google.com/go/cloudtasks v1.12.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM= -cloud.google.com/go/cloudtasks v1.12.2/go.mod h1:A7nYkjNlW2gUoROg1kvJrQGhJP/38UaWwsnuBDOBVUk= -cloud.google.com/go/cloudtasks v1.12.3/go.mod h1:GPVXhIOSGEaR+3xT4Fp72ScI+HjHffSS4B8+BaBB5Ys= -cloud.google.com/go/cloudtasks v1.12.4/go.mod h1:BEPu0Gtt2dU6FxZHNqqNdGqIG86qyWKBPGnsb7udGY0= -cloud.google.com/go/cloudtasks v1.12.6/go.mod h1:b7c7fe4+TJsFZfDyzO51F7cjq7HLUlRi/KZQLQjDsaY= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= -cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= -cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= -cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= -cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= -cloud.google.com/go/compute v1.23.2/go.mod h1:JJ0atRC0J/oWYiiVBmsSsrRnh92DhZPG4hFDcR04Rns= -cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= -cloud.google.com/go/compute v1.23.4/go.mod h1:/EJMj55asU6kAFnuZET8zqgwgJ9FvXWXOkkfQZa4ioI= -cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= -cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls= -cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= -cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= -cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= -cloud.google.com/go/contactcenterinsights v1.9.1/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= -cloud.google.com/go/contactcenterinsights v1.10.0/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= -cloud.google.com/go/contactcenterinsights v1.11.0/go.mod h1:hutBdImE4XNZ1NV4vbPJKSFOnQruhC5Lj9bZqWMTKiU= -cloud.google.com/go/contactcenterinsights v1.11.1/go.mod h1:FeNP3Kg8iteKM80lMwSk3zZZKVxr+PGnAId6soKuXwE= -cloud.google.com/go/contactcenterinsights v1.11.2/go.mod h1:A9PIR5ov5cRcd28KlDbmmXE8Aay+Gccer2h4wzkYFso= -cloud.google.com/go/contactcenterinsights v1.11.3/go.mod h1:HHX5wrz5LHVAwfI2smIotQG9x8Qd6gYilaHcLLLmNis= -cloud.google.com/go/contactcenterinsights v1.12.0/go.mod h1:HHX5wrz5LHVAwfI2smIotQG9x8Qd6gYilaHcLLLmNis= -cloud.google.com/go/contactcenterinsights v1.12.1/go.mod h1:HHX5wrz5LHVAwfI2smIotQG9x8Qd6gYilaHcLLLmNis= -cloud.google.com/go/contactcenterinsights v1.13.0/go.mod h1:ieq5d5EtHsu8vhe2y3amtZ+BE+AQwX5qAy7cpo0POsI= -cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= -cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= -cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= -cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= -cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= -cloud.google.com/go/container v1.22.1/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= -cloud.google.com/go/container v1.24.0/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= -cloud.google.com/go/container v1.26.0/go.mod h1:YJCmRet6+6jnYYRS000T6k0D0xUXQgBSaJ7VwI8FBj4= -cloud.google.com/go/container v1.26.1/go.mod h1:5smONjPRUxeEpDG7bMKWfDL4sauswqEtnBK1/KKpR04= -cloud.google.com/go/container v1.26.2/go.mod h1:YlO84xCt5xupVbLaMY4s3XNE79MUJ+49VmkInr6HvF4= -cloud.google.com/go/container v1.27.1/go.mod h1:b1A1gJeTBXVLQ6GGw9/9M4FG94BEGsqJ5+t4d/3N7O4= -cloud.google.com/go/container v1.28.0/go.mod h1:b1A1gJeTBXVLQ6GGw9/9M4FG94BEGsqJ5+t4d/3N7O4= -cloud.google.com/go/container v1.29.0/go.mod h1:b1A1gJeTBXVLQ6GGw9/9M4FG94BEGsqJ5+t4d/3N7O4= -cloud.google.com/go/container v1.30.1/go.mod h1:vkbfX0EnAKL/vgVECs5BZn24e1cJROzgszJirRKQ4Bg= -cloud.google.com/go/container v1.31.0/go.mod h1:7yABn5s3Iv3lmw7oMmyGbeV6tQj86njcTijkkGuvdZA= -cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= -cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= -cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= -cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= -cloud.google.com/go/containeranalysis v0.10.1/go.mod h1:Ya2jiILITMY68ZLPaogjmOMNkwsDrWBSTyBubGXO7j0= -cloud.google.com/go/containeranalysis v0.11.0/go.mod h1:4n2e99ZwpGxpNcz+YsFT1dfOHPQFGcAC8FN2M2/ne/U= -cloud.google.com/go/containeranalysis v0.11.1/go.mod h1:rYlUOM7nem1OJMKwE1SadufX0JP3wnXj844EtZAwWLY= -cloud.google.com/go/containeranalysis v0.11.2/go.mod h1:xibioGBC1MD2j4reTyV1xY1/MvKaz+fyM9ENWhmIeP8= -cloud.google.com/go/containeranalysis v0.11.3/go.mod h1:kMeST7yWFQMGjiG9K7Eov+fPNQcGhb8mXj/UcTiWw9U= -cloud.google.com/go/containeranalysis v0.11.4/go.mod h1:cVZT7rXYBS9NG1rhQbWL9pWbXCKHWJPYraE8/FTSYPE= -cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= -cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= -cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= -cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= -cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= -cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= -cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= -cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= -cloud.google.com/go/datacatalog v1.14.0/go.mod h1:h0PrGtlihoutNMp/uvwhawLQ9+c63Kz65UFqh49Yo+E= -cloud.google.com/go/datacatalog v1.14.1/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= -cloud.google.com/go/datacatalog v1.16.0/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4= -cloud.google.com/go/datacatalog v1.17.1/go.mod h1:nCSYFHgtxh2MiEktWIz71s/X+7ds/UT9kp0PC7waCzE= -cloud.google.com/go/datacatalog v1.18.0/go.mod h1:nCSYFHgtxh2MiEktWIz71s/X+7ds/UT9kp0PC7waCzE= -cloud.google.com/go/datacatalog v1.18.1/go.mod h1:TzAWaz+ON1tkNr4MOcak8EBHX7wIRX/gZKM+yTVsv+A= -cloud.google.com/go/datacatalog v1.18.2/go.mod h1:SPVgWW2WEMuWHA+fHodYjmxPiMqcOiWfhc9OD5msigk= -cloud.google.com/go/datacatalog v1.18.3/go.mod h1:5FR6ZIF8RZrtml0VUao22FxhdjkoG+a0866rEnObryM= -cloud.google.com/go/datacatalog v1.19.0/go.mod h1:5FR6ZIF8RZrtml0VUao22FxhdjkoG+a0866rEnObryM= -cloud.google.com/go/datacatalog v1.19.2/go.mod h1:2YbODwmhpLM4lOFe3PuEhHK9EyTzQJ5AXgIy7EDKTEE= -cloud.google.com/go/datacatalog v1.19.3/go.mod h1:ra8V3UAsciBpJKQ+z9Whkxzxv7jmQg1hfODr3N3YPJ4= -cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= -cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= -cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= -cloud.google.com/go/dataflow v0.9.1/go.mod h1:Wp7s32QjYuQDWqJPFFlnBKhkAtiFpMTdg00qGbnIHVw= -cloud.google.com/go/dataflow v0.9.2/go.mod h1:vBfdBZ/ejlTaYIGB3zB4T08UshH70vbtZeMD+urnUSo= -cloud.google.com/go/dataflow v0.9.3/go.mod h1:HI4kMVjcHGTs3jTHW/kv3501YW+eloiJSLxkJa/vqFE= -cloud.google.com/go/dataflow v0.9.4/go.mod h1:4G8vAkHYCSzU8b/kmsoR2lWyHJD85oMJPHMtan40K8w= -cloud.google.com/go/dataflow v0.9.5/go.mod h1:udl6oi8pfUHnL0z6UN9Lf9chGqzDMVqcYTcZ1aPnCZQ= -cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= -cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= -cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= -cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= -cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= -cloud.google.com/go/dataform v0.8.1/go.mod h1:3BhPSiw8xmppbgzeBbmDvmSWlwouuJkXsXsb8UBih9M= -cloud.google.com/go/dataform v0.8.2/go.mod h1:X9RIqDs6NbGPLR80tnYoPNiO1w0wenKTb8PxxlhTMKM= -cloud.google.com/go/dataform v0.8.3/go.mod h1:8nI/tvv5Fso0drO3pEjtowz58lodx8MVkdV2q0aPlqg= -cloud.google.com/go/dataform v0.9.1/go.mod h1:pWTg+zGQ7i16pyn0bS1ruqIE91SdL2FDMvEYu/8oQxs= -cloud.google.com/go/dataform v0.9.2/go.mod h1:S8cQUwPNWXo7m/g3DhWHsLBoufRNn9EgFrMgne2j7cI= -cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= -cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= -cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= -cloud.google.com/go/datafusion v1.7.1/go.mod h1:KpoTBbFmoToDExJUso/fcCiguGDk7MEzOWXUsJo0wsI= -cloud.google.com/go/datafusion v1.7.2/go.mod h1:62K2NEC6DRlpNmI43WHMWf9Vg/YvN6QVi8EVwifElI0= -cloud.google.com/go/datafusion v1.7.3/go.mod h1:eoLt1uFXKGBq48jy9LZ+Is8EAVLnmn50lNncLzwYokE= -cloud.google.com/go/datafusion v1.7.4/go.mod h1:BBs78WTOLYkT4GVZIXQCZT3GFpkpDN4aBY4NDX/jVlM= -cloud.google.com/go/datafusion v1.7.5/go.mod h1:bYH53Oa5UiqahfbNK9YuYKteeD4RbQSNMx7JF7peGHc= -cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= -cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= -cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= -cloud.google.com/go/datalabeling v0.8.1/go.mod h1:XS62LBSVPbYR54GfYQsPXZjTW8UxCK2fkDciSrpRFdY= -cloud.google.com/go/datalabeling v0.8.2/go.mod h1:cyDvGHuJWu9U/cLDA7d8sb9a0tWLEletStu2sTmg3BE= -cloud.google.com/go/datalabeling v0.8.3/go.mod h1:tvPhpGyS/V7lqjmb3V0TaDdGvhzgR1JoW7G2bpi2UTI= -cloud.google.com/go/datalabeling v0.8.4/go.mod h1:Z1z3E6LHtffBGrNUkKwbwbDxTiXEApLzIgmymj8A3S8= -cloud.google.com/go/datalabeling v0.8.5/go.mod h1:IABB2lxQnkdUbMnQaOl2prCOfms20mcPxDBm36lps+s= -cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= -cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= -cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= -cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= -cloud.google.com/go/dataplex v1.8.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.9.0/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.9.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE= -cloud.google.com/go/dataplex v1.10.1/go.mod h1:1MzmBv8FvjYfc7vDdxhnLFNskikkB+3vl475/XdCDhs= -cloud.google.com/go/dataplex v1.10.2/go.mod h1:xdC8URdTrCrZMW6keY779ZT1cTOfV8KEPNsw+LTRT1Y= -cloud.google.com/go/dataplex v1.11.1/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= -cloud.google.com/go/dataplex v1.11.2/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= -cloud.google.com/go/dataplex v1.13.0/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= -cloud.google.com/go/dataplex v1.14.0/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= -cloud.google.com/go/dataplex v1.14.1/go.mod h1:bWxQAbg6Smg+sca2+Ex7s8D9a5qU6xfXtwmq4BVReps= -cloud.google.com/go/dataplex v1.14.2/go.mod h1:0oGOSFlEKef1cQeAHXy4GZPB/Ife0fz/PxBf+ZymA2U= -cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= -cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= -cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= -cloud.google.com/go/dataproc/v2 v2.0.1/go.mod h1:7Ez3KRHdFGcfY7GcevBbvozX+zyWGcwLJvvAMwCaoZ4= -cloud.google.com/go/dataproc/v2 v2.2.0/go.mod h1:lZR7AQtwZPvmINx5J87DSOOpTfof9LVZju6/Qo4lmcY= -cloud.google.com/go/dataproc/v2 v2.2.1/go.mod h1:QdAJLaBjh+l4PVlVZcmrmhGccosY/omC1qwfQ61Zv/o= -cloud.google.com/go/dataproc/v2 v2.2.2/go.mod h1:aocQywVmQVF4i8CL740rNI/ZRpsaaC1Wh2++BJ7HEJ4= -cloud.google.com/go/dataproc/v2 v2.2.3/go.mod h1:G5R6GBc9r36SXv/RtZIVfB8SipI+xVn0bX5SxUzVYbY= -cloud.google.com/go/dataproc/v2 v2.3.0/go.mod h1:G5R6GBc9r36SXv/RtZIVfB8SipI+xVn0bX5SxUzVYbY= -cloud.google.com/go/dataproc/v2 v2.4.0/go.mod h1:3B1Ht2aRB8VZIteGxQS/iNSJGzt9+CA0WGnDVMEm7Z4= -cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= -cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= -cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= -cloud.google.com/go/dataqna v0.8.1/go.mod h1:zxZM0Bl6liMePWsHA8RMGAfmTG34vJMapbHAxQ5+WA8= -cloud.google.com/go/dataqna v0.8.2/go.mod h1:KNEqgx8TTmUipnQsScOoDpq/VlXVptUqVMZnt30WAPs= -cloud.google.com/go/dataqna v0.8.3/go.mod h1:wXNBW2uvc9e7Gl5k8adyAMnLush1KVV6lZUhB+rqNu4= -cloud.google.com/go/dataqna v0.8.4/go.mod h1:mySRKjKg5Lz784P6sCov3p1QD+RZQONRMRjzGNcFd0c= -cloud.google.com/go/dataqna v0.8.5/go.mod h1:vgihg1mz6n7pb5q2YJF7KlXve6tCglInd6XO0JGOlWM= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= -cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= -cloud.google.com/go/datastore v1.12.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.12.1/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.13.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70= -cloud.google.com/go/datastore v1.14.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= -cloud.google.com/go/datastore v1.15.0/go.mod h1:GAeStMBIt9bPS7jMJA85kgkpsMkvseWWXiaHya9Jes8= -cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= -cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= -cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= -cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= -cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= -cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= -cloud.google.com/go/datastream v1.9.1/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= -cloud.google.com/go/datastream v1.10.0/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q= -cloud.google.com/go/datastream v1.10.1/go.mod h1:7ngSYwnw95YFyTd5tOGBxHlOZiL+OtpjheqU7t2/s/c= -cloud.google.com/go/datastream v1.10.2/go.mod h1:W42TFgKAs/om6x/CdXX5E4oiAsKlH+e8MTGy81zdYt0= -cloud.google.com/go/datastream v1.10.3/go.mod h1:YR0USzgjhqA/Id0Ycu1VvZe8hEWwrkjuXrGbzeDOSEA= -cloud.google.com/go/datastream v1.10.4/go.mod h1:7kRxPdxZxhPg3MFeCSulmAJnil8NJGGvSNdn4p1sRZo= -cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= -cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= -cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= -cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= -cloud.google.com/go/deploy v1.11.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= -cloud.google.com/go/deploy v1.13.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g= -cloud.google.com/go/deploy v1.13.1/go.mod h1:8jeadyLkH9qu9xgO3hVWw8jVr29N1mnW42gRJT8GY6g= -cloud.google.com/go/deploy v1.14.1/go.mod h1:N8S0b+aIHSEeSr5ORVoC0+/mOPUysVt8ae4QkZYolAw= -cloud.google.com/go/deploy v1.14.2/go.mod h1:e5XOUI5D+YGldyLNZ21wbp9S8otJbBE4i88PtO9x/2g= -cloud.google.com/go/deploy v1.15.0/go.mod h1:e5XOUI5D+YGldyLNZ21wbp9S8otJbBE4i88PtO9x/2g= -cloud.google.com/go/deploy v1.16.0/go.mod h1:e5XOUI5D+YGldyLNZ21wbp9S8otJbBE4i88PtO9x/2g= -cloud.google.com/go/deploy v1.17.0/go.mod h1:XBr42U5jIr64t92gcpOXxNrqL2PStQCXHuKK5GRUuYo= -cloud.google.com/go/deploy v1.17.1/go.mod h1:SXQyfsXrk0fBmgBHRzBjQbZhMfKZ3hMQBw5ym7MN/50= -cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= -cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= -cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= -cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= -cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= -cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= -cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= -cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= -cloud.google.com/go/dialogflow v1.38.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= -cloud.google.com/go/dialogflow v1.40.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4= -cloud.google.com/go/dialogflow v1.43.0/go.mod h1:pDUJdi4elL0MFmt1REMvFkdsUTYSHq+rTCS8wg0S3+M= -cloud.google.com/go/dialogflow v1.44.0/go.mod h1:pDUJdi4elL0MFmt1REMvFkdsUTYSHq+rTCS8wg0S3+M= -cloud.google.com/go/dialogflow v1.44.1/go.mod h1:n/h+/N2ouKOO+rbe/ZnI186xImpqvCVj2DdsWS/0EAk= -cloud.google.com/go/dialogflow v1.44.2/go.mod h1:QzFYndeJhpVPElnFkUXxdlptx0wPnBWLCBT9BvtC3/c= -cloud.google.com/go/dialogflow v1.44.3/go.mod h1:mHly4vU7cPXVweuB5R0zsYKPMzy240aQdAu06SqBbAQ= -cloud.google.com/go/dialogflow v1.47.0/go.mod h1:mHly4vU7cPXVweuB5R0zsYKPMzy240aQdAu06SqBbAQ= -cloud.google.com/go/dialogflow v1.48.0/go.mod h1:mHly4vU7cPXVweuB5R0zsYKPMzy240aQdAu06SqBbAQ= -cloud.google.com/go/dialogflow v1.48.1/go.mod h1:C1sjs2/g9cEwjCltkKeYp3FFpz8BOzNondEaAlCpt+A= -cloud.google.com/go/dialogflow v1.48.2/go.mod h1:7A2oDf6JJ1/+hdpnFRfb/RjJUOh2X3rhIa5P8wQSEX4= -cloud.google.com/go/dialogflow v1.49.0/go.mod h1:dhVrXKETtdPlpPhE7+2/k4Z8FRNUp6kMV3EW3oz/fe0= -cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= -cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= -cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= -cloud.google.com/go/dlp v1.10.1/go.mod h1:IM8BWz1iJd8njcNcG0+Kyd9OPnqnRNkDV8j42VT5KOI= -cloud.google.com/go/dlp v1.10.2/go.mod h1:ZbdKIhcnyhILgccwVDzkwqybthh7+MplGC3kZVZsIOQ= -cloud.google.com/go/dlp v1.10.3/go.mod h1:iUaTc/ln8I+QT6Ai5vmuwfw8fqTk2kaz0FvCwhLCom0= -cloud.google.com/go/dlp v1.11.1/go.mod h1:/PA2EnioBeXTL/0hInwgj0rfsQb3lpE3R8XUJxqUNKI= -cloud.google.com/go/dlp v1.11.2/go.mod h1:9Czi+8Y/FegpWzgSfkRlyz+jwW6Te9Rv26P3UfU/h/w= -cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= -cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= -cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= -cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= -cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= -cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= -cloud.google.com/go/documentai v1.20.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= -cloud.google.com/go/documentai v1.22.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E= -cloud.google.com/go/documentai v1.22.1/go.mod h1:LKs22aDHbJv7ufXuPypzRO7rG3ALLJxzdCXDPutw4Qc= -cloud.google.com/go/documentai v1.23.0/go.mod h1:LKs22aDHbJv7ufXuPypzRO7rG3ALLJxzdCXDPutw4Qc= -cloud.google.com/go/documentai v1.23.2/go.mod h1:Q/wcRT+qnuXOpjAkvOV4A+IeQl04q2/ReT7SSbytLSo= -cloud.google.com/go/documentai v1.23.4/go.mod h1:4MYAaEMnADPN1LPN5xboDR5QVB6AgsaxgFdJhitlE2Y= -cloud.google.com/go/documentai v1.23.5/go.mod h1:ghzBsyVTiVdkfKaUCum/9bGBEyBjDO4GfooEcYKhN+g= -cloud.google.com/go/documentai v1.23.6/go.mod h1:ghzBsyVTiVdkfKaUCum/9bGBEyBjDO4GfooEcYKhN+g= -cloud.google.com/go/documentai v1.23.7/go.mod h1:ghzBsyVTiVdkfKaUCum/9bGBEyBjDO4GfooEcYKhN+g= -cloud.google.com/go/documentai v1.23.8/go.mod h1:Vd/y5PosxCpUHmwC+v9arZyeMfTqBR9VIwOwIqQYYfA= -cloud.google.com/go/documentai v1.25.0/go.mod h1:ftLnzw5VcXkLItp6pw1mFic91tMRyfv6hHEY5br4KzY= -cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= -cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= -cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= -cloud.google.com/go/domains v0.9.1/go.mod h1:aOp1c0MbejQQ2Pjf1iJvnVyT+z6R6s8pX66KaCSDYfE= -cloud.google.com/go/domains v0.9.2/go.mod h1:3YvXGYzZG1Temjbk7EyGCuGGiXHJwVNmwIf+E/cUp5I= -cloud.google.com/go/domains v0.9.3/go.mod h1:29k66YNDLDY9LCFKpGFeh6Nj9r62ZKm5EsUJxAl84KU= -cloud.google.com/go/domains v0.9.4/go.mod h1:27jmJGShuXYdUNjyDG0SodTfT5RwLi7xmH334Gvi3fY= -cloud.google.com/go/domains v0.9.5/go.mod h1:dBzlxgepazdFhvG7u23XMhmMKBjrkoUNaw0A8AQB55Y= -cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= -cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= -cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= -cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= -cloud.google.com/go/edgecontainer v1.1.1/go.mod h1:O5bYcS//7MELQZs3+7mabRqoWQhXCzenBu0R8bz2rwk= -cloud.google.com/go/edgecontainer v1.1.2/go.mod h1:wQRjIzqxEs9e9wrtle4hQPSR1Y51kqN75dgF7UllZZ4= -cloud.google.com/go/edgecontainer v1.1.3/go.mod h1:Ll2DtIABzEfaxaVSbwj3QHFaOOovlDFiWVDu349jSsA= -cloud.google.com/go/edgecontainer v1.1.4/go.mod h1:AvFdVuZuVGdgaE5YvlL1faAoa1ndRR/5XhXZvPBHbsE= -cloud.google.com/go/edgecontainer v1.1.5/go.mod h1:rgcjrba3DEDEQAidT4yuzaKWTbkTI5zAMu3yy6ZWS0M= -cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= -cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= -cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= -cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= -cloud.google.com/go/essentialcontacts v1.6.2/go.mod h1:T2tB6tX+TRak7i88Fb2N9Ok3PvY3UNbUsMag9/BARh4= -cloud.google.com/go/essentialcontacts v1.6.3/go.mod h1:yiPCD7f2TkP82oJEFXFTou8Jl8L6LBRPeBEkTaO0Ggo= -cloud.google.com/go/essentialcontacts v1.6.4/go.mod h1:iju5Vy3d9tJUg0PYMd1nHhjV7xoCXaOAVabrwLaPBEM= -cloud.google.com/go/essentialcontacts v1.6.5/go.mod h1:jjYbPzw0x+yglXC890l6ECJWdYeZ5dlYACTFL0U/VuM= -cloud.google.com/go/essentialcontacts v1.6.6/go.mod h1:XbqHJGaiH0v2UvtuucfOzFXN+rpL/aU5BCZLn4DYl1Q= -cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= -cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= -cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= -cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= -cloud.google.com/go/eventarc v1.12.1/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= -cloud.google.com/go/eventarc v1.13.0/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI= -cloud.google.com/go/eventarc v1.13.1/go.mod h1:EqBxmGHFrruIara4FUQ3RHlgfCn7yo1HYsu2Hpt/C3Y= -cloud.google.com/go/eventarc v1.13.2/go.mod h1:X9A80ShVu19fb4e5sc/OLV7mpFUKZMwfJFeeWhcIObM= -cloud.google.com/go/eventarc v1.13.3/go.mod h1:RWH10IAZIRcj1s/vClXkBgMHwh59ts7hSWcqD3kaclg= -cloud.google.com/go/eventarc v1.13.4/go.mod h1:zV5sFVoAa9orc/52Q+OuYUG9xL2IIZTbbuTHC6JSY8s= -cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= -cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= -cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= -cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= -cloud.google.com/go/filestore v1.7.1/go.mod h1:y10jsorq40JJnjR/lQ8AfFbbcGlw3g+Dp8oN7i7FjV4= -cloud.google.com/go/filestore v1.7.2/go.mod h1:TYOlyJs25f/omgj+vY7/tIG/E7BX369triSPzE4LdgE= -cloud.google.com/go/filestore v1.7.3/go.mod h1:Qp8WaEERR3cSkxToxFPHh/b8AACkSut+4qlCjAmKTV0= -cloud.google.com/go/filestore v1.7.4/go.mod h1:S5JCxIbFjeBhWMTfIYH2Jx24J6BqjwpkkPl+nBA5DlI= -cloud.google.com/go/filestore v1.8.0/go.mod h1:S5JCxIbFjeBhWMTfIYH2Jx24J6BqjwpkkPl+nBA5DlI= -cloud.google.com/go/filestore v1.8.1/go.mod h1:MbN9KcaM47DRTIuLfQhJEsjaocVebNtNQhSLhKCF5GM= -cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= -cloud.google.com/go/firestore v1.11.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= -cloud.google.com/go/firestore v1.12.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4= -cloud.google.com/go/firestore v1.13.0/go.mod h1:QojqqOh8IntInDUSTAh0c8ZsPYAr68Ma8c5DWOy8xb8= -cloud.google.com/go/firestore v1.14.0/go.mod h1:96MVaHLsEhbvkBEdZgfN+AS/GIkco1LRpH9Xp9YZfzQ= -cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= -cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= -cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= -cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= -cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= -cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= -cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= -cloud.google.com/go/functions v1.15.1/go.mod h1:P5yNWUTkyU+LvW/S9O6V+V423VZooALQlqoXdoPz5AE= -cloud.google.com/go/functions v1.15.2/go.mod h1:CHAjtcR6OU4XF2HuiVeriEdELNcnvRZSk1Q8RMqy4lE= -cloud.google.com/go/functions v1.15.3/go.mod h1:r/AMHwBheapkkySEhiZYLDBwVJCdlRwsm4ieJu35/Ug= -cloud.google.com/go/functions v1.15.4/go.mod h1:CAsTc3VlRMVvx+XqXxKqVevguqJpnVip4DdonFsX28I= -cloud.google.com/go/functions v1.16.0/go.mod h1:nbNpfAG7SG7Duw/o1iZ6ohvL7mc6MapWQVpqtM29n8k= -cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= -cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= -cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= -cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= -cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= -cloud.google.com/go/gaming v1.10.1/go.mod h1:XQQvtfP8Rb9Rxnxm5wFVpAp9zCQkJi2bLIb7iHGwB3s= -cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= -cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= -cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= -cloud.google.com/go/gkebackup v1.3.0/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= -cloud.google.com/go/gkebackup v1.3.1/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU= -cloud.google.com/go/gkebackup v1.3.2/go.mod h1:OMZbXzEJloyXMC7gqdSB+EOEQ1AKcpGYvO3s1ec5ixk= -cloud.google.com/go/gkebackup v1.3.3/go.mod h1:eMk7/wVV5P22KBakhQnJxWSVftL1p4VBFLpv0kIft7I= -cloud.google.com/go/gkebackup v1.3.4/go.mod h1:gLVlbM8h/nHIs09ns1qx3q3eaXcGSELgNu1DWXYz1HI= -cloud.google.com/go/gkebackup v1.3.5/go.mod h1:KJ77KkNN7Wm1LdMopOelV6OodM01pMuK2/5Zt1t4Tvc= -cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= -cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= -cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= -cloud.google.com/go/gkeconnect v0.8.1/go.mod h1:KWiK1g9sDLZqhxB2xEuPV8V9NYzrqTUmQR9shJHpOZw= -cloud.google.com/go/gkeconnect v0.8.2/go.mod h1:6nAVhwchBJYgQCXD2pHBFQNiJNyAd/wyxljpaa6ZPrY= -cloud.google.com/go/gkeconnect v0.8.3/go.mod h1:i9GDTrfzBSUZGCe98qSu1B8YB8qfapT57PenIb820Jo= -cloud.google.com/go/gkeconnect v0.8.4/go.mod h1:84hZz4UMlDCKl8ifVW8layK4WHlMAFeq8vbzjU0yJkw= -cloud.google.com/go/gkeconnect v0.8.5/go.mod h1:LC/rS7+CuJ5fgIbXv8tCD/mdfnlAadTaUufgOkmijuk= -cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= -cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= -cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= -cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= -cloud.google.com/go/gkehub v0.14.1/go.mod h1:VEXKIJZ2avzrbd7u+zeMtW00Y8ddk/4V9511C9CQGTY= -cloud.google.com/go/gkehub v0.14.2/go.mod h1:iyjYH23XzAxSdhrbmfoQdePnlMj2EWcvnR+tHdBQsCY= -cloud.google.com/go/gkehub v0.14.3/go.mod h1:jAl6WafkHHW18qgq7kqcrXYzN08hXeK/Va3utN8VKg8= -cloud.google.com/go/gkehub v0.14.4/go.mod h1:Xispfu2MqnnFt8rV/2/3o73SK1snL8s9dYJ9G2oQMfc= -cloud.google.com/go/gkehub v0.14.5/go.mod h1:6bzqxM+a+vEH/h8W8ec4OJl4r36laxTs3A/fMNHJ0wA= -cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= -cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= -cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= -cloud.google.com/go/gkemulticloud v0.6.1/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= -cloud.google.com/go/gkemulticloud v1.0.0/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw= -cloud.google.com/go/gkemulticloud v1.0.1/go.mod h1:AcrGoin6VLKT/fwZEYuqvVominLriQBCKmbjtnbMjG8= -cloud.google.com/go/gkemulticloud v1.0.2/go.mod h1:+ee5VXxKb3H1l4LZAcgWB/rvI16VTNTrInWxDjAGsGo= -cloud.google.com/go/gkemulticloud v1.0.3/go.mod h1:7NpJBN94U6DY1xHIbsDqB2+TFZUfjLUKLjUX8NGLor0= -cloud.google.com/go/gkemulticloud v1.1.0/go.mod h1:7NpJBN94U6DY1xHIbsDqB2+TFZUfjLUKLjUX8NGLor0= -cloud.google.com/go/gkemulticloud v1.1.1/go.mod h1:C+a4vcHlWeEIf45IB5FFR5XGjTeYhF83+AYIpTy4i2Q= -cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= -cloud.google.com/go/grafeas v0.3.0/go.mod h1:P7hgN24EyONOTMyeJH6DxG4zD7fwiYa5Q6GUgyFSOU8= -cloud.google.com/go/grafeas v0.3.4/go.mod h1:A5m316hcG+AulafjAbPKXBO/+I5itU4LOdKO2R/uDIc= -cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= -cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= -cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= -cloud.google.com/go/gsuiteaddons v1.6.1/go.mod h1:CodrdOqRZcLp5WOwejHWYBjZvfY0kOphkAKpF/3qdZY= -cloud.google.com/go/gsuiteaddons v1.6.2/go.mod h1:K65m9XSgs8hTF3X9nNTPi8IQueljSdYo9F+Mi+s4MyU= -cloud.google.com/go/gsuiteaddons v1.6.3/go.mod h1:sCFJkZoMrLZT3JTb8uJqgKPNshH2tfXeCwTFRebTq48= -cloud.google.com/go/gsuiteaddons v1.6.4/go.mod h1:rxtstw7Fx22uLOXBpsvb9DUbC+fiXs7rF4U29KHM/pE= -cloud.google.com/go/gsuiteaddons v1.6.5/go.mod h1:Lo4P2IvO8uZ9W+RaC6s1JVxo42vgy+TX5a6hfBZ0ubs= -cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= -cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= -cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/iam v1.0.1/go.mod h1:yR3tmSL8BcZB4bxByRv2jkSIahVmCtfKZwLYGBalRE8= -cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk= -cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= -cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= -cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE= -cloud.google.com/go/iam v1.1.4/go.mod h1:l/rg8l1AaA+VFMho/HYx2Vv6xinPSLMF8qfhRPIZ0L8= -cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= -cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= -cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= -cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= -cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= -cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= -cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= -cloud.google.com/go/iap v1.8.1/go.mod h1:sJCbeqg3mvWLqjZNsI6dfAtbbV1DL2Rl7e1mTyXYREQ= -cloud.google.com/go/iap v1.9.0/go.mod h1:01OFxd1R+NFrg78S+hoPV5PxEzv22HXaNqUUlmNHFuY= -cloud.google.com/go/iap v1.9.1/go.mod h1:SIAkY7cGMLohLSdBR25BuIxO+I4fXJiL06IBL7cy/5Q= -cloud.google.com/go/iap v1.9.2/go.mod h1:GwDTOs047PPSnwRD0Us5FKf4WDRcVvHg1q9WVkKBhdI= -cloud.google.com/go/iap v1.9.3/go.mod h1:DTdutSZBqkkOm2HEOTBzhZxh2mwwxshfD/h3yofAiCw= -cloud.google.com/go/iap v1.9.4/go.mod h1:vO4mSq0xNf/Pu6E5paORLASBwEmphXEjgCFg7aeNu1w= -cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= -cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= -cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= -cloud.google.com/go/ids v1.4.1/go.mod h1:np41ed8YMU8zOgv53MMMoCntLTn2lF+SUzlM+O3u/jw= -cloud.google.com/go/ids v1.4.2/go.mod h1:3vw8DX6YddRu9BncxuzMyWn0g8+ooUjI2gslJ7FH3vk= -cloud.google.com/go/ids v1.4.3/go.mod h1:9CXPqI3GedjmkjbMWCUhMZ2P2N7TUMzAkVXYEH2orYU= -cloud.google.com/go/ids v1.4.4/go.mod h1:z+WUc2eEl6S/1aZWzwtVNWoSZslgzPxAboS0lZX0HjI= -cloud.google.com/go/ids v1.4.5/go.mod h1:p0ZnyzjMWxww6d2DvMGnFwCsSxDJM666Iir1bK1UuBo= -cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= -cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= -cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= -cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= -cloud.google.com/go/iot v1.7.1/go.mod h1:46Mgw7ev1k9KqK1ao0ayW9h0lI+3hxeanz+L1zmbbbk= -cloud.google.com/go/iot v1.7.2/go.mod h1:q+0P5zr1wRFpw7/MOgDXrG/HVA+l+cSwdObffkrpnSg= -cloud.google.com/go/iot v1.7.3/go.mod h1:t8itFchkol4VgNbHnIq9lXoOOtHNR3uAACQMYbN9N4I= -cloud.google.com/go/iot v1.7.4/go.mod h1:3TWqDVvsddYBG++nHSZmluoCAVGr1hAcabbWZNKEZLk= -cloud.google.com/go/iot v1.7.5/go.mod h1:nq3/sqTz3HGaWJi1xNiX7F41ThOzpud67vwk0YsSsqs= -cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= -cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= -cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= -cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= -cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= -cloud.google.com/go/kms v1.11.0/go.mod h1:hwdiYC0xjnWsKQQCQQmIQnS9asjYVSK6jtXm+zFqXLM= -cloud.google.com/go/kms v1.12.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= -cloud.google.com/go/kms v1.15.0/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= -cloud.google.com/go/kms v1.15.2/go.mod h1:3hopT4+7ooWRCjc2DxgnpESFxhIraaI2IpAVUEhbT/w= -cloud.google.com/go/kms v1.15.3/go.mod h1:AJdXqHxS2GlPyduM99s9iGqi2nwbviBbhV/hdmt4iOQ= -cloud.google.com/go/kms v1.15.4/go.mod h1:L3Sdj6QTHK8dfwK5D1JLsAyELsNMnd3tAIwGS4ltKpc= -cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= -cloud.google.com/go/kms v1.15.6/go.mod h1:yF75jttnIdHfGBoE51AKsD/Yqf+/jICzB9v1s1acsms= -cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI= -cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= -cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= -cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= -cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= -cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= -cloud.google.com/go/language v1.10.1/go.mod h1:CPp94nsdVNiQEt1CNjF5WkTcisLiHPyIbMhvR8H2AW0= -cloud.google.com/go/language v1.11.0/go.mod h1:uDx+pFDdAKTY8ehpWbiXyQdz8tDSYLJbQcXsCkjYyvQ= -cloud.google.com/go/language v1.11.1/go.mod h1:Xyid9MG9WOX3utvDbpX7j3tXDmmDooMyMDqgUVpH17U= -cloud.google.com/go/language v1.12.1/go.mod h1:zQhalE2QlQIxbKIZt54IASBzmZpN/aDASea5zl1l+J4= -cloud.google.com/go/language v1.12.2/go.mod h1:9idWapzr/JKXBBQ4lWqVX/hcadxB194ry20m/bTrhWc= -cloud.google.com/go/language v1.12.3/go.mod h1:evFX9wECX6mksEva8RbRnr/4wi/vKGYnAJrTRXU8+f8= -cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= -cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= -cloud.google.com/go/lifesciences v0.9.1/go.mod h1:hACAOd1fFbCGLr/+weUKRAJas82Y4vrL3O5326N//Wc= -cloud.google.com/go/lifesciences v0.9.2/go.mod h1:QHEOO4tDzcSAzeJg7s2qwnLM2ji8IRpQl4p6m5Z9yTA= -cloud.google.com/go/lifesciences v0.9.3/go.mod h1:gNGBOJV80IWZdkd+xz4GQj4mbqaz737SCLHn2aRhQKM= -cloud.google.com/go/lifesciences v0.9.4/go.mod h1:bhm64duKhMi7s9jR9WYJYvjAFJwRqNj+Nia7hF0Z7JA= -cloud.google.com/go/lifesciences v0.9.5/go.mod h1:OdBm0n7C0Osh5yZB7j9BXyrMnTRGBJIZonUMxo5CzPw= -cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= -cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= -cloud.google.com/go/logging v1.8.1/go.mod h1:TJjR+SimHwuC8MZ9cjByQulAMgni+RkXeI3wwctHJEI= -cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A0y7BBBhE= -cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= -cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= -cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= -cloud.google.com/go/longrunning v0.4.2/go.mod h1:OHrnaYyLUV6oqwh0xiS7e5sLQhP1m0QU9R+WhGDMgIQ= -cloud.google.com/go/longrunning v0.5.0/go.mod h1:0JNuqRShmscVAhIACGtskSAWtqtOoPkwP0YF1oVEchc= -cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= -cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs= -cloud.google.com/go/longrunning v0.5.3/go.mod h1:y/0ga59EYu58J6SHmmQOvekvND2qODbu8ywBBW7EK7Y= -cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= -cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= -cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= -cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= -cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= -cloud.google.com/go/managedidentities v1.6.1/go.mod h1:h/irGhTN2SkZ64F43tfGPMbHnypMbu4RB3yl8YcuEak= -cloud.google.com/go/managedidentities v1.6.2/go.mod h1:5c2VG66eCa0WIq6IylRk3TBW83l161zkFvCj28X7jn8= -cloud.google.com/go/managedidentities v1.6.3/go.mod h1:tewiat9WLyFN0Fi7q1fDD5+0N4VUoL0SCX0OTCthZq4= -cloud.google.com/go/managedidentities v1.6.4/go.mod h1:WgyaECfHmF00t/1Uk8Oun3CQ2PGUtjc3e9Alh79wyiM= -cloud.google.com/go/managedidentities v1.6.5/go.mod h1:fkFI2PwwyRQbjLxlm5bQ8SjtObFMW3ChBGNqaMcgZjI= -cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= -cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= -cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= -cloud.google.com/go/maps v1.3.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= -cloud.google.com/go/maps v1.4.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s= -cloud.google.com/go/maps v1.4.1/go.mod h1:BxSa0BnW1g2U2gNdbq5zikLlHUuHW0GFWh7sgML2kIY= -cloud.google.com/go/maps v1.5.1/go.mod h1:NPMZw1LJwQZYCfz4y+EIw+SI+24A4bpdFJqdKVr0lt4= -cloud.google.com/go/maps v1.6.1/go.mod h1:4+buOHhYXFBp58Zj/K+Lc1rCmJssxxF4pJ5CJnhdz18= -cloud.google.com/go/maps v1.6.2/go.mod h1:4+buOHhYXFBp58Zj/K+Lc1rCmJssxxF4pJ5CJnhdz18= -cloud.google.com/go/maps v1.6.3/go.mod h1:VGAn809ADswi1ASofL5lveOHPnE6Rk/SFTTBx1yuOLw= -cloud.google.com/go/maps v1.6.4/go.mod h1:rhjqRy8NWmDJ53saCfsXQ0LKwBHfi6OSh5wkq6BaMhI= -cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= -cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= -cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= -cloud.google.com/go/mediatranslation v0.8.1/go.mod h1:L/7hBdEYbYHQJhX2sldtTO5SZZ1C1vkapubj0T2aGig= -cloud.google.com/go/mediatranslation v0.8.2/go.mod h1:c9pUaDRLkgHRx3irYE5ZC8tfXGrMYwNZdmDqKMSfFp8= -cloud.google.com/go/mediatranslation v0.8.3/go.mod h1:F9OnXTy336rteOEywtY7FOqCk+J43o2RF638hkOQl4Y= -cloud.google.com/go/mediatranslation v0.8.4/go.mod h1:9WstgtNVAdN53m6TQa5GjIjLqKQPXe74hwSCxUP6nj4= -cloud.google.com/go/mediatranslation v0.8.5/go.mod h1:y7kTHYIPCIfgyLbKncgqouXJtLsU+26hZhHEEy80fSs= -cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= -cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= -cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= -cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= -cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= -cloud.google.com/go/memcache v1.10.1/go.mod h1:47YRQIarv4I3QS5+hoETgKO40InqzLP6kpNLvyXuyaA= -cloud.google.com/go/memcache v1.10.2/go.mod h1:f9ZzJHLBrmd4BkguIAa/l/Vle6uTHzHokdnzSWOdQ6A= -cloud.google.com/go/memcache v1.10.3/go.mod h1:6z89A41MT2DVAW0P4iIRdu5cmRTsbsFn4cyiIx8gbwo= -cloud.google.com/go/memcache v1.10.4/go.mod h1:v/d8PuC8d1gD6Yn5+I3INzLR01IDn0N4Ym56RgikSI0= -cloud.google.com/go/memcache v1.10.5/go.mod h1:/FcblbNd0FdMsx4natdj+2GWzTq+cjZvMa1I+9QsuMA= -cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= -cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= -cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= -cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= -cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= -cloud.google.com/go/metastore v1.11.1/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= -cloud.google.com/go/metastore v1.12.0/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA= -cloud.google.com/go/metastore v1.13.0/go.mod h1:URDhpG6XLeh5K+Glq0NOt74OfrPKTwS62gEPZzb5SOk= -cloud.google.com/go/metastore v1.13.1/go.mod h1:IbF62JLxuZmhItCppcIfzBBfUFq0DIB9HPDoLgWrVOU= -cloud.google.com/go/metastore v1.13.2/go.mod h1:KS59dD+unBji/kFebVp8XU/quNSyo8b6N6tPGspKszA= -cloud.google.com/go/metastore v1.13.3/go.mod h1:K+wdjXdtkdk7AQg4+sXS8bRrQa9gcOr+foOMF2tqINE= -cloud.google.com/go/metastore v1.13.4/go.mod h1:FMv9bvPInEfX9Ac1cVcRXp8EBBQnBcqH6gz3KvJ9BAE= -cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= -cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= -cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= -cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= -cloud.google.com/go/monitoring v1.15.1/go.mod h1:lADlSAlFdbqQuwwpaImhsJXu1QSdd3ojypXrFSMr2rM= -cloud.google.com/go/monitoring v1.16.0/go.mod h1:Ptp15HgAyM1fNICAojDMoNc/wUmn67mLHQfyqbw+poY= -cloud.google.com/go/monitoring v1.16.1/go.mod h1:6HsxddR+3y9j+o/cMJH6q/KJ/CBTvM/38L/1m7bTRJ4= -cloud.google.com/go/monitoring v1.16.2/go.mod h1:B44KGwi4ZCF8Rk/5n+FWeispDXoKSk9oss2QNlXJBgc= -cloud.google.com/go/monitoring v1.16.3/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw= -cloud.google.com/go/monitoring v1.17.0/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw= -cloud.google.com/go/monitoring v1.17.1/go.mod h1:SJzPMakCF0GHOuKEH/r4hxVKF04zl+cRPQyc3d/fqII= -cloud.google.com/go/monitoring v1.18.0/go.mod h1:c92vVBCeq/OB4Ioyo+NbN2U7tlg5ZH41PZcdvfc+Lcg= -cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= -cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= -cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= -cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= -cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= -cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= -cloud.google.com/go/networkconnectivity v1.12.1/go.mod h1:PelxSWYM7Sh9/guf8CFhi6vIqf19Ir/sbfZRUwXh92E= -cloud.google.com/go/networkconnectivity v1.13.0/go.mod h1:SAnGPes88pl7QRLUen2HmcBSE9AowVAcdug8c0RSBFk= -cloud.google.com/go/networkconnectivity v1.14.0/go.mod h1:SAnGPes88pl7QRLUen2HmcBSE9AowVAcdug8c0RSBFk= -cloud.google.com/go/networkconnectivity v1.14.1/go.mod h1:LyGPXR742uQcDxZ/wv4EI0Vu5N6NKJ77ZYVnDe69Zug= -cloud.google.com/go/networkconnectivity v1.14.2/go.mod h1:5UFlwIisZylSkGG1AdwK/WZUaoz12PKu6wODwIbFzJo= -cloud.google.com/go/networkconnectivity v1.14.3/go.mod h1:4aoeFdrJpYEXNvrnfyD5kIzs8YtHg945Og4koAjHQek= -cloud.google.com/go/networkconnectivity v1.14.4/go.mod h1:PU12q++/IMnDJAB+3r+tJtuCXCfwfN+C6Niyj6ji1Po= -cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= -cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= -cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= -cloud.google.com/go/networkmanagement v1.8.0/go.mod h1:Ho/BUGmtyEqrttTgWEe7m+8vDdK74ibQc+Be0q7Fof0= -cloud.google.com/go/networkmanagement v1.9.0/go.mod h1:UTUaEU9YwbCAhhz3jEOHr+2/K/MrBk2XxOLS89LQzFw= -cloud.google.com/go/networkmanagement v1.9.1/go.mod h1:CCSYgrQQvW73EJawO2QamemYcOb57LvrDdDU51F0mcI= -cloud.google.com/go/networkmanagement v1.9.2/go.mod h1:iDGvGzAoYRghhp4j2Cji7sF899GnfGQcQRQwgVOWnDw= -cloud.google.com/go/networkmanagement v1.9.3/go.mod h1:y7WMO1bRLaP5h3Obm4tey+NquUvB93Co1oh4wpL+XcU= -cloud.google.com/go/networkmanagement v1.9.4/go.mod h1:daWJAl0KTFytFL7ar33I6R/oNBH8eEOX/rBNHrC/8TA= -cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= -cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= -cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= -cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= -cloud.google.com/go/networksecurity v0.9.1/go.mod h1:MCMdxOKQ30wsBI1eI659f9kEp4wuuAueoC9AJKSPWZQ= -cloud.google.com/go/networksecurity v0.9.2/go.mod h1:jG0SeAttWzPMUILEHDUvFYdQTl8L/E/KC8iZDj85lEI= -cloud.google.com/go/networksecurity v0.9.3/go.mod h1:l+C0ynM6P+KV9YjOnx+kk5IZqMSLccdBqW6GUoF4p/0= -cloud.google.com/go/networksecurity v0.9.4/go.mod h1:E9CeMZ2zDsNBkr8axKSYm8XyTqNhiCHf1JO/Vb8mD1w= -cloud.google.com/go/networksecurity v0.9.5/go.mod h1:KNkjH/RsylSGyyZ8wXpue8xpCEK+bTtvof8SBfIhMG8= -cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= -cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= -cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= -cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= -cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= -cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= -cloud.google.com/go/notebooks v1.9.1/go.mod h1:zqG9/gk05JrzgBt4ghLzEepPHNwE5jgPcHZRKhlC1A8= -cloud.google.com/go/notebooks v1.10.0/go.mod h1:SOPYMZnttHxqot0SGSFSkRrwE29eqnKPBJFqgWmiK2k= -cloud.google.com/go/notebooks v1.10.1/go.mod h1:5PdJc2SgAybE76kFQCWrTfJolCOUQXF97e+gteUUA6A= -cloud.google.com/go/notebooks v1.11.1/go.mod h1:V2Zkv8wX9kDCGRJqYoI+bQAaoVeE5kSiz4yYHd2yJwQ= -cloud.google.com/go/notebooks v1.11.2/go.mod h1:z0tlHI/lREXC8BS2mIsUeR3agM1AkgLiS+Isov3SS70= -cloud.google.com/go/notebooks v1.11.3/go.mod h1:0wQyI2dQC3AZyQqWnRsp+yA+kY4gC7ZIVP4Qg3AQcgo= -cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= -cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= -cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= -cloud.google.com/go/optimization v1.4.1/go.mod h1:j64vZQP7h9bO49m2rVaTVoNM0vEBEN5eKPUPbZyXOrk= -cloud.google.com/go/optimization v1.5.0/go.mod h1:evo1OvTxeBRBu6ydPlrIRizKY/LJKo/drDMMRKqGEUU= -cloud.google.com/go/optimization v1.5.1/go.mod h1:NC0gnUD5MWVAF7XLdoYVPmYYVth93Q6BUzqAq3ZwtV8= -cloud.google.com/go/optimization v1.6.1/go.mod h1:hH2RYPTTM9e9zOiTaYPTiGPcGdNZVnBSBxjIAJzUkqo= -cloud.google.com/go/optimization v1.6.2/go.mod h1:mWNZ7B9/EyMCcwNl1frUGEuY6CPijSkz88Fz2vwKPOY= -cloud.google.com/go/optimization v1.6.3/go.mod h1:8ve3svp3W6NFcAEFr4SfJxrldzhUl4VMUJmhrqVKtYA= -cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= -cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= -cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= -cloud.google.com/go/orchestration v1.8.1/go.mod h1:4sluRF3wgbYVRqz7zJ1/EUNc90TTprliq9477fGobD8= -cloud.google.com/go/orchestration v1.8.2/go.mod h1:T1cP+6WyTmh6LSZzeUhvGf0uZVmJyTx7t8z7Vg87+A0= -cloud.google.com/go/orchestration v1.8.3/go.mod h1:xhgWAYqlbYjlz2ftbFghdyqENYW+JXuhBx9KsjMoGHs= -cloud.google.com/go/orchestration v1.8.4/go.mod h1:d0lywZSVYtIoSZXb0iFjv9SaL13PGyVOKDxqGxEf/qI= -cloud.google.com/go/orchestration v1.8.5/go.mod h1:C1J7HesE96Ba8/hZ71ISTV2UAat0bwN+pi85ky38Yq8= -cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= -cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= -cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= -cloud.google.com/go/orgpolicy v1.11.0/go.mod h1:2RK748+FtVvnfuynxBzdnyu7sygtoZa1za/0ZfpOs1M= -cloud.google.com/go/orgpolicy v1.11.1/go.mod h1:8+E3jQcpZJQliP+zaFfayC2Pg5bmhuLK755wKhIIUCE= -cloud.google.com/go/orgpolicy v1.11.2/go.mod h1:biRDpNwfyytYnmCRWZWxrKF22Nkz9eNVj9zyaBdpm1o= -cloud.google.com/go/orgpolicy v1.11.3/go.mod h1:oKAtJ/gkMjum5icv2aujkP4CxROxPXsBbYGCDbPO8MM= -cloud.google.com/go/orgpolicy v1.11.4/go.mod h1:0+aNV/nrfoTQ4Mytv+Aw+stBDBjNf4d8fYRA9herfJI= -cloud.google.com/go/orgpolicy v1.12.0/go.mod h1:0+aNV/nrfoTQ4Mytv+Aw+stBDBjNf4d8fYRA9herfJI= -cloud.google.com/go/orgpolicy v1.12.1/go.mod h1:aibX78RDl5pcK3jA8ysDQCFkVxLj3aOQqrbBaUL2V5I= -cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= -cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= -cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= -cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= -cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= -cloud.google.com/go/osconfig v1.12.0/go.mod h1:8f/PaYzoS3JMVfdfTubkowZYGmAhUCjjwnjqWI7NVBc= -cloud.google.com/go/osconfig v1.12.1/go.mod h1:4CjBxND0gswz2gfYRCUoUzCm9zCABp91EeTtWXyz0tE= -cloud.google.com/go/osconfig v1.12.2/go.mod h1:eh9GPaMZpI6mEJEuhEjUJmaxvQ3gav+fFEJon1Y8Iw0= -cloud.google.com/go/osconfig v1.12.3/go.mod h1:L/fPS8LL6bEYUi1au832WtMnPeQNT94Zo3FwwV1/xGM= -cloud.google.com/go/osconfig v1.12.4/go.mod h1:B1qEwJ/jzqSRslvdOCI8Kdnp0gSng0xW4LOnIebQomA= -cloud.google.com/go/osconfig v1.12.5/go.mod h1:D9QFdxzfjgw3h/+ZaAb5NypM8bhOMqBzgmbhzWViiW8= -cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= -cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= -cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= -cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= -cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= -cloud.google.com/go/oslogin v1.10.1/go.mod h1:x692z7yAue5nE7CsSnoG0aaMbNoRJRXO4sn73R+ZqAs= -cloud.google.com/go/oslogin v1.11.0/go.mod h1:8GMTJs4X2nOAUVJiPGqIWVcDaF0eniEto3xlOxaboXE= -cloud.google.com/go/oslogin v1.11.1/go.mod h1:OhD2icArCVNUxKqtK0mcSmKL7lgr0LVlQz+v9s1ujTg= -cloud.google.com/go/oslogin v1.12.1/go.mod h1:VfwTeFJGbnakxAY236eN8fsnglLiVXndlbcNomY4iZU= -cloud.google.com/go/oslogin v1.12.2/go.mod h1:CQ3V8Jvw4Qo4WRhNPF0o+HAM4DiLuE27Ul9CX9g2QdY= -cloud.google.com/go/oslogin v1.13.0/go.mod h1:xPJqLwpTZ90LSE5IL1/svko+6c5avZLluiyylMb/sRA= -cloud.google.com/go/oslogin v1.13.1/go.mod h1:vS8Sr/jR7QvPWpCjNqy6LYZr5Zs1e8ZGW/KPn9gmhws= -cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= -cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= -cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= -cloud.google.com/go/phishingprotection v0.8.1/go.mod h1:AxonW7GovcA8qdEk13NfHq9hNx5KPtfxXNeUxTDxB6I= -cloud.google.com/go/phishingprotection v0.8.2/go.mod h1:LhJ91uyVHEYKSKcMGhOa14zMMWfbEdxG032oT6ECbC8= -cloud.google.com/go/phishingprotection v0.8.3/go.mod h1:3B01yO7T2Ra/TMojifn8EoGd4G9jts/6cIO0DgDY9J8= -cloud.google.com/go/phishingprotection v0.8.4/go.mod h1:6b3kNPAc2AQ6jZfFHioZKg9MQNybDg4ixFd4RPZZ2nE= -cloud.google.com/go/phishingprotection v0.8.5/go.mod h1:g1smd68F7mF1hgQPuYn3z8HDbNre8L6Z0b7XMYFmX7I= -cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= -cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= -cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= -cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= -cloud.google.com/go/policytroubleshooter v1.7.1/go.mod h1:0NaT5v3Ag1M7U5r0GfDCpUFkWd9YqpubBWsQlhanRv0= -cloud.google.com/go/policytroubleshooter v1.8.0/go.mod h1:tmn5Ir5EToWe384EuboTcVQT7nTag2+DuH3uHmKd1HU= -cloud.google.com/go/policytroubleshooter v1.9.0/go.mod h1:+E2Lga7TycpeSTj2FsH4oXxTnrbHJGRlKhVZBLGgU64= -cloud.google.com/go/policytroubleshooter v1.9.1/go.mod h1:MYI8i0bCrL8cW+VHN1PoiBTyNZTstCg2WUw2eVC4c4U= -cloud.google.com/go/policytroubleshooter v1.10.1/go.mod h1:5C0rhT3TDZVxAu8813bwmTvd57Phbl8mr9F4ipOsxEs= -cloud.google.com/go/policytroubleshooter v1.10.2/go.mod h1:m4uF3f6LseVEnMV6nknlN2vYGRb+75ylQwJdnOXfnv0= -cloud.google.com/go/policytroubleshooter v1.10.3/go.mod h1:+ZqG3agHT7WPb4EBIRqUv4OyIwRTZvsVDHZ8GlZaoxk= -cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= -cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= -cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= -cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= -cloud.google.com/go/privatecatalog v0.9.1/go.mod h1:0XlDXW2unJXdf9zFz968Hp35gl/bhF4twwpXZAW50JA= -cloud.google.com/go/privatecatalog v0.9.2/go.mod h1:RMA4ATa8IXfzvjrhhK8J6H4wwcztab+oZph3c6WmtFc= -cloud.google.com/go/privatecatalog v0.9.3/go.mod h1:K5pn2GrVmOPjXz3T26mzwXLcKivfIJ9R5N79AFCF9UE= -cloud.google.com/go/privatecatalog v0.9.4/go.mod h1:SOjm93f+5hp/U3PqMZAHTtBtluqLygrDrVO8X8tYtG0= -cloud.google.com/go/privatecatalog v0.9.5/go.mod h1:fVWeBOVe7uj2n3kWRGlUQqR/pOd450J9yZoOECcQqJk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= -cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= -cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= -cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= -cloud.google.com/go/pubsub v1.32.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= -cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc= -cloud.google.com/go/pubsub v1.34.0/go.mod h1:alj4l4rBg+N3YTFDDC+/YyFTs6JAjam2QfYsddcAW4c= -cloud.google.com/go/pubsub v1.36.1/go.mod h1:iYjCa9EzWOoBiTdd4ps7QoMtMln5NwaZQpK1hbRfBDE= -cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= -cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= -cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= -cloud.google.com/go/pubsublite v1.8.1/go.mod h1:fOLdU4f5xldK4RGJrBMm+J7zMWNj/k4PxwEZXy39QS0= -cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= -cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= -cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= -cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= -cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= -cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= -cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.2/go.mod h1:kR0KjsJS7Jt1YSyWFkseQ756D45kaYNTlDPPaRAvDBU= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.0/go.mod h1:QuE8EdU9dEnesG8/kG3XuJyNsjEqMlMzg3v3scCJ46c= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.1/go.mod h1:JZYZJOeZjgSSTGP4uz7NlQ4/d1w5hGmksVgM0lbEij0= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.2/go.mod h1:kpaDBOpkwD4G0GVMzG1W6Doy1tFFC97XAV3xy+Rd/pw= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.3/go.mod h1:Dak54rw6lC2gBY8FBznpOCAR58wKf+R+ZSJRoeJok4w= -cloud.google.com/go/recaptchaenterprise/v2 v2.8.4/go.mod h1:Dak54rw6lC2gBY8FBznpOCAR58wKf+R+ZSJRoeJok4w= -cloud.google.com/go/recaptchaenterprise/v2 v2.9.0/go.mod h1:Dak54rw6lC2gBY8FBznpOCAR58wKf+R+ZSJRoeJok4w= -cloud.google.com/go/recaptchaenterprise/v2 v2.9.2/go.mod h1:trwwGkfhCmp05Ll5MSJPXY7yvnO0p4v3orGANAFHAuU= -cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= -cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= -cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= -cloud.google.com/go/recommendationengine v0.8.1/go.mod h1:MrZihWwtFYWDzE6Hz5nKcNz3gLizXVIDI/o3G1DLcrE= -cloud.google.com/go/recommendationengine v0.8.2/go.mod h1:QIybYHPK58qir9CV2ix/re/M//Ty10OxjnnhWdaKS1Y= -cloud.google.com/go/recommendationengine v0.8.3/go.mod h1:m3b0RZV02BnODE9FeSvGv1qibFo8g0OnmB/RMwYy4V8= -cloud.google.com/go/recommendationengine v0.8.4/go.mod h1:GEteCf1PATl5v5ZsQ60sTClUE0phbWmo3rQ1Js8louU= -cloud.google.com/go/recommendationengine v0.8.5/go.mod h1:A38rIXHGFvoPvmy6pZLozr0g59NRNREz4cx7F58HAsQ= -cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= -cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= -cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= -cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= -cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= -cloud.google.com/go/recommender v1.10.1/go.mod h1:XFvrE4Suqn5Cq0Lf+mCP6oBHD/yRMA8XxP5sb7Q7gpA= -cloud.google.com/go/recommender v1.11.0/go.mod h1:kPiRQhPyTJ9kyXPCG6u/dlPLbYfFlkwHNRwdzPVAoII= -cloud.google.com/go/recommender v1.11.1/go.mod h1:sGwFFAyI57v2Hc5LbIj+lTwXipGu9NW015rkaEM5B18= -cloud.google.com/go/recommender v1.11.2/go.mod h1:AeoJuzOvFR/emIcXdVFkspVXVTYpliRCmKNYDnyBv6Y= -cloud.google.com/go/recommender v1.11.3/go.mod h1:+FJosKKJSId1MBFeJ/TTyoGQZiEelQQIZMKYYD8ruK4= -cloud.google.com/go/recommender v1.12.0/go.mod h1:+FJosKKJSId1MBFeJ/TTyoGQZiEelQQIZMKYYD8ruK4= -cloud.google.com/go/recommender v1.12.1/go.mod h1:gf95SInWNND5aPas3yjwl0I572dtudMhMIG4ni8nr+0= -cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= -cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= -cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= -cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= -cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= -cloud.google.com/go/redis v1.13.1/go.mod h1:VP7DGLpE91M6bcsDdMuyCm2hIpB6Vp2hI090Mfd1tcg= -cloud.google.com/go/redis v1.13.2/go.mod h1:0Hg7pCMXS9uz02q+LoEVl5dNHUkIQv+C/3L76fandSA= -cloud.google.com/go/redis v1.13.3/go.mod h1:vbUpCKUAZSYzFcWKmICnYgRAhTFg9r+djWqFxDYXi4U= -cloud.google.com/go/redis v1.14.1/go.mod h1:MbmBxN8bEnQI4doZPC1BzADU4HGocHBk2de3SbgOkqs= -cloud.google.com/go/redis v1.14.2/go.mod h1:g0Lu7RRRz46ENdFKQ2EcQZBAJ2PtJHJLuiiRuEXwyQw= -cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= -cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= -cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= -cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= -cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= -cloud.google.com/go/resourcemanager v1.9.1/go.mod h1:dVCuosgrh1tINZ/RwBufr8lULmWGOkPS8gL5gqyjdT8= -cloud.google.com/go/resourcemanager v1.9.2/go.mod h1:OujkBg1UZg5lX2yIyMo5Vz9O5hf7XQOSV7WxqxxMtQE= -cloud.google.com/go/resourcemanager v1.9.3/go.mod h1:IqrY+g0ZgLsihcfcmqSe+RKp1hzjXwG904B92AwBz6U= -cloud.google.com/go/resourcemanager v1.9.4/go.mod h1:N1dhP9RFvo3lUfwtfLWVxfUWq8+KUQ+XLlHLH3BoFJ0= -cloud.google.com/go/resourcemanager v1.9.5/go.mod h1:hep6KjelHA+ToEjOfO3garMKi/CLYwTqeAw7YiEI9x8= -cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= -cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= -cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= -cloud.google.com/go/resourcesettings v1.6.1/go.mod h1:M7mk9PIZrC5Fgsu1kZJci6mpgN8o0IUzVx3eJU3y4Jw= -cloud.google.com/go/resourcesettings v1.6.2/go.mod h1:mJIEDd9MobzunWMeniaMp6tzg4I2GvD3TTmPkc8vBXk= -cloud.google.com/go/resourcesettings v1.6.3/go.mod h1:pno5D+7oDYkMWZ5BpPsb4SO0ewg3IXcmmrUZaMJrFic= -cloud.google.com/go/resourcesettings v1.6.4/go.mod h1:pYTTkWdv2lmQcjsthbZLNBP4QW140cs7wqA3DuqErVI= -cloud.google.com/go/resourcesettings v1.6.5/go.mod h1:WBOIWZraXZOGAgoR4ukNj0o0HiSMO62H9RpFi9WjP9I= -cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= -cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= -cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= -cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= -cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= -cloud.google.com/go/retail v1.14.1/go.mod h1:y3Wv3Vr2k54dLNIrCzenyKG8g8dhvhncT2NcNjb/6gE= -cloud.google.com/go/retail v1.14.2/go.mod h1:W7rrNRChAEChX336QF7bnMxbsjugcOCPU44i5kbLiL8= -cloud.google.com/go/retail v1.14.3/go.mod h1:Omz2akDHeSlfCq8ArPKiBxlnRpKEBjUH386JYFLUvXo= -cloud.google.com/go/retail v1.14.4/go.mod h1:l/N7cMtY78yRnJqp5JW8emy7MB1nz8E4t2yfOmklYfg= -cloud.google.com/go/retail v1.15.1/go.mod h1:In9nSBOYhLbDGa87QvWlnE1XA14xBN2FpQRiRsUs9wU= -cloud.google.com/go/retail v1.16.0/go.mod h1:LW7tllVveZo4ReWt68VnldZFWJRzsh9np+01J9dYWzE= -cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= -cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= -cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= -cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= -cloud.google.com/go/run v1.2.0/go.mod h1:36V1IlDzQ0XxbQjUx6IYbw8H3TJnWvhii963WW3B/bo= -cloud.google.com/go/run v1.3.0/go.mod h1:S/osX/4jIPZGg+ssuqh6GNgg7syixKe3YnprwehzHKU= -cloud.google.com/go/run v1.3.1/go.mod h1:cymddtZOzdwLIAsmS6s+Asl4JoXIDm/K1cpZTxV4Q5s= -cloud.google.com/go/run v1.3.2/go.mod h1:SIhmqArbjdU/D9M6JoHaAqnAMKLFtXaVdNeq04NjnVE= -cloud.google.com/go/run v1.3.3/go.mod h1:WSM5pGyJ7cfYyYbONVQBN4buz42zFqwG67Q3ch07iK4= -cloud.google.com/go/run v1.3.4/go.mod h1:FGieuZvQ3tj1e9GnzXqrMABSuir38AJg5xhiYq+SF3o= -cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= -cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= -cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= -cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= -cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= -cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= -cloud.google.com/go/scheduler v1.10.1/go.mod h1:R63Ldltd47Bs4gnhQkmNDse5w8gBRrhObZ54PxgR2Oo= -cloud.google.com/go/scheduler v1.10.2/go.mod h1:O3jX6HRH5eKCA3FutMw375XHZJudNIKVonSCHv7ropY= -cloud.google.com/go/scheduler v1.10.3/go.mod h1:8ANskEM33+sIbpJ+R4xRfw/jzOG+ZFE8WVLy7/yGvbc= -cloud.google.com/go/scheduler v1.10.4/go.mod h1:MTuXcrJC9tqOHhixdbHDFSIuh7xZF2IysiINDuiq6NI= -cloud.google.com/go/scheduler v1.10.5/go.mod h1:MTuXcrJC9tqOHhixdbHDFSIuh7xZF2IysiINDuiq6NI= -cloud.google.com/go/scheduler v1.10.6/go.mod h1:pe2pNCtJ+R01E06XCDOJs1XvAMbv28ZsQEbqknxGOuE= -cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= -cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= -cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= -cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= -cloud.google.com/go/secretmanager v1.11.1/go.mod h1:znq9JlXgTNdBeQk9TBW/FnR/W4uChEKGeqQWAJ8SXFw= -cloud.google.com/go/secretmanager v1.11.2/go.mod h1:MQm4t3deoSub7+WNwiC4/tRYgDBHJgJPvswqQVB1Vss= -cloud.google.com/go/secretmanager v1.11.3/go.mod h1:0bA2o6FabmShrEy328i67aV+65XoUFFSmVeLBn/51jI= -cloud.google.com/go/secretmanager v1.11.4/go.mod h1:wreJlbS9Zdq21lMzWmJ0XhWW2ZxgPeahsqeV/vZoJ3w= -cloud.google.com/go/secretmanager v1.11.5/go.mod h1:eAGv+DaCHkeVyQi0BeXgAHOU0RdrMeZIASKc+S7VqH4= -cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= -cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= -cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= -cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= -cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= -cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= -cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= -cloud.google.com/go/security v1.15.1/go.mod h1:MvTnnbsWnehoizHi09zoiZob0iCHVcL4AUBj76h9fXA= -cloud.google.com/go/security v1.15.2/go.mod h1:2GVE/v1oixIRHDaClVbHuPcZwAqFM28mXuAKCfMgYIg= -cloud.google.com/go/security v1.15.3/go.mod h1:gQ/7Q2JYUZZgOzqKtw9McShH+MjNvtDpL40J1cT+vBs= -cloud.google.com/go/security v1.15.4/go.mod h1:oN7C2uIZKhxCLiAAijKUCuHLZbIt/ghYEo8MqwD/Ty4= -cloud.google.com/go/security v1.15.5/go.mod h1:KS6X2eG3ynWjqcIX976fuToN5juVkF6Ra6c7MPnldtc= -cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= -cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= -cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= -cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= -cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= -cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= -cloud.google.com/go/securitycenter v1.23.0/go.mod h1:8pwQ4n+Y9WCWM278R8W3nF65QtY172h4S8aXyI9/hsQ= -cloud.google.com/go/securitycenter v1.23.1/go.mod h1:w2HV3Mv/yKhbXKwOCu2i8bCuLtNP1IMHuiYQn4HJq5s= -cloud.google.com/go/securitycenter v1.24.1/go.mod h1:3h9IdjjHhVMXdQnmqzVnM7b0wMn/1O/U20eWVpMpZjI= -cloud.google.com/go/securitycenter v1.24.2/go.mod h1:l1XejOngggzqwr4Fa2Cn+iWZGf+aBLTXtB/vXjy5vXM= -cloud.google.com/go/securitycenter v1.24.3/go.mod h1:l1XejOngggzqwr4Fa2Cn+iWZGf+aBLTXtB/vXjy5vXM= -cloud.google.com/go/securitycenter v1.24.4/go.mod h1:PSccin+o1EMYKcFQzz9HMMnZ2r9+7jbc+LvPjXhpwcU= -cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= -cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= -cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= -cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= -cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= -cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= -cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= -cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= -cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= -cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= -cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= -cloud.google.com/go/servicedirectory v1.10.1/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= -cloud.google.com/go/servicedirectory v1.11.0/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ= -cloud.google.com/go/servicedirectory v1.11.1/go.mod h1:tJywXimEWzNzw9FvtNjsQxxJ3/41jseeILgwU/QLrGI= -cloud.google.com/go/servicedirectory v1.11.2/go.mod h1:KD9hCLhncWRV5jJphwIpugKwM5bn1x0GyVVD4NO8mGg= -cloud.google.com/go/servicedirectory v1.11.3/go.mod h1:LV+cHkomRLr67YoQy3Xq2tUXBGOs5z5bPofdq7qtiAw= -cloud.google.com/go/servicedirectory v1.11.4/go.mod h1:Bz2T9t+/Ehg6x+Y7Ycq5xiShYLD96NfEsWNHyitj1qM= -cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= -cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= -cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= -cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= -cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= -cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= -cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= -cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= -cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= -cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= -cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= -cloud.google.com/go/shell v1.7.1/go.mod h1:u1RaM+huXFaTojTbW4g9P5emOrrmLE69KrxqQahKn4g= -cloud.google.com/go/shell v1.7.2/go.mod h1:KqRPKwBV0UyLickMn0+BY1qIyE98kKyI216sH/TuHmc= -cloud.google.com/go/shell v1.7.3/go.mod h1:cTTEz/JdaBsQAeTQ3B6HHldZudFoYBOqjteev07FbIc= -cloud.google.com/go/shell v1.7.4/go.mod h1:yLeXB8eKLxw0dpEmXQ/FjriYrBijNsONpwnWsdPqlKM= -cloud.google.com/go/shell v1.7.5/go.mod h1:hL2++7F47/IfpfTO53KYf1EC+F56k3ThfNEXd4zcuiE= -cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= -cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= -cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= -cloud.google.com/go/spanner v1.47.0/go.mod h1:IXsJwVW2j4UKs0eYDqodab6HgGuA1bViSqW4uH9lfUI= -cloud.google.com/go/spanner v1.49.0/go.mod h1:eGj9mQGK8+hkgSVbHNQ06pQ4oS+cyc4tXXd6Dif1KoM= -cloud.google.com/go/spanner v1.50.0/go.mod h1:eGj9mQGK8+hkgSVbHNQ06pQ4oS+cyc4tXXd6Dif1KoM= -cloud.google.com/go/spanner v1.51.0/go.mod h1:c5KNo5LQ1X5tJwma9rSQZsXNBDNvj4/n8BVc3LNahq0= -cloud.google.com/go/spanner v1.53.0/go.mod h1:liG4iCeLqm5L3fFLU5whFITqP0e0orsAW1uUSrd4rws= -cloud.google.com/go/spanner v1.53.1/go.mod h1:liG4iCeLqm5L3fFLU5whFITqP0e0orsAW1uUSrd4rws= -cloud.google.com/go/spanner v1.54.0/go.mod h1:wZvSQVBgngF0Gq86fKup6KIYmN2be7uOKjtK97X+bQU= -cloud.google.com/go/spanner v1.55.0/go.mod h1:HXEznMUVhC+PC+HDyo9YFG2Ajj5BQDkcbqB9Z2Ffxi0= -cloud.google.com/go/spanner v1.56.0/go.mod h1:DndqtUKQAt3VLuV2Le+9Y3WTnq5cNKrnLb/Piqcj+h0= -cloud.google.com/go/spanner v1.57.0/go.mod h1:aXQ5QDdhPRIqVhYmnkAdwPYvj/DRN0FguclhEWw+jOo= -cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= -cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= -cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= -cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= -cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= -cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= -cloud.google.com/go/speech v1.17.1/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= -cloud.google.com/go/speech v1.19.0/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo= -cloud.google.com/go/speech v1.19.1/go.mod h1:WcuaWz/3hOlzPFOVo9DUsblMIHwxP589y6ZMtaG+iAA= -cloud.google.com/go/speech v1.19.2/go.mod h1:2OYFfj+Ch5LWjsaSINuCZsre/789zlcCI3SY4oAi2oI= -cloud.google.com/go/speech v1.20.1/go.mod h1:wwolycgONvfz2EDU8rKuHRW3+wc9ILPsAWoikBEWavY= -cloud.google.com/go/speech v1.21.0/go.mod h1:wwolycgONvfz2EDU8rKuHRW3+wc9ILPsAWoikBEWavY= -cloud.google.com/go/speech v1.21.1/go.mod h1:E5GHZXYQlkqWQwY5xRSLHw2ci5NMQNG52FfMU1aZrIA= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= -cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= -cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= -cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= -cloud.google.com/go/storage v1.37.0/go.mod h1:i34TiT2IhiNDmcj65PqwCjcoUX7Z5pLzS8DEmoiFq1k= -cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY= -cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= -cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= -cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= -cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= -cloud.google.com/go/storagetransfer v1.10.0/go.mod h1:DM4sTlSmGiNczmV6iZyceIh2dbs+7z2Ayg6YAiQlYfA= -cloud.google.com/go/storagetransfer v1.10.1/go.mod h1:rS7Sy0BtPviWYTTJVWCSV4QrbBitgPeuK4/FKa4IdLs= -cloud.google.com/go/storagetransfer v1.10.2/go.mod h1:meIhYQup5rg9juQJdyppnA/WLQCOguxtk1pr3/vBWzA= -cloud.google.com/go/storagetransfer v1.10.3/go.mod h1:Up8LY2p6X68SZ+WToswpQbQHnJpOty/ACcMafuey8gc= -cloud.google.com/go/storagetransfer v1.10.4/go.mod h1:vef30rZKu5HSEf/x1tK3WfWrL0XVoUQN/EPDRGPzjZs= -cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= -cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= -cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= -cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= -cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= -cloud.google.com/go/talent v1.6.2/go.mod h1:CbGvmKCG61mkdjcqTcLOkb2ZN1SrQI8MDyma2l7VD24= -cloud.google.com/go/talent v1.6.3/go.mod h1:xoDO97Qd4AK43rGjJvyBHMskiEf3KulgYzcH6YWOVoo= -cloud.google.com/go/talent v1.6.4/go.mod h1:QsWvi5eKeh6gG2DlBkpMaFYZYrYUnIpo34f6/V5QykY= -cloud.google.com/go/talent v1.6.5/go.mod h1:Mf5cma696HmE+P2BWJ/ZwYqeJXEeU0UqjHFXVLadEDI= -cloud.google.com/go/talent v1.6.6/go.mod h1:y/WQDKrhVz12WagoarpAIyKKMeKGKHWPoReZ0g8tseQ= -cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= -cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= -cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= -cloud.google.com/go/texttospeech v1.7.1/go.mod h1:m7QfG5IXxeneGqTapXNxv2ItxP/FS0hCZBwXYqucgSk= -cloud.google.com/go/texttospeech v1.7.2/go.mod h1:VYPT6aTOEl3herQjFHYErTlSZJ4vB00Q2ZTmuVgluD4= -cloud.google.com/go/texttospeech v1.7.3/go.mod h1:Av/zpkcgWfXlDLRYob17lqMstGZ3GqlvJXqKMp2u8so= -cloud.google.com/go/texttospeech v1.7.4/go.mod h1:vgv0002WvR4liGuSd5BJbWy4nDn5Ozco0uJymY5+U74= -cloud.google.com/go/texttospeech v1.7.5/go.mod h1:tzpCuNWPwrNJnEa4Pu5taALuZL4QRRLcb+K9pbhXT6M= -cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= -cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= -cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= -cloud.google.com/go/tpu v1.6.1/go.mod h1:sOdcHVIgDEEOKuqUoi6Fq53MKHJAtOwtz0GuKsWSH3E= -cloud.google.com/go/tpu v1.6.2/go.mod h1:NXh3NDwt71TsPZdtGWgAG5ThDfGd32X1mJ2cMaRlVgU= -cloud.google.com/go/tpu v1.6.3/go.mod h1:lxiueqfVMlSToZY1151IaZqp89ELPSrk+3HIQ5HRkbY= -cloud.google.com/go/tpu v1.6.4/go.mod h1:NAm9q3Rq2wIlGnOhpYICNI7+bpBebMJbh0yyp3aNw1Y= -cloud.google.com/go/tpu v1.6.5/go.mod h1:P9DFOEBIBhuEcZhXi+wPoVy/cji+0ICFi4TtTkMHSSs= -cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= -cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= -cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= -cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= -cloud.google.com/go/trace v1.10.1/go.mod h1:gbtL94KE5AJLH3y+WVpfWILmqgc6dXcqgNXdOPAQTYk= -cloud.google.com/go/trace v1.10.2/go.mod h1:NPXemMi6MToRFcSxRl2uDnu/qAlAQ3oULUphcHGh1vA= -cloud.google.com/go/trace v1.10.3/go.mod h1:Ke1bgfc73RV3wUFml+uQp7EsDw4dGaETLxB7Iq/r4CY= -cloud.google.com/go/trace v1.10.4/go.mod h1:Nso99EDIK8Mj5/zmB+iGr9dosS/bzWCJ8wGmE6TXNWY= -cloud.google.com/go/trace v1.10.5/go.mod h1:9hjCV1nGBCtXbAE4YK7OqJ8pmPYSxPA0I67JwRd5s3M= -cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= -cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= -cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= -cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/translate v1.8.1/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.8.2/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.9.0/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs= -cloud.google.com/go/translate v1.9.1/go.mod h1:TWIgDZknq2+JD4iRcojgeDtqGEp154HN/uL6hMvylS8= -cloud.google.com/go/translate v1.9.2/go.mod h1:E3Tc6rUTsQkVrXW6avbUhKJSr7ZE3j7zNmqzXKHqRrY= -cloud.google.com/go/translate v1.9.3/go.mod h1:Kbq9RggWsbqZ9W5YpM94Q1Xv4dshw/gr/SHfsl5yCZ0= -cloud.google.com/go/translate v1.10.0/go.mod h1:Kbq9RggWsbqZ9W5YpM94Q1Xv4dshw/gr/SHfsl5yCZ0= -cloud.google.com/go/translate v1.10.1/go.mod h1:adGZcQNom/3ogU65N9UXHOnnSvjPwA/jKQUMnsYXOyk= -cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= -cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= -cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= -cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= -cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/video v1.17.1/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= -cloud.google.com/go/video v1.19.0/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU= -cloud.google.com/go/video v1.20.0/go.mod h1:U3G3FTnsvAGqglq9LxgqzOiBc/Nt8zis8S+850N2DUM= -cloud.google.com/go/video v1.20.1/go.mod h1:3gJS+iDprnj8SY6pe0SwLeC5BUW80NjhwX7INWEuWGU= -cloud.google.com/go/video v1.20.2/go.mod h1:lrixr5JeKNThsgfM9gqtwb6Okuqzfo4VrY2xynaViTA= -cloud.google.com/go/video v1.20.3/go.mod h1:TnH/mNZKVHeNtpamsSPygSR0iHtvrR/cW1/GDjN5+GU= -cloud.google.com/go/video v1.20.4/go.mod h1:LyUVjyW+Bwj7dh3UJnUGZfyqjEto9DnrvTe1f/+QrW0= -cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= -cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= -cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= -cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= -cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= -cloud.google.com/go/videointelligence v1.11.1/go.mod h1:76xn/8InyQHarjTWsBR058SmlPCwQjgcvoW0aZykOvo= -cloud.google.com/go/videointelligence v1.11.2/go.mod h1:ocfIGYtIVmIcWk1DsSGOoDiXca4vaZQII1C85qtoplc= -cloud.google.com/go/videointelligence v1.11.3/go.mod h1:tf0NUaGTjU1iS2KEkGWvO5hRHeCkFK3nPo0/cOZhZAo= -cloud.google.com/go/videointelligence v1.11.4/go.mod h1:kPBMAYsTPFiQxMLmmjpcZUMklJp3nC9+ipJJtprccD8= -cloud.google.com/go/videointelligence v1.11.5/go.mod h1:/PkeQjpRponmOerPeJxNPuxvi12HlW7Em0lJO14FC3I= -cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= -cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= -cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= -cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= -cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= -cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= -cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= -cloud.google.com/go/vision/v2 v2.7.2/go.mod h1:jKa8oSYBWhYiXarHPvP4USxYANYUEdEsQrloLjrSwJU= -cloud.google.com/go/vision/v2 v2.7.3/go.mod h1:V0IcLCY7W+hpMKXK1JYE0LV5llEqVmj+UJChjvA1WsM= -cloud.google.com/go/vision/v2 v2.7.4/go.mod h1:ynDKnsDN/0RtqkKxQZ2iatv3Dm9O+HfRb5djl7l4Vvw= -cloud.google.com/go/vision/v2 v2.7.5/go.mod h1:GcviprJLFfK9OLf0z8Gm6lQb6ZFUulvpZws+mm6yPLM= -cloud.google.com/go/vision/v2 v2.7.6/go.mod h1:ZkvWTVNPBU3YZYzgF9Y1jwEbD1NBOCyJn0KFdQfE6Bw= -cloud.google.com/go/vision/v2 v2.8.0/go.mod h1:ocqDiA2j97pvgogdyhoxiQp2ZkDCyr0HWpicywGGRhU= -cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= -cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= -cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= -cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= -cloud.google.com/go/vmmigration v1.7.1/go.mod h1:WD+5z7a/IpZ5bKK//YmT9E047AD+rjycCAvyMxGJbro= -cloud.google.com/go/vmmigration v1.7.2/go.mod h1:iA2hVj22sm2LLYXGPT1pB63mXHhrH1m/ruux9TwWLd8= -cloud.google.com/go/vmmigration v1.7.3/go.mod h1:ZCQC7cENwmSWlwyTrZcWivchn78YnFniEQYRWQ65tBo= -cloud.google.com/go/vmmigration v1.7.4/go.mod h1:yBXCmiLaB99hEl/G9ZooNx2GyzgsjKnw5fWcINRgD70= -cloud.google.com/go/vmmigration v1.7.5/go.mod h1:pkvO6huVnVWzkFioxSghZxIGcsstDvYiVCxQ9ZH3eYI= -cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= -cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= -cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= -cloud.google.com/go/vmwareengine v0.4.1/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= -cloud.google.com/go/vmwareengine v1.0.0/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0= -cloud.google.com/go/vmwareengine v1.0.1/go.mod h1:aT3Xsm5sNx0QShk1Jc1B8OddrxAScYLwzVoaiXfdzzk= -cloud.google.com/go/vmwareengine v1.0.2/go.mod h1:xMSNjIk8/itYrz1JA8nV3Ajg4L4n3N+ugP8JKzk3OaA= -cloud.google.com/go/vmwareengine v1.0.3/go.mod h1:QSpdZ1stlbfKtyt6Iu19M6XRxjmXO+vb5a/R6Fvy2y4= -cloud.google.com/go/vmwareengine v1.1.1/go.mod h1:nMpdsIVkUrSaX8UvmnBhzVzG7PPvNYc5BszcvIVudYs= -cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= -cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= -cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= -cloud.google.com/go/vpcaccess v1.7.1/go.mod h1:FogoD46/ZU+JUBX9D606X21EnxiszYi2tArQwLY4SXs= -cloud.google.com/go/vpcaccess v1.7.2/go.mod h1:mmg/MnRHv+3e8FJUjeSibVFvQF1cCy2MsFaFqxeY1HU= -cloud.google.com/go/vpcaccess v1.7.3/go.mod h1:YX4skyfW3NC8vI3Fk+EegJnlYFatA+dXK4o236EUCUc= -cloud.google.com/go/vpcaccess v1.7.4/go.mod h1:lA0KTvhtEOb/VOdnH/gwPuOzGgM+CWsmGu6bb4IoMKk= -cloud.google.com/go/vpcaccess v1.7.5/go.mod h1:slc5ZRvvjP78c2dnL7m4l4R9GwL3wDLcpIWz6P/ziig= -cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= -cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= -cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= -cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= -cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= -cloud.google.com/go/webrisk v1.9.1/go.mod h1:4GCmXKcOa2BZcZPn6DCEvE7HypmEJcJkr4mtM+sqYPc= -cloud.google.com/go/webrisk v1.9.2/go.mod h1:pY9kfDgAqxUpDBOrG4w8deLfhvJmejKB0qd/5uQIPBc= -cloud.google.com/go/webrisk v1.9.3/go.mod h1:RUYXe9X/wBDXhVilss7EDLW9ZNa06aowPuinUOPCXH8= -cloud.google.com/go/webrisk v1.9.4/go.mod h1:w7m4Ib4C+OseSr2GL66m0zMBywdrVNTDKsdEsfMl7X0= -cloud.google.com/go/webrisk v1.9.5/go.mod h1:aako0Fzep1Q714cPEM5E+mtYX8/jsfegAuS8aivxy3U= -cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= -cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= -cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= -cloud.google.com/go/websecurityscanner v1.6.1/go.mod h1:Njgaw3rttgRHXzwCB8kgCYqv5/rGpFCsBOvPbYgszpg= -cloud.google.com/go/websecurityscanner v1.6.2/go.mod h1:7YgjuU5tun7Eg2kpKgGnDuEOXWIrh8x8lWrJT4zfmas= -cloud.google.com/go/websecurityscanner v1.6.3/go.mod h1:x9XANObUFR+83Cya3g/B9M/yoHVqzxPnFtgF8yYGAXw= -cloud.google.com/go/websecurityscanner v1.6.4/go.mod h1:mUiyMQ+dGpPPRkHgknIZeCzSHJ45+fY4F52nZFDHm2o= -cloud.google.com/go/websecurityscanner v1.6.5/go.mod h1:QR+DWaxAz2pWooylsBF854/Ijvuoa3FCyS1zBa1rAVQ= -cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= -cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= -cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= -cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= -cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= -cloud.google.com/go/workflows v1.11.1/go.mod h1:Z+t10G1wF7h8LgdY/EmRcQY8ptBD/nvofaL6FqlET6g= -cloud.google.com/go/workflows v1.12.0/go.mod h1:PYhSk2b6DhZ508tj8HXKaBh+OFe+xdl0dHF/tJdzPQM= -cloud.google.com/go/workflows v1.12.1/go.mod h1:5A95OhD/edtOhQd/O741NSfIMezNTbCwLM1P1tBRGHM= -cloud.google.com/go/workflows v1.12.2/go.mod h1:+OmBIgNqYJPVggnMo9nqmizW0qEXHhmnAzK/CnBqsHc= -cloud.google.com/go/workflows v1.12.3/go.mod h1:fmOUeeqEwPzIU81foMjTRQIdwQHADi/vEr1cx9R1m5g= -cloud.google.com/go/workflows v1.12.4/go.mod h1:yQ7HUqOkdJK4duVtMeBCAOPiN1ZF1E9pAMX51vpwB/w= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= -git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= -github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= -github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= -github.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= -github.com/alecthomas/participle/v2 v2.0.0/go.mod h1:rAKZdJldHu8084ojcWevWAL8KmEU+AT+Olodb+WoN2Y= -github.com/alecthomas/participle/v2 v2.1.0/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= -github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= -github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= -github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg= -github.com/apache/arrow/go/v12 v12.0.1/go.mod h1:weuTY7JvTG/HDPtMQxEUp7pU73vkLWMLpY67QwZ/WWw= -github.com/apache/arrow/go/v14 v14.0.2/go.mod h1:u3fgh3EdgN/YQ8cVQRguVW3R+seMybFg8QBQ5LU+eBY= -github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= -github.com/apache/thrift v0.17.0/go.mod h1:OLxhMRJxomX+1I/KUw03qoV3mMz16BwaKI+d4fPBx7Q= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230428030218-4003588d1b74/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= -github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= -github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= -github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= -github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= -github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= -github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= -github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87KZaeN4x9zpL9Qt8fQC7d+vs= -github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= -github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-yaml v1.9.8/go.mod h1:JubOolP3gh0HpiBc4BLRD4YmjEjHAmIIB2aaXKkTfoE= -github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= -github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-pkcs11 v0.2.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= -github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM= -github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= -github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= -github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= -github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= -github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= -github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= -github.com/googleapis/gax-go/v2 v2.12.1/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= -github.com/googleapis/gax-go/v2 v2.12.2/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= -github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/substrait-io/substrait-go v0.4.2/go.mod h1:qhpnLmrcvAnlZsUyPXZRqldiHapPTXC3t7xFgDi3aQg= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= -go.einride.tech/aip v0.66.0/go.mod h1:qAhMsfT7plxBX+Oy7Huol6YUvZ0ZzdUz26yZsQwfl1M= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0/go.mod h1:tIKj3DbO8N9Y2xo52og3irLsPI4GW02DSMtrVgNMgxg= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0/go.mod h1:rdENBZMT2OE6Ne/KLwpiXudnAsbdrdBaqBvTN8M8BgA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= -go.opentelemetry.io/otel v1.23.0/go.mod h1:YCycw9ZeKhcJFrb34iVSkyT0iczq/zYDtZYFufObyB0= -go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= -go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= -go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= -go.opentelemetry.io/otel/metric v1.23.0/go.mod h1:MqUW2X2a6Q8RN96E2/nqNoT+z9BSms20Jb7Bbp+HiTo= -go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= -go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= -go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= -go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= -go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= -go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= -go.opentelemetry.io/otel/trace v1.23.0/go.mod h1:GSGTbIClEsuZrGIzoEHqsVfxgn5UkggkflQwDScNUsk= -go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= -golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= -golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= -golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= -golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= -golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= -golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= -gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= -google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= -google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= -google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= -google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= -google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= -google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= -google.golang.org/api v0.118.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjYK+5E= -google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= -google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4= -google.golang.org/api v0.125.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= -google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= -google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750= -google.golang.org/api v0.139.0/go.mod h1:CVagp6Eekz9CjGZ718Z+sloknzkDJE7Vc1Ckj9+viBk= -google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= -google.golang.org/api v0.150.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg= -google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk= -google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g= -google.golang.org/api v0.160.0/go.mod h1:0mu0TpK33qnydLvWqbImq2b1eQ5FHRSDCBzAxX9ZHyw= -google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= -google.golang.org/api v0.164.0/go.mod h1:2OatzO7ZDQsoS7IFf3rvsE17/TldiU3F/zxFHeqUB5o= -google.golang.org/api v0.166.0/go.mod h1:4FcBc686KFi7QI/U51/2GKKevfZMpM17sCdibqe/bSA= -google.golang.org/api v0.169.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= -google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= -google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= -google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= -google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= -google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY= -google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto v0.0.0-20230629202037-9506855d4529/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y= -google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0= -google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= -google.golang.org/genproto v0.0.0-20230821184602-ccc8af3d0e93/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:CCviP9RmpZ1mxVr8MUjCnSiY09IbAXZxhLE6EhHIdPU= -google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= -google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:EMfReVxb80Dq1hhioy0sOsY9jCE46YDgHlJ7fWVUWRE= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= -google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= -google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f/go.mod h1:nWSwAFPb+qfNJXsoeO3Io7zf4tMSfN8EA8RlDA04GhY= -google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= -google.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY= -google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0= -google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k= -google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= -google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= -google.golang.org/genproto v0.0.0-20240205150955-31a09d347014/go.mod h1:xEgQu1e4stdSSsxPDK8Azkrk/ECl5HvdPf6nbZrTS5M= -google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q= -google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= -google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= -google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:RdyHbowztCGQySiCvQPgWQWgWhGnouTdCflKoDBt32U= -google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= -google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww= -google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= -google.golang.org/genproto/googleapis/api v0.0.0-20231030173426-d783a09b4405/go.mod h1:oT32Z4o8Zv2xPQTg0pbVaPr0MPOH6f14RgXt7zfIpwg= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= -google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI= -google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3/go.mod h1:k2dtGpRrbsSyKcNPKKI5sstZkrNCZwpU/ns96JoHbGg= -google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c= -google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= -google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= -google.golang.org/genproto/googleapis/api v0.0.0-20240122161410-6c6643bf1457/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= -google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= -google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= -google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014/go.mod h1:rbHMSEDyoYX62nRVLOCc4Qt1HbsdytAYoVwgjiOhF3I= -google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:PVreiBMirk8ypES6aw9d4p6iiBNSIfZEBqr3UGoAi2E= -google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= -google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y= -google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577/go.mod h1:NjCQG/D8JandXxM57PZbAJL1DCNL6EypA0vPPwfsc7c= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20231212172506-995d672761c0/go.mod h1:guYXGPwC6jwxgWKW5Y405fKWOFNwlvUlUnzyp9i0uqo= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:ZSvZ8l+AWJwXw91DoTjWjaVLpWU6o0eZ4YLYpH8aLeQ= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:SCz6T5xjNXM4QFPRwxHcfChp7V+9DcXR3ay2TkHR8Tg= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20240205150955-31a09d347014/go.mod h1:EhZbXt+eY4Yr3YVaEGLdNZF5viWowOJZ8KTPqjYMKzg= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:om8Bj876Z0v9ei+RD1LnEWig7vpHQ371PUqsgjmLQEA= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:vh/N7795ftP0AkN1w8XKqN4w1OdUKXW5Eummda+ofv8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230731190214-cbb8c96f2d6d/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920183334-c177e329c48b/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240122161410-6c6643bf1457/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014/go.mod h1:SaPjaZGWb0lPqs6Ittu0spdfrOArqji4ZdeP5IC/9N4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240228201840-1f18d85a4ec2/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= -google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= -google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= -google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= -google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= -google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= -google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= -google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= -google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= -google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= -google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= -google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= -modernc.org/cc/v3 v3.38.1/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= -modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= -modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= -modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= -modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI= -modernc.org/ccgo/v3 v3.0.0-20220910160915-348f15de615a/go.mod h1:8p47QxPkdugex9J4n9P2tLZ9bK01yngIVp00g4nomW0= -modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= -modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= -modernc.org/ccgo/v3 v3.16.13-0.20221017192402-261537637ce8/go.mod h1:fUB3Vn0nVPReA+7IG7yZDfjv1TMWjhQP8gCxrFAtL5g= -modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= -modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= -modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= -modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= -modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA= -modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0= -modernc.org/libc v1.19.0/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= -modernc.org/libc v1.20.3/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= -modernc.org/libc v1.21.2/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI= -modernc.org/libc v1.21.4/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI= -modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= -modernc.org/libc v1.22.4/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= -modernc.org/sqlite v1.18.2/go.mod h1:kvrTLEWgxUcHa2GfHBQtanR1H9ht3hTJNtKpzH9k1u0= -modernc.org/sqlite v1.21.2/go.mod h1:cxbLkB5WS32DnQqeH4h4o1B0eMr8W/y8/RGuxQ3JsC0= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= -modernc.org/tcl v1.13.2/go.mod h1:7CLiGIPo1M8Rv1Mitpv5akc2+8fxUd2y2UzC/MfMzy0= -modernc.org/tcl v1.15.1/go.mod h1:aEjeGJX2gz1oWKOLDVZ2tnEWLUrIn8H+GFu+akoDhqs= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= -modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/examples/grpc-bridge/server/kv/empty.go b/examples/grpc-bridge/server/kv/empty.go deleted file mode 100644 index d0c265a0e2af..000000000000 --- a/examples/grpc-bridge/server/kv/empty.go +++ /dev/null @@ -1,8 +0,0 @@ -// make the kv module is not empty, make go mod tidy happy. -// a kv.pb.go file will be generated by protoc, while running the example. -// also, introduce the empty.go file to import the protobuf package, -// which will be imported from the generated kv.pb.go file. - -package kv - -import _ "github.com/golang/protobuf/proto" diff --git a/examples/grpc-bridge/server/service.go b/examples/grpc-bridge/server/service.go deleted file mode 100644 index 80a335e8f927..000000000000 --- a/examples/grpc-bridge/server/service.go +++ /dev/null @@ -1,64 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "log" - "net" - - "sync" - - "github.com/envoyproxy/envoy/examples/grpc-bridge/server/kv" - "golang.org/x/net/context" - "google.golang.org/grpc" -) - -type KV struct { - sync.Mutex - store map[string]string -} - -func (k *KV) Get(ctx context.Context, in *kv.GetRequest) (*kv.GetResponse, error) { - log.Printf("get: %s", in.Key) - resp := new(kv.GetResponse) - if val, ok := k.store[in.Key]; ok { - resp.Value = val - } - - return resp, nil -} - -func (k *KV) Set(ctx context.Context, in *kv.SetRequest) (*kv.SetResponse, error) { - log.Printf("set: %s = %s", in.Key, in.Value) - k.Lock() - defer k.Unlock() - - k.store[in.Key] = in.Value - - return &kv.SetResponse{Ok: true}, nil -} - -func NewKVStore() (kv *KV) { - kv = &KV{ - store: make(map[string]string), - } - - return -} - -func main() { - port := flag.Int("port", 8081, "grpc port") - - flag.Parse() - - lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) - if err != nil { - log.Fatalf("failed to listen: %v", err) - } - gs := grpc.NewServer() - kv.RegisterKVServer(gs, NewKVStore()) - - log.Printf("starting grpc on :%d\n", *port) - - gs.Serve(lis) -} diff --git a/examples/grpc-bridge/verify.sh b/examples/grpc-bridge/verify.sh deleted file mode 100755 index 29527f93dc2b..000000000000 --- a/examples/grpc-bridge/verify.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -e - -export NAME=grpc-bridge -# this allows us to bring up the stack manually after generating stubs -export MANUAL=true - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - - -run_log "Generate protocol stubs" -"${DOCKER_COMPOSE[@]}" -f docker-compose-protos.yaml up --quiet-pull --build - -ls client/kv/kv_pb2.py -ls server/kv/kv.pb.go - -bring_up_example - -run_log "Set key value foo=bar" -"${DOCKER_COMPOSE[@]}" exec -T grpc-client /client/grpc-kv-client.py set foo bar | grep setf - -run_log "Get key foo" -"${DOCKER_COMPOSE[@]}" exec -T grpc-client /client/grpc-kv-client.py get foo | grep bar diff --git a/examples/gzip/README.md b/examples/gzip/README.md deleted file mode 100644 index 8f8a87470044..000000000000 --- a/examples/gzip/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/gzip.html) diff --git a/examples/gzip/docker-compose.yaml b/examples/gzip/docker-compose.yaml deleted file mode 100644 index a6241e2c18ed..000000000000 --- a/examples/gzip/docker-compose.yaml +++ /dev/null @@ -1,18 +0,0 @@ -services: - - envoy-stats: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - depends_on: - service: - condition: service_healthy - ports: - - "${PORT_PROXY:-10000}:10000" - - "${PORT_STATS0:-9901}:9901" - - "${PORT_STATS1:-9902}:9902" - - service: - build: - context: ../shared/python - target: aiohttp-data-service diff --git a/examples/gzip/envoy.yaml b/examples/gzip/envoy.yaml deleted file mode 100644 index 06cb2ee23d72..000000000000 --- a/examples/gzip/envoy.yaml +++ /dev/null @@ -1,129 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service - http_filters: - - name: envoy.filters.http.compressor - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor - response_direction_config: - common_config: - min_content_length: 100 - content_type: - - application/json - disable_on_etag_header: true - compressor_library: - name: text_optimized - typed_config: - "@type": type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip - memory_level: 3 - window_bits: 10 - - name: envoy.filters.http.decompressor - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.decompressor.v3.Decompressor - decompressor_library: - name: upload - typed_config: - "@type": "type.googleapis.com/envoy.extensions.compression.gzip.decompressor.v3.Gzip" - window_bits: 9 - chunk_size: 8192 - # If this ratio is set too low, then body data will not be decompressed completely. - max_inflate_ratio: 1000 - response_direction_config: - common_config: - enabled: - default_value: false - runtime_key: response_decompressor_enabled - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - address: - socket_address: - address: 0.0.0.0 - port_value: 9902 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/stats/prometheus" - route: - cluster: envoy-stats - http_filters: - - name: envoy.filters.http.compressor - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor - response_direction_config: - common_config: - min_content_length: 100 - content_type: - - text/plain - disable_on_etag_header: true - compressor_library: - name: text_optimized - typed_config: - "@type": type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip - memory_level: 3 - window_bits: 10 - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: envoy-stats - connect_timeout: 0.25s - type: STATIC - load_assignment: - cluster_name: envoy-stats - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: 127.0.0.1 - port_value: 9901 - - name: service - connect_timeout: 0.25s - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service - port_value: 8080 -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 9901 diff --git a/examples/gzip/example.rst b/examples/gzip/example.rst deleted file mode 100644 index 1ae54eec39e4..000000000000 --- a/examples/gzip/example.rst +++ /dev/null @@ -1,124 +0,0 @@ -.. _install_sandboxes_gzip: - -Gzip -==== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -By enabling compression in Envoy you can save some network bandwidth, at the expense of increased processor usage. - -Envoy supports compression and decompression for both requests and responses. - -This sandbox provides examples of response compression and request decompression served over ``HTTP``. Although ``HTTPS`` is not demonstrated, compression can be used for this also. - -The sandbox covers three scenarios: - -- compression of files from an upstream server -- decompression of files from a downstream client -- compression of Envoy's own statistics - -Step 1: Start all of our containers -*********************************** - -Change to the ``examples/gzip`` directory and bring up the docker composition. - -.. code-block:: console - - $ pwd - envoy/examples/gzip - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - Name Command State Ports - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - gzip_envoy-stats_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp,:::10000->10000/tcp, 0.0.0.0:9901->9901/tcp,:::9901->9901/tcp, 0.0.0.0:9902->9902/tcp,:::9902->9902/tcp - gzip_service_1 python3 /code/service.py Up (healthy) - -Step 2: Test Envoy’s compression of upstream files -************************************************** - -The sandbox is configured with two endpoints on port ``10000`` for serving upstream files: - -- ``/file.txt`` -- ``/file.json`` - -Only ``/file.json`` is configured to be compressed. - -Use ``curl`` to check that the response from requesting ``file.json`` contains the ``content-encoding: gzip`` header. - -You will need to add an ``accept-encoding: gzip`` request header. - -.. code-block:: console - - $ curl -si -H "Accept-Encoding: gzip" localhost:10000/file.json | grep "content-encoding" - content-encoding: gzip - -As only files with a content-type of ``application/json`` are configured to be gzipped, the response from requesting ``file.txt`` should not contain the ``content-encoding: gzip`` header, and the file will not be compressed: - -.. code-block:: console - - $ curl -si -H "Accept-Encoding: gzip" localhost:10000/file.txt | grep "content-encoding" - -Step 3: Test Envoy’s decompression of downstream files -****************************************************** - -The sandbox is configured with an endpoint for uploading downstream files: - -- ``/upload`` - -Use ``curl`` to get the compressed file ``file.gz`` - -.. code-block:: console - - $ curl -s -H "Accept-Encoding: gzip" -o file.gz localhost:10000/file.json - -Use ``curl`` to check that the response from uploading ``file.gz`` contains the ``decompressed-size`` header. - -You will need to add the ``content-encoding: gzip`` request header. - -.. code-block:: console - - $ curl -si -H "Content-Encoding: gzip" localhost:10000/upload --data-binary "@file.gz" | grep "decompressed-size" - decompressed-size: 10485760 - -Step 4: Test compression of Envoy’s statistics -********************************************** - -The sandbox is configured with two ports serving Envoy’s admin and statistics interface: - -- ``9901`` exposes the standard admin interface -- ``9902`` exposes a compressed version of the admin interface - -Use ``curl`` to make a request for uncompressed statistics on port ``9901``, it should not contain the ``content-encoding`` header in the response: - -.. code-block:: console - - $ curl -si -H "Accept-Encoding: gzip" localhost:9901/stats/prometheus | grep "content-encoding" - -Now, use ``curl`` to make a request for the compressed statistics: - -.. code-block:: console - - $ curl -si -H "Accept-Encoding: gzip" localhost:9902/stats/prometheus | grep "content-encoding" - content-encoding: gzip - -.. seealso:: - :ref:`Gzip Compression API ` - API and configuration reference for Envoy's gzip compression. - - :ref:`Gzip Decompression API ` - API and configuration reference for Envoy's gzip decompression. - - :ref:`Compression configuration ` - Reference documentation for Envoy's compressor filter. - - :ref:`Decompression configuration ` - Reference documentation for Envoy's decompressor filter. - - :ref:`Envoy admin quick start guide ` - Quick start guide to the Envoy admin interface. diff --git a/examples/gzip/verify.sh b/examples/gzip/verify.sh deleted file mode 100755 index 3ff8fe01760e..000000000000 --- a/examples/gzip/verify.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash -e - -export NAME=gzip -export PORT_PROXY="${GZIP_PORT_PROXY:-10700}" -export PORT_STATS0="${GZIP_PORT_STATS0:-10701}" -export PORT_STATS1="${GZIP_PORT_STATS1:-10702}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Test service: localhost:${PORT_PROXY}/file.json with compression" -responds_with_header \ - "content-encoding: gzip" \ - "http://localhost:${PORT_PROXY}/file.json" \ - -i -H "Accept-Encoding: gzip" - -run_log "Test service: localhost:${PORT_PROXY}/file.txt without compression" -responds_without_header \ - "content-encoding: gzip" \ - "http://localhost:${PORT_PROXY}/file.txt" \ - -i -H "Accept-Encoding: gzip" - -run_log "Test service: localhost:${PORT_PROXY}/upload with decompression" -curl -s -H "Accept-Encoding: gzip" -o file.gz "http://localhost:${PORT_PROXY}/file.json" -responds_with \ - "decompressed-size: 10485760" \ - "http://localhost:${PORT_PROXY}/upload" \ - -X POST -i -H "Content-Encoding: gzip" --data-binary "@file.gz" -rm file.gz - -run_log "Test service: localhost:${PORT_STATS0}/stats/prometheus without compression" -responds_without_header \ - "content-encoding: gzip" \ - "http://localhost:${PORT_STATS0}/stats/prometheus" \ - -i -H "Accept-Encoding: gzip" - -run_log "Test service: localhost:${PORT_STATS1}/stats/prometheus with compression" -responds_with_header \ - "content-encoding: gzip" \ - "http://localhost:${PORT_STATS1}/stats/prometheus" \ - -i -H "Accept-Encoding: gzip" diff --git a/examples/jaeger-tracing/README.md b/examples/jaeger-tracing/README.md deleted file mode 100644 index 5124026acd76..000000000000 --- a/examples/jaeger-tracing/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/jaeger_tracing) diff --git a/examples/jaeger-tracing/docker-compose.yaml b/examples/jaeger-tracing/docker-compose.yaml deleted file mode 100644 index 64e461fb8bb5..000000000000 --- a/examples/jaeger-tracing/docker-compose.yaml +++ /dev/null @@ -1,43 +0,0 @@ -services: - - # jaeger - front-envoy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - depends_on: - service1: - condition: service_healthy - service2: - condition: service_healthy - jaeger: - condition: service_healthy - ports: - - "${PORT_PROXY:-10000}:8000" - - service1: - build: - context: ../shared/python - target: aiohttp-tracing-service3 - volumes: - - ./service1-envoy-jaeger.yaml:/etc/service-envoy.yaml - environment: - - SERVICE_NAME=1 - - service2: - build: - context: ../shared/python - target: aiohttp-tracing-service3 - volumes: - - ./service2-envoy-jaeger.yaml:/etc/service-envoy.yaml - environment: - - SERVICE_NAME=2 - - jaeger: - build: - context: . - dockerfile: ../shared/jaeger/Dockerfile - environment: - - COLLECTOR_ZIPKIN_HOST_PORT=9411 - ports: - - "${PORT_UI:-10000}:16686" diff --git a/examples/jaeger-tracing/envoy.yaml b/examples/jaeger-tracing/envoy.yaml deleted file mode 100644 index b83db10d6598..000000000000 --- a/examples/jaeger-tracing/envoy.yaml +++ /dev/null @@ -1,70 +0,0 @@ -node: - cluster: front-proxy - -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 8000 - traffic_direction: OUTBOUND - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - generate_request_id: true - tracing: - provider: - name: envoy.tracers.zipkin - typed_config: - "@type": type.googleapis.com/envoy.config.trace.v3.ZipkinConfig - collector_cluster: jaeger - collector_endpoint: "/api/v2/spans" - shared_span_context: false - collector_endpoint_version: HTTP_JSON - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service1 - decorator: - operation: checkAvailability - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - use_remote_address: true - clusters: - - name: service1 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service1 - port_value: 8000 - - name: jaeger - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: jaeger - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: jaeger - port_value: 9411 diff --git a/examples/jaeger-tracing/example.rst b/examples/jaeger-tracing/example.rst deleted file mode 100644 index 83c5bbf3a621..000000000000 --- a/examples/jaeger-tracing/example.rst +++ /dev/null @@ -1,98 +0,0 @@ -.. _install_sandboxes_jaeger_tracing: - -Jaeger tracing -============== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -The Jaeger tracing sandbox demonstrates Envoy's :ref:`request tracing ` -capabilities using `Jaeger `_ as the tracing provider. This sandbox -is very similar to the front proxy architecture described above, with one difference: -service1 makes an API call to service2 before returning a response. -The three containers will be deployed inside a virtual network called ``envoymesh``. - -All incoming requests are routed via the front Envoy, which is acting as a reverse proxy -sitting on the edge of the ``envoymesh`` network. Port ``8000`` is exposed -by docker compose (see :download:`docker-compose.yaml <_include/jaeger-tracing/docker-compose.yaml>`). Notice that -all Envoys are configured to collect request traces (e.g., http_connection_manager/config/tracing setup in -:download:`envoy.yaml <_include/jaeger-tracing/envoy.yaml>`) and setup to propagate the spans generated -by the Jaeger tracer to a Jaeger cluster (trace driver setup -in :download:`envoy.yaml <_include/jaeger-tracing/envoy.yaml>`). - -Before routing a request to the appropriate service Envoy or the application, Envoy will take -care of generating the appropriate spans for tracing (parent/child context spans). -At a high-level, each span records the latency of upstream API calls as well as information -needed to correlate the span with other related spans (e.g., the trace ID). - -One of the most important benefits of tracing from Envoy is that it will take care of -propagating the traces to the Jaeger service cluster. However, in order to fully take advantage -of tracing, the application has to propagate trace headers that Envoy generates, while making -calls to other services. In the sandbox we have provided, the simple ``aiohttp`` app -(see trace function in :download:`examples/shared/python/tracing/service.py <_include/shared/python/tracing/service.py>`) acting as service1 propagates -the trace headers while making an outbound call to service2. - -Step 1: Build the sandbox -************************* - -To build this sandbox example, and start the example apps run the following commands: - -.. code-block:: console - - $ pwd - envoy/examples/jaeger-tracing - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- - jaeger-tracing_front-envoy_1 /docker-entrypoint.sh /bin ... Up 10000/tcp, 0.0.0.0:8000->8000/tcp - jaeger-tracing_jaeger_1 /go/bin/all-in-one-linux - ... Up 14250/tcp, 14268/tcp, 0.0.0.0:16686->16686/tcp, 5775/udp, 5778/tcp, 6831/udp, 6832/udp, 9411/tcp - jaeger-tracing_service1_1 /bin/sh -c /usr/local/bin/ ... Up 10000/tcp - jaeger-tracing_service2_1 /bin/sh -c /usr/local/bin/ ... Up 10000/tcp - -Step 2: Generate some load -************************** - -You can now send a request to service1 via the front-envoy as follows: - -.. code-block:: console - - $ curl -v localhost:8000/trace/1 - * Trying 192.168.99.100... - * Connected to 192.168.99.100 (192.168.99.100) port 8000 (#0) - > GET /trace/1 HTTP/1.1 - > Host: 192.168.99.100:8000 - > User-Agent: curl/7.54.0 - > Accept: */* - > - < HTTP/1.1 200 OK - < content-type: text/html; charset=utf-8 - < content-length: 89 - < x-envoy-upstream-service-time: 9 - < server: envoy - < date: Fri, 26 Aug 2018 19:39:19 GMT - < - Hello from behind Envoy (service 1)! hostname: f26027f1ce28 resolvedhostname: 172.19.0.6 - * Connection #0 to host 192.168.99.100 left intact - -Step 3: View the traces in Jaeger UI -************************************ - -Point your browser to http://localhost:16686 . You should see the Jaeger dashboard. -Set the service to "front-proxy" and hit 'Find Traces'. You should see traces from the front-proxy. -Click on a trace to explore the path taken by the request from front-proxy to service1 -to service2, as well as the latency incurred at each hop. - -.. seealso:: - - :ref:`Request tracing ` - Learn more about using Envoy's request tracing. - - `Jaeger `_ - Jaeger tracing website. diff --git a/examples/jaeger-tracing/service1-envoy-jaeger.yaml b/examples/jaeger-tracing/service1-envoy-jaeger.yaml deleted file mode 100644 index e8828675c01b..000000000000 --- a/examples/jaeger-tracing/service1-envoy-jaeger.yaml +++ /dev/null @@ -1,120 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 8000 - traffic_direction: INBOUND - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - tracing: - provider: - name: envoy.tracers.zipkin - typed_config: - "@type": type.googleapis.com/envoy.config.trace.v3.ZipkinConfig - collector_cluster: jaeger - collector_endpoint: "/api/v2/spans" - shared_span_context: false - collector_endpoint_version: HTTP_JSON - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: service1_route - virtual_hosts: - - name: service1 - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: local_service - decorator: - operation: checkAvailability - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - address: - socket_address: - address: 0.0.0.0 - port_value: 9000 - traffic_direction: OUTBOUND - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - tracing: - provider: - name: envoy.tracers.zipkin - typed_config: - "@type": type.googleapis.com/envoy.config.trace.v3.ZipkinConfig - collector_cluster: jaeger - collector_endpoint: "/api/v2/spans" - shared_span_context: false - collector_endpoint_version: HTTP_JSON - codec_type: AUTO - stat_prefix: egress_http - route_config: - name: service2_route - virtual_hosts: - - name: service2 - domains: - - "*" - routes: - - match: - prefix: "/trace/2" - route: - cluster: service2 - decorator: - operation: checkStock - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: local_service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: local_service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: 127.0.0.1 - port_value: 8080 - - name: service2 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service2 - port_value: 8000 - - name: jaeger - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: jaeger - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: jaeger - port_value: 9411 -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 8001 diff --git a/examples/jaeger-tracing/service2-envoy-jaeger.yaml b/examples/jaeger-tracing/service2-envoy-jaeger.yaml deleted file mode 100644 index fba4635d154a..000000000000 --- a/examples/jaeger-tracing/service2-envoy-jaeger.yaml +++ /dev/null @@ -1,70 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 8000 - traffic_direction: INBOUND - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - tracing: - provider: - name: envoy.tracers.zipkin - typed_config: - "@type": type.googleapis.com/envoy.config.trace.v3.ZipkinConfig - collector_cluster: jaeger - collector_endpoint: "/api/v2/spans" - shared_span_context: false - collector_endpoint_version: HTTP_JSON - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: service2 - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: local_service - decorator: - operation: checkStock - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: local_service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: local_service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: 127.0.0.1 - port_value: 8080 - - name: jaeger - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: jaeger - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: jaeger - port_value: 9411 -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 8001 diff --git a/examples/jaeger-tracing/verify.sh b/examples/jaeger-tracing/verify.sh deleted file mode 100755 index 9349c8f5280a..000000000000 --- a/examples/jaeger-tracing/verify.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -e - -export NAME=jaeger-tracing -export PORT_PROXY="${JAEGER_PORT_PROXY:-11010}" -export PORT_UI="${JAEGER_PORT_UI:-11011}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -wait_for 10 bash -c "responds_with Hello http://localhost:${PORT_PROXY}/trace/1" - -run_log "Test services" -responds_with \ - Hello \ - "http://localhost:${PORT_PROXY}/trace/1" - -run_log "Test Jaeger UI" -responds_with \ - "" \ - "http://localhost:${PORT_UI}" diff --git a/examples/kafka/Dockerfile-kafka b/examples/kafka/Dockerfile-kafka deleted file mode 100644 index 08e3c37aab9e..000000000000 --- a/examples/kafka/Dockerfile-kafka +++ /dev/null @@ -1 +0,0 @@ -FROM confluentinc/cp-kafka:latest@sha256:161aa5a125ebf49a88732bb3f2d37aa7605d4b758434503b1472c445f3132dc9 diff --git a/examples/kafka/Dockerfile-zookeeper b/examples/kafka/Dockerfile-zookeeper deleted file mode 100644 index 85eab02adeca..000000000000 --- a/examples/kafka/Dockerfile-zookeeper +++ /dev/null @@ -1 +0,0 @@ -FROM confluentinc/cp-zookeeper:latest@sha256:f89d332ecee856fe0bf2f1f8d936cb2603c3339d6417a97743d9fdac310f6656 diff --git a/examples/kafka/README.md b/examples/kafka/README.md deleted file mode 100644 index 34c0a473e71c..000000000000 --- a/examples/kafka/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/kafka) diff --git a/examples/kafka/docker-compose.yaml b/examples/kafka/docker-compose.yaml deleted file mode 100644 index c07ead7cd307..000000000000 --- a/examples/kafka/docker-compose.yaml +++ /dev/null @@ -1,54 +0,0 @@ -services: - - kafka-client: - build: - context: . - dockerfile: Dockerfile-kafka - restart: "no" - deploy: - replicas: 0 - - proxy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_VARIANT: contrib-dev - ports: - - "${PORT_PROXY:-10000}:10000" - - "${PORT_ADMIN:-8001}:8001" - - kafka-server: - build: - context: . - dockerfile: Dockerfile-kafka - depends_on: - zookeeper: - condition: service_healthy - environment: - KAFKA_BROKER_ID: 1 - KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 - # This Kafka server instance sets up two listener sockets: - # - external one for client traffic (this traffic will go through Envoy proxy), - # - internal one for cluster traffic (if we add more brokers). - KAFKA_LISTENERS: INTERNAL://kafka-server:9092,EXTERNAL://kafka-server:10000 - # Advertised listener value needs to be equal to Envoy's listener - # (will make clients discovering this broker talk to it through Envoy). - KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka-server:9092,EXTERNAL://proxy:10000 - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT - KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 - - zookeeper: - build: - context: . - dockerfile: Dockerfile-zookeeper - healthcheck: - test: ["CMD", "sh", "-c", "echo ruok | nc 127.0.0.1 2181 || exit -1"] - interval: 5s - timeout: 60s - retries: 120 - environment: - ZOOKEEPER_CLIENT_PORT: 2181 - ZOOKEEPER_TICK_TIME: 2000 - KAFKA_OPTS: "-Dzookeeper.4lw.commands.whitelist=ruok" diff --git a/examples/kafka/envoy.yaml b/examples/kafka/envoy.yaml deleted file mode 100644 index a0425882565f..000000000000 --- a/examples/kafka/envoy.yaml +++ /dev/null @@ -1,38 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 # Host that Kafka clients should connect to. - port_value: 10000 # Port that Kafka clients should connect to. - filter_chains: - - filters: - - name: envoy.filters.network.kafka_broker - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.kafka_broker.v3.KafkaBroker - stat_prefix: kafka_broker - - name: envoy.filters.network.tcp_proxy - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - stat_prefix: kafka_service - cluster: kafka_service - - clusters: - - name: kafka_service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: kafka_server - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - # Kafka server's listener for client traffic ('EXTERNAL'). - address: kafka-server - port_value: 10000 - -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 8001 diff --git a/examples/kafka/example.rst b/examples/kafka/example.rst deleted file mode 100644 index 7b522f2225c4..000000000000 --- a/examples/kafka/example.rst +++ /dev/null @@ -1,132 +0,0 @@ -.. _install_sandboxes_kafka: - -Kafka broker -============ - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -This example demonstrates some basic operations with a Kafka broker proxied through Envoy. - -For your convenience, the :download:`composition <_include/kafka/docker-compose.yaml>` provides -a dockerized Kafka client. - -If you have the ``kafka-console-*`` binaries installed on your host system, you can instead follow -the examples using the host binary with ``--bootstrap-server localhost:10000``. - -Statistics collected by Envoy for the Kafka broker extension and related cluster metrics are also demonstrated. - - -Step 1: Start all of our containers -*********************************** - -Change to the ``examples/kafka`` directory. - -.. code-block:: console - - $ pwd - envoy/examples/kafka - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ----------------------------------------------------------------------------------------------------------------------- - kafka_kafka-server_1 /etc/confluent/docker/run Up 9092/tcp - kafka_proxy_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp, 0.0.0.0:8001->8001/tcp - kafka_zookeeper_1 /etc/confluent/docker/run Up (healthy) 2181/tcp, 2888/tcp, 3888/tcp - - -Step 2: Create a Kafka topic -**************************** - -Start by creating a Kafka topic with the name ``envoy-kafka-broker``: - -.. code-block:: console - - $ export TOPIC="envoy-kafka-broker" - $ docker compose run --rm kafka-client kafka-topics --bootstrap-server proxy:10000 --create --topic $TOPIC - - -Step 3: Check the Kafka topic -***************************** - -You can view the topics that Kafka is aware of with the ``kafka-topics --list`` argument. - -Check that the topic you created exists: - -.. code-block:: console - - $ docker compose run --rm kafka-client kafka-topics --bootstrap-server proxy:10000 --list | grep $TOPIC - - -Step 4: Send a message using the Kafka producer -*********************************************** - -Next, send a message for the topic you have created using the ``kafka-console-producer``: - -.. code-block:: console - - $ export MESSAGE="Welcome to Envoy and Kafka broker filter!" - $ docker compose run --rm kafka-client /bin/bash -c " \ - echo $MESSAGE \ - | kafka-console-producer --request-required-acks 1 --broker-list proxy:10000 --topic $TOPIC" - - -Step 5: Receive a message using the Kafka consumer -************************************************** - -Now you can receive the message using the ``kafka-console-consumer`` : - -.. code-block:: console - - $ docker compose run --rm kafka-client kafka-console-consumer --bootstrap-server proxy:10000 --topic $TOPIC --from-beginning --max-messages 1 | grep "$MESSAGE" - - -Step 6: Check admin ``kafka_broker`` stats -****************************************** - -When you proxy to the Kafka broker, Envoy records various stats. - -You can check the broker stats by querying the Envoy admin interface -(the numbers might differ a little as the kafka-client does not expose precise control over its network traffic): - -.. code-block:: console - - $ curl -s "http://localhost:8001/stats?filter=kafka.kafka_broker" | grep -v ": 0" | grep "_request:" - kafka.kafka_broker.request.api_versions_request: 9 - kafka.kafka_broker.request.create_topics_request: 1 - kafka.kafka_broker.request.fetch_request: 2 - kafka.kafka_broker.request.find_coordinator_request: 8 - kafka.kafka_broker.request.join_group_request: 2 - kafka.kafka_broker.request.leave_group_request: 1 - kafka.kafka_broker.request.list_offsets_request: 1 - kafka.kafka_broker.request.metadata_request: 12 - kafka.kafka_broker.request.offset_fetch_request: 1 - kafka.kafka_broker.request.produce_request: 1 - kafka.kafka_broker.request.sync_group_request: 1 - - -Step 7: Check admin ``kafka_service`` cluster stats -*************************************************** - -Envoy also records cluster stats for the Kafka service: - -.. code-block:: console - - $ curl -s "http://localhost:8001/stats?filter=cluster.kafka_service" | grep -v ": 0" - cluster.kafka_service.max_host_weight: 1 - cluster.kafka_service.membership_healthy: 1 - cluster.kafka_service.membership_total: 1 - -.. seealso:: - - :ref:`Envoy Kafka broker filter ` - Learn more about the Kafka broker filter. - - `Kafka `_ - The Apache Kafka. diff --git a/examples/kafka/verify.sh b/examples/kafka/verify.sh deleted file mode 100755 index efcf0f4afadd..000000000000 --- a/examples/kafka/verify.sh +++ /dev/null @@ -1,81 +0,0 @@ -#!/bin/bash -e - -export NAME=kafka -export PORT_PROXY="${KAFKA_PORT_PROXY:-11100}" -export PORT_ADMIN="${KAFKA_PORT_ADMIN:-11101}" - -# Explicitly specified the service want to start, since the `kafka-client` is expected to -# not start. -UPARGS="proxy kafka-server zookeeper" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -kafka_client () { - "${DOCKER_COMPOSE[@]}" run --rm kafka-client "$@" -} - -TOPIC="envoy-kafka-broker" - -MESSAGE="Welcome to Envoy and Kafka broker filter!" - -run_log "Create a Kafka topic" -kafka_client kafka-topics --bootstrap-server proxy:10000 --create --topic $TOPIC - -run_log "Check the Kafka topic" -kafka_client kafka-topics --bootstrap-server proxy:10000 --list | grep $TOPIC - -run_log "Send a message using the Kafka producer" -kafka_client /bin/bash -c " \ - echo $MESSAGE \ - | kafka-console-producer --request-required-acks 1 --broker-list proxy:10000 --topic $TOPIC" - -run_log "Receive a message using the Kafka consumer" -kafka_client kafka-console-consumer --bootstrap-server proxy:10000 --topic $TOPIC --from-beginning --max-messages 1 | grep "$MESSAGE" - -run_log "Check admin kafka_broker stats" - -# This function verifies whether a given metric exists and has a value > 0. -has_metric_with_at_least_1 () { - local stat response value - stat="$1" - shift - response=$(_curl "http://localhost:${PORT_ADMIN}/stats?filter=${stat}") - # Extract number from rows like 'kafka.kafka_broker.request.api_versions_request: 123'. - value=$(echo "${response}" | grep "${stat}:" | cut -f2 -d':' | tr -d ' ') - re='^[0-9]+$' - [[ ${value} =~ ${re} && ${value} -gt 0 ]] || { - echo "ERROR: metric check for [${stat}]" >&2 - echo "EXPECTED: numeric value greater than 0" >&2 - echo "RECEIVED:" >&2 - echo "${response}" >&2 - return 1 - } -} - -EXPECTED_BROKER_STATS=( - "kafka.kafka_broker.request.api_versions_request" - "kafka.kafka_broker.request.metadata_request" - "kafka.kafka_broker.request.create_topics_request" - "kafka.kafka_broker.request.produce_request" - "kafka.kafka_broker.request.fetch_request" - "kafka.kafka_broker.response.api_versions_response" - "kafka.kafka_broker.response.metadata_response" - "kafka.kafka_broker.response.create_topics_response" - "kafka.kafka_broker.response.produce_response" - "kafka.kafka_broker.response.fetch_response") -for stat in "${EXPECTED_BROKER_STATS[@]}"; do - has_metric_with_at_least_1 "${stat}" -done - -run_log "Check admin kafka_service stats" -EXPECTED_BROKER_STATS=( - "cluster.kafka_service.max_host_weight: 1" - "cluster.kafka_service.membership_healthy: 1" - "cluster.kafka_service.membership_total: 1") -for stat in "${EXPECTED_BROKER_STATS[@]}"; do - filter="$(echo "$stat" | cut -d: -f1)" - responds_with \ - "$stat" \ - "http://localhost:${PORT_ADMIN}/stats?filter=${filter}" -done diff --git a/examples/load-reporting-service/README.md b/examples/load-reporting-service/README.md deleted file mode 100644 index 10335ed5b4ee..000000000000 --- a/examples/load-reporting-service/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/load_reporting_service.html) diff --git a/examples/load-reporting-service/docker-compose.yaml b/examples/load-reporting-service/docker-compose.yaml deleted file mode 100644 index b18d2c0264e6..000000000000 --- a/examples/load-reporting-service/docker-compose.yaml +++ /dev/null @@ -1,27 +0,0 @@ -services: - - envoy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - ports: - - "${PORT_PROXY0:-80}-${PORT_PROXY1:-81}:80" - - "${PORT_ADMIN:-8081}:8081" - depends_on: - http_service: - condition: service_healthy - lrs_server: - condition: service_started - - http_service: - build: - context: ../shared/python - target: aiohttp-tracing-service - - lrs_server: - build: - context: . - dockerfile: ../shared/golang/Dockerfile - target: golang-lrs - volumes: - - /go/src/github.com/envoyproxy/envoy/examples/load-reporting-service diff --git a/examples/load-reporting-service/envoy.yaml b/examples/load-reporting-service/envoy.yaml deleted file mode 100644 index 4e25a23fa31c..000000000000 --- a/examples/load-reporting-service/envoy.yaml +++ /dev/null @@ -1,69 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 80 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: service - domains: - - "*" - routes: - - match: - prefix: "/service" - route: - cluster: local_service - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: local_service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: local_service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: http_service - port_value: 8080 - - name: load_reporting_cluster - type: STRICT_DNS - lb_policy: ROUND_ROBIN - typed_extension_protocol_options: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicit_http_config: - http2_protocol_options: {} - load_assignment: - cluster_name: load_reporting_cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: lrs_server - port_value: 18000 -cluster_manager: - load_stats_config: - api_type: GRPC - grpc_services: - - envoy_grpc: - cluster_name: load_reporting_cluster -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 8081 diff --git a/examples/load-reporting-service/example.rst b/examples/load-reporting-service/example.rst deleted file mode 100644 index 0563ab8ca348..000000000000 --- a/examples/load-reporting-service/example.rst +++ /dev/null @@ -1,90 +0,0 @@ -.. _install_sandboxes_load_reporting_service: - -Load reporting service (``LRS``) -================================ - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - -This simple example demonstrates Envoy's :ref:`Load reporting service (LRS) ` -capability and how to use it. - -Lets say Cluster A (downstream) talks to Cluster B (Upstream) and Cluster C (Upstream). When enabling Load Report for -Cluster A, LRS server should be sending LoadStatsResponse to Cluster A with LoadStatsResponse.Clusters to be B and C. -LRS server will then receive LoadStatsRequests (with total requests, successful requests etc) from Cluster A to Cluster B and -from Cluster A to Cluster C. - -In this example, all incoming requests are routed via Envoy to a simple goLang web server aka http_server. -We scale up two containers and randomly send requests to each. Envoy is configured to initiate the connection with LRS Server. -LRS Server enables the stats by sending LoadStatsResponse. Sending requests to http_server will be counted towards successful -requests and will be visible in LRS Server logs. - -Step 1: Build the sandbox -************************* - -Change to the ``examples/load-reporting-service`` directory. - -Terminal 1 :: - - $ pwd - envoy/examples/load-reporting-service - $ docker compose pull - $ docker compose up --scale http_service=2 - - -Terminal 2 :: - - $ pwd - envoy/examples/load_reporting_service - $ docker compose ps - - Name Command State Ports - ----------------------------------------------------------------------------------------------------------------- - load-reporting-service_http_service_1 /docker-entrypoint.sh /usr ... Up 10000/tcp, 0.0.0.0:81->80/tcp - load-reporting-service_http_service_2 python3 /code/service.py ... Up (healthy) - load-reporting-service_lrs_server_1 go run main.go Up - -Step 2: Start sending stream of HTTP requests -********************************************* - -Terminal 2 :: - - $ pwd - envoy/examples/load_reporting_service - $ bash send_requests.sh - -The script above (:download:`send_requests.sh <_include/load-reporting-service/send_requests.sh>`) sends requests -randomly to each Envoy, which in turn forwards the requests to the backend service. - -Step 3: See Envoy Stats -*********************** - -You should see - -Terminal 1 :: - - ............................ - lrs_server_1 | 2020/02/12 17:08:20 LRS Server is up and running on :18000 - lrs_server_1 | 2020/02/12 17:08:23 Adding new cluster to cache `http_service` with node `0022a319e1e2` - lrs_server_1 | 2020/02/12 17:08:24 Adding new node `2417806c9d9a` to existing cluster `http_service` - lrs_server_1 | 2020/02/12 17:08:25 Creating LRS response for cluster http_service, node 2417806c9d9a with frequency 2 secs - lrs_server_1 | 2020/02/12 17:08:25 Creating LRS response for cluster http_service, node 0022a319e1e2 with frequency 2 secs - http_service_2 | 127.0.0.1 - - [12/Feb/2020 17:09:06] "GET /service HTTP/1.1" 200 - - http_service_1 | 127.0.0.1 - - [12/Feb/2020 17:09:06] "GET /service HTTP/1.1" 200 - - ............................ - lrs_server_1 | 2020/02/12 17:09:07 Got stats from cluster `http_service` node `0022a319e1e2` - cluster_name:"local_service" upstream_locality_stats: total_successful_requests:21 total_issued_requests:21 > load_report_interval: - lrs_server_1 | 2020/02/12 17:09:07 Got stats from cluster `http_service` node `2417806c9d9a` - cluster_name:"local_service" upstream_locality_stats: total_successful_requests:17 total_issued_requests:17 > load_report_interval: - http_service_2 | 127.0.0.1 - - [12/Feb/2020 17:09:07] "GET /service HTTP/1.1" 200 - - http_service_1 | 127.0.0.1 - - [12/Feb/2020 17:09:07] "GET /service HTTP/1.1" 200 - - ............................ - lrs_server_1 | 2020/02/12 17:09:09 Got stats from cluster `http_service` node `0022a319e1e2` - cluster_name:"local_service" upstream_locality_stats: total_successful_requests:3 total_issued_requests:3 > load_report_interval: - lrs_server_1 | 2020/02/12 17:09:09 Got stats from cluster `http_service` node `2417806c9d9a` - cluster_name:"local_service" upstream_locality_stats: total_successful_requests:9 total_issued_requests:9 > load_report_interval: - -.. seealso:: - - :ref:`Load reporting service ` - Overview of Envoy's Load reporting service. - - :ref:`Load reporting service API(V3) ` - The Load reporting service API. diff --git a/examples/load-reporting-service/go.mod b/examples/load-reporting-service/go.mod deleted file mode 100644 index 441ab451d9f8..000000000000 --- a/examples/load-reporting-service/go.mod +++ /dev/null @@ -1,21 +0,0 @@ -module github.com/envoyproxy/envoy/examples/load-reporting-service - -go 1.21 - -toolchain go1.22.5 - -require ( - github.com/envoyproxy/go-control-plane v0.12.0 - github.com/golang/protobuf v1.5.4 - google.golang.org/grpc v1.65.0 -) - -require ( - github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect - github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect - google.golang.org/protobuf v1.34.1 // indirect -) diff --git a/examples/load-reporting-service/go.sum b/examples/load-reporting-service/go.sum deleted file mode 100644 index 0af3b63c8b48..000000000000 --- a/examples/load-reporting-service/go.sum +++ /dev/null @@ -1,22 +0,0 @@ -github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw= -github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= -github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= -github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= -github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= -github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= diff --git a/examples/load-reporting-service/main.go b/examples/load-reporting-service/main.go deleted file mode 100644 index d9e6800066e5..000000000000 --- a/examples/load-reporting-service/main.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "log" - "net" - - "github.com/envoyproxy/envoy/examples/load-reporting-service/server" - gcpLoadStats "github.com/envoyproxy/go-control-plane/envoy/service/load_stats/v3" - "google.golang.org/grpc" -) - -func main() { - // Listening on port 18000 - address := ":18000" - lis, err := net.Listen("tcp", address) - if err != nil { - panic(err) - } - - grpcServer := grpc.NewServer() - xdsServer := server.NewServer() - gcpLoadStats.RegisterLoadReportingServiceServer(grpcServer, xdsServer) - - log.Printf("LRS Server is up and running on %s", address) - err = grpcServer.Serve(lis) - if err != nil { - panic(err) - } -} diff --git a/examples/load-reporting-service/send_requests.sh b/examples/load-reporting-service/send_requests.sh deleted file mode 100644 index 70cede981862..000000000000 --- a/examples/load-reporting-service/send_requests.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -PORT_PROXY0=${PORT_PROXY0:-80} -PORT_PROXY1=${PORT_PROXY1:-81} - -counter=1 -while [ $counter -le 50 ] -do - # generate random Port number to send requests - ports=("${PORT_PROXY0}" "${PORT_PROXY1}") - port=${ports[$RANDOM % ${#ports[@]} ]} - - curl -v "localhost:${port}/service/load" - ((counter++)) -done diff --git a/examples/load-reporting-service/server/lrs_server.go b/examples/load-reporting-service/server/lrs_server.go deleted file mode 100644 index 9c662a94a5c3..000000000000 --- a/examples/load-reporting-service/server/lrs_server.go +++ /dev/null @@ -1,77 +0,0 @@ -package server - -import ( - "log" - "sync" - - gcpLoadStats "github.com/envoyproxy/go-control-plane/envoy/service/load_stats/v3" - "github.com/golang/protobuf/ptypes/duration" -) - -// This is how often Envoy will send the load report -const StatsFrequencyInSeconds = 2 - -// Server handling Load Stats communication -type Server interface { - gcpLoadStats.LoadReportingServiceServer - HandleRequest(stream gcpLoadStats.LoadReportingService_StreamLoadStatsServer, request *gcpLoadStats.LoadStatsRequest) -} - -func NewServer() Server { - return &server{nodesConnected: make(map[string]bool)} -} - -type server struct { - // protects nodesConnected - mu sync.Mutex - - // This cache stores nodes connected to the LRS server - nodesConnected map[string]bool -} - -// Handles incoming stream connections and LoadStatsRequests -func (s *server) StreamLoadStats(stream gcpLoadStats.LoadReportingService_StreamLoadStatsServer) error { - for { - req, err := stream.Recv() - // input stream ended or errored out - if err != nil { - return err - } - - s.HandleRequest(stream, req) - } -} - -func (s *server) HandleRequest(stream gcpLoadStats.LoadReportingService_StreamLoadStatsServer, request *gcpLoadStats.LoadStatsRequest) { - nodeID := request.GetNode().GetId() - - s.mu.Lock() - defer s.mu.Unlock() - - // Check whether any Node has already connected or not. - // If not, add the NodeID to nodesConnected and enable Load Report with given frequency - // If yes, log stats - if _, exist := s.nodesConnected[nodeID]; !exist { - // Add NodeID to the nodesConnected - log.Printf("Adding new new node to cache `%s`", nodeID) - s.nodesConnected[nodeID] = true - - // Initialize Load Reporting - err := stream.Send(&gcpLoadStats.LoadStatsResponse{ - Clusters: []string{"local_service"}, - LoadReportingInterval: &duration.Duration{Seconds: StatsFrequencyInSeconds}, - ReportEndpointGranularity: true, - }) - if err != nil { - log.Panicf("Unable to send response to node %s due to err: %s", nodeID, err) - } - return - } - - // After Load Report is enabled, log the Load Report stats received - for _, clusterStats := range request.ClusterStats { - if len(clusterStats.UpstreamLocalityStats) > 0 { - log.Printf("Got stats from cluster `%s` node `%s` - %s", request.Node.Cluster, request.Node.Id, clusterStats) - } - } -} diff --git a/examples/load-reporting-service/verify.sh b/examples/load-reporting-service/verify.sh deleted file mode 100755 index 48fb61050965..000000000000 --- a/examples/load-reporting-service/verify.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -e - -export NAME=load-reporting -export UPARGS="--scale http_service=2" -export PORT_PROXY0="${LRS_PORT_PROXY0:-11200}" -export PORT_PROXY1="${LRS_PORT_PROXY1:-11201}" -export PORT_ADMIN="${LRS_PORT_ADMIN:-11202}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Send requests" -bash send_requests.sh 2> /dev/null -run_log "Check logs: http 1" -"${DOCKER_COMPOSE[@]}" logs http_service | grep http_service-1 | grep HTTP | grep 200 - -run_log "Check logs: http 2" -"${DOCKER_COMPOSE[@]}" logs http_service | grep http_service-2 | grep HTTP | grep 200 - -wait_for 20 bash -c "${DOCKER_COMPOSE[*]} logs lrs_server | grep 'up and running'" - -run_log "Check logs: lrs_server" -"${DOCKER_COMPOSE[@]}" logs lrs_server | grep "up and running" - -run_log "Check logs: envoy is connect to lrs_server" -responds_with \ - upstream_rq_200 \ - "http://localhost:${PORT_ADMIN}/stats?filter=load_reporting_cluster" - -wait_for 10 bash -c "${DOCKER_COMPOSE[*]} logs lrs_server | grep 'Got stats from cluster'" - -run_log "Check logs: lrs_server works normally" -"${DOCKER_COMPOSE[@]}" logs lrs_server | grep "Got stats from cluster" - -# TODO(phlax): add some test/docs for interacting with load reporting server diff --git a/examples/local_ratelimit/Dockerfile-nginx b/examples/local_ratelimit/Dockerfile-nginx deleted file mode 100644 index fb92648942c4..000000000000 --- a/examples/local_ratelimit/Dockerfile-nginx +++ /dev/null @@ -1 +0,0 @@ -FROM nginx@sha256:6af79ae5de407283dcea8b00d5c37ace95441fd58a8b1d2aa1ed93f5511bb18c diff --git a/examples/local_ratelimit/README.md b/examples/local_ratelimit/README.md deleted file mode 100644 index a1e9c0a453fc..000000000000 --- a/examples/local_ratelimit/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/local_ratelimit.html) diff --git a/examples/local_ratelimit/docker-compose.yaml b/examples/local_ratelimit/docker-compose.yaml deleted file mode 100644 index 307dc5f1ebbf..000000000000 --- a/examples/local_ratelimit/docker-compose.yaml +++ /dev/null @@ -1,14 +0,0 @@ -services: - envoy-stat: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - ports: - - "${PORT_PROXY:-10000}:10000" - - "${PORT_STATS0:-9901}:9901" - - "${PORT_STATS1:-9902}:9902" - - service: - build: - context: . - dockerfile: Dockerfile-nginx diff --git a/examples/local_ratelimit/envoy.yaml b/examples/local_ratelimit/envoy.yaml deleted file mode 100644 index a81273a65ca4..000000000000 --- a/examples/local_ratelimit/envoy.yaml +++ /dev/null @@ -1,131 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 9902 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/stats/prometheus" - route: - cluster: envoy-stat - http_filters: - - name: envoy.filters.http.local_ratelimit - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit - stat_prefix: http_local_rate_limiter - token_bucket: - max_tokens: 2 - tokens_per_fill: 2 - fill_interval: 5s - filter_enabled: - runtime_key: local_rate_limit_enabled - default_value: - numerator: 100 - denominator: HUNDRED - filter_enforced: - runtime_key: local_rate_limit_enforced - default_value: - numerator: 100 - denominator: HUNDRED - response_headers_to_add: - - append_action: OVERWRITE_IF_EXISTS_OR_ADD - header: - key: x-local-rate-limit - value: 'true' - local_rate_limit_per_downstream_connection: false - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service - http_filters: - - name: envoy.filters.http.local_ratelimit - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit - stat_prefix: http_local_rate_limiter - token_bucket: - max_tokens: 2 - tokens_per_fill: 2 - fill_interval: 5s - filter_enabled: - runtime_key: local_rate_limit_enabled - default_value: - numerator: 100 - denominator: HUNDRED - filter_enforced: - runtime_key: local_rate_limit_enforced - default_value: - numerator: 100 - denominator: HUNDRED - response_headers_to_add: - - append_action: OVERWRITE_IF_EXISTS_OR_ADD - header: - key: x-local-rate-limit - value: 'true' - local_rate_limit_per_downstream_connection: false - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: envoy-stat - connect_timeout: 0.25s - type: STATIC - load_assignment: - cluster_name: envoy-stat - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: 127.0.0.1 - port_value: 9901 - - name: service - connect_timeout: 0.25s - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service - port_value: 80 -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 9901 diff --git a/examples/local_ratelimit/example.rst b/examples/local_ratelimit/example.rst deleted file mode 100644 index 451d76ab1cf4..000000000000 --- a/examples/local_ratelimit/example.rst +++ /dev/null @@ -1,95 +0,0 @@ -.. _install_sandboxes_ratelimit: - -Local Ratelimit -=============== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -Rate limiting is used to control the rate of requests sent or received by a network interface controller, which is helpful to prevent DoS attacks and limit web scraping. - -Envoy supports both local (non-distributed) and global rate limiting, and two types for local rate limiting: - -- L4 connections via the :ref:`local rate limit filter ` -- HTTP requests via the :ref:`HTTP local rate limit filter ` - -This sandbox provides an example of rate limiting of L4 connections. - -Step 1: Start all of our containers -*********************************** - -Change to the ``examples/local_ratelimit`` directory and bring up the docker composition. - -.. code-block:: console - - $ pwd - envoy/examples/ratelimit - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - Name Command State Ports - ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ratelimtit_envoy-stat_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp,:::10000->10000/tcp, 0.0.0.0:9901->9901/tcp,:::9901->9901/tcp, 0.0.0.0:9902->9902/tcp,:::9902->9902/tcp - ratelimtit_service_1 /docker-entrypoint.sh ngin ... Up 80/tcp - -Step 2: Test rate limiting of upstream service -********************************************** - -The sandbox is configured with ``10000`` port for upstream service. - -If a request reaches the rate limit, Envoy will add ``x-local-rate-limit`` header and refuse the connection with a 429 HTTP response code and with the content ``local_rate_limited``. - -Now, use ``curl`` to make a request five times for the limited upstream service: - -.. code-block:: console - - $ for i in {1..5}; do curl -si localhost:10000 | grep -E "x-local-rate-limit|429|local_rate_limited"; done - HTTP/1.1 429 Too Many Requests - x-local-rate-limit: true - local_rate_limited - HTTP/1.1 429 Too Many Requests - x-local-rate-limit: true - local_rate_limited - HTTP/1.1 429 Too Many Requests - x-local-rate-limit: true - local_rate_limited - -The first two requests get responses, and the remaining requests are refused with expected responses. - - -Step 3: Test rate limiting of Envoy’s statistics -************************************************ - -The sandbox is configured with two ports serving Envoy’s admin and statistics interface: - -- ``9901`` exposes the standard admin interface -- ``9902`` exposes a rate limitied version of the admin interface - -Use ``curl`` to make a request five times for unlimited statistics on port ``9901``, it should not contain any rate limiting responses: - -.. code-block:: console - - $ for i in {1..5}; do curl -si localhost:9901/stats/prometheus | grep -E "x-local-rate-limit|429|local_rate_limited"; done - -Now, use ``curl`` to make a request five times for the limited statistics: - -.. code-block:: console - - $ for i in {1..5}; do curl -si localhost:9902/stats/prometheus | grep -E "x-local-rate-limit|429|local_rate_limited"; done - HTTP/1.1 429 Too Many Requests - x-local-rate-limit: true - local_rate_limited - HTTP/1.1 429 Too Many Requests - x-local-rate-limit: true - local_rate_limited - HTTP/1.1 429 Too Many Requests - x-local-rate-limit: true - local_rate_limited - -.. seealso:: - :ref:`global rate limiting ` - Reference documentation for Envoy's global rate limiting. diff --git a/examples/local_ratelimit/verify.sh b/examples/local_ratelimit/verify.sh deleted file mode 100755 index 973302230536..000000000000 --- a/examples/local_ratelimit/verify.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash -e - -export NAME=local_ratelimit -export PORT_PROXY="${LOCAL_RATELIMIT_PORT_PROXY:-11210}" -export PORT_STATS0="${LOCAL_RATELIMIT_PORT_STATS0:-11211}" -export PORT_STATS1="${LOCAL_RATELIMIT_PORT_STATS1:-11212}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - - -responds_with_rate_limit () { - local count="$1" url="$2" - - for ((i=1;i<=count;i++)); do - responds_with \ - "local_rate_limited" \ - "$url" - done -} - -responds_without_rate_limit () { - local count="$1" url="$2" - - for ((i=1;i<=count;i++)); do - responds_without \ - "local_rate_limited" \ - "$url" - done -} -export -f responds_without_rate_limit - - -run_log "Test upstream: localhost:${PORT_PROXY} without rate limit header two times" -for i in {1..2}; do - output=$(curl -s -X GET --head "http://localhost:${PORT_PROXY}") - echo "${output}" | grep "429 Too Many Requests" && exit 1 - echo "${output}" | grep "x-local-rate-limit: true" && exit 1 -done - -run_log "Test upstream: localhost:${PORT_PROXY} with rate limit header three times" -for i in {1..3}; do - output=$(curl -s -X GET --head "http://localhost:${PORT_PROXY}") - echo "${output}" | grep "429 Too Many Requests" || exit 1 - echo "${output}" | grep "x-local-rate-limit: true" || exit 1 -done - -run_log "Test upstream: localhost:${PORT_PROXY} without rate limit response two times" -wait_for 5 responds_without_rate_limit 2 "http://localhost:${PORT_PROXY}" - -run_log "Test upstream: localhost:${PORT_PROXY} with rate limit response three times" -responds_with_rate_limit 3 "http://localhost:${PORT_PROXY}" - -run_log "Test admin interface: localhost:${PORT_STATS0}/stats/prometheus without rate limit header five times" -for i in {1..5}; do - output=$(curl -s -X GET --head "http://localhost:${PORT_STATS0}/stats/prometheus") - echo "${output}" | grep "429 Too Many Requests" && exit 1 - echo "${output}" | grep "x-local-rate-limit: true" && exit 1 -done - -run_log "Test admin interface: localhost:${PORT_STATS0}/stats/prometheus without rate limit response five times" -responds_without_rate_limit 5 "http://localhost:${PORT_STATS0}/stats/prometheus" - -run_log "Test admin interface: localhost:${PORT_STATS1}/stats/prometheus without rate limit header two times" -for i in {1..2}; do - output=$(curl -s -X GET --head "http://localhost:${PORT_STATS1}/stats/prometheus") - echo "${output}" | grep "429 Too Many Requests" && exit 1 - echo "${output}" | grep "x-local-rate-limit: true" && exit 1 -done - -run_log "Test admin interface: localhost:${PORT_STATS1}/stats/prometheus with rate limit header three times" -for i in {1..3}; do - output=$(curl -s -X GET --head "http://localhost:${PORT_STATS1}/stats/prometheus") - echo "${output}" | grep "429 Too Many Requests" || exit 1 - echo "${output}" | grep "x-local-rate-limit: true" || exit 1 -done - -run_log "Test admin interface: localhost:${PORT_STATS1}/stats/prometheus without rate limit response two times" -wait_for 5 responds_without_rate_limit 2 "http://localhost:${PORT_STATS1}/stats/prometheus" - -run_log "Test admin interface: localhost:${PORT_STATS1}/stats/prometheus with rate limit response three times" -responds_with_rate_limit 3 "http://localhost:${PORT_STATS1}/stats/prometheus" diff --git a/examples/locality-load-balancing/client.py b/examples/locality-load-balancing/client.py deleted file mode 100644 index 0eef5ff4dda4..000000000000 --- a/examples/locality-load-balancing/client.py +++ /dev/null @@ -1,20 +0,0 @@ -import sys -import urllib.request -from collections import Counter - -url, n_requests = sys.argv[1], int(sys.argv[2]) - -count = Counter() -count_fail = 0 - -for i in range(n_requests): - try: - with urllib.request.urlopen(url) as resp: - content = resp.read().decode("utf-8").strip() - count[content] += 1 - except: - count_fail += 1 - -for k in count: - print(f"{k}: actual weight {count[k] / n_requests * 100}%") -print(f"Failed: {count_fail}") diff --git a/examples/locality-load-balancing/docker-compose.yaml b/examples/locality-load-balancing/docker-compose.yaml deleted file mode 100644 index 7366425bc8e5..000000000000 --- a/examples/locality-load-balancing/docker-compose.yaml +++ /dev/null @@ -1,52 +0,0 @@ -services: - - client-envoy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - target: envoy-load-balancing - depends_on: - backend-local-1: - condition: service_healthy - backend-local-2: - condition: service_healthy - backend-remote-1: - condition: service_healthy - backend-remote-2: - condition: service_healthy - - backend-local-1: - build: - context: ../shared/python - target: aiohttp-service - volumes: - - ./service.py:/code/service.py - environment: - - HOST=backend-local-1 - - backend-local-2: - build: - context: ../shared/python - target: aiohttp-service - volumes: - - ./service.py:/code/service.py - environment: - - HOST=backend-local-2 - - backend-remote-1: - build: - context: ../shared/python - target: aiohttp-service - volumes: - - ./service.py:/code/service.py - environment: - - HOST=backend-remote-1 - - backend-remote-2: - build: - context: ../shared/python - target: aiohttp-service - volumes: - - ./service.py:/code/service.py - environment: - - HOST=backend-remote-2 diff --git a/examples/locality-load-balancing/envoy.yaml b/examples/locality-load-balancing/envoy.yaml deleted file mode 100644 index be155f9bef53..000000000000 --- a/examples/locality-load-balancing/envoy.yaml +++ /dev/null @@ -1,108 +0,0 @@ -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 8001 -static_resources: - listeners: - - name: backend - address: - socket_address: - address: 0.0.0.0 - port_value: 3000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: backend - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: backend - type: STRICT_DNS - lb_policy: ROUND_ROBIN - common_lb_config: - locality_weighted_lb_config: {} - health_checks: - - interval: 2s - timeout: 3s - no_traffic_interval: 4s - no_traffic_healthy_interval: 4s - unhealthy_threshold: 1 - healthy_threshold: 1 - http_health_check: - path: "/" - load_assignment: - cluster_name: backend - endpoints: - - locality: - region: local - zone: zone-1 - load_balancing_weight: 1 - priority: 0 # highest - lb_endpoints: - - endpoint: - address: - socket_address: - address: backend-local-1 - port_value: 8080 - health_check_config: - port_value: 8080 - hostname: backend-local-1 - - locality: - region: local - zone: zone-2 - load_balancing_weight: 1 - priority: 1 - lb_endpoints: - - endpoint: - address: - socket_address: - address: backend-local-2 - port_value: 8080 - health_check_config: - port_value: 8080 - hostname: backend-local-2 - - locality: - region: remote - zone: zone-1 - load_balancing_weight: 1 - priority: 1 - lb_endpoints: - - endpoint: - address: - socket_address: - address: backend-remote-1 - port_value: 8080 - health_check_config: - port_value: 8080 - hostname: backend-remote-1 - - locality: - region: remote - zone: zone-2 - load_balancing_weight: 1 - priority: 2 - lb_endpoints: - - endpoint: - address: - socket_address: - address: backend-remote-2 - port_value: 8080 - health_check_config: - port_value: 8080 - hostname: backend-remote-2 diff --git a/examples/locality-load-balancing/example.rst b/examples/locality-load-balancing/example.rst deleted file mode 100644 index 369379cecb30..000000000000 --- a/examples/locality-load-balancing/example.rst +++ /dev/null @@ -1,160 +0,0 @@ -.. _install_sandboxes_locality_load_balancing: - -Locality Weighted Load Balancing -================================ - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -This example demonstrates the :ref:`locality weighted load balancing ` feature in Envoy proxy. The demo simulates a scenario that a backend service resides in two local zones and one remote zone. - -The components used in this demo are as follows: - -- A client container: runs Envoy proxy -- Backend container in the same locality as the client, with priority set to 0, referred to as ``local-1``. -- Backend container in the same locality as the client, with priority set to 1, referred to as ``local-2``. -- Backend container in the the remote locality, with priority set to 1, referred to as ``remote-1``. -- Backend container in the the remote locality, with priority set to 2, referred to as ``remote-2``. - -The client Envoy proxy configures the 4 backend containers in the same Envoy cluster, so that Envoy handles load balancing to those backend servers. From here we can see, we have localities with 3 different priorities: - -- priority 0: ``local-1`` -- priority 1: ``local-2`` and ``remote-1`` -- priority 2: ``remote-2`` - -In Envoy, when the healthiness of a given locality drops below a threshold (71% by default), the next priority locality will start to share the request loads. The demo below will show this behavior. - -Step 1: Start all of our containers -*********************************** - -In terminal, move to the ``examples/locality_load_balancing`` directory. - -To build this sandbox example and start the example services, run the following commands: - -.. code-block:: console - - # Start demo - $ docker compose up --build -d - -The locality configuration is set in the client container via static Envoy configuration file. Please refer to the ``cluster`` section of the :download:`proxy configuration <_include/locality-load-balancing/envoy.yaml>` file. - -.. note:: - The ``locality_weighted_lb_config`` must be set in ``common_lb_config`` for the ``load_balancing_weight`` to be used. - -Step 2: Scenario with one replica in the highest priority locality -****************************************************************** - -In this scenario, each locality has 1 healthy replica running and all the requests should be sent to the locality with the highest priority (i.e. lowest integer set for priority - ``0``), which is ``local-1``. - -.. code-block:: console - - # all requests to local-1 - $ docker compose exec -T client-envoy python3 client.py http://localhost:3000/ 100 - Hello from backend-local-1!: 100, 100.0% - Failed: 0 - -If locality ``local-1`` becomes unhealthy (i.e. fails the Envoy health check), the requests should be load balanced among the subsequent priority localities, which are ``local-2`` and ``remote-1``. They both have priority 1. We then send 100 requests to the backend cluster, and check the responders. - -.. code-block:: console - - # bring down local-1 - $ docker compose exec -T client-envoy curl -s locality-load-balancing_backend-local-1_1:8080/unhealthy - [backend-local-1] Set to unhealthy - - # local-2 and remote-1 localities split the traffic 50:50 - $ docker compose exec -T client-envoy python3 client.py http://localhost:3000/ 100 - Hello from backend-remote-1!: 51, 51.0% - Hello from backend-local-2!: 49, 49.0% - Failed: 0 - -Now if ``local-2`` becomes unhealthy also, priority 1 locality is only 50% healthy. Thus priority 2 locality starts to share the request load. Requests will be sent to both ``remote-1`` and ``remote-2``. - -.. code-block:: console - - # bring down local-2 - $ docker compose exec -T client-envoy curl -s locality-load-balancing_backend-local-2_1:8080/unhealthy - - # remote-1 locality receive 100% of the traffic - $ docker compose exec -T client-envoy python3 client.py http://localhost:3000/ 100 - Hello from backend-remote-1!: actual weight 69.0% - Hello from backend-remote-2!: actual weight 31.0% - Failed: 0 - - -Step 3: Recover servers -*********************** - -Before moving on, we need to server local-1 and local-2 first. - -.. code-block:: console - - # recover local-1 and local-2 after the demo - $ docker compose exec -T client-envoy curl -s locality-load-balancing_backend-local-1_1:8080/healthy - $ docker compose exec -T client-envoy curl -s locality-load-balancing_backend-local-2_1:8080/healthy - - -Step 4: Scenario with multiple replicas in the highest priority locality -************************************************************************ - -To demonstrate how locality based load balancing works in multiple replicas setup, let's now scale up the ``local-1`` locality to 5 replicas. - -.. code-block:: console - - $ docker compose up --scale backend-local-1=5 -d - -We are going to show the scenario that ``local-1`` is just partially healthy. So let's bring down 4 of the replicas in ``local-1``. - -.. code-block:: console - - # bring down local-1 replicas - $ docker compose exec -T client-envoy curl -s locality-load-balancing_backend-local-1_2:8080/unhealthy - $ docker compose exec -T client-envoy curl -s locality-load-balancing_backend-local-1_3:8080/unhealthy - $ docker compose exec -T client-envoy curl -s locality-load-balancing_backend-local-1_4:8080/unhealthy - $ docker compose exec -T client-envoy curl -s locality-load-balancing_backend-local-1_5:8080/unhealthy - -Then we check the endpoints again: - -.. code-block:: console - - # check healthiness - $ docker compose exec -T client-envoy curl -s localhost:8001/clusters | grep health_flags - - backend::172.28.0.4:8080::health_flags::/failed_active_hc - backend::172.28.0.2:8080::health_flags::/failed_active_hc - backend::172.28.0.5:8080::health_flags::/failed_active_hc - backend::172.28.0.6:8080::health_flags::/failed_active_hc - backend::172.28.0.7:8080::health_flags::healthy - backend::172.28.0.8:8080::health_flags::healthy - backend::172.28.0.3:8080::health_flags::healthy - -We can confirm that 4 backend endpoints become unhealthy. - -Now we send the 100 requests again. - -.. code-block:: console - - # watch traffic change - $ docker compose exec -T client-envoy python3 client.py http://localhost:3000/ 100 - - Hello from backend-remote-1!: actual weight 37.0% - Hello from backend-local-2!: actual weight 36.0% - Hello from backend-local-1!: actual weight 27.0% - Failed: 0 - -As ``local-1`` does not have enough healthy workloads, requests are partially shared by secondary localities. - -If we bring down all the servers in priority 1 locality, it will make priority 1 locality 0% healthy. The traffic should split between priority 0 and priority 2 localities. - -.. code-block:: console - - $ docker compose exec -T client-envoy curl -s locality-load-balancing_backend-local-2_1:8080/unhealthy - $ docker compose exec -T client-envoy curl -s locality-load-balancing_backend-remote-1_1:8080/unhealthy - $ docker compose exec -T client-envoy python3 client.py http://localhost:3000/ 100 - - Hello from backend-remote-2!: actual weight 77.0% - Hello from backend-local-1!: actual weight 23.0% - Failed: 0 diff --git a/examples/locality-load-balancing/service.py b/examples/locality-load-balancing/service.py deleted file mode 100644 index c36ac65d5c74..000000000000 --- a/examples/locality-load-balancing/service.py +++ /dev/null @@ -1,37 +0,0 @@ -import logging -import os - -from aiohttp import web - -routes = web.RouteTableDef() -healthy = True - - -@routes.get("/") -async def get(request): - global healthy - if healthy: - return web.Response(text=f"Hello from {os.environ['HOST']}!\n") - else: - raise web.HTTPServiceUnavailable(reason="Unhealthy") - - -@routes.get("/healthy") -async def healthy(request): - global healthy - healthy = True - return web.Response(text=f"[{os.environ['HOST']}] Set to healthy\n", status=201) - - -@routes.get("/unhealthy") -async def unhealthy(request): - global healthy - healthy = False - return web.Response(text=f"[{os.environ['HOST']}] Set to unhealthy\n", status=201) - - -if __name__ == "__main__": - app = web.Application() - logging.basicConfig(level=logging.DEBUG) - app.add_routes(routes) - web.run_app(app, host='0.0.0.0', port=8080) diff --git a/examples/locality-load-balancing/verify.sh b/examples/locality-load-balancing/verify.sh deleted file mode 100755 index 7f6727811257..000000000000 --- a/examples/locality-load-balancing/verify.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/bash -e - -export NAME=locality-load-balancing - - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - - -dump_clusters () { - "${DOCKER_COMPOSE[@]}" exec -T client-envoy curl -s "localhost:8001/clusters" -} - -check_health() { - local ip_address - ip_address="$(docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "${NAME}-${1}")" - dump_clusters | grep "backend::${ip_address}:8080::health_flags::${2}" -} - -check_backend() { - output=$("${DOCKER_COMPOSE[@]}" exec -T client-envoy python3 client.py http://localhost:3000/ 100) - echo "$output" - for expected in "$@"; do - count=$(echo "$output" | grep -c "$expected" | xargs) - if [ "$count" -eq 0 ]; then - echo "Test fail: locality $expected is expected to be routed to." - return 1 - fi - done -} - -make_healthy() { - "${DOCKER_COMPOSE[@]}" exec -T client-envoy curl -s "${NAME}-${1}:8080/healthy" - wait_for 5 check_health "${1}" healthy -} - -make_unhealthy() { - "${DOCKER_COMPOSE[@]}" exec -T client-envoy curl -s "${NAME}-${1}:8080/unhealthy" - wait_for 5 check_health "${1}" /failed_active_hc -} - -run_log "Wait for backend clusters to become healthy." -wait_for 5 check_health backend-local-1-1 healthy -wait_for 5 check_health backend-local-2-1 healthy -wait_for 5 check_health backend-remote-1-1 healthy -wait_for 5 check_health backend-remote-2-1 healthy - -run_log "Dump configured Envoy clusters" -dump_clusters - -run_log "=== Demo setup -client -> backend-local-1 [priority: 0, weight: 1] - -> backend-local-2 [priority: 1, weight: 1] - -> backend-remote-1 [priority: 1, weight: 1] - -> backend-remote-2 [priority: 2, weight: 1] -" - -run_log "=== Scenario 1: one replica in the highest priority locality" - -run_log "Send requests to backend." -wait_for 5 check_health backend-local-1-1 healthy -check_backend backend-local-1 - -run_log "Bring down backend-local-1. Priority 0 locality is 0% healthy." -make_unhealthy backend-local-1-1 - -run_log "Send requests to backend." -check_backend backend-local-2 backend-remote-1 - -run_log "Bring down backend-local-2. Priority 1 locality is 50% healthy." -make_unhealthy backend-local-2-1 - -run_log "Traffic is load balanced goes to remote only." -check_backend backend-remote-1 backend-remote-2 - -run_log "=== Scenario 2: multiple replica in the highest priority locality" -run_log "Recover local-1 and local-2" -make_healthy backend-local-1-1 -make_healthy backend-local-2-1 - -run_log "Scale backend-local-1 to 5 replicas." -"${DOCKER_COMPOSE[@]}" -p "${NAME}" up --scale backend-local-1=5 -d --build -wait_for 5 check_health backend-local-1-2 healthy -wait_for 5 check_health backend-local-1-3 healthy -wait_for 5 check_health backend-local-1-4 healthy -wait_for 5 check_health backend-local-1-5 healthy - -run_log "Bring down 4 replicas in backend-local-1. Priority 0 locality is 20% healthy." -make_unhealthy backend-local-1-2 -make_unhealthy backend-local-1-3 -make_unhealthy backend-local-1-4 -make_unhealthy backend-local-1-5 - -run_log "Send requests to backend." -check_backend backend-local-1 backend-local-2 backend-remote-1 - -run_log "Bring down all endpoints of priority 1. Priority 1 locality is 0% healthy." -make_unhealthy backend-local-2-1 -make_unhealthy backend-remote-1-1 - -run_log "Send requests to backend." -check_backend backend-local-1 backend-remote-2 diff --git a/examples/lua-cluster-specifier/README.md b/examples/lua-cluster-specifier/README.md deleted file mode 100644 index 0a1cd344b9b1..000000000000 --- a/examples/lua-cluster-specifier/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/lua-cluster-specifier.html) diff --git a/examples/lua-cluster-specifier/docker-compose.yaml b/examples/lua-cluster-specifier/docker-compose.yaml deleted file mode 100644 index 8faf8df65a2c..000000000000 --- a/examples/lua-cluster-specifier/docker-compose.yaml +++ /dev/null @@ -1,8 +0,0 @@ -services: - - proxy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - ports: - - "${PORT_PROXY:-10000}:10000" diff --git a/examples/lua-cluster-specifier/envoy.yaml b/examples/lua-cluster-specifier/envoy.yaml deleted file mode 100644 index 178ced586715..000000000000 --- a/examples/lua-cluster-specifier/envoy.yaml +++ /dev/null @@ -1,56 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_http - codec_type: AUTO - route_config: - name: example_route - virtual_hosts: - - name: example_hosts - domains: - - "*" - routes: - - match: - prefix: "/" - route: - inline_cluster_specifier_plugin: - extension: - name: envoy.router.cluster_specifier_plugin.lua - typed_config: - "@type": type.googleapis.com/envoy.extensions.router.cluster_specifiers.lua.v3.LuaConfig - source_code: - inline_string: | - function envoy_on_route(route_handle) - local header_value = route_handle:headers():get("header_key") - if header_value == "fake" then - return "fake_cluster" - end - return "example_cluster" - end - default_cluster: example_cluster - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - clusters: - - name: example_cluster - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: example_cluster_service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: httpbin.org - port_value: 80 diff --git a/examples/lua-cluster-specifier/example.rst b/examples/lua-cluster-specifier/example.rst deleted file mode 100644 index f927333ad755..000000000000 --- a/examples/lua-cluster-specifier/example.rst +++ /dev/null @@ -1,69 +0,0 @@ -.. _install_sandboxes_lua_cluster_specifier: - -Lua cluster specifier -===================== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -In this example, we show how the `Lua `_ cluster specifier can be used with the -Envoy proxy. - -The example Envoy proxy configuration includes a Lua cluster specifier plugin that contains a function: - -- ``envoy_on_route(route_handle)`` - -.. tip:: - - See the :ref:`Lua cluster configuration documentation ` for an overview and - documentation regarding the function. - -Step 1: Build the sandbox -************************* - -Change to the ``examples/lua-cluster-specifier`` directory, and bring up the composition. - -.. code-block:: console - - $ pwd - envoy/examples/lua-cluster-specifier - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - -------------------------------------------------------------------------------------------- - lua-cluster-specifier-proxy-1 /docker-entrypoint.sh /usr ... Up 10000/tcp, 0.0.0.0:10000->10000/tcp - -Step 2: Send a request to the normal service -******************************************** - -The output from the ``curl`` command below should return 200, since the lua code select the normal service. - -.. code-block:: console - - $ curl -i localhost:10000/anything 2>&1 |grep 200 - HTTP/1.1 200 OK - -Step 3: Send a request to the fake service -****************************************** - -If you specify the request header ``header_key:fake``, curl will return a ``503`` response, as -the Lua code will select the fake service. - -.. code-block:: console - - $ curl -i localhost:8000/anything -H "header_key:fake" 2>&1 |grep 503 - HTTP/1.1 503 Service Unavailable - -.. seealso:: - - :ref:`Envoy Lua cluster specifier ` - Learn more about the Envoy Lua cluster specifier. - - `Lua `_ - The Lua programming language. diff --git a/examples/lua-cluster-specifier/verify.sh b/examples/lua-cluster-specifier/verify.sh deleted file mode 100755 index ec37d5b46d89..000000000000 --- a/examples/lua-cluster-specifier/verify.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -e - -export NAME=lua-cluster-specifier -export PORT_PROXY="${LUA_CLUSTER_PORT_PROXY:-12620}" -export PORT_WEB="${LUA_CLUSTER_PORT_WEB:-12621}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Test Lua cluster specifier with normal cluster" -responds_with_header \ - "HTTP/1.1 200 OK" \ - "http://localhost:${PORT_PROXY}/" - -run_log "Test Lua cluster specifier with fake cluster" -responds_with_header \ - "HTTP/1.1 503 Service Unavailable" \ - "http://localhost:${PORT_PROXY}/" \ - -H 'header_key: fake' diff --git a/examples/lua/README.md b/examples/lua/README.md deleted file mode 100644 index 912bd3992749..000000000000 --- a/examples/lua/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/lua.html). diff --git a/examples/lua/docker-compose.yaml b/examples/lua/docker-compose.yaml deleted file mode 100644 index b503ac37193b..000000000000 --- a/examples/lua/docker-compose.yaml +++ /dev/null @@ -1,15 +0,0 @@ -services: - - proxy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - target: envoy-lua - ports: - - "${PORT_PROXY:-8000}:8000" - - web_service: - build: - context: ../shared/echo - ports: - - "${PORT_WEB:-8080}:8080" diff --git a/examples/lua/envoy.yaml b/examples/lua/envoy.yaml deleted file mode 100644 index 0a8bf2d98c81..000000000000 --- a/examples/lua/envoy.yaml +++ /dev/null @@ -1,68 +0,0 @@ -static_resources: - listeners: - - name: main - address: - socket_address: - address: 0.0.0.0 - port_value: 8000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_http - codec_type: AUTO - route_config: - name: local_route - virtual_hosts: - - name: local_service - domains: - - "*" - routes: - - match: - prefix: "/multiple/lua/scripts" - route: - cluster: web_service - typed_per_filter_config: - lua_filter_with_custom_name_1: - "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute - source_code: - inline_string: | - function envoy_on_response(response_handle) - response_handle:headers():add("header_key_1", "header_value_1") - end - - match: - prefix: "/" - route: - cluster: web_service - http_filters: - - name: lua_filter_with_custom_name_0 - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua - default_source_code: - inline_string: | - local mylibrary = require("lib.mylibrary") - - function envoy_on_request(request_handle) - request_handle:headers():add("foo", mylibrary.foobar()) - end - - name: lua_filter_with_custom_name_1 - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - clusters: - - name: web_service - type: STRICT_DNS # static - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: web_service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: web_service - port_value: 8080 diff --git a/examples/lua/example.rst b/examples/lua/example.rst deleted file mode 100644 index 480f180916dc..000000000000 --- a/examples/lua/example.rst +++ /dev/null @@ -1,75 +0,0 @@ -.. _install_sandboxes_lua: - -Lua filter -========== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -In this example, we show how the `Lua `_ filter can be used with the Envoy -proxy. - -The example Envoy proxy configuration includes two Lua filters that contain two different functions: - -- ``envoy_on_request(request_handle)`` -- ``envoy_on_response(response_handle)`` - -:ref:`See here ` for an overview of Envoy's Lua filter and documentation -regarding these functions. - -Step 1: Build the sandbox -************************* - -Change to the ``examples/lua`` directory. - -.. code-block:: console - - $ pwd - envoy/examples/lua - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - -------------------------------------------------------------------------------------------- - lua_proxy_1 /docker-entrypoint.sh /bin ... Up 10000/tcp, 0.0.0.0:8000->8000/tcp - lua_web_service_1 node ./index.js Up 0.0.0.0:8080->80/tcp - -Step 2: Send a request to the service -************************************* - -The output from the ``curl`` command below should include the header added by the Lua filter. - -Terminal 1 - -.. code-block:: console - - $ curl -v localhost:8000 2>&1 | grep Foo - Foo: bar <-- This is added by the common Lua filter. --< - -Step 3: Using multiple Lua filters at the same time -********************************************************* - -Two Lua filters are configured in the example Envoy proxy configuration. But the second one can only work at a -specific route. - -The output from the ``curl`` command below should include the headers that added by multiple Lua filters. - -Terminal 1 - -.. code-block:: console - - curl -v localhost:8000/multiple/lua/scripts 2>&1 | grep header_key_1 - < header_key_1: header_value_1 <-- This is added by the second route-specific Lua filter. --< - -.. seealso:: - - :ref:`Envoy Lua filter ` - Learn more about the Envoy Lua filter. - - `Lua `_ - The Lua programming language. diff --git a/examples/lua/lib/mylibrary.lua b/examples/lua/lib/mylibrary.lua deleted file mode 100644 index 6b4ffed1b476..000000000000 --- a/examples/lua/lib/mylibrary.lua +++ /dev/null @@ -1,7 +0,0 @@ -M = {} - -function M.foobar() - return "bar" -end - -return M diff --git a/examples/lua/verify.sh b/examples/lua/verify.sh deleted file mode 100755 index 28a043f0feb5..000000000000 --- a/examples/lua/verify.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -e - -export NAME=lua -export PORT_PROXY="${LUA_PORT_PROXY:-11230}" -export PORT_WEB="${LUA_PORT_WEB:-11231}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - - -run_log "Test common Lua script" -responds_with \ - "Foo: bar" \ - "http://localhost:${PORT_PROXY}" - -run_log "Test route-specific Lua script" -responds_with_header \ - "header_key_1: header_value_1" \ - "http://localhost:${PORT_PROXY}/multiple/lua/scripts" - -# TODO(phlax): Add some docs/tests for web service diff --git a/examples/mysql/Dockerfile-mysql b/examples/mysql/Dockerfile-mysql deleted file mode 100644 index 6f5dde2defe9..000000000000 --- a/examples/mysql/Dockerfile-mysql +++ /dev/null @@ -1 +0,0 @@ -FROM mysql:9.0.1@sha256:d8df069848906979fd7511db00dc22efeb0a33a990d87c3c6d3fcdafd6fc6123 diff --git a/examples/mysql/README.md b/examples/mysql/README.md deleted file mode 100644 index a6987ece4c85..000000000000 --- a/examples/mysql/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/mysql.html). diff --git a/examples/mysql/docker-compose.yaml b/examples/mysql/docker-compose.yaml deleted file mode 100644 index 891358dd3127..000000000000 --- a/examples/mysql/docker-compose.yaml +++ /dev/null @@ -1,20 +0,0 @@ -services: - - proxy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_VARIANT: contrib-dev - depends_on: - mysql: - condition: service_started - ports: - - "${PORT_ADMIN:-8001}:8001" - - mysql: - build: - context: . - dockerfile: Dockerfile-mysql - environment: - - MYSQL_ALLOW_EMPTY_PASSWORD=yes diff --git a/examples/mysql/envoy.yaml b/examples/mysql/envoy.yaml deleted file mode 100644 index 4760b2c3eaca..000000000000 --- a/examples/mysql/envoy.yaml +++ /dev/null @@ -1,37 +0,0 @@ -static_resources: - listeners: - - name: mysql_listener - address: - socket_address: - address: 0.0.0.0 - port_value: 1999 - filter_chains: - - filters: - - name: envoy.filters.network.mysql_proxy - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.mysql_proxy.v3.MySQLProxy - stat_prefix: egress_mysql - - name: envoy.filters.network.tcp_proxy - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - stat_prefix: mysql_tcp - cluster: mysql_cluster - - clusters: - - name: mysql_cluster - type: STRICT_DNS - load_assignment: - cluster_name: mysql_cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: mysql - port_value: 3306 - -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 8001 diff --git a/examples/mysql/example.rst b/examples/mysql/example.rst deleted file mode 100644 index 859c54df9753..000000000000 --- a/examples/mysql/example.rst +++ /dev/null @@ -1,134 +0,0 @@ -.. _install_sandboxes_mysql: - -MySQL filter -============ - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -In this example, we show how the :ref:`MySQL filter ` can be used with the Envoy proxy. - -The Envoy proxy configuration includes a MySQL filter that parses queries and collects MySQL-specific -metrics. - -.. note:: - The current implementation of the protocol filter was tested extensively with MySQL - v5.7. It may also work with other versions. This example uses the current latest version. - - -Step 1: Build the sandbox -************************* - -Change to the ``examples/mysql`` directory. - -Build and start the containers. - -.. code-block:: console - - $ pwd - envoy/examples/mysql - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ---------------------------------------------------------------------------------------------------- - mysql_mysql_1 docker-entrypoint.sh mysqld Up 3306/tcp - mysql_proxy_1 /docker-entrypoint.sh /bin Up 10000/tcp, 1999/tcp, 0.0.0.0:8001->8001/tcp - -Step 2: Issue commands using mysql -********************************** - -Use ``mysql`` to issue some commands and verify they are routed via Envoy. - -.. code-block:: console - - $ docker run --rm -it --network mysql_default mysql:5.7 mysql -h proxy -P 1999 -u root --skip-ssl - ... snip ... - - mysql> CREATE DATABASE test; - Query OK, 1 row affected (0.00 sec) - - mysql> USE test; - Database changed - mysql> CREATE TABLE test ( text VARCHAR(255) ); - Query OK, 0 rows affected (0.01 sec) - - mysql> SELECT COUNT(*) FROM test; - +----------+ - | COUNT(*) | - +----------+ - | 0 | - +----------+ - 1 row in set (0.01 sec) - - mysql> INSERT INTO test VALUES ('hello, world!'); - Query OK, 1 row affected (0.00 sec) - - mysql> SELECT COUNT(*) FROM test; - +----------+ - | COUNT(*) | - +----------+ - | 1 | - +----------+ - 1 row in set (0.00 sec) - - mysql> exit - Bye - -Step 3: Check egress stats -************************** - -Check egress stats were updated. - -.. code-block:: console - - $ curl -s "http://localhost:8001/stats?filter=egress_mysql" - mysql.egress_mysql.auth_switch_request: 0 - mysql.egress_mysql.decoder_errors: 0 - mysql.egress_mysql.login_attempts: 1 - mysql.egress_mysql.login_failures: 0 - mysql.egress_mysql.protocol_errors: 0 - mysql.egress_mysql.queries_parse_error: 2 - mysql.egress_mysql.queries_parsed: 7 - mysql.egress_mysql.sessions: 6 - mysql.egress_mysql.upgraded_to_ssl: 0 - - - -Step 4: Check TCP stats -*********************** - -Check TCP stats were updated. - -.. code-block:: console - - $ curl -s "http://localhost:8001/stats?filter=mysql_tcp" - tcp.mysql_tcp.downstream_cx_no_route: 0 - tcp.mysql_tcp.downstream_cx_rx_bytes_buffered: 0 - tcp.mysql_tcp.downstream_cx_rx_bytes_total: 446 - tcp.mysql_tcp.downstream_cx_total: 1 - tcp.mysql_tcp.downstream_cx_tx_bytes_buffered: 0 - tcp.mysql_tcp.downstream_cx_tx_bytes_total: 677 - tcp.mysql_tcp.downstream_flow_control_paused_reading_total: 0 - tcp.mysql_tcp.downstream_flow_control_resumed_reading_total: 0 - tcp.mysql_tcp.idle_timeout: 0 - tcp.mysql_tcp.max_downstream_connection_duration: 0 - tcp.mysql_tcp.upstream_flush_active: 0 - tcp.mysql_tcp.upstream_flush_total: 0 - - -.. seealso:: - - :ref:`Envoy MySQL filter ` - Learn more about using the Envoy MySQL filter. - - :ref:`Envoy admin quick start guide ` - Quick start guide to the Envoy admin interface. - - `MySQL `_ - The MySQL database. diff --git a/examples/mysql/verify.sh b/examples/mysql/verify.sh deleted file mode 100755 index 212d4e204465..000000000000 --- a/examples/mysql/verify.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash -e - -export NAME=mysql -export PORT_ADMIN="${MYSQL_PORT_ADMIN:-11300}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - - -_mysql () { - local mysql_client - mysql_client=(docker run --rm --network mysql_default mysql:latest mysql -h proxy -P 1999 -u root) - - "${mysql_client[@]}" "${@}" -} - -export -f _mysql - -wait_for 40 bash -c "_mysql -e 'SHOW DATABASES;'" - -run_log "Create a mysql database" -_mysql -e "CREATE DATABASE test;" -_mysql -e "show databases;" | grep test - -run_log "Create a mysql table" -_mysql -e "USE test; CREATE TABLE test ( text VARCHAR(255) ); INSERT INTO test VALUES ('hello, world!');" -_mysql -e "SELECT COUNT(*) from test.test;" | grep 1 - -run_log "Check mysql egress stats" -responds_with \ - egress_mysql \ - "http://localhost:${PORT_ADMIN}/stats?filter=egress_mysql" - -run_log "Check mysql TCP stats" -responds_with \ - mysql_tcp \ - "http://localhost:${PORT_ADMIN}/stats?filter=mysql_tcp" diff --git a/examples/opentelemetry/Dockerfile-opentelemetry b/examples/opentelemetry/Dockerfile-opentelemetry deleted file mode 100644 index 432b0f151160..000000000000 --- a/examples/opentelemetry/Dockerfile-opentelemetry +++ /dev/null @@ -1,11 +0,0 @@ -FROM alpine:3.20@sha256:0a4eaa0eecf5f8c050e5bba433f58c052be7587ee8af3e8b3910ef9ab5fbe9f5 AS otelc_curl -RUN apk --update add curl - -FROM otel/opentelemetry-collector:latest@sha256:56b275978745d866d4e8bcb15de9e51ef259b61210b87c387bfd336a30a69acb - -COPY --from=otelc_curl / / - -COPY ./otel-collector-config.yaml /etc/otel-collector-config.yaml -USER 0 -RUN chmod o+r /etc/otel-collector-config.yaml -USER nobody diff --git a/examples/opentelemetry/README.md b/examples/opentelemetry/README.md deleted file mode 100644 index b2d6c335a2a8..000000000000 --- a/examples/opentelemetry/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/opentelemetry) diff --git a/examples/opentelemetry/docker-compose.yaml b/examples/opentelemetry/docker-compose.yaml deleted file mode 100644 index 153da43ee9ba..000000000000 --- a/examples/opentelemetry/docker-compose.yaml +++ /dev/null @@ -1,71 +0,0 @@ -services: - - envoy-front-proxy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: envoy-front-proxy.yaml - depends_on: - opentelemetry: - condition: service_healthy - envoy-1: - condition: service_started - envoy-2: - condition: service_started - ports: - - "${PORT_PROXY:-10000}:10000" - - envoy-1: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: envoy-1.yaml - depends_on: - opentelemetry: - condition: service_healthy - service-1: - condition: service_healthy - envoy-2: - condition: service_started - - envoy-2: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: envoy-2.yaml - depends_on: - opentelemetry: - condition: service_healthy - service-2: - condition: service_healthy - - service-1: - build: - context: ../shared/python - target: aiohttp-tracing-service - environment: - - SERVICE_NAME=1 - - service-2: - build: - context: ../shared/python - target: aiohttp-tracing-service - environment: - - SERVICE_NAME=2 - - opentelemetry: - build: - context: . - dockerfile: Dockerfile-opentelemetry - healthcheck: - test: ["CMD-SHELL", "curl -sf http://localhost:13133 || exit 1"] - interval: 1s - timeout: 120s - retries: 120 - start_period: 5s - command: ["--config=/etc/otel-collector-config.yaml"] - ports: - - "${PORT_UI:-55679}:55679" diff --git a/examples/opentelemetry/envoy-1.yaml b/examples/opentelemetry/envoy-1.yaml deleted file mode 100644 index 085bdc7d2ab1..000000000000 --- a/examples/opentelemetry/envoy-1.yaml +++ /dev/null @@ -1,128 +0,0 @@ -# This proxy listens on 2 ports: -# -# 10000 -> routes to `service-1` -# 10001 -> routes to `envoy-2` - -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - traffic_direction: INBOUND - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - tracing: - provider: - name: envoy.tracers.opentelemetry - typed_config: - "@type": type.googleapis.com/envoy.config.trace.v3.OpenTelemetryConfig - grpc_service: - envoy_grpc: - cluster_name: opentelemetry_collector - timeout: 0.250s - service_name: envoy-1 - codec_type: AUTO - stat_prefix: ingress_http - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - route_config: - name: service1_route - virtual_hosts: - - name: service1 - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service_cluster1 - decorator: - operation: routeToService1 - - address: - socket_address: - address: 0.0.0.0 - port_value: 10001 - traffic_direction: OUTBOUND - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - tracing: - provider: - name: envoy.tracers.opentelemetry - typed_config: - "@type": type.googleapis.com/envoy.config.trace.v3.OpenTelemetryConfig - grpc_service: - envoy_grpc: - cluster_name: opentelemetry_collector - timeout: 0.250s - service_name: envoy-1 - codec_type: AUTO - stat_prefix: egress_http - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - route_config: - name: envoy2_route - virtual_hosts: - - name: envoy2 - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: envoy_cluster2 - decorator: - operation: routeToEnvoy2 - - clusters: - - name: service_cluster1 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service_cluster1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-1 - port_value: 8080 - - name: envoy_cluster2 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: envoy_cluster2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: envoy-2 - port_value: 10000 - - name: opentelemetry_collector - type: STRICT_DNS - lb_policy: ROUND_ROBIN - typed_extension_protocol_options: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicit_http_config: - http2_protocol_options: {} - load_assignment: - cluster_name: opentelemetry_collector - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: opentelemetry - port_value: 4317 diff --git a/examples/opentelemetry/envoy-2.yaml b/examples/opentelemetry/envoy-2.yaml deleted file mode 100644 index f2ff064439b8..000000000000 --- a/examples/opentelemetry/envoy-2.yaml +++ /dev/null @@ -1,74 +0,0 @@ -# This proxy listens on port 10000 and routes all queries to `service-2`. - -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - traffic_direction: INBOUND - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - tracing: - provider: - name: envoy.tracers.opentelemetry - typed_config: - "@type": type.googleapis.com/envoy.config.trace.v3.OpenTelemetryConfig - grpc_service: - envoy_grpc: - cluster_name: opentelemetry_collector - timeout: 0.250s - service_name: envoy-2 - codec_type: AUTO - stat_prefix: ingress_http - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - route_config: - name: service2_route - virtual_hosts: - - name: service2 - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service_cluster2 - decorator: - operation: routeToService2 - - clusters: - - name: service_cluster2 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service_cluster2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-2 - port_value: 8080 - - name: opentelemetry_collector - type: STRICT_DNS - lb_policy: ROUND_ROBIN - typed_extension_protocol_options: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicit_http_config: - http2_protocol_options: {} - load_assignment: - cluster_name: opentelemetry_collector - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: opentelemetry - port_value: 4317 diff --git a/examples/opentelemetry/envoy-front-proxy.yaml b/examples/opentelemetry/envoy-front-proxy.yaml deleted file mode 100644 index 3e4f2007c55f..000000000000 --- a/examples/opentelemetry/envoy-front-proxy.yaml +++ /dev/null @@ -1,96 +0,0 @@ -# This proxy listens on port 10000, and routes the following paths: -# -# /trace/1 -> routes to `envoy-1` on port 10000 -# /trace/2 -> routes to `envoy-1` on port 10001 (for onward routing to `envoy-2`) - -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - traffic_direction: OUTBOUND - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - generate_request_id: true - tracing: - provider: - name: envoy.tracers.opentelemetry - typed_config: - "@type": type.googleapis.com/envoy.config.trace.v3.OpenTelemetryConfig - grpc_service: - envoy_grpc: - cluster_name: opentelemetry_collector - timeout: 0.250s - service_name: front-envoy - codec_type: AUTO - stat_prefix: ingress_http - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - route_config: - name: proxy_routes - virtual_hosts: - - name: proxy - domains: - - "*" - routes: - - match: - prefix: "/trace/1" - route: - cluster: envoy_cluster1 - decorator: - operation: routeToEnvoy1 - - match: - prefix: "/trace/2" - route: - cluster: envoy_cluster2 - decorator: - operation: routeToEnvoy2ViaEnvoy1 - - clusters: - - name: envoy_cluster1 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: envoy_cluster1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: envoy-1 - port_value: 10000 - - name: envoy_cluster2 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: envoy_cluster2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: envoy-1 - port_value: 10001 - - name: opentelemetry_collector - type: STRICT_DNS - lb_policy: ROUND_ROBIN - typed_extension_protocol_options: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicit_http_config: - http2_protocol_options: {} - load_assignment: - cluster_name: opentelemetry_collector - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: opentelemetry - port_value: 4317 diff --git a/examples/opentelemetry/example.rst b/examples/opentelemetry/example.rst deleted file mode 100644 index afbf11c20bcd..000000000000 --- a/examples/opentelemetry/example.rst +++ /dev/null @@ -1,121 +0,0 @@ -.. _install_sandboxes_opentelemetry: - -OpenTelemetry tracing -===================== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -The OpenTelemetry tracing sandbox demonstrates Envoy's :ref:`request tracing ` -capabilities using `OpenTelemetry `_ as the tracing provider. - -In this example, 2 backend services are provided: - -- ``service-1`` -- ``service-2`` - -3 Envoy proxies are also provided to route requests to them: - -- ``envoy-front-proxy`` (:download:`envoy-front-proxy.yaml <_include/opentelemetry/envoy-front-proxy.yaml>`) -- ``envoy-1`` (:download:`envoy-1.yaml <_include/opentelemetry/envoy-1.yaml>`) -- ``envoy-2`` (:download:`envoy-2.yaml <_include/opentelemetry/envoy-2.yaml>`) - -Of these services, only the Envoy ``front-proxy`` service is exposed outside of the -:download:`composition <_include/opentelemetry/docker-compose.yaml>`, on port ``10000``. - -For ``service-1``, requests are routed based on the request path ``trace/1``, as follows: - - User -> Envoy(``envoy-front-proxy``) -> Envoy(``envoy-1``) -> ``service-1`` - -For ``service-2``, requests are routed based on the request path ``trace/2`` as follows: - - User -> Envoy(``envoy-front-proxy``) -> Envoy(``envoy-1``) -> Envoy(``envoy-2``) -> ``service-2`` - -All Envoy proxies are configured to collect request traces, as can be seen in their configurations, -propagating the spans (parent/child/shared context) generated by the OpenTelemetry tracer to a OpenTelemetry cluster. - -Each span records the latency of upstream API calls as well as information -needed to correlate the span with other related spans (e.g., the trace ID). - -The OpenTelemetry collector provides a web UI for viewing the collected traces on port ``55679``. - -Step 1: Build the sandbox -************************* - -Change directory to ``examples/opentelemetry`` in the Envoy repository. - -To build this sandbox example, and start the example services run the following commands: - -.. code-block:: console - - $ pwd - envoy/examples/opentelemetry - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ------------------------------------------------------------------------------------------------------------------------------- - opentelemetry_envoy-1_1 /docker-entrypoint.sh /usr ... Up 10000/tcp - opentelemetry_envoy-2_1 /docker-entrypoint.sh /usr ... Up 10000/tcp - opentelemetry_envoy-front-proxy_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp - opentelemetry_opentelemetry_1 /otelcol --config=/etc/ote ... Up (healthy) 4317/tcp, 55678/tcp, 0.0.0.0:55679->55679/tcp - opentelemetry_service-1_1 python3 /code/service.py Up (healthy) - opentelemetry_service-2_1 python3 /code/service.py Up (healthy) - -Step 2: Make a request to ``service-1`` -*************************************** - -Now send a request to ``service-1``, by calling http://localhost:10000/trace/1. - -This will be routed via 2 of the Envoy proxies: - -- ``front-proxy`` -- ``envoy-1`` - -.. code-block:: console - - $ curl localhost:10000/trace/1 - Hello from behind Envoy (service 1)! - -Step 3: Make a request to ``service-2`` -*************************************** - -Now send a request to ``service-2``, by calling http://localhost:10000/trace/2. - -This will be routed via all 3 of the Envoy proxies: - -- ``front-proxy`` -- ``envoy-1`` -- ``envoy-2`` - -.. code-block:: console - - $ curl localhost:10000/trace/2 - Hello from behind Envoy (service 2)! - -Step 4: View the traces in OpenTelemetry UI -******************************************* - -Point your browser to http://localhost:55679/debug/tracez. - -You should see the OpenTelemetry dashboard. - -.. image:: /start/sandboxes/_static/opentelemetry-ui.png - -In the ``Latency Samples`` of ``opentelemetry.proto.collector.trace.v1.TraceService/Export`` you can explore the traces by clicking any value of -``[>0s][>10µs][>100µs][>1ms][>10ms][>100ms][>1s][>10s][>1m40s]``. - -.. image:: /start/sandboxes/_static/opentelemetry-ui-traces.png - -.. seealso:: - - :ref:`Request tracing ` - Learn more about using Envoy's request tracing. - - `OpenTelemetry `_ - OpenTelemetry tracing website. diff --git a/examples/opentelemetry/otel-collector-config.yaml b/examples/opentelemetry/otel-collector-config.yaml deleted file mode 100755 index b0ac5b631aba..000000000000 --- a/examples/opentelemetry/otel-collector-config.yaml +++ /dev/null @@ -1,38 +0,0 @@ -extensions: - memory_ballast: - size_mib: 512 - zpages: - endpoint: 0.0.0.0:55679 - health_check: - -receivers: - otlp: - protocols: - grpc: - http: - -processors: - batch: - memory_limiter: - # 75% of maximum memory up to 4G - limit_mib: 1536 - # 25% of limit up to 2G - spike_limit_mib: 512 - check_interval: 5s - -exporters: - logging: - loglevel: debug - -service: - pipelines: - traces: - receivers: [otlp] - processors: [memory_limiter, batch] - exporters: [logging] - metrics: - receivers: [otlp] - processors: [memory_limiter, batch] - exporters: [logging] - - extensions: [memory_ballast, zpages, health_check] diff --git a/examples/opentelemetry/verify.sh b/examples/opentelemetry/verify.sh deleted file mode 100755 index 9387c3853165..000000000000 --- a/examples/opentelemetry/verify.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -e - -export NAME=opentelemetry -export PORT_PROXY="${OPENTELEMETRY_PORT_PROXY:-12000}" -export PORT_UI="${OPENTELEMETRY_PORT_UI:-12001}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Make a request to service-1" -responds_with \ - "Hello from behind Envoy (service 1)!" \ - "http://localhost:${PORT_PROXY}/trace/1" - -run_log "Make a request to service-2" -responds_with \ - "Hello from behind Envoy (service 2)!" \ - "http://localhost:${PORT_PROXY}/trace/2" - -run_log "View the traces in OpenTelemetry UI" -responds_with \ - "" \ - "http://localhost:${PORT_UI}/debug/tracez" diff --git a/examples/postgres/README.md b/examples/postgres/README.md deleted file mode 100644 index 346b020e98c8..000000000000 --- a/examples/postgres/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/postgres.html). diff --git a/examples/postgres/docker-compose.yaml b/examples/postgres/docker-compose.yaml deleted file mode 100644 index 5a9f026c0eba..000000000000 --- a/examples/postgres/docker-compose.yaml +++ /dev/null @@ -1,21 +0,0 @@ -services: - - proxy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_VARIANT: contrib-dev - ports: - - "${PORT_ADMIN:-8001}:8001" - - postgres: - build: - context: ../shared/postgres - environment: - # WARNING! Do not use it on production environments because this will - # allow anyone with access to the Postgres port to access your - # database without a password, even if POSTGRES_PASSWORD is set. - # See PostgreSQL documentation about "trust": - # https://www.postgresql.org/docs/current/auth-trust.html - POSTGRES_HOST_AUTH_METHOD: trust diff --git a/examples/postgres/envoy.yaml b/examples/postgres/envoy.yaml deleted file mode 100644 index bc3bbe7b373e..000000000000 --- a/examples/postgres/envoy.yaml +++ /dev/null @@ -1,37 +0,0 @@ -static_resources: - listeners: - - name: postgres_listener - address: - socket_address: - address: 0.0.0.0 - port_value: 1999 - filter_chains: - - filters: - - name: envoy.filters.network.postgres_proxy - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.postgres_proxy.v3alpha.PostgresProxy - stat_prefix: egress_postgres - - name: envoy.filters.network.tcp_proxy - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - stat_prefix: postgres_tcp - cluster: postgres_cluster - - clusters: - - name: postgres_cluster - type: STRICT_DNS - load_assignment: - cluster_name: postgres_cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: postgres - port_value: 5432 - -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 8001 diff --git a/examples/postgres/example.rst b/examples/postgres/example.rst deleted file mode 100644 index 2e3be045f2bc..000000000000 --- a/examples/postgres/example.rst +++ /dev/null @@ -1,148 +0,0 @@ -.. _install_sandboxes_postgres: - -PostgreSQL filter -================= - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -In this example, we show how the :ref:`PostgreSQL filter ` -can be used with the Envoy proxy. - -The Envoy proxy configuration includes a PostgreSQL filter that parses queries and collects Postgres-specific metrics. - - -Step 1: Build the sandbox -************************* - -Change to the ``examples/postgres`` directory. - -Build and start the containers. - -.. code-block:: console - - $ pwd - envoy/examples/postgres - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - -------------------------------------------------------------------------------------------------------- - postgres_postgres_1 docker-entrypoint.sh postgres Up 5432/tcp - postgres_proxy_1 /docker-entrypoint.sh /usr ... Up 10000/tcp, 1999/tcp, 0.0.0.0:8001->8001/tcp - -Step 2: Issue commands using psql -********************************* - -This example uses ``psql`` client inside a container to issue some commands and -verify they are routed via Envoy. Note that we should set the environment variable -``PGSSLMODE=disable`` to disable ``SSL`` because the current implementation of the -filter can't decode encrypted sessions. - -.. code-block:: console - - $ docker run --rm -it --network envoymesh -e PGSSLMODE=disable postgres:latest psql -U postgres -h proxy -p 1999 - ... snip ... - - postgres=# CREATE DATABASE testdb; - CREATE DATABASE - postgres=# \c testdb - You are now connected to database "testdb" as user "postgres". - testdb=# CREATE TABLE tbl ( f SERIAL PRIMARY KEY ); - CREATE TABLE - testdb=# INSERT INTO tbl VALUES (DEFAULT); - INSERT 0 1 - testdb=# SELECT * FROM tbl; - f - --- - 1 - (1 row) - - testdb=# UPDATE tbl SET f = 2 WHERE f = 1; - UPDATE 1 - testdb=# INSERT INTO tbl VALUES (DEFAULT); - ERROR: duplicate key value violates unique constraint "tbl_pkey" - DETAIL: Key (f)=(2) already exists. - testdb=# DELETE FROM tbl; - DELETE 1 - testdb=# INSERT INTO tbl VALUES (DEFAULT); - INSERT 0 1 - testdb=# \q - - -Step 3: Check egress stats -************************** - -Check egress stats were updated. - -.. code-block:: console - - $ curl -s http://localhost:8001/stats?filter=egress_postgres - postgres.egress_postgres.errors: 1 - postgres.egress_postgres.errors_error: 1 - postgres.egress_postgres.errors_fatal: 0 - postgres.egress_postgres.errors_panic: 0 - postgres.egress_postgres.errors_unknown: 0 - postgres.egress_postgres.messages: 42 - postgres.egress_postgres.messages_backend: 32 - postgres.egress_postgres.messages_frontend: 10 - postgres.egress_postgres.messages_unknown: 0 - postgres.egress_postgres.notices: 0 - postgres.egress_postgres.notices_debug: 0 - postgres.egress_postgres.notices_info: 0 - postgres.egress_postgres.notices_log: 0 - postgres.egress_postgres.notices_notice: 0 - postgres.egress_postgres.notices_unknown: 0 - postgres.egress_postgres.notices_warning: 0 - postgres.egress_postgres.sessions: 1 - postgres.egress_postgres.sessions_encrypted: 0 - postgres.egress_postgres.sessions_unencrypted: 1 - postgres.egress_postgres.statements: 7 - postgres.egress_postgres.statements_delete: 1 - postgres.egress_postgres.statements_insert: 2 - postgres.egress_postgres.statements_other: 2 - postgres.egress_postgres.statements_parse_error: 4 - postgres.egress_postgres.statements_parsed: 4 - postgres.egress_postgres.statements_select: 1 - postgres.egress_postgres.statements_update: 1 - postgres.egress_postgres.transactions: 7 - postgres.egress_postgres.transactions_commit: 7 - postgres.egress_postgres.transactions_rollback: 0 - - -Step 4: Check TCP stats -*********************** - -Check TCP stats were updated. - -.. code-block:: console - - $ curl -s http://localhost:8001/stats?filter=postgres_tcp - tcp.postgres_tcp.downstream_cx_no_route: 0 - tcp.postgres_tcp.downstream_cx_rx_bytes_buffered: 0 - tcp.postgres_tcp.downstream_cx_rx_bytes_total: 373 - tcp.postgres_tcp.downstream_cx_total: 1 - tcp.postgres_tcp.downstream_cx_tx_bytes_buffered: 0 - tcp.postgres_tcp.downstream_cx_tx_bytes_total: 728 - tcp.postgres_tcp.downstream_flow_control_paused_reading_total: 0 - tcp.postgres_tcp.downstream_flow_control_resumed_reading_total: 0 - tcp.postgres_tcp.idle_timeout: 0 - tcp.postgres_tcp.max_downstream_connection_duration: 0 - tcp.postgres_tcp.upstream_flush_active: 0 - tcp.postgres_tcp.upstream_flush_total: 0 - -.. seealso:: - - :ref:`Envoy PostgreSQL filter ` - Learn more about using the Envoy PostgreSQL filter. - - :ref:`Envoy admin quick start guide ` - Quick start guide to the Envoy admin interface. - - `PostgreSQL `_ - The PostgreSQL database. diff --git a/examples/postgres/verify.sh b/examples/postgres/verify.sh deleted file mode 100755 index 97f9d3262328..000000000000 --- a/examples/postgres/verify.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash -e - -export NAME=postgres -export PORT_ADMIN="${POSTGRES_PORT_ADMIN:-11600}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -_psql () { - local postgres_client - postgres_client=(docker run -i --rm --network postgres_default -e "PGSSLMODE=disable" postgres:latest psql -U postgres -h proxy -p 1999) - "${postgres_client[@]}" "${@}" -} - -export -f _psql - -wait_for 40 bash -c "_psql -c '\l'" - -DBNAME=testdb - -run_log "Create a postgres database" -_psql -c "CREATE DATABASE ${DBNAME};" -_psql -c '\l' | grep ${DBNAME} - -run_log "Create a postgres table" -_psql -d ${DBNAME} -c 'CREATE TABLE tbl ( f SERIAL PRIMARY KEY );' - -run_log "Insert some data" -_psql -d ${DBNAME} -c 'INSERT INTO tbl VALUES (DEFAULT);' - -run_log "Checking inserted data" -_psql -d ${DBNAME} -c 'SELECT * FROM tbl;' | grep -E '1$' - -run_log "Updating data" -_psql -d ${DBNAME} -c 'UPDATE tbl SET f = 2 WHERE f = 1;' - -run_log "Raise an exception for duplicate key violation" -_psql -d ${DBNAME} -c 'INSERT INTO tbl VALUES (DEFAULT);' 2>&1 | grep -A1 'duplicate key value violates unique constraint' - -run_log "Change some more data" -_psql -d ${DBNAME} -c 'DELETE FROM tbl;' -_psql -d ${DBNAME} -c 'INSERT INTO tbl VALUES (DEFAULT);' - -run_log "Check postgres egress stats" -responds_with \ - egress_postgres \ - "http://localhost:${PORT_ADMIN}/stats?filter=egress_postgres" - -run_log "Check postgres TCP stats" -responds_with \ - postgres_tcp \ - "http://localhost:${PORT_ADMIN}/stats?filter=postgres_tcp" diff --git a/examples/rbac/README.md b/examples/rbac/README.md deleted file mode 100644 index 33fdf8037fe6..000000000000 --- a/examples/rbac/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/rbac.html). diff --git a/examples/rbac/docker-compose.yaml b/examples/rbac/docker-compose.yaml deleted file mode 100644 index 6b51b21c40f9..000000000000 --- a/examples/rbac/docker-compose.yaml +++ /dev/null @@ -1,12 +0,0 @@ -services: - envoy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - ports: - - "${PORT_PROXY:-10000}:10000" - - "${PORT_ADMIN:-10001}:10001" - - backend: - build: - context: ../shared/echo diff --git a/examples/rbac/envoy.yaml b/examples/rbac/envoy.yaml deleted file mode 100644 index 599e89a1b59c..000000000000 --- a/examples/rbac/envoy.yaml +++ /dev/null @@ -1,79 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: service - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: local_service - http_filters: - - name: envoy.filters.http.rbac - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC - matcher: - matcher_list: - matchers: - - predicate: - not_matcher: - single_predicate: - input: - name: envoy.matching.inputs.request_headers - typed_config: - "@type": type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput - header_name: referer - value_match: - safe_regex: - google_re2: {} - regex: https?://(www.)?envoyproxy.io/docs/envoy.* - on_match: - action: - name: action - typed_config: - "@type": type.googleapis.com/envoy.config.rbac.v3.Action - name: illegal-referer - action: DENY - on_no_match: - action: - name: action - typed_config: - "@type": type.googleapis.com/envoy.config.rbac.v3.Action - name: all-pass - action: ALLOW - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - clusters: - - name: local_service - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: local_service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: backend - port_value: 8080 - -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 10001 diff --git a/examples/rbac/example.rst b/examples/rbac/example.rst deleted file mode 100644 index d8607a4a14a6..000000000000 --- a/examples/rbac/example.rst +++ /dev/null @@ -1,100 +0,0 @@ -.. _install_sandboxes_rbac: - -Role Based Access Control (RBAC) - HTTP -======================================= - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -RBAC is used to check if the incoming request is authorized or not. - -Envoy supports 2 types for RBAC: - -- L4 connections via the :ref:`Role Based Access Control (RBAC) Network Filter ` -- HTTP requests via the :ref:`Role Based Access Control (RBAC) Filter ` - -This sandbox provides an example of RBAC of HTTP requests. - -In the example, requests should only be allowed if its ``Referer`` header -matches the regex pattern ``https?://(www.)?envoyproxy.io/docs/envoy.*``. - -Step 1: Start all of our containers -*********************************** - -Change to the ``examples/rbac`` directory and bring up the docker composition. - -.. code-block:: console - - $ pwd - envoy/examples/rbac - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ------------------------------------------------------------------------------------------------------------ - rbac_backend_1 gunicorn -b 0.0.0.0:80 htt ... Up 0.0.0.0:8080->80/tcp - rbac_envoy_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp, 0.0.0.0:10001->10001/tcp - -Step 2: Denial of upstream service using RBAC -********************************************* - -The sandbox is configured to proxy port ``10000`` to the upstream service. - -As the request does not have the required header it is denied, and Envoy refuses the connection with an HTTP 403 return code and with the content ``RBAC: access denied``. - -Now, use ``curl`` to make a request for the upstream service. - -.. code-block:: console - - $ curl -si localhost:10000 - HTTP/1.1 403 Forbidden - content-length: 19 - content-type: text/plain - date: Thu, 28 Jul 2022 06:48:43 GMT - server: envoy - - RBAC: access denied - -Step 3: Authorization of upstream service using RBAC -**************************************************** - -Now, we can make another request with proper headers set. - -.. code-block:: console - - $ curl -si -H "Referer: https://www.envoyproxy.io/docs/envoy" localhost:10000 | grep 200 - HTTP/1.1 200 OK - -Step 4: Check stats via admin -***************************** - -The sandbox is configured with the ``10001`` port for Envoy admin. - -Checking the admin interface we should now see that the RBAC stats are updated, with one request denied and the other allowed - -.. code-block:: console - - $ curl -s "http://localhost:10001/stats?filter=rbac" - http.ingress_http.rbac.allowed: 1 - http.ingress_http.rbac.denied: 1 - http.ingress_http.rbac.shadow_allowed: 0 - http.ingress_http.rbac.shadow_denied: 0 - -.. seealso:: - - :ref:`Role Based Access Control ` - Learn more about using Envoy's ``RBAC`` filter. - - :ref:`RBAC Network Filter API ` - API and configuration reference for Envoy's ``RBAC`` network filter. - - :ref:`RBAC HTTP Filter API ` - API and configuration reference for Envoy's ``RBAC`` HTTP filter. - - :ref:`Envoy admin quick start guide ` - Quick start guide to the Envoy admin interface. diff --git a/examples/rbac/verify.sh b/examples/rbac/verify.sh deleted file mode 100755 index 4ed8e1a79c65..000000000000 --- a/examples/rbac/verify.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -e - -export NAME=rbac -export PORT_PROXY="${RBAC_PORT_PROXY:-11810}" -export PORT_ADMIN="${RBAC_PORT_ADMIN:-11811}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Test upstream with access denied response" -responds_with "RBAC: access denied" "http://localhost:${PORT_PROXY}" - -run_log "Test authorized upstream response" -responds_without "RBAC: access denied" "http://localhost:${PORT_PROXY}" -H "Referer: https://www.envoyproxy.io/docs/envoy" - -run_log "Check admin stats" -responds_with rbac "http://localhost:${PORT_ADMIN}/stats?fitler=rbac" diff --git a/examples/redis/Dockerfile-redis b/examples/redis/Dockerfile-redis deleted file mode 100644 index 5d19ee90a244..000000000000 --- a/examples/redis/Dockerfile-redis +++ /dev/null @@ -1 +0,0 @@ -FROM redis@sha256:e59c42a34bdb950f988a1578504c31ce981096e3a18b83db1808bd7a32302e7f diff --git a/examples/redis/README.md b/examples/redis/README.md deleted file mode 100644 index f4b8b8885953..000000000000 --- a/examples/redis/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/redis.html). diff --git a/examples/redis/docker-compose.yaml b/examples/redis/docker-compose.yaml deleted file mode 100644 index b1559e6c82bb..000000000000 --- a/examples/redis/docker-compose.yaml +++ /dev/null @@ -1,14 +0,0 @@ -services: - - proxy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - ports: - - "${PORT_PROXY:-1999}:1999" - - "${PORT_ADMIN:-8001}:8001" - - redis: - build: - context: . - dockerfile: Dockerfile-redis diff --git a/examples/redis/envoy.yaml b/examples/redis/envoy.yaml deleted file mode 100644 index 79cfda734792..000000000000 --- a/examples/redis/envoy.yaml +++ /dev/null @@ -1,36 +0,0 @@ -static_resources: - listeners: - - name: redis_listener - address: - socket_address: - address: 0.0.0.0 - port_value: 1999 - filter_chains: - - filters: - - name: envoy.filters.network.redis_proxy - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.redis_proxy.v3.RedisProxy - stat_prefix: egress_redis - settings: - op_timeout: 5s - prefix_routes: - catch_all_route: - cluster: redis_cluster - clusters: - - name: redis_cluster - type: STRICT_DNS # static - lb_policy: MAGLEV - load_assignment: - cluster_name: redis_cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: redis - port_value: 6379 -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 8001 diff --git a/examples/redis/example.rst b/examples/redis/example.rst deleted file mode 100644 index e6aaf53032cc..000000000000 --- a/examples/redis/example.rst +++ /dev/null @@ -1,72 +0,0 @@ -.. _install_sandboxes_redis_filter: - -Redis filter -============ - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - -In this example, we show how a :ref:`Redis filter ` can be used with the Envoy proxy. - -The Envoy proxy configuration includes a Redis filter that routes egress requests to redis server. - -.. note:: - The example uses a redis container as the client but you could use a local redis client instead. - -Step 1: Build the sandbox -************************* - -Change to the ``examples/redis`` directory. - -Build and start the containers. - -.. code-block:: console - - $ pwd - envoy/examples/redis - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ------------------------------------------------------------------------------------------------------------------ - redis_proxy_1 /docker-entrypoint.sh /bin Up 10000/tcp, 0.0.0.0:1999->1999/tcp, 0.0.0.0:8001->8001/tcp - redis_redis_1 docker-entrypoint.sh redis Up 6379/tcp - -Step 2: Issue Redis commands -**************************** - -Issue Redis commands using your favourite Redis client, such as ``redis-cli``, and verify they are routed via Envoy. - -.. code-block:: console - - $ docker run --rm --network host redis:latest redis-cli -h localhost -p 1999 set foo foo - OK - $ docker run --rm --network host redis:latest redis-cli -h localhost -p 1999 set bar bar - OK - $ docker run --rm --network host redis:latest redis-cli -h localhost -p 1999 get foo - "foo" - $ docker run --rm --network host redis:latest redis-cli -h localhost -p 1999 get bar - "bar" - -Step 3: Verify egress stats -*************************** - -Go to ``http://localhost:8001/stats?usedonly&filter=redis.egress_redis.command`` and verify the following stats: - -.. code-block:: none - - redis.egress_redis.command.get.total: 2 - redis.egress_redis.command.set.total: 2 - -.. seealso:: - - :ref:`Envoy Redis filter ` - Learn more about using the Envoy Redis filter. - - :ref:`Envoy admin quick start guide ` - Quick start guide to the Envoy admin interface. - - `Redis `_ - The Redis in-memory data structure store. diff --git a/examples/redis/verify.sh b/examples/redis/verify.sh deleted file mode 100755 index 005937a0339d..000000000000 --- a/examples/redis/verify.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -e - -export NAME=redis -export PORT_PROXY="${REDIS_PORT:-11800}" -export PORT_ADMIN="${REDIS_PORT_ADMIN:-11801}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -_redis_cli () { - local redis_client - redis_client=(docker run --rm --network host redis:latest redis-cli) - "${redis_client[@]}" "${@}" -} - -export -f _redis_cli - -wait_for 40 bash -c "_redis_cli -h localhost -p ${PORT_PROXY} set baz BAZ | grep OK" - -run_log "Test set" -_redis_cli -h localhost -p "${PORT_PROXY}" set foo FOO | grep OK -_redis_cli -h localhost -p "${PORT_PROXY}" set bar BAR | grep OK - -run_log "Test get" -_redis_cli -h localhost -p "${PORT_PROXY}" get foo | grep FOO -_redis_cli -h localhost -p "${PORT_PROXY}" get bar | grep BAR - -run_log "Test redis stats" -responds_with \ - egress_redis \ - "http://localhost:${PORT_ADMIN}/stats?usedonly&filter=redis.egress_redis.command" diff --git a/examples/route-mirror/README.md b/examples/route-mirror/README.md deleted file mode 100644 index 899406a73f16..000000000000 --- a/examples/route-mirror/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/route-mirror.html) diff --git a/examples/route-mirror/docker-compose.yaml b/examples/route-mirror/docker-compose.yaml deleted file mode 100644 index a002e4d2ec08..000000000000 --- a/examples/route-mirror/docker-compose.yaml +++ /dev/null @@ -1,45 +0,0 @@ -services: - - envoy-front-proxy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - ports: - - "${PORT_PROXY:-10000}:10000" - depends_on: - service1: - condition: service_healthy - service1-mirror: - condition: service_healthy - service2: - condition: service_healthy - service2-mirror: - condition: service_healthy - - service1: - build: - context: ../shared/python - target: aiohttp-tracing-service - environment: - - SERVICE_NAME=1 - - service1-mirror: - build: - context: ../shared/python - target: aiohttp-tracing-service - environment: - - SERVICE_NAME=1 - - service2: - build: - context: ../shared/python - target: aiohttp-tracing-service - environment: - - SERVICE_NAME=2 - - service2-mirror: - build: - context: ../shared/python - target: aiohttp-tracing-service - environment: - - SERVICE_NAME=2 diff --git a/examples/route-mirror/envoy.yaml b/examples/route-mirror/envoy.yaml deleted file mode 100644 index 200dd4dadf67..000000000000 --- a/examples/route-mirror/envoy.yaml +++ /dev/null @@ -1,90 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/service/1" - route: - cluster: service1 - request_mirror_policies: - - cluster: "service1-mirror" - - match: - prefix: "/service/2" - route: - cluster: service2 - request_mirror_policies: - - cluster_header: "x-mirror-cluster" - - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - clusters: - - name: service1 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service1 - port_value: 8080 - - - name: service1-mirror - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service1-mirror - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service1-mirror - port_value: 8080 - - - name: service2 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service2 - port_value: 8080 - - - name: service2-mirror - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service2-mirror - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service2-mirror - port_value: 8080 diff --git a/examples/route-mirror/example.rst b/examples/route-mirror/example.rst deleted file mode 100644 index 795bbf1b8c12..000000000000 --- a/examples/route-mirror/example.rst +++ /dev/null @@ -1,188 +0,0 @@ -.. _install_sandboxes_route_mirror: - -Route mirroring policies -======================== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - -This simple example demonstrates Envoy's request mirroring capability using -`request mirror policies `__. - -Incoming requests are received by ``envoy-front-proxy`` service. - -Requests for the path ``/service/1`` are statically mirrored. - -Each request is handled by the ``service1`` cluster, and in addition, forwarded to -the ``service1-mirror`` cluster: - -.. literalinclude:: _include/route-mirror/envoy.yaml - :language: yaml - :lines: 16-34 - :linenos: - :emphasize-lines: 6-11 - :caption: Envoy configuration with static route mirror policy :download:`envoy.yaml <_include/route-mirror/envoy.yaml>` - -Requests for the path ``/service/2`` are dynamically mirrored according to the presence and value of -the ``x-mirror-cluster`` header. - -All reqests for this path are forwarded to the ``service2`` cluster, and are also mirrored -to the cluster named in the header. - -For example, if we send a request with the header ``x-mirror-cluster: service2-mirror``, -the request will be forwarded to the ``service2-mirror`` cluster. - -.. literalinclude:: _include/route-mirror/envoy.yaml - :language: yaml - :lines: 16-34 - :linenos: - :emphasize-lines: 12-17 - :caption: Envoy configuration with header based route mirror policy :download:`envoy.yaml <_include/route-mirror/envoy.yaml>` - - -.. warning:: - - Allowing a request header to determine the cluster that the request is mirrored to is most useful in - a trusted environment. - - For example, a downstream Envoy instance (or other application acting as a proxy) might - automatically add this header to requests for processing by an upstream Envoy instance - configured with request mirror policies. - - If you allow dynamic mirroring according to request header, you may wish to restrict which requests - can set or proxy the header. - -.. note:: - - Envoy will only return the response it receives from the primary cluster to the client. - - For this example, responses from ``service1`` and ``service2`` clusters will be sent - to the client. A response returned by the ``service1-mirror`` or the ``service2-mirror`` - cluster is not sent back to the client. - - This also means that any problems or latency in request processing in the mirror cluster - don't affect the response received by the client. - -Step 1: Build the sandbox -************************* - -Change to the ``examples/route-mirror`` directory. - -.. code-block:: console - - $ pwd - envoy/examples/route-mirror - $ docker compose build - $ docker compose up -d - $ docker compose ps - NAME COMMAND SERVICE STATUS PORTS - route-mirror-envoy-front-proxy-1 "/docker-entrypoint.…" envoy-front-proxy running 0.0.0.0:10000->10000/tcp, :::10000->10000/tcp - route-mirror-service1-1 "python3 /code/servi…" service1 running (healthy) - route-mirror-service1-mirror-1 "python3 /code/servi…" service1-mirror running (healthy) - route-mirror-service2-1 "python3 /code/servi…" service2 running (healthy) - route-mirror-service2-mirror-1 "python3 /code/servi…" service2-mirror running (healthy) - -Step 2: Make a request to the statically mirrored route -******************************************************* - -Let's send a request to the ``envoy-front-proxy`` service which forwards the request to -``service1`` and also sends the request to the service 1 mirror, ``service1-mirror``. - -.. code-block:: console - - $ curl localhost:10000/service/1 - Hello from behind Envoy (service 1)! - -Step 3: View logs for the statically mirrored request -***************************************************** - -The logs from the ``service1`` and ``service1-mirror`` services show that -both the ``service1`` and ``service1-mirror`` services received the request made -in Step 2. - -You can also see that for the request to the ``service1-mirror`` -service, the ``Host`` header was modified by Envoy to have a ``-shadow`` suffix -in the hostname. - -.. code-block:: console - - $ docker compose logs service1 - ... - Host: localhost:10000 - 192.168.80.6 - - [06/Oct/2022 03:56:22] "GET /service/1 HTTP/1.1" 200 - - - $ docker compose logs service1-mirror - ... - Host: localhost-shadow:10000 - 192.168.80.6 - - [06/Oct/2022 03:56:22] "GET /service/1 HTTP/1.1" 200 - - - -Step 4: Make a request to the route mirrored by request header -************************************************************** - -In this step, we will see a demonstration where the request specifies via a header, ``x-mirror-cluster``, -the cluster that envoy will mirror the request to. - -Let's send a request to the ``envoy-front-proxy`` service which forwards the request to -``service2`` and also mirrors the request to the cluster named, ``service2-mirror``. - -.. code-block:: console - - $ curl --header "x-mirror-cluster: service2-mirror" localhost:10000/service/2 - Hello from behind Envoy (service 2)! - - -Step 5: View logs for the request mirrored by request header -************************************************************ - -The logs show that both the ``service2`` and ``service2-mirror`` services -got the request. - -.. code-block:: console - - $ docker compose logs service2 - ... - Host: localhost:10000 - 192.168.80.6 - - [06/Oct/2022 03:56:22] "GET /service/2 HTTP/1.1" 200 - - - $ docker compose logs service2-mirror - ... - Host: localhost-shadow:10000 - 192.168.80.6 - - [06/Oct/2022 03:56:22] "GET /service/2 HTTP/1.1" 200 - - -You can also see that for the request to the ``service2-mirror`` service, the -``Host`` header was modified by Envoy to have a ``-shadow`` suffix in the -hostname. - -Step 6: Missing or invalid cluster name in request header -********************************************************* - -If you do not specify the ``x-mirror-cluster`` in the request to ``service2``, -or specify an unknown cluster, the request will not be mirrored but will be -handled in the normal way. - -.. code-block:: console - - $ curl localhost:10000/service/2 - Hello from behind Envoy (service 2)! - - $ curl --header "x-mirror-cluster: service2-mirror-non-existent" localhost:10000/service/2 - Hello from behind Envoy (service 2)! - -View the logs for ``service2`` and ``service2-mirror`` services. - -.. code-block:: console - - $ docker compose logs service2 - ... - Host: localhost:10000 - 192.168.80.6 - - [06/Oct/2022 03:56:22] "GET /service/2 HTTP/1.1" 200 - - - $ docker compose logs service2-mirror - # No new logs - -.. seealso:: - - :ref:`Envoy request mirror policy ` - Learn more Envoy's request mirroring policy. diff --git a/examples/route-mirror/verify.sh b/examples/route-mirror/verify.sh deleted file mode 100755 index e7a17bab07c0..000000000000 --- a/examples/route-mirror/verify.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -e - -export NAME=route-mirroring -export PORT_PROXY="${FRONT_PROXY_PORT_PROXY:-11820}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Make a request to the statically mirrored route" -responds_with "Hello from behind Envoy (service 1)!" "http://localhost:${PORT_PROXY}/service/1" - -run_log "View logs for the request mirrored by request header" -"${DOCKER_COMPOSE[@]}" logs service1 | grep --quiet "Host: localhost:${PORT_PROXY}" -"${DOCKER_COMPOSE[@]}" logs service1-mirror | grep --quiet "Host: localhost-shadow:${PORT_PROXY}" -"${DOCKER_COMPOSE[@]}" logs service1-mirror | grep --quiet GET - -run_log "Make a request to the route mirrored by request header" -responds_with \ - "Hello from behind Envoy (service 2)!" \ - "http://localhost:${PORT_PROXY}/service/2" \ - --header 'x-mirror-cluster: service2-mirror' - -run_log "View logs for the request mirrored by request header" -"${DOCKER_COMPOSE[@]}" logs service2 | grep --quiet "Host: localhost:${PORT_PROXY}" -"${DOCKER_COMPOSE[@]}" logs service2-mirror | grep --quiet "Host: localhost-shadow:${PORT_PROXY}" -"${DOCKER_COMPOSE[@]}" logs service2-mirror | grep --quiet GET - -run_log "Missing or invalid cluster name in request header" -responds_with \ - "Hello from behind Envoy (service 2)!" \ - "http://localhost:${PORT_PROXY}/service/2" -responds_with \ - "Hello from behind Envoy (service 2)!" \ - "http://localhost:${PORT_PROXY}/service/2" \ - --header 'x-mirror-cluster: service2-mirror-non-existent' -"${DOCKER_COMPOSE[@]}" logs service2-mirror | grep -c GET | grep --quiet 1 diff --git a/examples/shared/build/Dockerfile b/examples/shared/build/Dockerfile deleted file mode 100644 index 92ea0ad5f146..000000000000 --- a/examples/shared/build/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM envoyproxy/envoy-build-ubuntu:f94a38f62220a2b017878b790b6ea98a0f6c5f9c@sha256:2dd96b6f43c08ccabd5f4747fce5854f5f96af509b32e5cf6493f136e9833649 -ENV DEBIAN_FRONTEND=noninteractive -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \ - apt-get -qq install --no-install-recommends -y gosu \ - && groupadd -f envoygroup \ - && useradd -g envoygroup -m -d /home/envoybuild envoybuild diff --git a/examples/shared/build/build-entrypoint.sh b/examples/shared/build/build-entrypoint.sh deleted file mode 100755 index 7b824337aab6..000000000000 --- a/examples/shared/build/build-entrypoint.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -set -e - -if [[ $(id -u envoybuild) != "${BUILD_UID}" ]]; then - usermod -u "${BUILD_UID}" envoybuild - chown envoybuild /home/envoybuild -fi - -chown envoybuild /output -chmod 1777 /tmp - -exec gosu envoybuild "$@" diff --git a/examples/shared/echo/Dockerfile b/examples/shared/echo/Dockerfile deleted file mode 100644 index 16d356efc745..000000000000 --- a/examples/shared/echo/Dockerfile +++ /dev/null @@ -1 +0,0 @@ -FROM jmalloc/echo-server@sha256:86f2c45aa7e7ebe1be30b21f8cfff25a7ed6e3b059751822d4b35bf244a688d5 diff --git a/examples/shared/echo2/Dockerfile b/examples/shared/echo2/Dockerfile deleted file mode 100644 index 48a273ba75d2..000000000000 --- a/examples/shared/echo2/Dockerfile +++ /dev/null @@ -1 +0,0 @@ -FROM mendhak/http-https-echo@sha256:a5661adca985057c94e31d633ee57051dbf29ada1ccfaa4f5fb58f4a5b1a2b2b diff --git a/examples/shared/envoy/Dockerfile b/examples/shared/envoy/Dockerfile deleted file mode 100644 index 2c7e17f67853..000000000000 --- a/examples/shared/envoy/Dockerfile +++ /dev/null @@ -1,96 +0,0 @@ -ARG ENVOY_IMAGE="${ENVOY_IMAGE:-envoyproxy/envoy}" -ARG ENVOY_VARIANT="${ENVOY_VARIANT:-dev}" - - -FROM ${ENVOY_IMAGE}:${ENVOY_VARIANT} AS envoy-base -ARG ENVOY_CONFIG=envoy.yaml -ENV ENVOY_CONFIG="$ENVOY_CONFIG" -ENV DEBIAN_FRONTEND=noninteractive -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \ - rm -f /etc/apt/apt.conf.d/docker-clean \ - && echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' | tee /etc/apt/apt.conf.d/keep-cache \ - && apt-get -qq update -y \ - && apt-get -qq install --no-install-recommends -y curl -COPY --chmod=777 "$ENVOY_CONFIG" /etc/envoy.yaml -CMD ["/usr/local/bin/envoy", "-c", "/etc/envoy.yaml"] - -FROM envoy-base AS envoy-admin -ARG ENVOY_ADMIN_PORT=10001 -ENV ENVOY_ADMIN_PORT="$ENVOY_ADMIN_PORT" -HEALTHCHECK \ - --interval=1s \ - --timeout=1s \ - --start-period=1s \ - --retries=3 \ - CMD curl -s "localhost:${ENVOY_ADMIN_PORT}/stats?filter=server.state" | grep 0 \ - && curl -s "localhost:${ENVOY_ADMIN_PORT}/stats?filter=listener_manager.workers_started" | grep 1 - -FROM envoy-base AS envoy-fault-injection -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \ - apt-get -qq update -y \ - && apt-get -qq install --no-install-recommends -y tree -COPY enable_delay_fault_injection.sh disable_delay_fault_injection.sh enable_abort_fault_injection.sh disable_abort_fault_injection.sh send_request.sh / - - -FROM envoy-base AS envoy-jaeger-native -# -# for discussion on jaeger binary compatibility, and the source of the file, see here: -# https://github.com/envoyproxy/envoy/issues/11382#issuecomment-638012072 -# -RUN echo "4a7d17d4724ee890490bcd6cfdedb12a02316a3d33214348d30979abd201f1ca /usr/local/lib/libjaegertracing_plugin.so" > /tmp/checksum \ - && curl -Ls https://github.com/envoyproxy/misc/releases/download/jaegertracing-plugin/jaegertracing-plugin-centos.tar.gz \ - | tar zxf - -C /usr/local/lib \ - && mv /usr/local/lib/libjaegertracing.so.0.4.2 /usr/local/lib/libjaegertracing_plugin.so \ - && sha256sum -c /tmp/checksum \ - && rm /tmp/checksum - - -FROM envoy-base AS envoy-load-balancing -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \ - apt-get -qq update -y \ - && apt-get -qq install --no-install-recommends -y python3 -COPY ./client.py /client.py -EXPOSE 8001 - - -FROM envoy-base AS envoy-double-proxy-base -COPY --chmod=777 ./certs/ca.crt /certs/cacert.pem - - -FROM envoy-double-proxy-base AS envoy-double-proxy-frontend -COPY --chmod=777 ./certs/postgres-frontend.example.com.crt /certs/clientcert.pem -COPY --chmod=777 ./certs/example.com.key /certs/clientkey.pem - - -FROM envoy-double-proxy-base AS envoy-double-proxy-backend -COPY --chmod=777 ./certs/postgres-backend.example.com.crt /certs/servercert.pem -COPY --chmod=777 ./certs/example.com.key /certs/serverkey.pem - - -FROM envoy-base AS envoy-certs -COPY --chmod=777 ./certs /certs - - -FROM envoy-base AS envoy-lua -ADD --chmod=777 ./lib/mylibrary.lua /lib/mylibrary.lua - - -FROM envoy-base AS envoy-go -ENV GODEBUG=cgocheck=0 -COPY --chmod=777 ./lib/simple.so /lib/simple.so - - -FROM envoy-base AS envoy-ext_authz -COPY --chmod=777 ./config /etc/envoy-config -COPY --chmod=777 ./run_envoy.sh /run_envoy.sh -CMD ["/bin/sh", "/run_envoy.sh"] - - -FROM envoy-base AS envoy-dynamic-fs -COPY --chmod=777 ./configs /var/lib/envoy - - -FROM envoy-base diff --git a/examples/shared/golang/Dockerfile b/examples/shared/golang/Dockerfile deleted file mode 100644 index b2d762cd2a17..000000000000 --- a/examples/shared/golang/Dockerfile +++ /dev/null @@ -1,77 +0,0 @@ -FROM debian:bookworm-slim@sha256:5f7d5664eae4a192c2d2d6cb67fc3f3c7891a8722cd2903cc35aa649a12b0c8d AS os-base -RUN rm -f /etc/apt/apt.conf.d/docker-clean \ - && echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' | tee /etc/apt/apt.conf.d/keep-cache - - -FROM golang:1.22.5-bookworm@sha256:af9b40f2b1851be993763b85288f8434af87b5678af04355b1e33ff530b5765f AS golang-base - - -FROM golang-base AS golang-control-plane-builder -ARG GO_RESOURCE=resource.go -RUN git clone https://github.com/envoyproxy/go-control-plane && cd go-control-plane && git checkout b4adc3bb5fe5288bff01cd452dad418ef98c676e -ADD "$GO_RESOURCE" /go/go-control-plane/internal/example/resource.go -RUN cd go-control-plane && make bin/example -WORKDIR /go/go-control-plane - - -FROM os-base AS golang-control-plane -ENV DEBIAN_FRONTEND=noninteractive -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \ - apt-get -qq update \ - && apt-get -qq install --no-install-recommends -y netcat-traditional -COPY --from=golang-control-plane-builder /go/go-control-plane/bin/example /usr/local/bin/example - - -FROM golang-base AS golang-base-builder -ARG GO_APP=. -ARG GO_APP_NAME=grpc-service -RUN echo "COPY $GO_APP -> ${GO_APP_NAME} ..." -COPY "$GO_APP" /app - - -FROM golang-base-builder AS golang-grpc-auth-builder -RUN make -C "/app/${GO_APP_NAME}" - - -FROM os-base AS golang-grpc-auth -COPY --from=golang-grpc-auth-builder /app/grpc-service/server /app/server -CMD ["/app/server", "-users", "/etc/users.json"] - - -FROM golang-base AS golang-grpc-server-builder -WORKDIR /build -# Resolve and build Go dependencies as Docker cache -COPY go.mod /build/go.mod -COPY go.sum /build/go.sum -ENV GO111MODULE=on -RUN go mod download -COPY service.go /build/main.go -COPY kv/ /build/kv -# Build for linux -ENV GOOS=linux -ENV GOARCH=amd64 -ENV CGO_ENABLED=0 -RUN go build -o server - - -FROM os-base AS golang-grpc-server -WORKDIR /root/ -# Copy the linux amd64 binary -COPY --from=golang-grpc-server-builder /build/server /bin/ -ENTRYPOINT /bin/server - - -FROM golang-base AS golang-lrs-builder -COPY ./server /go/src/github.com/envoyproxy/envoy/example/load-reporting-service/server -COPY *.go /go/src/github.com/envoyproxy/envoy/example/load-reporting-service/ -COPY go.sum /go/src/github.com/envoyproxy/envoy/example/load-reporting-service -COPY go.mod /go/src/github.com/envoyproxy/envoy/example/load-reporting-service -WORKDIR /go/src/github.com/envoyproxy/envoy/example/load-reporting-service -RUN go mod download \ - && go install /go/src/github.com/envoyproxy/envoy/example/load-reporting-service - - -FROM os-base AS golang-lrs -COPY --from=golang-lrs-builder /go/bin/load-reporting-service /usr/local/bin/load-reporting-service -CMD ["load-reporting-service"] diff --git a/examples/shared/jaeger/Dockerfile b/examples/shared/jaeger/Dockerfile deleted file mode 100644 index dc05858f6d17..000000000000 --- a/examples/shared/jaeger/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM jaegertracing/all-in-one@sha256:dd79a8dcf45a48e85b419d19559f7448ad83ea6f4d631b8f361b52686b3e5f72 -HEALTHCHECK \ - --interval=1s \ - --timeout=1s \ - --start-period=1s \ - --retries=60 \ - CMD wget -q --header='Content-Type:application/json' -O - http://localhost:14269/health | grep "Server available" diff --git a/examples/shared/node/Dockerfile b/examples/shared/node/Dockerfile deleted file mode 100644 index afc7a67449ed..000000000000 --- a/examples/shared/node/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -FROM node:22.5-bookworm-slim@sha256:2fb92fe9d7350866a73c5cc311c1a19919ffd47e8592d4233374ee330e3bdb1e AS node-base - - -FROM node-base AS node-http-auth -ARG NODE_APP=. -ARG NODE_APP_NAME=http-service/server -# Add an env to save ARG -ENV NODE_APP_PATH "/app/${NODE_APP_NAME}" -COPY "$NODE_APP" /app -# Dont use exec form to interpolate correctly -CMD node $NODE_APP_PATH - - -FROM node-base AS yarn -ARG SERVICE_PORT=3000 -ENV DEBIAN_FRONTEND=noninteractive \ - SERVICE_PORT=$SERVICE_PORT -COPY --chmod=755 ./scripts/entrypoint.sh /entrypoint.sh -COPY --chmod=755 ./scripts/build.sh /usr/local/bin/build.sh -COPY --chmod=755 ./scripts/dev.sh /usr/local/bin/dev.sh -COPY --chmod=644 ./routes.jq /usr/local/share/routes.jq -RUN apt-get update \ - && apt-get -qq install -y --no-install-recommends gosu jq netcat-traditional yq -ENTRYPOINT ["/entrypoint.sh"] -CMD ["dev.sh"] -HEALTHCHECK \ - --interval=2s \ - --timeout=1s \ - --start-period=1s \ - --retries=30 \ - CMD nc -zv localhost "$SERVICE_PORT" - - -FROM yarn AS yarn-routed -COPY --chmod=755 ./scripts/build-routed.sh /usr/local/bin/build.sh diff --git a/examples/shared/node/routes.jq b/examples/shared/node/routes.jq deleted file mode 100644 index f5a61d71c558..000000000000 --- a/examples/shared/node/routes.jq +++ /dev/null @@ -1,49 +0,0 @@ -# Generate direct_response routes for a passed in file list -# Expects an xDS config to be passed as the `base` argument to -# inject the routes into. - -def getMimeType: - {".js": "text/javascript", - ".jpg": "image/jpeg", - ".jpeg": "image/jpeg", - ".png": "image/png", - ".gif": "image/gif", - ".svg": "image/svg+xml", - ".ico": "image/x-icon", - ".css": "text/css", - ".html": "text/html", - ".txt": "text/plain", - ".xml": "application/xml", - ".json": "application/json", - ".pdf": "application/pdf", - ".zip": "application/zip", - ".tar": "application/x-tar", - ".gz": "application/gzip" - }[match("\\.[^.]*$").string // "application/octet-stream"] -; - -def pathToDirectResponse: - . as $path - | sub("^dist/"; "/") as $asset - | $asset - | sub("^/index.html$"; "/") as $path - | if $path == "/" then - {prefix: $path} - else {$path} end - | {match: ., - direct_response: { - status: 200, - body: {filename: "/var/www/html\($asset)"} - }, - response_headers_to_add: [ - {header: { - key: "Content-Type", - value: ($asset | getMimeType)}}]} -; - -split("\n") as $assets -| ($base | fromjson) as $base -| $assets -| map(select(. != "") | pathToDirectResponse) as $routes -| $base -| .resources[0].filter_chains[0].filters[0].typed_config.route_config.virtual_hosts[0].routes = $routes diff --git a/examples/shared/node/scripts/build-routed.sh b/examples/shared/node/scripts/build-routed.sh deleted file mode 100755 index 875d97b7746a..000000000000 --- a/examples/shared/node/scripts/build-routed.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -yarn -yarn build - -BASE=$(yq -c < /var/lib/envoy/lds.tmpl.yml) -ASSETS=$(find dist -type f) -echo "$ASSETS" \ - | jq --arg base "$BASE" -Rs -f /usr/local/share/routes.jq \ - > /var/lib/envoy/lds.update.yml -mv -f /var/lib/envoy/lds.update.yml /var/lib/envoy/lds.yml diff --git a/examples/shared/node/scripts/build.sh b/examples/shared/node/scripts/build.sh deleted file mode 100644 index 4c3c1994bb0b..000000000000 --- a/examples/shared/node/scripts/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -yarn -yarn build diff --git a/examples/shared/node/scripts/dev.sh b/examples/shared/node/scripts/dev.sh deleted file mode 100755 index fdf461a027f5..000000000000 --- a/examples/shared/node/scripts/dev.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -yarn -yarn dev --host 0.0.0.0 --port 3000 diff --git a/examples/shared/node/scripts/entrypoint.sh b/examples/shared/node/scripts/entrypoint.sh deleted file mode 100755 index bea8c478a328..000000000000 --- a/examples/shared/node/scripts/entrypoint.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -e - -# Add local user -# Either use the LOCAL_USER_ID if passed in at runtime or -# fallback - -DEFAULT_USER_NAME=node - -USER_UID=${LOCAL_UID:-1000} -USER_NAME=${LOCAL_USER_NAME:-worker} -USER_HOME=${LOCAL_USER_HOME:-"/home/${USER_NAME}"} - -echo "Starting (${*}) with user: $USER_UID $USER_NAME $USER_HOME" -usermod -l "$USER_NAME" -md "$USER_HOME" "$DEFAULT_USER_NAME" || : -chown "$USER_NAME" "$USER_HOME" -export HOME="${USER_HOME}" -export PATH=$PATH:"${HOME}/.yarn/bin/" -mkdir -p ./dist -chown -R "$USER_NAME" ./dist -exec /usr/sbin/gosu "${USER_NAME}" "$@" diff --git a/examples/shared/postgres/Dockerfile b/examples/shared/postgres/Dockerfile deleted file mode 100644 index b6355bb29481..000000000000 --- a/examples/shared/postgres/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM postgres:latest@sha256:d0f363f8366fbc3f52d172c6e76bc27151c3d643b870e1062b4e8bfe65baf609 -COPY docker-healthcheck.sh /usr/local/bin/ -HEALTHCHECK CMD ["docker-healthcheck.sh"] diff --git a/examples/shared/postgres/docker-healthcheck.sh b/examples/shared/postgres/docker-healthcheck.sh deleted file mode 100755 index ddb39f942c67..000000000000 --- a/examples/shared/postgres/docker-healthcheck.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -# https://github.com/docker-library/healthcheck/tree/master/postgres - -set -eo pipefail - -host="$(hostname -i || echo '127.0.0.1')" -user="${POSTGRES_USER:-postgres}" -db="${POSTGRES_DB:-$POSTGRES_USER}" -export PGPASSWORD="${POSTGRES_PASSWORD:-}" - -args=( - # force postgres to not use the local unix socket (test "external" connectibility) - --host "$host" - --username "$user" - --dbname "$db" - --quiet --no-align --tuples-only -) - -if select="$(echo 'SELECT 1' | psql "${args[@]}")" && [ "$select" = '1' ]; then - exit 0 -fi - -exit 1 diff --git a/examples/shared/python/Dockerfile b/examples/shared/python/Dockerfile deleted file mode 100644 index 1ce69ebf8a6e..000000000000 --- a/examples/shared/python/Dockerfile +++ /dev/null @@ -1,82 +0,0 @@ -FROM python:3.11.5-slim-bookworm@sha256:edaf703dce209d774af3ff768fc92b1e3b60261e7602126276f9ceb0e3a96874 AS python-base -RUN rm -f /etc/apt/apt.conf.d/docker-clean \ - && echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' | tee /etc/apt/apt.conf.d/keep-cache -ARG PYTHON_REQUIREMENTS_FILE=aiohttp/requirements.txt -ADD "$PYTHON_REQUIREMENTS_FILE" /tmp/requirements.txt - - -FROM python-base AS python-grpc-client -WORKDIR /client -RUN pip install --require-hashes -qr /tmp/requirements.txt -# Copy the sources, including the stubs -COPY --chmod=777 client.py /client/grpc-kv-client.py -COPY kv /client/kv -CMD tail -f /dev/null - - -FROM python-base AS aiohttp-service -ARG SERVICE_PORT=8080 -ENV DEBIAN_FRONTEND=noninteractive \ - SERVICE_PORT=$SERVICE_PORT -ADD "$PYTHON_REQUIREMENTS_FILE" /tmp/requirements.txt -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \ - pip3 install --require-hashes -qr /tmp/requirements.txt \ - && apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends netcat-traditional \ - && mkdir /code -HEALTHCHECK \ - --interval=1s \ - --timeout=1s \ - --start-period=1s \ - --retries=3 \ - CMD nc -zv localhost "$SERVICE_PORT" -ENTRYPOINT ["python3", "/code/service.py"] - - -FROM aiohttp-service AS aiohttp-tracing-service -ADD tracing/service.py /code/service.py - - -FROM aiohttp-service AS aiohttp-tracing-service2 -ADD tracing/service2.py /code/service.py - - -FROM aiohttp-service AS aiohttp-tracing-service3 -COPY --from=envoyproxy/envoy:dev /usr/local/bin/envoy /usr/local/bin/envoy -COPY --chmod=777 tracing/start_service.sh /usr/local/bin/start_service.sh -ADD tracing/service2.py /code/service.py -ENTRYPOINT ["/usr/local/bin/start_service.sh"] - - -FROM aiohttp-tracing-service3 AS aiohttp-jaeger-service -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \ - apt-get -qq update \ - && apt-get -qq install --no-install-recommends -y curl -# -# for discussion on jaeger binary compatibility, and the source of the file, see here: -# https://github.com/envoyproxy/envoy/issues/11382#issuecomment-638012072 -# -RUN echo "4a7d17d4724ee890490bcd6cfdedb12a02316a3d33214348d30979abd201f1ca /usr/local/lib/libjaegertracing_plugin.so" > /tmp/checksum \ - && curl -Ls https://github.com/envoyproxy/misc/releases/download/jaegertracing-plugin/jaegertracing-plugin-centos.tar.gz \ - | tar zxf - -C /usr/local/lib \ - && mv /usr/local/lib/libjaegertracing.so.0.4.2 /usr/local/lib/libjaegertracing_plugin.so \ - && sha256sum -c /tmp/checksum \ - && rm /tmp/checksum - - -FROM aiohttp-service AS aiohttp-hello-service -ADD service.py /code/service.py - - -FROM aiohttp-service AS aiohttp-data-service -RUN mkdir -p /code/data -RUN dd if=/dev/zero of="/code/data/file.txt" bs=1024 count=10240 \ - && dd if=/dev/zero of="/code/data/file.json" bs=1024 count=10240 -ADD data-service.py /code/service.py - - -FROM aiohttp-service AS aiohttp-postgres-service -ADD postgres/requirements.txt /tmp/requirements.txt -RUN pip3 install -qr /tmp/requirements.txt diff --git a/examples/shared/python/aiohttp/requirements.in b/examples/shared/python/aiohttp/requirements.in deleted file mode 100644 index df84c65abf50..000000000000 --- a/examples/shared/python/aiohttp/requirements.in +++ /dev/null @@ -1,2 +0,0 @@ -aiohttp -pyyaml diff --git a/examples/shared/python/aiohttp/requirements.txt b/examples/shared/python/aiohttp/requirements.txt deleted file mode 100644 index 80f534780de7..000000000000 --- a/examples/shared/python/aiohttp/requirements.txt +++ /dev/null @@ -1,367 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile --allow-unsafe --generate-hashes requirements.in -# -aiohttp==3.9.5 \ - --hash=sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8 \ - --hash=sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c \ - --hash=sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475 \ - --hash=sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed \ - --hash=sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf \ - --hash=sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372 \ - --hash=sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81 \ - --hash=sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f \ - --hash=sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1 \ - --hash=sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd \ - --hash=sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a \ - --hash=sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb \ - --hash=sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46 \ - --hash=sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de \ - --hash=sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78 \ - --hash=sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c \ - --hash=sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771 \ - --hash=sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb \ - --hash=sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430 \ - --hash=sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233 \ - --hash=sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156 \ - --hash=sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9 \ - --hash=sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59 \ - --hash=sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888 \ - --hash=sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c \ - --hash=sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c \ - --hash=sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da \ - --hash=sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424 \ - --hash=sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2 \ - --hash=sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb \ - --hash=sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8 \ - --hash=sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a \ - --hash=sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10 \ - --hash=sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0 \ - --hash=sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09 \ - --hash=sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031 \ - --hash=sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4 \ - --hash=sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3 \ - --hash=sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa \ - --hash=sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a \ - --hash=sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe \ - --hash=sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a \ - --hash=sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2 \ - --hash=sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1 \ - --hash=sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323 \ - --hash=sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b \ - --hash=sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b \ - --hash=sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106 \ - --hash=sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac \ - --hash=sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6 \ - --hash=sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832 \ - --hash=sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75 \ - --hash=sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6 \ - --hash=sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d \ - --hash=sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72 \ - --hash=sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db \ - --hash=sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a \ - --hash=sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da \ - --hash=sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678 \ - --hash=sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b \ - --hash=sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24 \ - --hash=sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed \ - --hash=sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f \ - --hash=sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e \ - --hash=sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58 \ - --hash=sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a \ - --hash=sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342 \ - --hash=sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558 \ - --hash=sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2 \ - --hash=sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551 \ - --hash=sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595 \ - --hash=sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee \ - --hash=sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11 \ - --hash=sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d \ - --hash=sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7 \ - --hash=sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f - # via -r requirements.in -aiosignal==1.3.1 \ - --hash=sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc \ - --hash=sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17 - # via aiohttp -attrs==23.1.0 \ - --hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \ - --hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015 - # via aiohttp -frozenlist==1.4.0 \ - --hash=sha256:007df07a6e3eb3e33e9a1fe6a9db7af152bbd8a185f9aaa6ece10a3529e3e1c6 \ - --hash=sha256:008eb8b31b3ea6896da16c38c1b136cb9fec9e249e77f6211d479db79a4eaf01 \ - --hash=sha256:09163bdf0b2907454042edb19f887c6d33806adc71fbd54afc14908bfdc22251 \ - --hash=sha256:0c7c1b47859ee2cac3846fde1c1dc0f15da6cec5a0e5c72d101e0f83dcb67ff9 \ - --hash=sha256:0e5c8764c7829343d919cc2dfc587a8db01c4f70a4ebbc49abde5d4b158b007b \ - --hash=sha256:10ff5faaa22786315ef57097a279b833ecab1a0bfb07d604c9cbb1c4cdc2ed87 \ - --hash=sha256:17ae5cd0f333f94f2e03aaf140bb762c64783935cc764ff9c82dff626089bebf \ - --hash=sha256:19488c57c12d4e8095a922f328df3f179c820c212940a498623ed39160bc3c2f \ - --hash=sha256:1a0848b52815006ea6596c395f87449f693dc419061cc21e970f139d466dc0a0 \ - --hash=sha256:1e78fb68cf9c1a6aa4a9a12e960a5c9dfbdb89b3695197aa7064705662515de2 \ - --hash=sha256:261b9f5d17cac914531331ff1b1d452125bf5daa05faf73b71d935485b0c510b \ - --hash=sha256:2b8bcf994563466db019fab287ff390fffbfdb4f905fc77bc1c1d604b1c689cc \ - --hash=sha256:38461d02d66de17455072c9ba981d35f1d2a73024bee7790ac2f9e361ef1cd0c \ - --hash=sha256:490132667476f6781b4c9458298b0c1cddf237488abd228b0b3650e5ecba7467 \ - --hash=sha256:491e014f5c43656da08958808588cc6c016847b4360e327a62cb308c791bd2d9 \ - --hash=sha256:515e1abc578dd3b275d6a5114030b1330ba044ffba03f94091842852f806f1c1 \ - --hash=sha256:556de4430ce324c836789fa4560ca62d1591d2538b8ceb0b4f68fb7b2384a27a \ - --hash=sha256:5833593c25ac59ede40ed4de6d67eb42928cca97f26feea219f21d0ed0959b79 \ - --hash=sha256:6221d84d463fb110bdd7619b69cb43878a11d51cbb9394ae3105d082d5199167 \ - --hash=sha256:6918d49b1f90821e93069682c06ffde41829c346c66b721e65a5c62b4bab0300 \ - --hash=sha256:6c38721585f285203e4b4132a352eb3daa19121a035f3182e08e437cface44bf \ - --hash=sha256:71932b597f9895f011f47f17d6428252fc728ba2ae6024e13c3398a087c2cdea \ - --hash=sha256:7211ef110a9194b6042449431e08c4d80c0481e5891e58d429df5899690511c2 \ - --hash=sha256:764226ceef3125e53ea2cb275000e309c0aa5464d43bd72abd661e27fffc26ab \ - --hash=sha256:7645a8e814a3ee34a89c4a372011dcd817964ce8cb273c8ed6119d706e9613e3 \ - --hash=sha256:76d4711f6f6d08551a7e9ef28c722f4a50dd0fc204c56b4bcd95c6cc05ce6fbb \ - --hash=sha256:7f4f399d28478d1f604c2ff9119907af9726aed73680e5ed1ca634d377abb087 \ - --hash=sha256:88f7bc0fcca81f985f78dd0fa68d2c75abf8272b1f5c323ea4a01a4d7a614efc \ - --hash=sha256:8d0edd6b1c7fb94922bf569c9b092ee187a83f03fb1a63076e7774b60f9481a8 \ - --hash=sha256:901289d524fdd571be1c7be054f48b1f88ce8dddcbdf1ec698b27d4b8b9e5d62 \ - --hash=sha256:93ea75c050c5bb3d98016b4ba2497851eadf0ac154d88a67d7a6816206f6fa7f \ - --hash=sha256:981b9ab5a0a3178ff413bca62526bb784249421c24ad7381e39d67981be2c326 \ - --hash=sha256:9ac08e601308e41eb533f232dbf6b7e4cea762f9f84f6357136eed926c15d12c \ - --hash=sha256:a02eb8ab2b8f200179b5f62b59757685ae9987996ae549ccf30f983f40602431 \ - --hash=sha256:a0c6da9aee33ff0b1a451e867da0c1f47408112b3391dd43133838339e410963 \ - --hash=sha256:a6c8097e01886188e5be3e6b14e94ab365f384736aa1fca6a0b9e35bd4a30bc7 \ - --hash=sha256:aa384489fefeb62321b238e64c07ef48398fe80f9e1e6afeff22e140e0850eef \ - --hash=sha256:ad2a9eb6d9839ae241701d0918f54c51365a51407fd80f6b8289e2dfca977cc3 \ - --hash=sha256:b206646d176a007466358aa21d85cd8600a415c67c9bd15403336c331a10d956 \ - --hash=sha256:b826d97e4276750beca7c8f0f1a4938892697a6bcd8ec8217b3312dad6982781 \ - --hash=sha256:b89ac9768b82205936771f8d2eb3ce88503b1556324c9f903e7156669f521472 \ - --hash=sha256:bd7bd3b3830247580de99c99ea2a01416dfc3c34471ca1298bccabf86d0ff4dc \ - --hash=sha256:bdf1847068c362f16b353163391210269e4f0569a3c166bc6a9f74ccbfc7e839 \ - --hash=sha256:c11b0746f5d946fecf750428a95f3e9ebe792c1ee3b1e96eeba145dc631a9672 \ - --hash=sha256:c5374b80521d3d3f2ec5572e05adc94601985cc526fb276d0c8574a6d749f1b3 \ - --hash=sha256:ca265542ca427bf97aed183c1676e2a9c66942e822b14dc6e5f42e038f92a503 \ - --hash=sha256:ce31ae3e19f3c902de379cf1323d90c649425b86de7bbdf82871b8a2a0615f3d \ - --hash=sha256:ceb6ec0a10c65540421e20ebd29083c50e6d1143278746a4ef6bcf6153171eb8 \ - --hash=sha256:d081f13b095d74b67d550de04df1c756831f3b83dc9881c38985834387487f1b \ - --hash=sha256:d5655a942f5f5d2c9ed93d72148226d75369b4f6952680211972a33e59b1dfdc \ - --hash=sha256:d5a32087d720c608f42caed0ef36d2b3ea61a9d09ee59a5142d6070da9041b8f \ - --hash=sha256:d6484756b12f40003c6128bfcc3fa9f0d49a687e171186c2d85ec82e3758c559 \ - --hash=sha256:dd65632acaf0d47608190a71bfe46b209719bf2beb59507db08ccdbe712f969b \ - --hash=sha256:de343e75f40e972bae1ef6090267f8260c1446a1695e77096db6cfa25e759a95 \ - --hash=sha256:e29cda763f752553fa14c68fb2195150bfab22b352572cb36c43c47bedba70eb \ - --hash=sha256:e41f3de4df3e80de75845d3e743b3f1c4c8613c3997a912dbf0229fc61a8b963 \ - --hash=sha256:e66d2a64d44d50d2543405fb183a21f76b3b5fd16f130f5c99187c3fb4e64919 \ - --hash=sha256:e74b0506fa5aa5598ac6a975a12aa8928cbb58e1f5ac8360792ef15de1aa848f \ - --hash=sha256:f0ed05f5079c708fe74bf9027e95125334b6978bf07fd5ab923e9e55e5fbb9d3 \ - --hash=sha256:f61e2dc5ad442c52b4887f1fdc112f97caeff4d9e6ebe78879364ac59f1663e1 \ - --hash=sha256:fec520865f42e5c7f050c2a79038897b1c7d1595e907a9e08e3353293ffc948e - # via - # aiohttp - # aiosignal -idna==3.7 \ - --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \ - --hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0 - # via yarl -multidict==6.0.4 \ - --hash=sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9 \ - --hash=sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8 \ - --hash=sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03 \ - --hash=sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710 \ - --hash=sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161 \ - --hash=sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664 \ - --hash=sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569 \ - --hash=sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067 \ - --hash=sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313 \ - --hash=sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706 \ - --hash=sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2 \ - --hash=sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636 \ - --hash=sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49 \ - --hash=sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93 \ - --hash=sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603 \ - --hash=sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0 \ - --hash=sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60 \ - --hash=sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4 \ - --hash=sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e \ - --hash=sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1 \ - --hash=sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60 \ - --hash=sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951 \ - --hash=sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc \ - --hash=sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe \ - --hash=sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95 \ - --hash=sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d \ - --hash=sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8 \ - --hash=sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed \ - --hash=sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2 \ - --hash=sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775 \ - --hash=sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87 \ - --hash=sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c \ - --hash=sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2 \ - --hash=sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98 \ - --hash=sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3 \ - --hash=sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe \ - --hash=sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78 \ - --hash=sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660 \ - --hash=sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176 \ - --hash=sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e \ - --hash=sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988 \ - --hash=sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c \ - --hash=sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c \ - --hash=sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0 \ - --hash=sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449 \ - --hash=sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f \ - --hash=sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde \ - --hash=sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5 \ - --hash=sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d \ - --hash=sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac \ - --hash=sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a \ - --hash=sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9 \ - --hash=sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca \ - --hash=sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11 \ - --hash=sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35 \ - --hash=sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063 \ - --hash=sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b \ - --hash=sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982 \ - --hash=sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258 \ - --hash=sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1 \ - --hash=sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52 \ - --hash=sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480 \ - --hash=sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7 \ - --hash=sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461 \ - --hash=sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d \ - --hash=sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc \ - --hash=sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779 \ - --hash=sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a \ - --hash=sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547 \ - --hash=sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0 \ - --hash=sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171 \ - --hash=sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf \ - --hash=sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d \ - --hash=sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba - # via - # aiohttp - # yarl -pyyaml==6.0.1 \ - --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \ - --hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \ - --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \ - --hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \ - --hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \ - --hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \ - --hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \ - --hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \ - --hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \ - --hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \ - --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \ - --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \ - --hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \ - --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \ - --hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \ - --hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \ - --hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \ - --hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \ - --hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \ - --hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \ - --hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \ - --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \ - --hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \ - --hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \ - --hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \ - --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \ - --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \ - --hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \ - --hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \ - --hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \ - --hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \ - --hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \ - --hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \ - --hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \ - --hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \ - --hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \ - --hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \ - --hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \ - --hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \ - --hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \ - --hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \ - --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \ - --hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \ - --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \ - --hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \ - --hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \ - --hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \ - --hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \ - --hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \ - --hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f - # via -r requirements.in -yarl==1.9.2 \ - --hash=sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571 \ - --hash=sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3 \ - --hash=sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3 \ - --hash=sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c \ - --hash=sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7 \ - --hash=sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04 \ - --hash=sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191 \ - --hash=sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea \ - --hash=sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4 \ - --hash=sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4 \ - --hash=sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095 \ - --hash=sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e \ - --hash=sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74 \ - --hash=sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef \ - --hash=sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33 \ - --hash=sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde \ - --hash=sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45 \ - --hash=sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf \ - --hash=sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b \ - --hash=sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac \ - --hash=sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0 \ - --hash=sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528 \ - --hash=sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716 \ - --hash=sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb \ - --hash=sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18 \ - --hash=sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72 \ - --hash=sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6 \ - --hash=sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582 \ - --hash=sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5 \ - --hash=sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368 \ - --hash=sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc \ - --hash=sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9 \ - --hash=sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be \ - --hash=sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a \ - --hash=sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80 \ - --hash=sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8 \ - --hash=sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6 \ - --hash=sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417 \ - --hash=sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574 \ - --hash=sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59 \ - --hash=sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608 \ - --hash=sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82 \ - --hash=sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1 \ - --hash=sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3 \ - --hash=sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d \ - --hash=sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8 \ - --hash=sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc \ - --hash=sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac \ - --hash=sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8 \ - --hash=sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955 \ - --hash=sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0 \ - --hash=sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367 \ - --hash=sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb \ - --hash=sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a \ - --hash=sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623 \ - --hash=sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2 \ - --hash=sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6 \ - --hash=sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7 \ - --hash=sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4 \ - --hash=sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051 \ - --hash=sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938 \ - --hash=sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8 \ - --hash=sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9 \ - --hash=sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3 \ - --hash=sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5 \ - --hash=sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9 \ - --hash=sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333 \ - --hash=sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185 \ - --hash=sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3 \ - --hash=sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560 \ - --hash=sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b \ - --hash=sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7 \ - --hash=sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78 \ - --hash=sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7 - # via aiohttp diff --git a/examples/shared/python/data-service.py b/examples/shared/python/data-service.py deleted file mode 100644 index 944c3b7f5a6c..000000000000 --- a/examples/shared/python/data-service.py +++ /dev/null @@ -1,30 +0,0 @@ -from aiohttp import web - -routes = web.RouteTableDef() - - -@routes.get('/file.{suffix}') -async def get(request): - suffix = request.match_info["suffix"] - - with open(f"/code/data/file.{suffix}") as f: - if suffix == "txt": - return web.Response(text=f.read()) - return web.json_response(body=f.read()) - - -@routes.post("/upload") -async def post(request): - data = await request.post() - datalen = 0 - for k in data: - datalen += len(k) - resp = web.Response(text="OK") - resp.headers["decompressed-size"] = str(datalen) - return resp - - -if __name__ == "__main__": - app = web.Application(client_max_size=1024**4) - app.add_routes(routes) - web.run_app(app, host='0.0.0.0', port=8080) diff --git a/examples/shared/python/postgres/requirements.in b/examples/shared/python/postgres/requirements.in deleted file mode 100644 index 37ec460f84e8..000000000000 --- a/examples/shared/python/postgres/requirements.in +++ /dev/null @@ -1 +0,0 @@ -psycopg2-binary diff --git a/examples/shared/python/postgres/requirements.txt b/examples/shared/python/postgres/requirements.txt deleted file mode 100644 index 272d6720b807..000000000000 --- a/examples/shared/python/postgres/requirements.txt +++ /dev/null @@ -1,77 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile --allow-unsafe --generate-hashes requirements.in -# -psycopg2-binary==2.9.9 \ - --hash=sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9 \ - --hash=sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77 \ - --hash=sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e \ - --hash=sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84 \ - --hash=sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3 \ - --hash=sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2 \ - --hash=sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67 \ - --hash=sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876 \ - --hash=sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152 \ - --hash=sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f \ - --hash=sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a \ - --hash=sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6 \ - --hash=sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503 \ - --hash=sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f \ - --hash=sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493 \ - --hash=sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996 \ - --hash=sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f \ - --hash=sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e \ - --hash=sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59 \ - --hash=sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94 \ - --hash=sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7 \ - --hash=sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682 \ - --hash=sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420 \ - --hash=sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae \ - --hash=sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291 \ - --hash=sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe \ - --hash=sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980 \ - --hash=sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692 \ - --hash=sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119 \ - --hash=sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716 \ - --hash=sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472 \ - --hash=sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b \ - --hash=sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2 \ - --hash=sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc \ - --hash=sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c \ - --hash=sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5 \ - --hash=sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984 \ - --hash=sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9 \ - --hash=sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf \ - --hash=sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0 \ - --hash=sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f \ - --hash=sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212 \ - --hash=sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb \ - --hash=sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be \ - --hash=sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90 \ - --hash=sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041 \ - --hash=sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7 \ - --hash=sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860 \ - --hash=sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245 \ - --hash=sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27 \ - --hash=sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417 \ - --hash=sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359 \ - --hash=sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202 \ - --hash=sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0 \ - --hash=sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7 \ - --hash=sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba \ - --hash=sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1 \ - --hash=sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd \ - --hash=sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07 \ - --hash=sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98 \ - --hash=sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55 \ - --hash=sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d \ - --hash=sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972 \ - --hash=sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f \ - --hash=sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e \ - --hash=sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26 \ - --hash=sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957 \ - --hash=sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53 \ - --hash=sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52 - # via -r requirements.in diff --git a/examples/shared/python/service.py b/examples/shared/python/service.py deleted file mode 100644 index 9d79a76d205b..000000000000 --- a/examples/shared/python/service.py +++ /dev/null @@ -1,14 +0,0 @@ -from aiohttp import web - -routes = web.RouteTableDef() - - -@routes.get("/") -async def get(request): - return web.Response(text="Hello, World") - - -if __name__ == "__main__": - app = web.Application() - app.add_routes(routes) - web.run_app(app, host='0.0.0.0', port=8080) diff --git a/examples/shared/python/tracing/service.py b/examples/shared/python/tracing/service.py deleted file mode 100644 index 3a5c786f7eef..000000000000 --- a/examples/shared/python/tracing/service.py +++ /dev/null @@ -1,26 +0,0 @@ -import logging -import os - -from aiohttp import web - -routes = web.RouteTableDef() - - -@routes.get("/{service_type}/{service}") -async def get(request): - service = request.match_info["service"] - print(f"Host: {request.headers.get('Host')}", flush=True) - - service_name = os.environ.get("SERVICE_NAME") - - if service_name and service != service_name: - raise web.HTTPNotFound() - - return web.Response(text=f"Hello from behind Envoy (service {service})!\n") - - -if __name__ == "__main__": - app = web.Application() - logging.basicConfig(level=logging.DEBUG) - app.add_routes(routes) - web.run_app(app, host='0.0.0.0', port=8080) diff --git a/examples/shared/python/tracing/service2.py b/examples/shared/python/tracing/service2.py deleted file mode 100644 index 6e19ab0a229e..000000000000 --- a/examples/shared/python/tracing/service2.py +++ /dev/null @@ -1,60 +0,0 @@ -import logging -import os -import socket -import sys - -import aiohttp -from aiohttp import web - -routes = web.RouteTableDef() - -TRACE_HEADERS_TO_PROPAGATE = [ - 'X-Ot-Span-Context', - 'X-Request-Id', - - # Zipkin headers - 'X-B3-TraceId', - 'X-B3-SpanId', - 'X-B3-ParentSpanId', - 'X-B3-Sampled', - 'X-B3-Flags', - - # Jaeger header (for native client) - "uber-trace-id", - - # SkyWalking headers. - "sw8" -] - - -@routes.get("/{service_type}/{service}") -async def get(request): - service_type = request.match_info["service_type"] - service = request.match_info["service"] - service_name = os.environ.get("SERVICE_NAME") - - if service_name and service != service_name: - raise web.HTTPNotFound() - - if service_type == "trace" and int(service_name) == 1: - # call service 2 from service 1 - headers = {} - for header in TRACE_HEADERS_TO_PROPAGATE: - if header in request.headers: - headers[header] = request.headers[header] - async with aiohttp.ClientSession() as session: - async with session.get("http://localhost:9000/trace/2", headers=headers) as resp: - pass - - return web.Response( - text=( - f"Hello from behind Envoy (service {service})! " - f"hostname {socket.gethostname()} " - f"resolved {socket.gethostbyname(socket.gethostname())}\n")) - - -if __name__ == "__main__": - app = web.Application() - logging.basicConfig(level=logging.DEBUG) - app.add_routes(routes) - web.run_app(app, host='0.0.0.0', port=8080) diff --git a/examples/shared/python/tracing/start_service.sh b/examples/shared/python/tracing/start_service.sh deleted file mode 100644 index 43a8c112e636..000000000000 --- a/examples/shared/python/tracing/start_service.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -python3 /code/service.py & -envoy -c /etc/service-envoy.yaml --service-cluster "service${SERVICE_NAME}" diff --git a/examples/shared/websocket/Dockerfile b/examples/shared/websocket/Dockerfile deleted file mode 100644 index 4f714ce9fcb7..000000000000 --- a/examples/shared/websocket/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -FROM debian:bookworm-slim@sha256:5f7d5664eae4a192c2d2d6cb67fc3f3c7891a8722cd2903cc35aa649a12b0c8d AS websocket-base -ENV DEBIAN_FRONTEND=noninteractive -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \ - apt-get -qq update \ - && apt-get -qq install --no-install-recommends -y ca-certificates musl wget -RUN wget -qO /usr/local/bin/websocat https://github.com/vi/websocat/releases/download/v1.11.0/websocat.x86_64-unknown-linux-musl \ - && echo 'dc5524b9f03a344b88a12c859fb02f8bb56b3373dbc43a6e0c45a2ab52b853d7 /usr/local/bin/websocat' | sha256sum -c - \ - && chmod +x /usr/local/bin/websocat -ENTRYPOINT ["websocat"] - - -FROM websocket-base AS websocket-client -ENV DEBIAN_FRONTEND=noninteractive -ADD interact.sh /interact.sh -RUN chmod +x /interact.sh -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \ - apt-get -qq update \ - && apt-get -qq install --no-install-recommends -y expect -ENTRYPOINT ["/interact.sh"] - - -FROM websocket-base diff --git a/examples/single-page-app/_github-clusters.yml b/examples/single-page-app/_github-clusters.yml deleted file mode 100644 index 9d5645ac06a0..000000000000 --- a/examples/single-page-app/_github-clusters.yml +++ /dev/null @@ -1,34 +0,0 @@ - - name: github - type: LOGICAL_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: hub - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: github.com - port_value: 443 - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - sni: github.com - - name: github-api - type: LOGICAL_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: hub-api - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: api.github.com - port_value: 443 - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - sni: api.github.com diff --git a/examples/single-page-app/_static/spa-cookies.png b/examples/single-page-app/_static/spa-cookies.png deleted file mode 100644 index ce099ab99fcec3c2c5960066d0d09bc9362b017e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56881 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 diff --git a/examples/single-page-app/_static/spa-github-oauth.png b/examples/single-page-app/_static/spa-github-oauth.png deleted file mode 100644 index 90788486c3eb6b2f4c355ba89d98241a01fd711c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22205 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 diff --git a/examples/single-page-app/_static/spa-login.png b/examples/single-page-app/_static/spa-login.png deleted file mode 100644 index 75471c8094287071df9646d1c040589456589959..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14972 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�RYl>?>gKdokBdJ#DpCuVukX*5llor3*uGn{ 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 We0yZG^9~O7PwKP0SotSC|NjLC{REK! diff --git a/examples/single-page-app/docker-compose.yml b/examples/single-page-app/docker-compose.yml deleted file mode 100644 index 2524dd6d11c9..000000000000 --- a/examples/single-page-app/docker-compose.yml +++ /dev/null @@ -1,82 +0,0 @@ -services: - ui: - build: - context: ../shared/node - target: yarn-routed - volumes: - - ${UI_PATH:-./ui}:/workspace - - ${XDS_PATH:-./xds}:/var/lib/envoy - working_dir: /workspace - - envoy: - depends_on: - ui: - condition: service_healthy - myhub: - condition: service_started - myhub-api: - condition: service_started - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: ${ENVOY_CONFIG:-envoy.yml} - ports: - - ${PORT_DEV_PROXY:-10001}:10001 - - ${PORT_PROXY:-10000}:10000 - command: - - envoy - - "-c" - - /etc/envoy.yaml - - "--component-log-level" - - oauth2:trace,http:debug - entrypoint: - - /bin/bash - - -c - - | - set -eo pipefail - touch /var/lib/envoy/lds.yml - chmod +w /var/lib/envoy/lds.yml - exec envoy "$${@}" - volumes: - - ${SECRETS_PATH:-./secrets}:/etc/envoy/secrets - - ${XDS_PATH:-./xds}:/var/lib/envoy - - ${UI_PATH:-./ui}/dist:/var/www/html - - myhub: - build: - context: ../shared/python - target: aiohttp-service - args: - SERVICE_PORT: 7000 - ports: - - ${PORT_MYHUB:-7000}:7000 - volumes: - - myhub:/var/lib/myhub - - ./myhub/myhub.py:/code/service.py - - ./myhub/data.yml:/etc/myhub/data.yml - - ./myhub/images:/var/lib/myhub/images - - ./myhub/shared.py:/code/shared.py - environment: - DATA_PATH: /etc/myhub/data.yml - TOKEN_STORAGE_PATH: /var/lib/myhub/auth.json - - myhub-api: - build: - context: ../shared/python - target: aiohttp-service - args: - SERVICE_PORT: 7000 - volumes: - - myhub:/var/lib/myhub - - ./myhub/api.py:/code/service.py - - ./myhub/data.yml:/etc/myhub/data.yml - - ./myhub/images:/var/lib/myhub/images - - ./myhub/shared.py:/code/shared.py - environment: - DATA_PATH: /etc/myhub/data.yml - MYHUB_URL: "http://localhost:${PORT_MYHUB:-7000}" - TOKEN_STORAGE_PATH: /var/lib/myhub/auth.json - -volumes: - myhub: diff --git a/examples/single-page-app/envoy.yml b/examples/single-page-app/envoy.yml deleted file mode 100644 index a3e497f4bc15..000000000000 --- a/examples/single-page-app/envoy.yml +++ /dev/null @@ -1,325 +0,0 @@ -node: - id: FOO - cluster: BAR - -dynamic_resources: - lds_config: - path_config_source: - path: /var/lib/envoy/lds.yml - -static_resources: - listeners: - - name: dev - address: - socket_address: - protocol: TCP - address: 0.0.0.0 - port_value: 10001 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - upgrade_configs: - - upgrade_type: websocket - http_filters: - - name: envoy.filters.http.oauth2 - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2 - config: - token_endpoint: - cluster: hub - uri: http://myhub:7000/authenticate - timeout: 3s - default_expires_in: 600s - authorization_endpoint: http://localhost:7000/authorize - redirect_uri: "%REQ(x-forwarded-proto)%://%REQ(:authority)%/authorize" - forward_bearer_token: true - pass_through_matcher: - name: ":path" - string_match: - safe_regex: - regex: >- - ^\/(authorize.*|login|logout)$ - invert_match: true - redirect_path_matcher: - path: - prefix: /authorize - signout_path: - path: - exact: /logout - credentials: - client_id: "0123456789" - token_secret: - name: token - sds_config: - path_config_source: - path: /etc/envoy/secrets/myhub-token-secret.yml - hmac_secret: - name: hmac - sds_config: - path_config_source: - path: /etc/envoy/secrets/hmac-secret.yml - auth_scopes: - - user:email - - name: envoy.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - codec_type: "AUTO" - stat_prefix: ingress_http - route_config: - virtual_hosts: - - name: web - domains: ["*"] - routes: - - match: - prefix: "/hub/" - route: - host_rewrite_literal: api.myhub - regex_rewrite: - pattern: - regex: '^/hub/(.*)' - substitution: '/\1' - cluster: hub-api - - match: - prefix: "/" - route: - cluster: ui - - - name: production - address: - socket_address: - protocol: TCP - address: 0.0.0.0 - port_value: 10000 - access_log: - - name: envoy.access_loggers.stdout - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StderrAccessLog - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - http_filters: - - name: envoy.filters.http.oauth2 - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2 - config: - token_endpoint: - cluster: hub - uri: http://myhub:7000/authenticate - timeout: 3s - default_expires_in: 600s - authorization_endpoint: http://localhost:7000/authorize - redirect_uri: "%REQ(x-forwarded-proto)%://%REQ(:authority)%/authorize" - forward_bearer_token: true - pass_through_matcher: - name: ":path" - string_match: - safe_regex: - regex: >- - ^\/(authorize.*|login|logout)$ - invert_match: true - redirect_path_matcher: - path: - prefix: /authorize - signout_path: - path: - exact: /logout - credentials: - client_id: "0123456789" - token_secret: - name: token - sds_config: - path_config_source: - path: /etc/envoy/secrets/myhub-token-secret.yml - hmac_secret: - name: hmac - sds_config: - path_config_source: - path: /etc/envoy/secrets/hmac-secret.yml - auth_scopes: - - user:email - - name: envoy.filters.http.compressor - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor - response_direction_config: - common_config: - min_content_length: 100 - disable_on_etag_header: true - compressor_library: - name: text_optimized - typed_config: - "@type": type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip - memory_level: 3 - window_bits: 10 - - name: envoy.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - codec_type: "AUTO" - stat_prefix: ingress_http - route_config: - virtual_hosts: - - name: web - domains: ["*"] - routes: - - match: - prefix: "/other/" - route: - regex_rewrite: - pattern: - regex: '^/other/(.*)' - substitution: '/\1' - cluster: other - - - match: - prefix: "/hub/" - route: - host_rewrite_literal: api.myhub - regex_rewrite: - pattern: - regex: '^/hub/(.*)' - substitution: '/\1' - cluster: hub-api - - match: - prefix: "/" - route: - cluster: loopback - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext - common_tls_context: - tls_certificates: - # The following self-signed certificate pair is generated using: - # $ openssl req -x509 -newkey rsa:2048 -keyout a/front-proxy-key.pem -out a/front-proxy-crt.pem \ - # -days 3650 -nodes -subj '/CN=front-envoy' - # - # Instead of feeding it as an inline_string, certificate pair can also be fed to Envoy - # via filename. Reference: https://envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#config-core-v3-datasource. - # - # Or in a dynamic configuration scenario, certificate pair can be fetched remotely via - # Secret Discovery Service (SDS). Reference: https://envoyproxy.io/docs/envoy/latest/configuration/security/secret. - - certificate_chain: - inline_string: | - -----BEGIN CERTIFICATE----- - MIICqDCCAZACCQCquzpHNpqBcDANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtm - cm9udC1lbnZveTAeFw0yMDA3MDgwMTMxNDZaFw0zMDA3MDYwMTMxNDZaMBYxFDAS - BgNVBAMMC2Zyb250LWVudm95MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC - AQEAthnYkqVQBX+Wg7aQWyCCb87hBce1hAFhbRM8Y9dQTqxoMXZiA2n8G089hUou - oQpEdJgitXVS6YMFPFUUWfwcqxYAynLK4X5im26Yfa1eO8La8sZUS+4Bjao1gF5/ - VJxSEo2yZ7fFBo8M4E44ZehIIocipCRS+YZehFs6dmHoq/MGvh2eAHIa+O9xssPt - ofFcQMR8rwBHVbKy484O10tNCouX4yUkyQXqCRy6HRu7kSjOjNKSGtjfG+h5M8bh - 10W7ZrsJ1hWhzBulSaMZaUY3vh5ngpws1JATQVSK1Jm/dmMRciwlTK7KfzgxHlSX - 58ENpS7yPTISkEICcLbXkkKGEQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCmj6Hg - vwOxWz0xu+6fSfRL6PGJUGq6wghCfUvjfwZ7zppDUqU47fk+yqPIOzuGZMdAqi7N - v1DXkeO4A3hnMD22Rlqt25vfogAaZVToBeQxCPd/ALBLFrvLUFYuSlS3zXSBpQqQ - Ny2IKFYsMllz5RSROONHBjaJOn5OwqenJ91MPmTAG7ujXKN6INSBM0PjX9Jy4Xb9 - zT+I85jRDQHnTFce1WICBDCYidTIvJtdSSokGSuy4/xyxAAc/BpZAfOjBQ4G1QRe - 9XwOi790LyNUYFJVyeOvNJwveloWuPLHb9idmY5YABwikUY6QNcXwyHTbRCkPB2I - m+/R4XnmL4cKQ+5Z - -----END CERTIFICATE----- - private_key: - inline_string: | - -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2GdiSpVAFf5aD - tpBbIIJvzuEFx7WEAWFtEzxj11BOrGgxdmIDafwbTz2FSi6hCkR0mCK1dVLpgwU8 - VRRZ/ByrFgDKcsrhfmKbbph9rV47wtryxlRL7gGNqjWAXn9UnFISjbJnt8UGjwzg - Tjhl6EgihyKkJFL5hl6EWzp2Yeir8wa+HZ4Achr473Gyw+2h8VxAxHyvAEdVsrLj - zg7XS00Ki5fjJSTJBeoJHLodG7uRKM6M0pIa2N8b6HkzxuHXRbtmuwnWFaHMG6VJ - oxlpRje+HmeCnCzUkBNBVIrUmb92YxFyLCVMrsp/ODEeVJfnwQ2lLvI9MhKQQgJw - tteSQoYRAgMBAAECggEAeDGdEkYNCGQLe8pvg8Z0ccoSGpeTxpqGrNEKhjfi6NrB - NwyVav10iq4FxEmPd3nobzDPkAftfvWc6hKaCT7vyTkPspCMOsQJ39/ixOk+jqFx - lNa1YxyoZ9IV2DIHR1iaj2Z5gB367PZUoGTgstrbafbaNY9IOSyojCIO935ubbcx - DWwL24XAf51ez6sXnI8V5tXmrFlNXhbhJdH8iIxNyM45HrnlUlOk0lCK4gmLJjy9 - 10IS2H2Wh3M5zsTpihH1JvM56oAH1ahrhMXs/rVFXXkg50yD1KV+HQiEbglYKUxO - eMYtfaY9i2CuLwhDnWp3oxP3HfgQQhD09OEN3e0IlQKBgQDZ/3poG9TiMZSjfKqL - xnCABMXGVQsfFWNC8THoW6RRx5Rqi8q08yJrmhCu32YKvccsOljDQJQQJdQO1g09 - e/adJmCnTrqxNtjPkX9txV23Lp6Ak7emjiQ5ICu7iWxrcO3zf7hmKtj7z+av8sjO - mDI7NkX5vnlE74nztBEjp3eC0wKBgQDV2GeJV028RW3b/QyP3Gwmax2+cKLR9PKR - nJnmO5bxAT0nQ3xuJEAqMIss/Rfb/macWc2N/6CWJCRT6a2vgy6xBW+bqG6RdQMB - xEZXFZl+sSKhXPkc5Wjb4lQ14YWyRPrTjMlwez3k4UolIJhJmwl+D7OkMRrOUERO - EtUvc7odCwKBgBi+nhdZKWXveM7B5N3uzXBKmmRz3MpPdC/yDtcwJ8u8msUpTv4R - JxQNrd0bsIqBli0YBmFLYEMg+BwjAee7vXeDFq+HCTv6XMva2RsNryCO4yD3I359 - XfE6DJzB8ZOUgv4Dvluie3TB2Y6ZQV/p+LGt7G13yG4hvofyJYvlg3RPAoGAcjDg - +OH5zLN2eqah8qBN0CYa9/rFt0AJ19+7/smLTJ7QvQq4g0gwS1couplcCEnNGWiK - 72y1n/ckvvplmPeAE19HveMvR9UoCeV5ej86fACy8V/oVpnaaLBvL2aCMjPLjPP9 - DWeCIZp8MV86cvOrGfngf6kJG2qZTueXl4NAuwkCgYEArKkhlZVXjwBoVvtHYmN2 - o+F6cGMlRJTLhNc391WApsgDZfTZSdeJsBsvvzS/Nc0burrufJg0wYioTlpReSy4 - ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU - q5sGxGrC1RECGB5Zwx2S2ZY= - -----END PRIVATE KEY----- - - clusters: - - # UI development server - # tail the logs by running: - # `docker compose logs ui -f` - - name: ui - type: LOGICAL_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: ui - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: ui - port_value: 3000 - - # UI static routes (production) - # build app and create listener/routes by running: - # `docker compose run ui build.sh` - - name: loopback - type: STATIC - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: loopback - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: 127.0.0.1 - port_value: 10002 - - - name: hub - type: LOGICAL_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: hub - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: myhub - port_value: 7000 - - - name: hub-api - type: LOGICAL_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: hub-api - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: myhub-api - port_value: 7000 - - - name: other - type: LOGICAL_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: other - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: other - port_value: 7000 diff --git a/examples/single-page-app/example.rst b/examples/single-page-app/example.rst deleted file mode 100644 index ea702bda6f5f..000000000000 --- a/examples/single-page-app/example.rst +++ /dev/null @@ -1,650 +0,0 @@ -.. _install_sandboxes_single_page_app: - -Single page React app (with OAuth) -================================== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make HTTP requests. - - :ref:`envsubst ` - Used to interpolate environment vars in templates. - - :ref:`jq ` - Used to parse JSON. - - :ref:`mkpasswd ` - Used to generate a ~random HMAC token. - -This sandbox provides an example of building and developing a single page app with Envoy. - -The sandbox covers a number of Envoy's features, including: - -- :ref:`direct_response ` -- :ref:`OAuth ` -- Dynamic xDS filesystem updates -- Websocket proxy -- Gzip :ref:`compression ` -- TLS/SNI up/downstream connection/termination -- Path/host rewrites - -The app is built with `React `__ using `Vite `__ and demonstrates OAuth authentication using -Envoy's :ref:`OAuth filter `. - -This covers a scenario where we want OAuth to both authenticate the user and provide credentials -for further API interactions. - -This is enabled by setting the OAuth configuration -:ref:`forward_bearer_token ` -to ``true`` - -.. literalinclude:: _include/single-page-app/envoy.yml - :language: yaml - :lines: 36-40 - :linenos: - :lineno-start: 36 - :emphasize-lines: 3 - :caption: :download:`envoy.yml <_include/single-page-app/envoy.yml>` - -.. warning:: - Setting - :ref:`forward_bearer_token ` - means the provided access token will be forwarded to any cluster/upstreams proxied by Envoy for this HTTP filter chain.. - - If untrusted upstreams are present, care will need to be taken to remove any sensitive cookies, such as ``BearerToken``. - - This can be achieved by setting :ref:`request_headers_to_remove ` - for the affected route. - -A dummy "Myhub" backend is provided with a minimal OAuth provider and API for use in the example. - -Setup is provided to :ref:`build and update the app for production use `, -as well as a :ref:`development environment ` with -:ref:`automatic code reloading `. - -The production and development environments are exposed on ports ``10000`` and ``10001`` respectively. - -The Myhub backend can easily be replaced with `Github `__ or some other OAuth-based upstream service, -and some :ref:`guidance is provided on how to do this `. - -.. _install_sandboxes_single_page_app_step_local: - -Step 1: Create a ``.local`` directory for sandbox customizations -**************************************************************** - -Change to the ``examples/single-page-app`` directory, and create a directory to store sandbox customizations. - -You can use ``.local`` which will be ignored by Git: - -.. code-block:: console - - $ mkdir .local - -Copy the ``ui/`` directory to ``.local`` and set the ``UI_PATH``. This will allow customizations without changing committed files. - -.. code-block:: console - - $ cp -a ui .local - $ export UI_PATH=./.local/ui - -.. _install_sandboxes_single_page_app_step_hmac: - -Step 2: Generate an HMAC secret -******************************* - -Envoy's :ref:`OAuth filter ` requires an HMAC secret for encoding credentials. - -Copy the default sandbox secrets to the customization directory, and create the required HMAC secret. - -Replace ``MY_HMAC_SECRET_SEED`` with a phrase of your choosing: - -.. code-block:: console - - $ cp -a secrets .local - $ HMAC_SECRET=$(echo "MY_HMAC_SECRET_SEED" | mkpasswd -s) - $ export HMAC_SECRET - $ envsubst < hmac-secret.tmpl.yml > .local/secrets/hmac-secret.yml - -Export the path to the secrets folder for Docker: - -.. code-block:: console - - $ export SECRETS_PATH=./.local/secrets - -.. _install_sandboxes_single_page_app_step_start: - -Step 3: Start the containers -**************************** - -First export ``UID`` to ensure files created by the containers are created with your user id. - -Then bring up the Docker composition: - -.. code-block:: console - - $ pwd - envoy/examples/single-page-app - $ export UID - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS - --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - single-page-app-envoy-1 single-page-app-envoy "/docker-entrypoint.sh envoy -c /etc/envoy/envoy.yaml ..." envoy 2 minutes ago Up 2 minutes 0.0.0.0:10000-10001->10000-10001/tcp, :::10000-10001->10000-10001/tcp - single-page-app-myhub-1 single-page-app-myhub "/opt/myhub/app.py" myhub 2 minutes ago Up 2 minutes (healthy) 0.0.0.0:7000->7000/tcp, :::7000->7000/tcp - single-page-app-myhub-api-1 single-page-app-myhub-api "/opt/myhub/app.py" myhub-api 2 minutes ago Up 2 minutes (healthy) - single-page-app-ui-1 single-page-app-ui "/entrypoint.sh dev.sh" ui 2 minutes ago Up 2 minutes (healthy) - -.. _install_sandboxes_single_page_app_step_login: - -Step 4: Browse to the dev app and login -*************************************** - -The development app should now be available at http://localhost:10001 and provide a login button: - -.. image:: /start/sandboxes/_include/single-page-app/_static/spa-login.png - :align: center - -.. note:: - The dummy OAuth provider automatically trusts everyone as a hard-coded ``envoydemo`` user and redirects back to the app. - - In a real world scenario the provider would authenticate and authorize the user before proceeding. - -The sandbox is configured with an inverted match on -:ref:`pass_through_matcher `. - -This ignores all paths for OAuth other than: - -- ``/authorize.*`` -- ``/hub.*`` -- ``/login`` -- ``/logout``. - -.. literalinclude:: _include/single-page-app/envoy.yml - :language: yaml - :lines: 37-46 - :linenos: - :lineno-start: 37 - :emphasize-lines: 3-8 - :caption: :download:`envoy.yml <_include/single-page-app/envoy.yml>` - -When a user clicks ``login`` the app initiates the OAuth flow by calling the ``/login`` path in Envoy. - -This redirects the user to the OAuth provider for authorization/authentication with a further redirect link: - -.. literalinclude:: _include/single-page-app/envoy.yml - :language: yaml - :lines: 34-39 - :linenos: - :lineno-start: 34 - :emphasize-lines: 3-4 - :caption: :download:`envoy.yml <_include/single-page-app/envoy.yml>` - -On successful authorization/authentication the user is redirected back via this link to the app with the necessary OAuth -`authorization code `__ to proceed: - -.. literalinclude:: _include/single-page-app/envoy.yml - :language: yaml - :lines: 44-50 - :linenos: - :lineno-start: 44 - :emphasize-lines: 3-5 - :caption: :download:`envoy.yml <_include/single-page-app/envoy.yml>` - -Envoy then uses this authorization code with its client secret to confirm authorization and obtain an access token for the user: - -.. literalinclude:: _include/single-page-app/envoy.yml - :language: yaml - :lines: 50-61 - :linenos: - :lineno-start: 50 - :emphasize-lines: 3-10 - :caption: :download:`envoy.yml <_include/single-page-app/envoy.yml>` - -.. literalinclude:: _include/single-page-app/secrets/myhub-token-secret.yml - :language: yaml - :linenos: - :emphasize-lines: 6 - :caption: :download:`myhub-token-secret.yml <_include/single-page-app/secrets/myhub-token-secret.yml>` - -Once logged in, you should be able to make queries to the API using the OAuth credentials: - -.. image:: /start/sandboxes/_include/single-page-app/_static/spa-resources.png - :align: center - -.. warning:: - Envoy's OAuth implementation defaults to triggering the OAuth flow for all paths on the endpoint. - - This can readily trigger an OAuth flood as assets are requested, and doom loops when the OAuth flows fail. - - This can be avoided by restricting the paths that are used by the OAuth flow. - - The sandbox example does this by inverting the - :ref:`pass_through_matcher ` - to only match on the required OAuth paths. - -.. tip:: - The Myhub OAuth provider does not provide an expiry for issued credentials. Likewise Github may or may - not depending on configuration. This is valid in terms of the OAuth2 specification. - - If the authorization provider does not include an expiry, Envoy will, by default, fail the authentication. - - This can be resolved by setting - :ref:`default_expires_in `: - - .. literalinclude:: _include/single-page-app/envoy.yml - :language: yaml - :lines: 33-37 - :linenos: - :lineno-start: 33 - :emphasize-lines: 3 - :caption: :download:`envoy.yml <_include/single-page-app/envoy.yml>` - -.. _install_sandboxes_single_page_app_step_api: - -Step 5: Make API queries -************************ - -For the sandbox app, -:ref:`forward_bearer_token ` -is set, and so Envoy also passes the acquired access token back to the user as a cookie: - -.. image:: /start/sandboxes/_include/single-page-app/_static/spa-cookies.png - :align: center - -This cookie is then passed through Envoy in any subsequent requests to the proxied Myhub API: - -.. literalinclude:: _include/single-page-app/envoy.yml - :language: yaml - :lines: 76-88 - :linenos: - :lineno-start: 76 - :emphasize-lines: 3-11 - :caption: :download:`envoy.yml <_include/single-page-app/envoy.yml>` - -.. _install_sandboxes_single_page_app_step_reload: - -Step 6: Live reload code changes -******************************** - -With your browser open on http://localhost:10001 make some change to the UI. - -For example, you might change the page title: - -.. code-block:: console - - $ sed -i s/Envoy\ single\ page\ app\ example/DEV\ APP/g .local/ui/index.html - -The page should automatically refresh. - -Likewise any changes to the Typescript app components in ``.local/ui/src/...`` should automatically reload in -the browser. - -This is enabled in Envoy by allowing the proxied connection to the `Vite `__ -development backend to be "upgraded" to use Websockets: - -.. literalinclude:: _include/single-page-app/envoy.yml - :language: yaml - :lines: 22-27 - :linenos: - :lineno-start: 22 - :emphasize-lines: 3-4 - :caption: :download:`envoy.yml <_include/single-page-app/envoy.yml>` - -You can view the logs for the development server with: - -.. code-block:: console - - $ docker compose logs ui - single-page-app-ui-1 | Starting (dev.sh) with user: 1000 worker /home/worker - single-page-app-ui-1 | yarn run v1.22.19 - single-page-app-ui-1 | $ vite --host 0.0.0.0 --port 3000 - single-page-app-ui-1 | - single-page-app-ui-1 | VITE v5.0.10 ready in 119 ms - single-page-app-ui-1 | - single-page-app-ui-1 | ➜ Local: http://localhost:3000/ - single-page-app-ui-1 | ➜ Network: http://172.30.0.5:3000/ - -You can also use ``docker attach`` should you want to interact with the process. - -.. tip:: - You can manage the Typescript package using `Yarn `__: - - .. code-block:: console - - $ docker compose run --rm ui yarn - -.. _install_sandboxes_single_page_app_step_logout: - -Step 7: Log out of the app -************************** - -On signing out, the app makes a request to Envoy's configured -:ref:`signout_path `: - -.. literalinclude:: _include/single-page-app/envoy.yml - :language: yaml - :lines: 47-53 - :linenos: - :lineno-start: 47 - :emphasize-lines: 3-5 - :caption: :download:`envoy.yml <_include/single-page-app/envoy.yml>` - -This clears the cookies and the credentials stored by Envoy before returning the user to the app home page. - -The app also clears any stored data associated with the user session: - -.. _install_sandboxes_single_page_app_step_production_build: - -Step 8: Build production assets -******************************* - -First, create and set a custom ``xds/`` directory. - -You will need to rebuild Envoy to ensure it sees the correct directory: - -.. code-block:: console - - $ mkdir .local/production - $ cp -a xds .local/production/ - $ export XDS_PATH=./.local/production/xds - $ docker compose up --build -d envoy - -You can build the production assets for the app with the following: - -.. code-block:: console - - $ docker compose run --rm ui build.sh - -After building the `React `__ app, the sandbox script automatically updates Envoy's configuration with the -static routes required to serve the app. - -You can view the generated routes: - -.. code-block:: console - - $ jq '.resources[0].filter_chains[0].filters[0].typed_config.route_config.virtual_hosts[0].routes' < .local/production/xds/lds.yml - -.. code-block:: json - - [ - { - "match": { - "path": "/assets/index-dKz4clFg.js" - }, - "direct_response": { - "status": 200, - "body": { - "filename": "/var/www/html/assets/index-dKz4clFg.js" - } - }, - "response_headers_to_add": [ - { - "header": { - "key": "Content-Type", - "value": "text/javascript" - } - } - ] - }, - { - "match": { - "path": "/myhub.svg" - }, - "direct_response": { - "status": 200, - "body": { - "filename": "/var/www/html/myhub.svg" - } - }, - "response_headers_to_add": [ - { - "header": { - "key": "Content-Type", - "value": "image/svg+xml" - } - } - ] - }, - { - "match": { - "prefix": "/" - }, - "direct_response": { - "status": 200, - "body": { - "filename": "/var/www/html/index.html" - } - }, - "response_headers_to_add": [ - { - "header": { - "key": "Content-Type", - "value": "text/html" - } - } - ] - } - ] - -.. note:: - This setup configures Envoy to store the necessary files in memory. - - This may be a good fit for the single page app use case, but would not scale well - for many or large files. - -.. tip:: - When you make changes to the javascript/typescript files rebuilding the app creates new routes to the - compiled assets. - - In this case Envoy will update via xDS and use the newly routed assets. - - If you make changes only to assets that do not get a new route - e.g. ``index.html`` - you - should both rebuild the app and restart Envoy after: - - .. code-block:: console - - $ docker compose run --rm ui build.sh - $ docker compose restart envoy - -.. _install_sandboxes_single_page_app_step_production_browse: - -Step 9: Browse to the production server -*************************************** - -You can browse to this server on https://localhost:10000 - -Unlike the development endpoint the production endpoint is configured with: - -- TLS (self-signed) -- Gzip compression -- Statically served assets - -.. _install_sandboxes_single_page_app_step_github_oauth: - -Step 10: Setup Github OAuth/API access -************************************** - -.. tip:: - Setup for `Github `__ is explained in this sandbox, but it should be easy to adapt these instructions for other providers. - -You will need to set up either a `Github OAuth or full app `__. The latter provides -more control and is generally preferable. - -This can be done either at the `user `_ or organization levels: - -.. image:: /start/sandboxes/_include/single-page-app/_static/spa-github-oauth.png - :align: center - -.. note:: - - When setting up `Github OAuth `__ you will need to provide the redirect URI - - This must match the configured URI in Envoy - - For the purposes of this example set it to https://localhost:10000. - - You will need a separate OAuth app for development. - -Depending on your use case, you may also want to set up any permissions required for your app. - -Once you have this set up, you will need the `provided client id and secret `__. - -.. _install_sandboxes_single_page_app_step_github_config: - -Step 11: Update Envoy's configuration to use Github -*************************************************** - -Add the `Github provided client secret `__: - -.. code-block:: console - - $ TOKEN_SECRET="GITHUB PROVIDED CLIENT SECRET" - $ export TOKEN_SECRET - $ envsubst < secrets/token-secret.tmpl.yml > .local/secrets/github-token-secret.yml - -The file created will be available in the container under ``/etc/envoy/secrets`` - -.. tip:: - The following instructions use ``sed``, but you may wish to make the necessary replacements - using your editor. - - For each configuration there are 2 places to update, one for the development listener and the other for production. - -Create a copy of the Envoy config and tell Docker to use it: - -.. code-block:: console - - $ cp -a envoy.yml .local/envoy.yml - $ export ENVOY_CONFIG=.local/envoy.yml - -For the OAuth configuration in ``.local/envoy.yml`` set the `Github provided client secret `__: - -.. code-block:: console - - $ sed -i s@client_id:\ \"0123456789\"@client_id:\ \"$GITHUB_PROVIDED_CLIENT_ID\"@g .local/envoy.yml - -Replace the -:ref:`authorization_endpoint ` -with ``https://github.com/login/oauth/authorize``: - -.. code-block:: console - - $ sed -i s@authorization_endpoint:\ http://localhost:7000/authorize@authorization_endpoint:\ https://github.com/login/oauth/authorize@g .local/envoy.yml - -Replace the -:ref:`token_endpoint ` > -:ref:`uri ` -with ``https://github.com/login/oauth/access_token``: - -.. code-block:: console - - $ sed -i s@uri:\ http://myhub:7000/authenticate@uri:\ https://github.com/login/oauth/access_token@g .local/envoy.yml - -Point the -:ref:`token_secret ` > -:ref:`path ` -to the ``github-token-secret.yml`` created above: - -.. code-block:: console - - $ sed -i s@path:\ /etc/envoy/secrets/myhub-token-secret.yml@path:\ /etc/envoy/secrets/github-token-secret.yml@g .local/envoy.yml - -Replace the :ref:`host rewrites `: - -.. code-block:: console - - $ sed -i s@host_rewrite_literal:\ api.myhub@host_rewrite_literal:\ api.github.com@g .local/envoy.yml - -Finally add (or replace the ``myhub*`` clusters with) the ``github`` and ``github-api`` clusters -:download:`Github configured clusters <_include/single-page-app/_github-clusters.yml>`: - -.. code-block:: console - - $ cat _github-clusters.yml >> .local/envoy.yml - -Step 12: Update the app configuration to use Github -*************************************************** - -We need to tell the app the name of the provider. - -Currently providers for Myhub and `Github `__ are implemented: - -.. literalinclude:: _include/single-page-app/ui/src/providers.tsx - :language: typescript - :lines: 7-13 - :linenos: - :lineno-start: 7 - :caption: :download:`providers.tsx <_include/single-page-app/ui/src/providers.tsx>` - -If you followed the above steps, the `Vite `__ app environment settings are read from ``.local/ui/.env*``: - -.. code-block:: console - - $ echo "VITE_APP_AUTH_PROVIDER=github" > .local/ui/.env.local - -.. _install_sandboxes_single_page_app_step_github_restart: - -Step 13: Rebuild the app and restart Envoy -****************************************** - -.. code-block:: console - - $ docker compose run --rm ui build.sh - $ docker compose up --build -d envoy - -.. tip:: - Note the use of ``up --build -d`` rather than ``restart``. - - This is necessary as we have changed ``envoy.yml`` which is loaded into the container at build time. - -Browse to the production server https://localhost:10000 - -You can now log in and use the `Github APIs `__.: - -.. image:: /start/sandboxes/_include/single-page-app/_static/spa-login-github.png - :align: center - -.. seealso:: - - :ref:`Envoy OAuth filter ` - Configuration reference for Envoy's OAuth filter. - - :ref:`Envoy OAuth filter API ` - API reference for Envoy's OAuth filter. - - `OAuth2 specification `__ - OAuth 2.0 is the industry-standard protocol for authorization. - - `React `__ - The library for web and native user interfaces. - - `Vite `__ - Next Generation Frontend Tooling. - - :ref:`Envoy Gzip Compression API ` - API and configuration reference for Envoy's gzip compression. - - :ref:`Securing Envoy quick start guide ` - Outline of key concepts for securing Envoy. - - `Github OAuth apps `__ - Information about setting up `Github `__ OAuth apps. - - `Github API `__ - References for `Github `__'s APIs. - - -.. _github: https://github.com/ -.. _github-api: https://api.github.com/ -.. _github-oauth: https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps -.. _github-oauth-credentials: https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#2-users-are-redirected-back-to-your-site-by-github -.. _github-user-settings: https://github.com/settings/developers -.. _oauth-auth-code: https://oauth.net/2/grant-types/authorization-code/ -.. _oauth-spec: https://oauth.net/2/ -.. _react: https://react.dev/ -.. _vite: https://vitejs.dev/ -.. _yarn: https://yarnpkg.com/ diff --git a/examples/single-page-app/hmac-secret.tmpl.yml b/examples/single-page-app/hmac-secret.tmpl.yml deleted file mode 100644 index 972ae5c507ba..000000000000 --- a/examples/single-page-app/hmac-secret.tmpl.yml +++ /dev/null @@ -1,6 +0,0 @@ -resources: -- "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret - name: hmac - generic_secret: - secret: - inline_string: $HMAC_SECRET diff --git a/examples/single-page-app/myhub/api.py b/examples/single-page-app/myhub/api.py deleted file mode 100755 index 2204dc08e40b..000000000000 --- a/examples/single-page-app/myhub/api.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python3 - -# Dummy Github-like API with OAuth - -# NOTE: This is a partial and insecure implementation for testing only - -import json -import logging -import os -import pathlib -import secrets -import urllib.parse - -import yaml - -from aiohttp import web - -from shared import Data, debug_request, TokenStorage - -logger = logging.getLogger(__name__) -MYHUB_URL = os.environ.get("MYHUB_URL") or "http://localhost:7000" - -# TODO: add to app -# Note: You should not persist data in this way for any production system! -token_storage = TokenStorage(pathlib.Path(os.environ["TOKEN_STORAGE_PATH"])) - - -async def user(request): - debug_request(request, "user") - _data = Data(pathlib.Path(os.environ["DATA_PATH"])) - access_token = request.cookies["BearerToken"] - if access_token not in token_storage: - raise web.HTTPForbidden() - user_id = token_storage[access_token]["user_id"] - user = _data["users"][user_id] - user["avatar_url"] = f"{MYHUB_URL}/images{user['avatar_url']}" - for resource in ["public_repos", "followers", "following"]: - user[resource] = len(user[resource]) - return web.json_response(user, dumps=_dumps) - - -async def resources(request): - resource_type = request.match_info["resource"] - debug_request(request, resource_type) - _data = Data(pathlib.Path(os.environ["DATA_PATH"])) - access_token = request.cookies.get("BearerToken") - allowed = ( - access_token - and (token_storage.get(access_token, {}).get("user_id") == request.match_info["user"])) - if not allowed: - raise web.HTTPForbidden() - user = _data["users"].get(request.match_info["user"]) - if not user: - raise web.HttpNotFound() - if resource_type == "repos": - resources = [ - dict( - html_url=f"{MYHUB_URL}/{user['login']}/{resource}", - updated_at=_data["repos"][resource]["updated_at"], - full_name=f"{user['login']}/{resource}") for resource in user["public_repos"] - ] - elif resource_type in ["followers", "following"]: - resources = [ - dict( - avatar_url=f"{MYHUB_URL}/images{_data['users'][related_user]['avatar_url']}", - name=_data['users'][related_user]["name"], - html_url=f"{MYHUB_URL}/users/{related_user}", - login=related_user) for related_user in user[resource_type] - ] - else: - raise web.HTTPNotFound() - return web.json_response(resources, dumps=_dumps) - - -def _dumps(s): - return json.dumps(s, separators=(',', ':')) - - -def main(): - logging.basicConfig(level=logging.DEBUG) - app = web.Application() - app.router.add_route("GET", '/user', user) - app.router.add_route("GET", '/users/{user}/{resource}', resources) - web.run_app(app, port=7000) - - -if __name__ == '__main__': - main() diff --git a/examples/single-page-app/myhub/data.yml b/examples/single-page-app/myhub/data.yml deleted file mode 100644 index b88178e5bec5..000000000000 --- a/examples/single-page-app/myhub/data.yml +++ /dev/null @@ -1,40 +0,0 @@ -users: - envoydemo: - avatar_url: /users/envoy.svg - followers: - - user0 - - user1 - - user3 - following: - - user0 - - user2 - name: Envoy Demo - login: envoydemo - public_repos: - - repo0 - - repo1 - - repo2 - user0: - avatar_url: /users/user0.png - name: User 0 - login: user0 - user1: - avatar_url: /users/user1.png - name: User 1 - login: user1 - user2: - avatar_url: /users/user2.png - name: User 2 - login: user2 - user3: - avatar_url: /users/user3.png - name: User 3 - login: user3 - -repos: - repo0: - updated_at: September 14, 2022 - repo1: - updated_at: February 3, 2023 - repo2: - updated_at: July 28, 2023 diff --git a/examples/single-page-app/myhub/images/users/envoy.svg b/examples/single-page-app/myhub/images/users/envoy.svg deleted file mode 100644 index 124f6dfc1a2a..000000000000 --- a/examples/single-page-app/myhub/images/users/envoy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/examples/single-page-app/myhub/images/users/user0.png b/examples/single-page-app/myhub/images/users/user0.png deleted file mode 100644 index 2df2b4f6ede48a589776f21e9770b4a0c4c51007..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 298364 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
ttnPQM^!8N;xWtm3UEUmTfzdfZFt|ur6&?)zzRo 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

b1zm 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

Ap!+Z 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

y>Pk2oX6> z9Eh>?luvpk#``v*sWnPbQigJ)V+ECKhdZ4^yJzp)dwn2*^8g|dTd=^Y&O&CD z4hmP5o)oI2VkU5As1lYCMnpmiful(0kerq}1X>GxE66kEeNxH}6LX|I(?&9RdaSg) zH)`;73@w;36UBH5IZyEzU`C$j0}i>`dEiqjMmEzv?v{U)MD9#*^4;b4C#!6W?_HIA|CJR5bMr^5AF!na1jD zs;tV{krAZo!3dCIXr+>O6qJaB!3mJ| zCtGq(BhjzH1!U^9+A-7Lzc`<-pB-l|MC!S0)MQIq#1?Hi8Y6&AT~gD6M0YASVN1^$ zJFIC$u^A{>-+5mYJImqUd-nrxJ#cWYsJtc6kh+wz2__6isgWJe)Oc!i)W*yK{FPo; z6xErkj<p|#(Uockz|EjmIfj~9S#ieD;2qCMd7_*B z_Pg(W^^ZUG*twOgpRW@F5`5YS%0z%_f?YTJXX>Ff_6(Q32Aj8S&V1E@{e6%Vfdsg$ znYSkWJ7y-1O{S$7ORez}tRNPNf|R`zM`{X*_GHAa!N_?+RrNtAgH$k7L`IYW${>Yk z(==F=$wMk~u-J$gF0vAtQLN&DRLn$?m5VG{{g$d$fSAYH2N*Atv({*>h`)#kCd-Vf;XB$)lS7lg8$# zi3>Kxi(F4wdjbJ5N17s>#N-H^;u4NQN^D?-NoE8yt!fB-P^aVqy8=*xvJ1==LtN}< zPEdnH>wtNrD=C5!v?2iFKp7J2Uo7&BO(~)fx03#`8Fb1Exh57HZ74P;B1hit9H+5=ch9Hqdg8+7L9i^NkV{yX^OT(4- z+;HtXPhGXF;zbYy6fE}2pa3=xW+km%2TsV9c+|xoHeLUTN#Q%^t%i{tPxeh+v9%eILgUlb)@T!q z$~A6S)q=FjfMHA!Qpqwsw7Af#N;6Y-W=d3dd?Ia1(hQLFJ2yGbI^LZkC$#}VsQqeZ z3J!2WKy0>4%A_7jR5hZatm2ee9ECtZO)3)gfwB^=WTzQK>qvcRC=CKIVuubfs2O{T z9=~HlM#_mA#bU?t3NBEtfF$CFi0ezxRd1vX|Fb~ab7&@wxlakNrHJAFm=mD{EKf{! z%zzM?BM<~CS>{}%=cz=4!Aye?qu0+J!s^QTjj9-O1qvc^?&8MU1}Y^ucW%9Ub!DK$ zWKKjhh+mhDe%1ZhpL|TJ@aik4zrL~xkuQGniyN<=rImHFaOT1)=XsXr&Ukos)n^$e z5`WF2#H4O_4e&8We$bYrXw;;ncTmiJ+k5R#TO)0$9){odBtF4AQrI`YDDeBoE z?hUbSjT(C*8oeo}lNOg_jK6{OaGioldu1%7Lrt1CIWyKGW(pHff%3#K^ zE4xa^+o3(XXFPY~CQ)M|b{vARbCFfa#zTtR#Bl4Wj!lKR-oGSl-bIL;Ik3Rg%G`|0 za<1ICeb=tPdGEc;j`A`fD7cPC*N$Dinj;0+jA|79_)i528j?c)Ry+ z8Psaa_9(?SrHX!Rv1)PRMwun!Jz^!2jf}e2spLcm<&8U*e(U~sUi|!*ezrcaEGtwq z??r{-6=JGna8NdauY@ojPt7DQ1ZTRCcaHAcmqRE*XjD~|UCdCo zP8}nPo!V&BQgQ1gwum2T33`N-xafF8rH0KQ(NU-8Z{H@StZH@9>`<#I>xoSRV%w0> zUx{keMzRxBqpM&wmn}B+o=jAdNy{Z318Q0gxAxJc1u`a-V^UB^otl7&Jh96%?=u)f z)Oiz=5! zZ`+XmL&tV3bW)LR>l~%sVU&W}bqv~CYKc<1#x<<1jeUVw9Kw`->d&S+1fpcKa~7Lb zjSY~RiUn7jx=hBYm*V(dYVWsc6Qv_xSPd$f>9M^#cXT^E96Axll-kbKl?vK28z;VE zitr$YAR~CIW-li&xd0PYcozXAoN?Exc^RCV>;6~ofAFa1PBDN40tKx`hz=NFP2Lx= zXPVU=zBH1(*ncwZzpZp!3DMj3Rb!Xv(y|+(I|4vZg}r|-px*ltKIvTj-1%vpYwb6 zEgV07kPWZA@ajMLfB&-|JpSX`-*nf~uDx7V!)va+diUPF?ELkou7Bv49xBV~&9~j& z?RL@a4vI3LnT>hGFxapl-Y7POZ?wOeX%HK;i_!M?C!EdN8)~}gT0;&zRm>*jbLtz~ zazu%IW9%RS&^o5A_P@r>oYCL8Uij4J0%$KI6FH0;dc-dsdBRcsQEUI#V{e@S=@+Jm zj{=)U-$F^t<=Nie#koF=p$c=JRh45vaC~F5BxZ~0Bi{UeGc$?Zg-AjmBG1mMsv~kD z=ihtiz1Pmq^{RqZV&o|WB26MFVw8E-|3A?3b=rS)xQ)&54|x0j3XAyEu1>AZ^kP=LM*8g7hEa(pm$-HRD8oSGY{imGAo&d~<9)Pnq(}tv zs9kDCP4pH=H0BVE4y*dH5(%Ky-$SSy1xM`rG^rRHe?*MLKSwIi;czIy9Q%H^XNk(p z|L})D{=_H#Y;}ELg!}G)=X>7$OU`BeneGiYUJJ6U+bOCLlz#rgi$zg*8&*H~?qg?P zKi8X?d-~~TUp)OP1do3EyRuQjXQ2{ZTl19#f}81aP`+5GNSo*-{#Pln6-*#tV$=r{ zq@mmnL8=jRT^Cn}{*^{p`O#vGFC`;oLF+TXMm}<)?I1O!SR`DV0nH;Oc;bi_swr4E zgT!2EN?=oiWl1cdiKnO*X0xV5tCfJQ+way6sLz?Kn3J+k^n*mL%Z~9e7S1hTc3EfTGeRld^H=|`n$h?U^*wpv8{yrA) zoc;tO^*-U%C2&nMOpVQUL{V3#swy)&RP87){`I?WTPgI@KYHpF(7IX(!8s5q5i_}l z&v_E0G#&`rPPD9vL`5?FvDt7cGbYy^+{>YKwc=#yX1Y6;mgW~{e)`j=Rxe&)pZVGD3>&@u9rr!+v!{n;!MW@8`@{1q zy@k0jC_*Ki=YfM824f4RT@Vpj~D1lr?aqs59N8RQ+wcuz;#@OmmJ3cdhk&S<$@tDyuCbrRX*^JjT zm^9?0oF2>wmODFjVE^2Ao;YhpY#e7y%{dZnWe!`s3zyZzDt0AH2CkSGz|3B>^yTVq z9sK%R?s(tLH!qdy1P}p?95jk`{h$V>h6-(q==Y_bBh5Cp)ivY8NlZnSaK?ldWT2_O zZf3lBF0POCMgS&rxZ_yn#zUP@KikZ$0=yL)ZmlzD>rQMn$5VZ`2KSSmzvdaPLjuX3 z;QDt}g1|U8SUK3;$$sPBx14_M^)J4DrtC1h3sxB{yhH%l2K;uC5uB z;`)%rwk>J?rk3)KBxEFB^*^;y-jh;N7iX(VrFKh#1BEqI%<;P zbu^V})bb?Rg{>MLq=pBis$xn+-shBMRTWfB$TV~2ozK{JJK#>QKkIV2cwv3*{02-{ zR@R<*=9zL>R5~nVfgBH3hVye7U$}7L_1Dib^Xb#Cz54Pix7~Kz%{Si+wS~RA=H_P+ zs=fR7@l4nEIyx+Etu(W$s=yTaNCBXZvQ3qgwlpV67(2b2c+i+(GK#)6?`&LrYdd1& zYe-GwUDvk6Ba;YBU}BrxD@s2R0iXs+&B9s;`Y}(C6cG&@!;HpeOvEq;o1z6`_va?u zRC}^k^JOa7;_+bh`OYdQckS5K>3FGJj3-85vW@{ZCXgFb13{#z*{o{34-BkEMC6<^ zz){G<+8!M~bmFRCfAgJlrBX2t);mg!?BDK08`K}Y;lrt ziK||gaXCpFrrtA6>~%r~G=I{q6k6I%W2qESqy^j*&mD8yChthG&T*TkPhU7DQDiNR zG=4WCVrGtPR76A$#n`=R0w5N4tMX_$xdP7yLpyV zSq@c0=KR{)+S>W^024B1pUuzDKk=gU-1FyF&qKq@&pe|m zYgJWwmveykUd>`-_aro`&9-UBQb*y`dUjfTpVKlS6ANa7kxr@qsy5QasG*sy563KS zgglaQ6V6MihLn7sFzD(I=Iwt%0gb3zYpjD5ei1W?O&+6xFe=X7Oo@~ZvgxIdW+u(H zR7;>kC@k#m^$#sC=VlhfF>38UcGkA1qWyL4D@J&E6d-p*j>wsMRd}{+u(D69`;MLX zo%gwGKn~)5Rk`M-l#jIO&DsA|CF;W4s)Hc1^WV0JxQ6CIi$BdG$P=Ybye^V1u)LIYe81n{zxTim*WG;h;E{LUcP~5v zVRn9ov)Oz@hn+6(*}D|9T3+5YySQvVGd1!Z#~EP0lPj1Uqk~FH019I2IQ0O)0u8Z9 z1!fd!%!!f!M}c+}v93hK>|?SM#0nwQ{eWW|1hdF63NTE`fJk5v^-g1DlSYy`PI>hd zrH`EVgaA|#-Ve2COhxG@C^zNFao)ioXT%0ho%!k?uKi^o>iGnvhxwNBZGN_}!EHQ7 zL5+9GM2?UlWD$_6P*n(LUdn~sZaTcHpE-b4K*qu_@=X+s07py)B8LXtfEuk_D`{9o zL~3M2Q1ayAm{>qc4ql2yEAO1^{lok3-6f$nRA*&Go;FQV!EA{aH~u>YU5%_xh8Rzw znM|(I*blKm$y%Fm3oT$xE*%k9OGyEl7Zo1f-x5u2E%5llLYB2%jtq@@<4f!FFT+uYo$ z7;l9Vk&pEHW`IgkCdO(EGs>u2UVO`P@Bj3{_nqo?`eI#punT1XSWHcn+I|h4`X(!BMeW%U_+_S zdren(l*;HvQ(a3&jcRK_XiN#@2oC1SIEpAEhKbS^X0qtwH_kn!3Yi*Q!(>Ec95n{4 zZWx|q)ez+iW5-Nmp~m@yDpW}7)HEEKq++L7K^;8D?YZFA7L@7zWpOufbC*KmawF^~4u zxYsiEnn=x07@k|(uv_1!6>vaY;999LgJVgRYR_=u!2TY=h2V&o!KSLg#*NBR{AY`= zGwO&EIp_1799JPoRk6ZJ=vKqU!N!gK{y+TiM~~$Fd{~i|>F|#2NnL+4ysmgQi`%>DihRrbX99^0|B1Ei0B^EDGPI8Rgl3KMe+47fAxD`{pQN~^F;Lc#1VEbsmw zzwxcp;K-MA)aslBON1h=C%Z*v~hXy!L?4ov8%I>V<) zhqp6-s2#x3^G6?oL?%<7jhMB*$XV+&_3ScsK}1BD9Xl67S&6DuZoXetMKu^!BwzmG zmpZ+fJkQIrc>T4a3nxX%-P~n8PfP&sURdTzR@T>M`*Xu#dHR)CR*E(5P*rRU%YhAs z7#0ZO;>wzz=?XEqR2eH~HVWf5Hkx8QW7GVyIcbS2G2thoE7OB5BhPVDet1*3eClPm zLKC%}N!#`~xrDF4tus=M!7SxMo?X3f&m7LnCVLa4HBx_L8cUDtuZw9x3zjnUWR!!b)uT7*!uLPM*G)^ z&KP+c)txjqMNKzSXH;WxValf1C{wQ~auuT`+Dg`_qG7R)=2(J)CA0#ujTV>ce**Q% z&Eq*Ou77|gM81W&qjjijD>T*gnVj_XVjWWqGDozSKvYS)6n0zj{+qA;;0@R8E(TqZ zOkM7K?6O!9MM0r;*%Z^JbhnG;jqwfjWX6e)a4d#Jq8u{Sj)ZFtADSWJ5FC$O93n7Z z@wRI4LKAW_0%DB6T0gXIDVgI@$B``kqO~j|VNQ8+nv&Ng8czaSEhI)}7D7nuHPZ}# zjGL;>_9;S@cRG=ijl2Vs&$Dc%*X{L&Rhf4?S;w&xPylHt%WlWb_H&=RvJ3=YRN}nn zmDkR$tgk=x{txcI`YI_(@BLfve9P?o9FSK-*?aXV_>3txMhcO`Nvj?^Nes8tdVQEp zaQjT=hsR%>SeU8O5N#{kLi@MBXqp2xn%;?{=ofSV-#{3u`DDwiZY?l~VP?|tcH_|_ z^Tdu+Yaei{V{EI((7H>rgM$(icZjpH+$Zuk?z!jIJ-ZjGDldaGa$qozl59zW+o=w0 zI=mZ=F#c5aK{WzwG=tmnQAeTPHjhXuqlqbmR!FCYdjY_xoVy*As|lKv&78JUHD+A@ ziOg6*Y7Qg19}_dN*dy6lyYW%Hac#8I$onBp6@#YjD8yz#9nI;al-AF-33r4k_oxAd zV+;iKe3+RDiLeUFwPhLn-M8KKk!!EnEt-Q-MdC~3Ca4OhfuJ&aY^@;@Y+#D!UfL!# zCZ?$*P$gy)0YE_)h;KS_xGR>Y%o@SLNOjur7VoK7zIPgeoyf)ei)`L4_7N`Up<18a zyvDVVQ&XX4t*}Qs{*gu^k)pfi%%7&dqNF7oD3}HnpF5vtss7nPOyRKS@V+~F%m9#X!v+xr(#)8cK)HsUp0D1u?5fJ8 ziYNj%ggn2<=Cy79HDU5!I?_1N7%$H9zSrxKcYum%DdmeVzYqg@ML9Te;&@pOiBXoN_x^zg z?z{8OH-&OohO&^ros7HvUZ>N^d)>u_x$fNTYp=Z)f+85G3Mhvc&#j+1M<&(!z%wY% zBD5>0w9{kt=4s7x!-+P^h+Nukod|CHZ+cxK_d81EZMXbWN3s3;r#d}N-B0{qqz`1P z4$RJ++_mf_#Hx3K8~<=qqA~?kmtb&Aa9#-PiAYea?XcqhYma~E_8WH$hkiIPQF0uO zh(u1umhgx?rmDu)n8dXxQi({ES6M4V^ zPK*^!;o`Dt(ytBTi$*b`i2J57S8{XuE>;+U6QVJO#bV9BjTbu9sJJ&|t1_hJaHyY_eA^^Ti+d2g^P!;KI^AhOJXIcl3Y53QWDPC0Kz zGoZ$fnPJ9c&NQp4lRKC8!s>*^P@Sl9_6Q@s~>Gt}a-i#6N+k3d*oh3q94ue$BJomhR?|a_!e$5=b7^Ll+QsOER&7S!o{|`j*t|2h*-ys`OTvNt_*sKQdGcVge3&gqvN5LDUut;Uk(c_K;^4Pg@oyb`M6PQo4g zcKw~V-+eI1OeH}jz%qqnfHd`yB8<^;TVf6157xiNK6lY*>?^V4|(D z8Abugm;?|dBRFxS?7*aE#x96&QbdUKs}^IYF&<+~5iJ{eQ;mo*Zh6(VTKzkZl8r_H zWR$6<(eb3?gKQrpS9B!QL8Hn1S=5L%$!^LA1IFfKGbC>|>ntk=ckNy-AH4q`fA;fM zg+a%O_mvIJ0yD?M!qBcKS0xz54wdPU7)?Ri4n#~UfdG9#g1-zI28-YYPz`gJaaMG!4EsMd!A@NRbNEn+QHP zA7N&f3W1q)s>`cZmw6(|e@(PoXPw%EBO%jg(&gA{My$28aPks6`i#wK{{PB~63#*v#KlRd! zUMcJJmv`-0yZHL>+-r+__h|@JRh5xtGtLlQKUMX_PJN>K! z5a+s9S48BL&D%)H5)s2;q=p*6L!3g_#3DCujENnxS;ErmntZZGE05M>lfs$|6K#qe zU2;FSA9C{MQF08%tWhf&M9eW3DBOPH@Qxq<@N6L!6LW6K+#^mcLkx99E=Et(V&`9W zsUXQbF~qX4(F^N0E-(D=A9(jwjhlzvRDk(!|A zXbUy_Hj2ph2W@P!G4WSbaO@UWn@EmH4Wi9wfKbyZFdPf9fh%KknQ#_-r(&<{0+SL# zBqmdJWDIg-Y{F_8A%{AtI0PpZt1&=gaHLE=2Ea*GD?$ctssJN)I{_ae${B17XR($e4_ruydnYr?_c2(d8%70gj!3jo@H_Da4Tyh?In>>K7N^a%kyy z-}|nA`svU8v?zuV{mz5Kw`-%VOD_|GbK(NRuW_Hs0=fizi#i& zt_n+`K1&TyqOL&AaY{AwN;B#fNNVD2i-W2&!7YqKo#$vM8wjpZ+~cMdr}s?gfr#JN zFzqzmyx&KQwON48jL4;ph{*rIqn$!Za0-KxBaX9CQ`UQnJ0}&HRn75Zk%6qX1v#E& z<_y8iL@VZAeJ?G`(q*~tWR&}2eM2)_+B5g&x4q>{4}UYL?b*Hi10Q(bzx~5MEQf29 z^;QRdSQQA8t*yg&aItu9?P>3Ny_a5j^}JTpbu+HJXL-kuzyHJP!uk)t`5p8;I-T=p z&O39)&i7|+qY5Fw5{G3y-L}clNo!Ltz?dFPzf2nS)UFCiy|5`EJsN$`6kiZc8uHs* z%^^WVP`%{)`4Wfzq^MoaGhFgLOsKon*p z?w4fBjEDFp0xU5Lh6rh}y3J&?XA=<-#nNmL)S02OVC=i4IqJ-ofVfQa%$%>31|nzI z2d|yK@Y>4Cg^j`awe_{qilPuRkzi(yk+JJ`vR-a;-TqvsvvXl?X=Y|ezdOsFxn3_b z>!`7cX^>DkR?pF@RSAJpgK|=m1kjCVu4<$@W_Cz*LlbgdPhIpowzVPJ2L>jzk6EpE zlA(@R7nXD(!-0$<%Ls#*$rFNv`ws7Y_4b>7|Eu37@Sx|60Cu8v5<3<8nJF0*yoqX; zQqTF=HABUi!K8D2e|+!WJk(lsqC6!nHq{p0=#Z;zjY}ubre@u+DK9nUJ=5-n%ebby zBa^Qrr!}pV)|{Y3ghYr(bsX+*+Ynie-Npv3TjdX84R=8 znW`wgXDv(0b7S|_uYNU@ob|hB&Yk=BfBZ*-D!^xXx9^?rb#kSGI0!;eI(FiupUL~@ z=)%#XN7vU^XZtg|_U`%m7yg{{-u|0ze&wYXY#>@zQVCncmPwZ708~xY9AMTIK5$D^ zGzzGq)iEg6XV($2tyj9HW2G%aM(y+FazZ!lRliKHyQbjU(zL(AkZuamOc@T_aVQ;A7K%L_{^18caQjTZ zv$2K{53u{_s!HbQr)(OF30al-6k(5nZ~2e{>p#XV1|cOS?LLSKeEKWmVzf7(|Xhq(PiH08)t~hD$`Vv9KVy zQB726nnY~o#7v!5$BfPGLq;4mattKaZIFlvV&<)8>(o?})w6-Gst?|F!*gdY{OJ#$ zD!Ehos8j+w0upmn?VY$tx>;{FH73&7x|<^dk#o+$NHz}d+Oyp2xQ&ZVy+(Zg$%3Jg zc#f_9*sMgI8U=wS-#K|VHN%vd8GG~CSJX$^q-Nmu8>W5Z7KRShOav*68#}g=sn&Ey z@eXVX6%fU`jhJ5)s(XzjUhC9jToVQC*ppM8ONfaVLdM+5;a%R%T&GhCIx{`>K4=A| zZnsOmIKRGLmV<0=cGsbU)#?U$S)SW7tjgEUo_G1enF|;Fi!VJ#-R{D|!VkXxm~-yp z*>m~QLRCqBCO@)o-!ngdiA*WuJnN~5tPQm8bKmktrv+PTM;VXF55SCM z3((k91wxS*W$SBV+nCwqtL@EOZYzDatsdNT0!`fy_3;p`W>W%;9GR9K*T41TkaRyHh1P|aeJJF;!i*n@7K zwC%^ZT{^@jx;T&WI@CU2siDWHw`&TDfkJ?HS_*qlM3pFUUJ*%_4V*hCcz$)`$NEJIt!H*wr<-b93P z6(51~^}E}%y@lxV_GV%Ijnn@WV}WT8jZkSIVnE4EwGlaL4kU}*B*YX81lUqR#rVR< z;aNJ!4H#8a#JsBWL`2@Ay`_d1pFKUdFe8%ZUWy#c`*y$f>e-F+=Z_!U z{X2jE_dfHv&p!R!OF={+et!4j&Ru)F$4{17m6-%Cj5))HWNW{<^XJIgO*{ zuM~sPXuHkX=53^8n=jh(0W@WO6VXEKo>2RhM)1x~9oVyXrvK9VfUIN0Jn9wIN;;_} zJ|tpOC=pE6h|D^}^|$Tb^U+&w?-fG}6)QR7h+v6^YHhTc8JNfI9csu=CVN(FKP^q* zVyj^Hl+UWQ-c$GI38XM~PRoiMv(Gjt8}>RsFXfxhKKJ<_{P@Q&zrI?C5^tbX&*Czd z=d9inn@MAYR-7?&Fmt|Uk^zjUV_aHb@8iU(6(Cll4d;hJ&s5<@ub%nxE3eIb`?3A= zv$q^O{`ONxZ{E3gzw@NSEHf)ZOaLctJE90m0uh@{1V5+G6)B82&(aj(X*5EM?#UvE zV2oW=YdvHjC{s-E3u22qbjppZd-H$q!TZaHzw+qW3l|9$C#noH)0#t@45pFgT#?em zq^Zm;=E{}GaR|#9-+b(_S0zo9_;GbxTC>jCv)rfjjuaVu`|TFfuydN$R+JpJ{* z(yLi1iv&$0k2#?d2#ANYbIv(pc6FQucRRD)nbiv`Pk!%lH#4)myqsrraV0n(-f{OG%lr0x>_2=F7Y4c%KK>s+@!jt~ z=CWK&iOt_~%MAh9pZ?Jw7aN7k^7+NNeMb(wcIH)itx~7mP7Wyt=g$sa6E2A$c_(L% zm_Y`Usxs5&`h2s*_!Cz)K@>*A+hkd{e%CA0)lIP_(J!#rHcdnQ6<*t)L$OunC zNR3!+)$IIGe*WWUzWbx6?mTkjeYf3w)$-hqjQSjSSU8s2lWR=Dt*=(9y!FZ^Lg}gD z>=+Ph?G}PfRh|~<(PCH$1R|FbHkOl6SiNa^?xT0za{4o08V;+#d8Am3xK9OhQllxN z9l;wGl~N>6Q-fn7@}&D`x+iz-^r51%31OygK~d4!HZ!7E;FM2F7d1$$u{*}I>T>RY z%W%J%w{QNF#i|cZ;yR`UlM8FAY5^u{#te=;qXw6{3R=}hl%#B^u^}aaiP_>Yz=@R$ zj)Mv@=bg?VRBB+Cm1STO#GXH@rIds7XI9S*7nWvbx-;HfRg@4eE91QXz7Krp)1Uvs zh1KCqx8uM2-Nz0bIbK{?hZ=SK)z@Bq<83#+e)^@;&%V^1TUb5&8eQB#Rl;XlQOEVm zV%R12-X{d{Xbr|Xcp^;t#s6^|{MQZ#jm&|uMIEi&6fnI7cyz}_doL?XKvLaQG5i9C^VV$#zcdb(qkh&#CW0`J=s@iVz zw9#Shr>(ufk=&0gyZBF7EcjxTJS%-~E$jVYW98rf<<}m4?xk0URcXF(e#1hi<4jb{ z%3#iU7wLSN1b~B)2a%X&DO5`ln9NK_nNyCiDd!v+1Qg;wz{as(oUvmu+Yk$8gW#Wk z_W3{m`HT0QIR4&SZ@h77VJCCnRJbBFkj8TAWbsf94Q!wXTM!kr#b*0_$<*oI9I_KL zr%VQr#3);=AO;73GqaiDg|{ABKJ%7a|EE9yYN0T=x{ZVKI9W}^sYLkBSg%S^W_D8D zu>Z*ZZkNw)IOky_R6re%n%oqlf7d}7s?E$VtPM7NK0kNt=<%0- z`oeHyurN3Cv!DLx;`;d)p86SQonCjg>^2tb8N4vO7H+O4%Fb6xc;XrwSl zjR9ySfm;?awPV|Lj<@~Ksmb>#zi*bX%^z$$ugTD1>r{#VPYt>@-(hRf*{tKXdh9X5 zVy;hsR`!dK(xM^sBwTa&zW@e zbqYx0gg2zAHl|36tA_DbEt{?NZN{>BDu~B|xNY3j1OzAspKIt?9c2a;d4Gkn7cLAw z^}R>F@a)q+TOX|X{-85U7Kn(9i=u!7J4eQ1AS36TnWnZ-CTiw56H#WT8sb-r!Z|M@ zN=#rE8OvG+m6DUH;+&xhg|Rp1OXDHwMPQ}E+EXt+`Qq6R+;PMGr;Z%ReNV)(a=m^r zP-k_`wr!QgYBU<@w&hC_oLn9X5B)sYR-n7(CL* zR_(o~IL$P^v)aJClve-%5eXqwMVZad^=4)Y33D?ugN=$kmrN@w7e4u?pE7p+x&B~) zVL_|wE1jhs-A+HtJGQo(=iR~jS~(o%9q;$E+j7{qgsHOy^4F z+(@%dr|=ndsXyB(*F#v{Fut_DuUX3bpb@|!I45rL5^W$ zB(A1Y2goEMW{rtWjX-K})b18J!isFVE)h#@J{D2WNzQ1~8b^NiQ6kSS70KCjT#co( zqG3`)9@7ViNJWTaXNBbS#q1z4K~0EgSRL89uy3yO%$ZfsPF*fqF*_*4!3h8rUop98 zyD6(jU>okh@HgN2;6X@U4poemiDHZ7*wI)St`1!oNwiwIJ%OX%UadIn)cFJ^!mmV# z=HM8TIyTEl1Utrn#aOv`Wi^Tvk}fh*(ni)_?JYd~)X)C;!(aUQ`fycU$maqmXO!@4 z%EZjE`4YwEHwG3%ED1IM@DY?l>Y2@u*mBeeASSEHGpz0zQ1e}b zl*nLy3UrNX#mbh*`Xhw)^Q3=F?pdc88l_XOK z0$ko9kJ#Xfh+;<*M;zHbqm_`cc>+j9)e%?bV%E(J&NOh-orBCwD-jrHzAI(eSX+0! zp7&mL5K7GNTP`%1tP*EZifK`?@m$wYcINDb?mEaxAu=2c$z@&dIIs9?U;N_I&V|K= z#eT21Fw>Wn^*u+A5Zvlu7=nO33f&kC**G>8a?Y_I*@MK~Hbo~6$;P`!%N7{thpJQL z;~BI17Bx(2;>^E75AG6IouHYp!N!UeCg1*7bT#QCQWJ&N(9S;yK~P9{nIb}#sB?}|Kx`ogLD1f#{A5%n<39d zge@|cNu;7`IJUML$mB1Z9(eS@ZCacH)X6Hc3K}|oJ@cJ zkj)C`q(G#8g zZ@=T7!@ay)4ow5dne$>q2GvmSiP3qJ9?#4Z@=%@Fy?f8%q7#d;w#{D(jJc|~NQdTlTIr>~mRH%@r8S<*Y#<5r`a2Gz3=h1ciw~gTag)%mk$D04=&+ zO~S;?r7&-c@Gf=MFc}sDe(W#4^Xw0vobS!8ude>dzyH{y-~KLl`xi?AFub@r zytoP?se}Y&S-L#eIu^`~m{_X;?D45nf2ep=P^*n!Lkc9*7PuWbX{NTlisOG86R*F5 zYrH~-kQ&>jYMnOmMI{?+$>@IpSj=4S;x38adkVoXd%O+jv5stV)n znTHz<5#@GkHSe}OLc}t5j@V3vXv242=g#*x!aw@V7yqZPefNc`Cw^f=Vwq z*4(THAag#Hm1tn*q9{^rylAzy>K$Ky!!^BrHzFeLy7RUVyziX`ftI4>V7PkW+}Tc+ zn@YdmWfSYpEHL|Eq1Wra{qA?n?%aLw$YFFH*k%_OAcAhj%ni4nm{y@Mh)_FTylD*@ zYNYXFc~ZpFzsATc+xHv;(@k0FEn%0N_wZD0H3gye3rN5vuUH9^+I!adlaVs5cdUe9 zgcKSw@fuF;+_{6yi5R5m#Rj!h(3L7^92t{@j#hUaJ$&n)otadT4o6{>`A8HYb#49R zgwQCByp_=u56A=tY`J&s$=Nms1MrA>t$C*s6z0qY-#JbG<3D)v|NOB}JUSSz^k>R$ z&wOSC=NyqZ-!#}}A_sPeU5*LcD4?xgC$xj=V<+NNfpO$R5Hlkv80s+ee9_snFW~5z=UHlF}E?zLo|{jO4&6~P;v|@7m1H8EqA1;3t=+v z&7(4}x+r>}*Kwk$16|!87ZAjD!X^#Xc|2K&K@LI1IVWZ=&pQ2{5s`QB`GpG?KmEzi zTsV8dXHHdLdFAxO4}XcBqi!~o;$Q#U|MkhAJq5@8ZqG4z_TZiaCk`*~S~M+BU3Y5d z^3rf+<-)nM*WP&xmimkPc5#+v{Z7`)AtFT)h1ZB^)EAzKT+`*T-bi)KcZ(vu*6fdF zdGqX#ZOGKkkvH9HTOi8l7n=IcCNr!xXRiT`;lI5)yBrs!{qfeGY^*9Xv&cXhS&EyC znHi}_qQNi9y;*+afqfk_YW3veCV<9_h$SG+p$m);yydPPR%KNMHFC_}OGFVk=h>m< zlWpUex2;Qz8l9x-<4tz{grm&Xv;YAP)k_> zfrN^junhKd_`452aHyAeO(HrBnTt=D!eWSQv1Vqf>P-8AFl3&?__iw>nG4ju~xnl$t>!Hk3@%i!G|Y>G;vb4rdfR z#m4l`%$$WeDX(3eKR(lQVzGZUCoN}Y!A!YDsoj2(N(BzS+X0NdW9JlR1lQ?Hu(PLE z*Dh4fWdi#CV^2T!*i&U$a+c38?l^wx>Kkskm598ttH71vt#7;g+H0=P^4@yTi-Yy@ z%$ebEc;c#)7!)r*|H9I~-PP(wF&u)-Lfyli)cKfnqPlKjv;fV)kXC3idOxv0r}3p2 zBhlwwX6%Kgy)p_4kIv`uWX|R|_*OzNcI)PCmmeH&TM+Gl_{fwXi&^kEjjCi|cqp7v z7G$32+5-o21*^DNh?4?AV1PBUsVbReq1Zp)zwyAnInz!;*>j}A_s+qrl5k1qdaJQt zq4z_;*c35F(me9_%^+67t-0>^PoMke4}bX?McM6BmYbPs$Ot4FjSN!roDrOXmEuqH zZ91`tR)5#{>pJy8FXyTup2p-NT~X$8Q&&JkxBI%w9)9wf-~aR%UqK$ay`YhVfe=YC zN0HLnra`j!nv~2mA)MqCnznYj>9eigWKCR}qDP{!*%usB?!oL#^J@LXT=y*}4$p?t zqzn!kIp*r+8}AHZOz^62K6PTYD!8h&tbiC0ubaw^o^tY+F%dGuG(R%^X?xk4^7l)g z0eY#|FI^e1*&C?Q0|qq{HM25Qu}>QZ>6YakCyy?L^SlyS&?|$UN z%k1;G@y8)^RQEA$Cx@)2n65%7iUN*(CtuvL8C#Vi9hT3?$4fn?vY2o*PCBjJ#*%>fAVKMR4b~p zuf4{VQlTK{yB+dQRm)ISA*k8JL`+W5XlHJUKXnTnE`=-#&w> zXX=>DSK-d9PAq1Q%VDAiO|!*NQC06_f?<3sZjL9kav^D5n!|6aO?!n`(~Vt;t8Dh^ zfdBckUwEmMs@p3Jr3gD0OpO5VBSSn((`IWj>oEvz>!NFdF92wOK4tQCYp znAswu36p|?sxrf6A?M@$?-viL@q8l@c){Qisc$(B!hz z-3z9jYAKF5>DVO}OqhX0wLrwms^M`RF*I%rU75sO+TP;!&m|B3E$)z>v%USN4q?>$cYH59=Kx~z ztd>>9RZBC!^X_+EgQBCW8P+oz%wlCvdExKe|F&EA?aNEeTCL#X+$q7xwWp3!_&5=_ zd*=}Qt%(lSH>#q9$?LB^zfrE|bG^KmYY4Ea+o9cicQG@nFj(36%%?v4_~VZgSqP!3 zDqo#DcUB8kSzKJ|lJEEXXTqzzz9G)_mgd*bT|gn~JO>4_b$wVVjVQ}9=UfaCQlhV- ziDCea#Km;k>b8wOq{x*K$yFw}YHHEB6c%{;RY#3lvN}S4U(T~({J`7dOQ!WQZHJ@` zwWpjSW~M|;DKTqiD(qZDGe%9}Km_%u8{QIW#Lp5DQ3dgEZ05^yr{h~s9Pic53(axr zrOjrQu`z-q9+H~HaAI`4#fdkz!{6XVK+TyG%W`9s+5E*y{_v|`dHlk;VrEIREGJ)v z0x)(=s!(%;_|G6h=9Cm1jnQZ*joW3duT%n0KCo`K^~{Vwwk;%|h@j@dUCPiZo_qd~&Jbd&{XZ3o4hF<`ON3mJz%W$D>L19L?E z(pV?8X8TTgtn|thg5FC?R|+Q5X0o8oYdVsGQQ@Js@hQ_T2BvUUwsmoOg=|diQEK5! z3Nr;0GeKphD&BqS>K%*A|NeVVJbC(j!L~^HD-XQwBe&hyUp-F}fK&)%~iNfg7{8nu(n==%Ciqev3kDPdE)5eu03a8J3Cz8*tK)<*i}bfI&-Ew2QtqH z*WYkTSikx0Z&L0k_kQN!uEE;H8{c&64}SC`7etws4jedq`0(>jKDlx3Jb9+Vj9F(0 zrCJe~*;B5nCZ-zel|7p&DYde2ga#utN|cPvW87oOMNYBO##@JIYB*O@eK(R6Hwtkw z`=*^QV+%5U*KAVvTSc*_zCJa#0bH6)V%*V*^&qHeB$L^ds6~uZNXL_@k!Q_2s~f{% z5agoFATnYiB7<7+);?4ojCh z9ovU@`z#rSUx;9)Y^-2m&0;{_%(GfgeD{Z^U;gToKR@SY3uC3ylpWwIVH5BV>D$2qh4b(GZ(40$$`|AmEj3s zC57({%Hka9AAI%ETdu$Hcs|!1uE9sNJCPZbl$eN#7_p&likn-FXPPcMNuDQ`W=wgG zu|$kM*FFm%2|MM1MDCMA;JwqL+{GL3y8Y_EICJK_Xi&}I5X-+f?rVw#IqXR*CBrbN z!Yd6KLLjITyA-dQ+U$N5%c?h#wvrsp_P?Z=zd6RxTFu64ZY8(0*`7p1O%9XtECedt}Q!|?j~3ww4f?wjl6>npj5 z8W{o{!w@M$Va$^3J_9ik7^=q1nMc<rSUet2VJ zRc&dezx3*BKdFpP9Y4Nte(lhKLqB`@X=WILk+Gl6_8d95Z`W=(C&b;n6FTHObpC}G zvtH*tAN+uJyz97Le3n3o&?F3MLnhaca z{C(54WlVqkJ)85lZGEx%dSeeWb!;GO$Atr^FgbnUf^d^~&>9O=^Ev=fb5fn$w{NMF zd)2yeN^4N$vSbOxUA)vKxDpw6=eJWG54oYmR;9*Ao%Tl&b@Dl7ehwQ z$V?y;12K~TNPyrVQWPhVlqgDe$(C%ZJ+$rBuAx`2s@2`KR#h)od6j#0S9c9vrB>N) z+2xk4ffmJ?6bF#v2!H?xfSBh@WF~TsIo@#ZIeYhybMExsi^#|XDECzejC^t5y=UBK z58wW_gdfCek4g&lVvO-nq;|vd8|we{x4xYviL;3p(MSv<2#hY6)N`+XRp7_K=L^X!uYBnK>sIsw6<8&rfM+0QJX__N3ML!%lA{^XV0w{U!182Aw$-M($(R>Q>`Q(Y$PrdflFMj^@S6(9`pJg-cmfe5N?!Ai#AO6V` zG^D%Ueb0UGx$j^6_y5atOY5ee&CkrAJ98Eg*B{8x8BU2-y$%fw8P^6HatJkKR?k;irmOttVW@tjwOSKst3*M-<@`zP5E{W zrlB<)${6yku>gwu!yP5GY)tysx=>#!MHC=nc?~o;H;N-Uz^Ra{ung8!m5qT!9E5Y; z1}Ps&A;UX-tZ}V@msNuqZX3-dY~6ZM^fYyXWQ>)XCb?^3#t!ZraJw!$*8O*&J;?{n+E^=O#@h zclky?F{vd^j7^LU1uyf_s5W@D5L}~NYAxio)jXl<6`11MomdY$_;Ab$kum*lLebl) z)SjxjL__{c4x-66SD~;1s#v}yD2(bwjTwS2_6L!)0DyX)NC*OH$I#*3^Bu`euqDxe z+t+&i*4ZnU8Mr1kKNRGkGC@gH)meJz(% z&G?v@yJoAJIu{XzAWJ!t6A!819Dm`kuqF6=E8S28HW`^@WrV+Y;+cW53RYJvypV%d zgrchd?s!0Va#LdmMWc&QM7e{TqOAN}#=}XatM0xVuG^omB_Hb1mt)9wt20W_q4&Gn?^4hg_~-tL%* z{?9tn4PktR3U@fb3RQ9Bh@Dtb5`l=S62e-awEtKMAXDZu2kXi*CngQfK4$hlIt8HW zz4ykT)oypvUa#A3r)kUX+PyG8Kf8P1-fM0+I)7l_jkn(P&imh$bW<>O+HKxg8vfa5 zKX>8c8B6-=lh6O?J3l1Y6USe*bF*s~FXe*`h-xOmliHr^d6zL2*bpz4xd>vS9E{q6LNe(+uMc85L$w&o#GqdZhIi`eec7wr5OvQ+^YEYU!kXR&ZTyTcyV4%7!ILQ@ zxk_;=s0y_2veiK!CM05NAn^%~BA`ej_0nx8dw0#-SY}{aNU%7MwWg8({H~hgHN-Xu z%Ai7cssb269(>`&wZvxD2H=LO3cP0&kqH%2Oni zMXHptb((JkCNOrXTG7M3nWOWw9aU30W`SUbHC!UhtD|g)kT~GS+P|%1ExtWkZV?cm z%9RujsCFeNas6kj=__3g<3Hv5%3gDDG?^eQ-)Q2;dCyYbH3j@@zt z5`pnYt~>npyY93dGn3%nyWf7~@S#tA>JxX|{x)a^KlFo5tG0c1X(b|8JeuR5$XJ}pxk zT@fh~A!a;Q=u;Me3F7^XksJ2z`HQm`NJk)33~$XU3iXD7^NNlc=(f?9!#qntB=0!$=gZZV6Ti$*Tx7zQx`>tR7 z#77tAXN@rz*H`VX*_l1_yRX^pAY>AFqCrjypXH;`sM=|j*Q}aOAn>>q{*8%Rfc5Dt zhp#$F>+5x5CU$gbNu6}XQNE&)Rcij&;@jzSwz;dccE|lu#XpPp7QPSe4+ojcOZQqd zm_SryX)<8d?VFkHS_|h1VyIbtvtl-luy=*$E3v4yAF|0AsQn|xOM|MZM#)4xhSTKC z#g&26oD7k%mP3vq12&u%(d`pT=Mk!MCF7&bJD8S9qj2gOu;q72!kIK?iHbPpG96Eu z4ZtJI>nf*LSA4>S>BejfOg#crjNf01Pfk^e0(?IFPr&#G+YD?;^I5AmAKKqxum~0O z;89anjVz&n60eU28B{I9jPo58i-MY{o-K9idSIZ;?3)x9jZdaUdN0?1t|)4@xu&31 z%a?|qDNvjcMc5jFSI)Sl!p<9C?jR*8GDRd5Yl^uT5flh; zK_AVes>a$RNla>zmL*ody8OgL4?ptbpJ-0%=;h~Ldici=4_4M>sekU3HwG)ClP{iJ zJ+skU*x%W`XW+ek?bQ==*B=CHw$^{@ndeuRS4ZBt!RGqr=(fA=c=pE+ZI1e|U`@RW zlPJ7%uGMOVK6Byt+iKGUb67>Fz$lyAgiS|r20v>2lTIYMV>R8P;m`02L<&Nr_M*c#(9ms6WRe??wAISprqSFx84OIf*YiYburU}WGxJ{4-SfNFm)5{$>C$SBoDCr%J4y%h zGc)Iw&Zpg0wtVqi!kZhJ$y2X*?U|Rb)+d)Sj7LUn$SP}#Tf@Y*4O{l=L~Wp6S5|DsVs^Z?n=&z=u9{kN_{}LRm`KPtzjuCich|hK)+bFp8zbu3 zlY-#^^2EeeIPb%+Igbu22?$ zZyB3*b{#mly0n_I>CPmbqr2^Fdu3UAGjnYGeml3(A6>k(21^6(@Qg`sx#gC>_Upgi z?X+6mnNNMmmQb`W^)}|Q>Z)`Kv zs(=-P8PNjukX-;{Y~>@Gz7HyOfDwyf6guyfxKp>fxr$D2)24V$x14S?J9sTcjq0jUa#%KNt~FP+>TNny^da>ztD;3qd^b8OS*qIdYVwP)^u- z@4W~SdG9D;v@8;nxskHp?60YJ@44?iZiv;h7mgo4{*jM- z=GpaU9(np_fAQ$*%F_Je;=bLxAvq{-b<(-HPIifHYDdl`X_EJcOl%UHWmzcZWtb%w za*l0Kcer%dHW%`^gJ>+_aeS?hdFbje-YL6cT+&^sr~ZnrhE+Sw>pkw%s)tD$AC{kM z`Q!k(3q`-A|s4Y5euxH37@u)!nn-;?L}awE0EjJ-0U6~& z2sJQM(lKVf(}6g32+-r{lTv==E$LJto{Hppn#!aQnGTA5kH{8fnJkm7kC2EOu!d@( zTgRv;#X8j}$ts~R&Bc$)sn}bAqv{W@y_;j7Rm;38C@H~_Ix65X#W>W)z2UWZOSG2K z&Zrt(6Lt{~7LK82;E6*mQc{MriTBRWw~02riiHoIq2Qe;rjp=E2U#ACp5U{sMa$|| zR+#}>_YOu7gkg|^jrzclL_`n~fiRIHB?wtkBCleNAtE0MDlC{)Rccc#jcJp@@yBD! zfZ0TMJ2$pb9mvz1i@JecEdyW6Y^^Vda1Jnm=HBO00f;ZC`W)7v{gNN2c za4Z?677W9ydL^hKHI_Lww&g|HBu;#%JCk{rxxBxznc9RD!$BYFcg^nV%`FV`6)~|? zIW_;N~hK5|F?3LU&O{S3w&>BgIi;@G1KGqZSZ8qo#d&m6C}H zG;rm&3D2v|>|`hu&Bcn^7pLM@rzrJR`ocEWW&M>A6kpd?!V2TMWGOXJfsR}*B@Q~6 zo*D^zK@d?U>av|}wF}eOsQR%+RFf2)jAH|G~nj_2)WY6EPU@0)?m=VpY$iyLvOJ7FpyG zvWqxQg%r(I``-CbIxiSCp&t5pgQ?)9t_~zsB~=L&0$w8Q!y!Q1V4=b}iBDo`g_0GD z^oOw@%6S2mRElC7vE>6 z;*D*!TBE^mc7E5XGiO=#_B-$R<3IWXf?iCQkH7pi1(g| z429HW&sCgx#oJ7w>7)mPj0wNlxsaTSZ>dZH(!h72q3nzJ08KFgFXEL0Ox~AVa^9szy9u+zVqpZMlNxjr zdP_n($X3`v|7wceLR_|4qq+5Qau67q{c4>X6QeIB>4-fG^=Er6(Lm8;6X`CTu8L+v*Zvhbz@x&tHLHT=bUp+L^g(-xtG4nhRzLLK5*F}8)e=N zoGQE)rl)1o>$bb?&UL%z?>u_!UDv;DZ+E7L838#js+2ILX*vR&cr}E?K&ahrWt2NF z*D(NEpFaMmCFZ0HRWCjtWj0CD_4T#yeDzxxigWHuU;pa0 zHyk_p>@!$eHr?(3Kw`X6Kuls&;yfRAlVms?8e>GnASAOjG)<0~BGJV+RRR<~?ul4j zTrLvVnKqpOOj|kcifM~4o7nkdn-*I?q}G-#O!CO5GEMpyuJ)=|_2*816w2>3N2Ag* z;u4Hz6OUnyLNJO+GzHqs>Ya)}nYhUjf8}EF@)v2K(P<*6N*RjU+=m(W96$~t#G$Z3 zzH4?SF&3gA2etqdK4KwuECa`4KRcA3(?g@%-d|09zz7DyB@}hO0FJ>NWv^{uzSAWy zF+LPhe|6%S#KC7&2ENNkGA;*M*9z{hE0{B&K=@O@G*xHPwkatySwmR9{Hh2E z$U{fD4~2<1mNx|jj#MpzpUSjll?8>*36R{8P1JJVq?b(cMmGAv)6bq+UOIW<{J{HR z?y}sEyjO4@MJ*{oC6Gjw532|PW1@NO5DgtG{d3{ih)k9glL|3Slc1OqG{LJFGq%QA zWVPfT$PBNu+EdLF(I6OmU|CMXlJjyw;9v5C{0?Wazqqi=uW{`-IP{qH+G*VCTy zhNuOFbGcvKcOaRYxp48KI5&`yrm$(d-RTU6LxD$1dG6Ds&1q}6y0VfDjUl$1sv>ae zon2pFo8Pl=?Aq%tU0hAN>Cx+NJa_I4C2gHcuQ_n&)X9^uEvqD%m*KhwyS9Qf81FL^ zxDe~hZvvz>9(OAJvt@XFViAY1Qn?ql*#c9S{#CnEYbb@Q_;%63E6}RB-1P;5QUMmJ z{6Eo27M4z#-PW{OK!Ezxwh|%}A4nn-m0I7t&u6@+6m-`TVpDFRYFQetM_c%wno<)JEc;;41kciTgC)f(QYM44WeE;f&#jwm}Sbnt5W^L zo(<7Hod`iSp&IcQP$&bNIk% z3=bnz)W(*fP1O;7~WdHY^(cRu21EBxxrrCmISX+PfeVHa%8YmOZQj>zIhVmxnK)(;Tz9JE2}X!mNrN|nI0Ap0 z9p1X()5%U^HIBMpqNWOH0WzjSWUF`aSHMBUXeUW1ln+qDPQc!aAtEM&5F;VuLBSbg zLJ2!T5q?&XQi)~`2%&&pV^XhyKOf9c%Ha%|^^~4R`yYSpJI^d#*ht$$CW1kd0^o!L z*B+~C%+F8|S+I&HFoBdpMu!$*Ur>O_5pf0JG!(Z0DXO%cFoJ=ID2zXu0*PNU3&O4m z1KT)L<2}LJjggYZX%tsWNFDOfaM)N?WfO1nF|H6a0}m6EikY{niNxwfVc-lAd9g~iAbBk8NhJ`8f@@*JW@6-Id$w!gj;;wpu4yV#<1MwA zJaMoWrLGdUZE|RF?=!3COJ;vxX^ohqz1bo4nn`m;0+&YbCZ|GKU4t`XqMPF1pkjOe(u4g3;E29w0h*78df1M zxj`Vbjtt9DGD|1}6eAC!5|y?}xeB`^c-9I@6=jab*n1ghiwe0|vKmi_nx0g6gcT~N z%54H6Bb384f#SeGbd%JH(R`gVDpnGc5+|yNr>DkkEfFxI5MnTdS&aom4PZD?!Bk=Tmo|9h^3e!zKw_Xw z0l^X%hlp^~TGxn^^VHyo9@|`^WSYL?>i2M*)KP2OUlVfevfr5!jBV)ekqzQ4flBgp ziBzn<=uD0c7`uf)3h@b>6o}3?WnTo`86eh>SLH+MWL$)`t`;je^t(P*^k$qwN|$9- z34JJD6uPQJwv0}TNSpb(-Fw?Fo+U8Uuwf4$X%M4-e4<|Q&keI%#*vB_G}!<{+FfB- z)w<|m2re*MvL^xM3H!Gl*t5XKW-dm_DIRYjubs5O0S8I-W?MZzL{m+rvG0{YBNb5! zdb5g%F@|d=QmMQZxhOTic4Xy4Bjcy$x2*>RRV&1es)tdbWV1E<%_n~L;A^Kxo%tL# z(bfpeAg};NpcGPT2_Rcyj#2Z|R&|HMV{7pz3e-H1Qk^F>qW<=4vZlVaLWkm4BK@Pd8r5;#+0HY%bB=630Ey0Ug&krB}JPKVA&p$s?f6lkn`GB$0@A|5cT?7c9& z)vV2%-!s6eC5Z z;^V97osB^g_94w&Bv36tf|WoX-gXm8QZ+oC)2IzxuV63m1%G+fGfd z_r|F+4}SgI{@f*_pv0131;3;|_6<4`jQ1eJmEQ{(Z^*>E6;_*Q7*8iM( z9t6@tsv@b=Tk`X|3as+@d*gD6?!{m~Myia0Fk>cdTM(qcoJGH7bmoRna#a@~Sb)|& z)wL|p7{-Iv)W0k50*ZZDWsz1B-SD#2K93L@A2a5Q%qU5rH||~B-!_Q~B09r|?D^Wf zRC1?2x!X+KRm|uVeWh8Lq*|Y`*HL%z&H;wlDwv2YQ{uBjv)!An*_((DmBQ=MsmdS@ z-6@LE z8fOgE$vKJI1kG!7oC$N@>H7yFS)sfHT&-ivM{BOn>V~9*q zn?X5@U(2u+n2|%4B^@gXlw5lzotx_=N#b&un_IZ;w%gwS3-4>Eb~Ngb@?kpDI(z9H zx6CLXdY{=n`}Y3mM?W5}t|vsRtE;W~nSDnNoLf4(a>h$G!rD3;7M}-gosAU{W(30C zxO@v2Te(A`)l|xWz&r4(!b1YM(fM~bVG6iZol6^K@5DLHvaz)G_*IFYQ>a*5=dsAaD z&FbPU&R`-!4DUGT@pBi!>tO2>`>Kc zQg2;GDWT>)wSOuZf)qjO0&-ARN)KX_3`5k5%ZAxn4$B}~?{5rODfc6&-yCjko;)Fx zVCYs?Hs5&tjW-_o>GFk5WZA_F7ryd^FV61X?MFjvsNHV!+UDr=g>%_(uxr;YH|T%h zBfoUhEjOgIEh0ZN(}j}sM+PLrArvODB93aA5ePmA^~HcjH5O9ExXM8p)7Ha0jA1P* zb1di%Oj~+Jn=1TH{XaX_`mfF-yM^6dVv1R$B zj^RNP5OUKkxhrWlZT++TRMxk2aqxK5Z7GSWkrNMQHl(Z|;XIpDdH-#<&8U)kMvd4m zgd78LSP>De)%khLu5n;nX{&{Qi%z~yMW8S+6oZl!(J_(gks2U9jXssBk1886-XyK6 zg9u>KBojTgw7i<-j^Rih)UgsPo4~DKT%u4BQXvUH5t$lPY{dN)%E~JkWHc~lRR(WR zr2kc_TgogRyjcwcS11Pgr}=Pc^-}M^V!qzbH#cAs43Wb_#3Sa{5gAoS zwJsqZ@@39=tZ@_k8UYVbnUET(N6IKvd_wJjFDXRo>q!fdGdZT^U&jzf)i2HLv+_?< zcgv1}FAhiJ{2prp}JF3{Yo6BS#=0H~ac zZ6v0aR@e?Lf+ozP`gn|Urc{ve~_$>Xd70yB$uY+4g7 z=eUKpNdCC`c=irWiYOO?`a*FU6)?efO;9C~mUoM6?z;9mE3r0I99xcT{8Xlc#Uj1@ zmIHvcpY+P_T#$H+2nmA%TdVjyaG1tNsZhi#Ay%;u&Y!7zO#Em3L}KKiu%06NSxHq5 zrN$%|mseqJYOO#W1(GNfhmA%8b`YtNDyK|@Dh;RvaTHon8N97}TCZumlZkHLy(7i3 zHlz~|j1P!FVdAhW6%=5N6~B3K8z?~(Mq=tUF$D%N2t$O}iWlh*IAu-R@BhI2&Mqy# z^ye|je){!%(>?q2^Znr^)0^47r#H8M&*nz9d~vx=)Jc*-o~>`Lk@DK=x<%Uh z;733F-7kFg(#kT4KK8>$pL^-~;qnG@W1Qc6%XK)IpLq6ZR-;~YFl4nxSe=OXAg?53 zl2E$_hDcOG(Nj}a+f+g@-w23Q8$$3jVnsDLo(h>&Ze>WlFk5pC?8rr+sOvaX(E(C`dc@4;=--_cbz`Xcw1w!Mhd@~nyd}4}dOuSk zA7Ds8N+u*A1U({15D*A1xJso6fJwzsS^;!XK!BL380rw7o#Kg|7{u+A_i3WPnHE!p zqf1|9Aw8m|Tr?M;l0k>#{834}6h9|l;zYxFr-3AtRXxZvTc73Br^mkiKzngMb@{hG z`-Sz$l!`Ybz{${&Jn-=)|(%D{ft-d#h4_?ouhENFp+rFaCn6X zkZMw=oj;S;R@1iCgi;AdF7J$U6e)j51S?8IB`+oVOG}rC*W|)CtHdxurHV39+Ka>&+R0b*H z>@-!6dc0SQiW$vrHfDD`czacj_}VPizd!Dq3*PoL^p;5<%z!3P(by!Zxt%v;V4kq^XTLr|Cu&W zS|wGLZ&U`u%HY0ZhmX#*?O@2FF88sz0!cU#ORs&w$4Q}-dDX#*pQ^DFpX_{&#-zVA zmRE$bC=(c7Bq3>2c4VP@`!xqz`H+fg4McQxi}|u`oD63uO`qvS9;J*B{DYFzOG~ z5Rut+@L)1C~*{Ao12_kmyLuC5!Tl(IiDf(M1(vSqav=%GLJJ_8(FPE8^_j2UZVI{Wyp*hWW~0F6yuH`AXmUG9%rzSDMNIcPzfD)echdQU{W_g=kv zm%<;|wP1}R_av2eM2wgxLX<<62OGi>_OsQlo9s@I%2yT;-ulU7k^k{=(=o1n2H@e0 z<_9eQ(rvf&NK+;Q;;_NB5$h%*1VCJ)Gg6XkHiCFN>roRAInHoSO?xglFcqO#H^-nT zpkBdb)z4)8`))dRXueA#Mr*_`llZ8KgZa&0J2JaT$`LEtL!~Vhx--n3tI^rBf>l`y zR~TT3454L{s{O3kN{b?z`p?9+oTxy9XEHDi2e^6RP-3BiHQ;jH(%J=FPN69d!eakk zc|y$9UehJX)E;d)oJ4?45>oYfP6ExHj`F0P>^gLy)9Lh=S9j0NQJR1)awg^3`T5TL ztP%)jkjd`-2WHwG-dNwfbo$i$-+SND>#ki}-Td;G{`|)Mhu2?u_0ezrz%a1UZ>Ong zS(ThxiMEnJK5UE$G)xG@r$oi@1xY03G)xr%;hs`%3N<@6rkrlKnx~ur(}hkIbFcn- z*b$@Po!!P%QLlV3^r5EXg(dceM>Hfk7OCBLROEh<6NU(2MKq zo)kcJSbL_9&aVF`*DaXfg)=G)k%ZX7ww*gd z`1l>Sq{H0i6$IetU+;a34mPj)1dV$&y{Jeqi#>E@u$faV&c*X;KtO zE+c0X#Fq`P`}F+Ft<%2afHV-@j*lv^mfrrv}DC z=9*V5Hn*)U;Lo{4 ze%mmJWk|Ce*@+&Y4k(*uxW!m5>y&D3CH3wO6s60c`*wmUtRE6}v3NCVS{?9H|9UC+ zD^p7Awxmk*G=w}ZRY^n-EY7zLC8S0rWQ4_#I1I5WI8$6$Uj^G}rKwiT>-EkSDTOr4{x zrmbvc6}#J_7j>Z-RA&mbz0pUcq+%h+haI1P=+>LxvF~6jQ}Sl~O+{oclm(7;k0;71@r(6#~Bv!7`O^c?ZK)bAMe`9^{+6bPJ1l(%Ddnok>Rs zVm7W8t|6ZboWkkarHdzDd?jDqAfi2c_Lxq4Zr`3`ciz0#--P1Op@aAT!g~%LJV=7! zaJbsvV36+Fy?g)O14@{g?cDRux4-uCi`O2y?(X;9EB(G*=pDZPm>Z73B*V-Na{#4+ zAL56^+qk+4=Dgwt4qnIU13zvizll+v7_yqyKe@Xm+y9nX<0x4ashoIAQ*u!WqfUp8cg;iKkS^`uwc!zVh z)jFFD6*Lv+ki-zF=Y7OFBVofn+okzl_>aEh9gCiM98C zH@#BjfTP6xY~^#WBRh69P(}RgEp)zZ)vCsO3j{aHl^mgwQ(|(prb-cOn{s|Lvt>45 zh`I|)tLM%GNakmc9J^`vq3iY@INWM=X4^ev`M$-y$F4it>$LOHrfns6y#4L`-Vc3X z(9gg0rLT$yvH$K5ztvvoZS*&vf9e@t=!x^E&YctQhZ}t`feo<=Ax+Z?Ln2^f%ILIgZ%1Bk7k16_N1uDN14DS>TXwpZ#?X=acX1bCFjQM%0PwGzj@?5OzN2LojlrIW2 zxFN}0{EX!z`wzB5T~x!NoCPbhCl6IN!sg=2dVerfVj?4j8Bu%t_y#Na2&Z>#`|N=& z!YcLMSl&ZK#4P2z>ZXrIOo=s=I(Ng|>_=|9eF12LOp%>aclodiM}yK($cZbAgDX~< zukd~%!w8h}Bq93EY8b{z;$3ez`q1rn-nh8CoB5PX!3ZXe_0SYLTSc3;U!q$zZtH&5 zSVKgsHdJ%Ov5YM<42z-V{zfKV3x)$geXW{=sv1zs@NB1J2@@4`%V{lKI^}Ag@WD<@ zLEYBpsUyG>!Nsc_v7hI)6K4ai;LvKGtW3W2yNFWA$#6osj8@ifxb>!ed-lHa;tQ9~ zpKEpPnb%%sW^T8SpE&u{lTU7}EGMl5;+}u{8T;H*Ppn>Al|g@Y@4~Ld`QA(?%SXeN zfdjRY?tnMVrFC%v2V$G_QokvRkzpIRx3XGR(Hh`&ta|{s;JS)F4QQwxT7YN86DPs{ z&CI1yAJB^tQ5&)9&0@A4VaZ&XjVp9n)Ytow}0;WSNceuBUmPcOfU+K9KjYXoI7R7{GfdIp1uOfY8%!4_p(uRC7@JC2Lji5Z{LZvX%j`B3pxm6^kVN`NQ` z8WM7hFp-DmvxCjs67yH?e*0n$7WJ$~13D54C9lyFN`Wr2p$U}=>6?;YH7%;>#>BZu zhT!7~Y)HIkvLWNfgNdAXIZQVfzOc6GO`^tn{tQo1IVwf}OB{ZK{^-d+>P|2Wp zVQc~wIfHTkdMLmKEbt<*N|cLw?_ulpuw!i?MFpX(RZE=4*z=nOn#QrJVN*yYg6h7yu+wKraW z^UXI6v%$ebhkU}?Ynhq0K(jnAD^dl!1$A9}+DJCEn`vg6Vxqvp#6Y8_P}CnC7yV`2 z{yV+KRM&C2zx~eCCc1e7_fqdpZGYBInqu;U!WcsyK7hv=FwE9MkL}(yr?BFL(hu%{8rqR~Ho9N)gi{;SbTrOXclQNbU$sPEx|5g}D#j ze%oGn>wIcc7R!!V2u_)e_>lfzd9qLXlSrNx~C_nW3y~ zh!O$T2gAH92^qozOhPf=E+LTj3*By;*{Bo@snbP3vngfkC(T42i-i5Z z$ubd9Vlm`NLIt|W`xxM4)j2gGJBnj-D#-!xRW^0&K6vcObGuB$m%>9jxPH*PRU;Z+nuvpx?=%7k4Dm2Jmd*u}jJQ6cpT1|{aD+%e# zPJC07Et?P$2f6%fV?-w0e~kgEPf0=BQM$!xeCg;;q5^N;Cm6-0XoVTrp-#P(HBf8Z z*f?kvr&X%Q4`Ct3s^Nh@l$s;mb|P7D!b5}D%d_1{e2bdbH_IC zWJtD%zE|6-+XO2Tb(7+=C!y>8rqX+l5#fKzqL{!?e#WJ=R zi0VgmJOE<)LJ%Eh;vw7Rd|mOJ)>N)Z5mA@ab@|(fTkIH1tyPfMggAUD38yp+RsPfV zva8%Uv2YAg^bmOoBuqmi(qVr?Rk?rs z%$b*u4~Ln0y7`V*PL7|yu$<@Kn6bcOvl`7? zwV!knIJ1R4Q3cYe=6opEiFXcxvV%EMZH@ZBbjPh9z2mlpY?KU#EQ-vrkEE2QC{vDV zKZhk(XJoAZOmFwJsoPd*FigY=8>=P(vvaLH+s*o!`|f?);;wdN%w#CQ7B$-W9j%HT zUEAps+kLI3NCr4bX~RXK#0qg>8WCPSdu||sjWznOBJ;m8f=i3kvfgm;U|WDRQIxIBOG(T72_xUfs-IzG>x zbCl&FPXvJX9)=4$rXc7qCoq7PAhp_Wsttb;h^V0VVk*Y5KAH-hS|;f)A)$sO%6gRv z!BICmwzmVe1=aXi9isl1>dC|^O6mf=g4cY=q#$TwM(vq&-#$+a^~K(*S1dl6$qi@qI^y52v>+QfB+IxX4SU3 zU7G#1d+)lf({2qnA>On)IU8dVb%FD@8Gmk)9Bgyd8hTlm0*@K8!RXG#gVjsca)W(t z_#5xO=UvxbyN^vO8X_4QshC1{8iQRQ-T=tiNt>_iYIqZx^e1&gHqK|*hjA!8@i0xF zd;Nq{CBvdBMSi6+NeRjl-(kLHaUm7Y;xXl1ns)R~TF=^`xQUea0r7Yx*VHu}EQuYK z{g_NTo8odIKsnr5?u+n?)KD-rt(V8x5O!>ISA6f`+#Tb_h|uTw-`Z2HaoGr}Jj&OX zm-g)4&7zMy_~R3=y^?SA&!0K_{qOzohd=ybmSwPJeKcU&)j59Q% zv^MO3pTmK$Js@%mpLo#`o&MkYEmTv-!e2!rGNjm_94hxYkKD60BlT-cD>I#k`x~GA z>7&Cm35f!1jT#at1k4OF&L&IFKXc-^*j7v_P(a2vUPEn}X4a@yYmg^9xOM+)p#jA- z`v3?&Xs%aQA`agtlvCi{=<)d_=u%s(C0iCfy=xF71;nxWa2}1=J6|= zBE(ZggKm3LrY2zk2?;s&gI<5*S8uubuikmfB4Au*$;47BOzItq7(fyA{CQ>p>|h^E zN3sQC{h}-xY_g{E%K3AixHQlOp}>cRI4vnO&vq}&&!;VRjHp|hs()| z3NjNjE5-j(Em$26u*(W@4-Ghg@<-Wh^{r-figvba0?kFkH>Ikgm2DJr6_um62L^BY zQk3kcD9?bP``57p3>X+z7I-pD!m7Y9yZi3DXJ_Wt*VpIf=DKt97tURJ;o0ZK)8T8c zJ9f*h^Sk%(#_Afx9XxVPp63R1-+lLQURrClTC)fCTzk_ERtc(U(!wb4f>cLeZNv&- zUp>6p9Ph%&Y^wPhpGE~Rxr(`k{?!QD1P#d>tf7YG@#e?6uIffjyilBLxSi=>r1DSA zgqO*8GzX@kbWnsZhP($6$sK4&yauz;>Amdqzy6cY{inb8yHBky=dG4{uy{BJQA5_U z0U#=3%!u&RORqS9Z9=7M?h3Dx-!=xRCJu}@cTuU=C>2$n46BMj$y)eaU7nKb`HlBn zd(CgY_dR=@bVk0z++xcN@?eP;M5()}UZ%2cUHSuCnv+xGd*PSNIc7HAcSiXh=Ra`$ zvEO>%`}SLDi}x-MXnPP;T%FHS>t!LfwYT2%z{Y1yAM{C&K}|!O-Kj8Bq3j* z;3|*#EFdc23$kTq*;pyp$=gate*hJ-7HOrEVa##6wOOdLct`bD1VFzXKM@P_B?L+o z4oo0V1Vt!$?I5Dw1M1TxPm)2mcWHL^iziO}qks7apMT}G<(YjW?!cr%EM&ccVH|@f z5w;)_;!W;7eRgTnWg7X!Gn5&gqFN(wr7^lz__D29WNb^o+Dqk$EFnF$Ek{IL;8Lhe z5-6Olg}a$K{1*U0pq|CEs0kdxL;#s^VzDJ+Lu4{degC)az4Pzfcke!uR_?5bD&_EE zin1~w5(6P3RWEhlXl>Gi7F$rMs1#}&NR$y0%1R}dJeY|*yl^ZArDP46#F2GEZaAN9 zynD9y4?pnU>$+ynn^bZ_tak$vQV$Z42F0)>oKaPHr6SY14p1slJ!7(L`K#X~NkeXl ziVX!9$rcQ5!TVtVid8GB*i4DvkINu2#2^THg^w&uh~WWZ0sFXqbN%`-{2X#oUyeN28RL;PA(U0gX3z)eUEkPy~eb zXkk55S&Fh?_Y;IVt0N^KqYJN|%KB^Y{a0Q+{@7!W<-@_k-d*Wz#}c1^<2(;?yXU~( zKLT}{nLD+=7O$Up2#;$WswgVnuz_(| zE;e=6IGfN}?@Cz^h*+UQh(QmPCli>^3B?ytS~-b;hcFF*av`6Q*Z+e(t)C(82?TL#D~t2eT`XLhtzZMT)+niZ!H%wg;}5&7SgxilRc0B^;L(>}TvidwmXka#bYVjtfQli*;(NeN z2M*dG1ejD1Ye-J4`m!|2)GOo_MGiQ!qM_ETA{^H`K`(!~K^q^LEk?V3pzcJT(6p4X zAoqd<+u``X$*X)*!zEsC4|8N`cM3%|8X?8P;e$8ae$UfSKi#s)U;V3p_4QMyzVY?1 z`;qiT?Vb1B^^LFmdH>Q%(&?%H3UhLDph8X#L=~PC-0Jmhx@PUGeSG2JA3XTttEV>x>rA7WJw7sXHQ0s)zEk=#^9Y5I5@((_Azxd5BKL5&+u{j&( zVXaNU>ea)MF<$8^fa3KP5)vy_k*kAA)Emnt(-A=MzC}!KxQpC~hdS;MD38VMmuT~oDK5AeVWz@KvJ;p*xhO`WpB==3WzT~R80-1y z#dGHeN$QOe@nN~dmlm!QcKLNPy<_vc5^+Q!89SzgPaEK9bk%`?;a3j6p8{_|F}TPS zHbWVySB64rySh=wX&S`q>O3%4A^^W{ael!P zHaFHzojks_y4q@4>ZVz)_Vwc@4jw%C%F_B>cipuy>c4v8wZHYj-+A;Wk6<)V=NAtg zP$mqs#I~}5Q_BpFAqXionqo%P*8<^HaGxf8arNopV$6&CwO*nNWBPUYB_=3NpRFA# z{u-_Q@lVHFsYSWOW(UUg{qnXg^bW1{aSS&GCJ`m3+_qL>pE|qzg@=Cf$e9brH~X8N zu4~U_ilCuoNQo1}dx+s|a3h2}tnvL@*nxMxPA>3Tq)GOu5gPl`SXGF5LbW zzx&{k|L7whJl0BPeQrd(69R)&;R&RHXrY3-%VZy$bi8%|>QH!x11QUSSs5Rf!?_qgaULKKk3aXU7YJQh?ar)Hbdc;jJ-@^x1mW1E;4 zU?+L^v4V8Xpd4#wuwg+;qyll$PTPBC7k>8WPdArWFdTgC%U?Ns^g3nJ&+~=F-FDr( zL)RaD>4|6Z&Hky^UL6hlfB1)g43n}+UwiJQlh40`^$f;%V?e%CVNId*sTPIHi|A#c zMAjv#0UXLNO!H={#%hRT<-zKAj0@s%fZev^woPDmXWwrq_}d%u(@%?u4+9ud1x^{9 zCF!MJ@3Rm7uvUIpZgRou)OFc7S*Gi`lR_tL9%)awXj1JCIn;^5pRD+J6ejTJk+ zkEJn65|Pf!Bk@*-uz7gT6aSMLVm+ArI#*vUc*bw)1@g_YDWGa08&R3I{3 zS$pcokL0#PYE%p~F-d!2VV|sR^fz;R>Cy^&wyoCl&p$6BP~Ej>cTfEJbF1A}YvauM zR>Ius4AzHGZQI1>IdLdvAFvlb`v0lXo4TByG65Boh-21YL5v#3!R4l*fK^Nn|L(*H zh+YopPBRS0PNU1-gND!~Zt$a3I(V!xP6JCndhON!@ArRjBGXZC(GfWqf+aHEdlg3_ zoLD%YXqK+8-Ej08bUUw}xtO(6M?x^h5Fv3{&IX#aSN!m+k3M?uM?UOEeFld?38P+y zB#T44Y&LJ%DQWDbPAQw0bIr=$bF?)dpw93jc5PmUH_BTz!aa)xR3kFwHZ|+V)CTM zqwrz_5aQgVr~BE%FCWisixQ(=$*EX{Qm#U5RY9brZoZY=bm+j0im8bnDnfLz%TtPy zR`J=IUtg`YQ5*z2A=aQTf`YXuNMP36c}nnfc*u6D+vRlmw;tQW6aUX(<2R!&NWy>nn=j3L%cWYr{(zINh`;pPo# z3g;34qD)K%5Gf|DfbbS!%&AoC)BRc@x9V(=fd7Al5XY#ohDw-t*2&D@%_)^+e_d7cQQ)4BMJt=*`Ux zmsa2V-uFNM>@zPv_uR_T#b=*;8ZLk1`0>Sqi_6FRn&kdNCwT5d^AUNwB7pE5B%b1jvie!vis0A-|WmjI&gNrD*#!;m=b}^legY zYM{FMunQD{iN+MhzZYUavqb$OW%nIiSop2a{Qfup;`^sgpU*}!7Dwyt^-E?*;yrnY=EfL=96XqCXvLLDv1AH0 znQq_AHTICwjx6!KQyfIouHa^;qOwRq;>k=VBlDe~J-x!#852|-Cqe>Yu#$q9z}}@Q z@4ETst|4}wh(v@9)C&}<0v<0493=w)R)JQ8ZqWyz#u$hOIFLvMv6JB6U}7azQTS~( z-&B;&HiKDYB9(d3W!agshVzx30dIbaw|&QU$3Wo-3X{gFB7wk30Z-z+o$2(~);Id= zZ@hkTcK1T7-QL*j=Xo~2u*lMtcfI$Wd7gjwf$y9@dk##W|LmVFy?Pd^GRh8JckRJD zZbh#vEdvAfIid%Ry;Bj9!r2#)DPy^&@tjZ}m~jiT%X)AzxFo8CdqGJ4Dm?%ZB2bSa zB5F$80)YzP&1Fi>)OGo-wSJ7)boyWQRh8~ zGjE3d1?hk6`fL81-~QFVd*`jkY&x6iHF^HYyKdW?8snT~juhlGG7!t6k zW|p!6@dg4YAxZ-L6K+T{64!5Ia3sYiZ#wip{)fNw8}GbzPd@4lGs?xejJ=X5nFxm? zGDW%~fjFKbB=Dqel2mFU6jbm2j_U!GU5I;Z=jEx1a#Atsp->C28bo+IdHLM=7tXJ& zrX4W^1iD8fWUFnWHp%Ir_`8oCY7Yi1A^8>XhKyC2e#loJ3XxDK=Aue!h)IbxN!cV8 z#v&ZuaGs%HBBNkcQg%e1K}NUK@h2lmQ#}pUppDH-Kpv8--9BDyphV>gAWZ*ufFu^nUkm3a*`xQm5Hs53nEDpV~lCgImy&8 z#q?8QV)C}X>OyBn1GIHA zRXBU;KqOAq`Kc>7ZkvgGqgy3Pem049W%oF>@yC zODpfW@4o%>b8T^{I&oedGo^rmNk~DgNC;Mm0gX9O8W_efp4T#6Mr@npHMVS~jMdn6 z884GS6-B+BALMhK~T)r*F|_UOaQ-oaV{=L&&(0viLRP{suS`~2?1hmLi->@!kP zl9GRkwq88r_@(+MDxFqpC}0Rde)Hn8E^p|{=4f-nJD0SQ;Q7zJk0lF3RxWR8E={`2Yp`(!xGV;} z&DX~vBH`wpfToDX8qg5FJI0Le^x8?FxOsU}VX)zDKHPQb;(z-)f8#&?*oSWICI{Wd zL5z;IrUKsAJa3s7bwv^YgA-MUrS z$)B42xZFWPBDtdo3ie>AkhD9muJ^zD!t;G*fZ^3B3;=QBG7ij_sw%Dv|KZzj-S0;x zb07f+RkTWji;47?Q-&%ZvS=r;-|I$~#GTPYW-0wNCc z(=q_+j-X&t1<#>0rE~pj(;qqk>79Jn6<^yYcJ1&9llodc+*E6xVfO01 zir1)0k#3&HjZZ>gx!n9jYM4YoAfFK#^&-aR%#Q7vZCzL!efOy+{^*g1j|;r*va%Q% z^U0QaGZ-2GtUy!0pDCQB zka2^+vH^`C=wNs&w26>nkW3X8R}E0!;mH96*kz1xYCsi7GDj6sHg;sqH-7y1$x)`= zIRGIQiy;IOW)eWe5Rh$^RXez(DT{*?FG??MR(uFJ|T0P+*+z_|Zsi%w}lWK3mx z{p1sN0122zYp}CLvy&O!9;Z~t&r~)qfLiHVFMzU5RMe*2c3F1*%>JacCrykYv%c!H zVWLi;MwLY%q{?IxD8Q_e?@2o!e*3Ktt*sBesE8*h8?po?@fdOI+-UPlk3D|$;~(q} z2MLo?WnvShf4l;O-IPr&LyP6GmDyiAyu$Xlx^gEaFjZAOi;A(viz5hIlNqzoN-l8c z>(4&*3~ z8?|9&3G@%5Bp{9i#t;Z8>0~-|)$u|Z?6q5ad$VSJN!hCDfDH&LVjAf)Q{idT57e!l zQCC;tbmB3^f3E<1wRJCc^v0dUnXBHhkZ4R9Ruw^FXl<}DI;%1mKziL-nl>>K zk-fWj|H`lY%F62cx#i_|-hUq%x_IW~J$Ju@5`N+N=LarRroD^1+cPuBy-gDj6^XTc zy_Vkd$Oqdb0S~`a7JU&Go<5ygz&*y)qS*PdA($Y09kb)6*mM)-;nmlVRK-qCk@B{& zSV+-SAuzLgC9rz2gr23I8!W7E&aJG?tgX)Fc?VL?VtG)Siy@fA6_wxVopZ%-f{n`VMEw5eZ z&aSpP((bB-=t$K!YrY6NSE7l{dO;B>!HM6;bSPg$gADiBGTz=8(WIDvF;S)XSG;@YRJRan#~2o9U97c z5wrK+k|9Z4+J5YnS5NjgM>bVZ+|+F1z96HlV6mF-rr{^vac66DkUC`;uqmkx0imd_ zSKW#3AZ_-hvG-ZTS?IoAFBMH~A|m2dL|GekA`8l)zbuzab!}O$ax(0Ex$8|O3~wbi zCz~7C=>RZ<1rRj?b-9q(x!%DWkM6zhKtJziQGed5w{^H-UDj_^ zo_@>cYz-Jy+U}WCWTF;~&^o;l6LA6}B^3dA27}4F%oqdp0K5vE5HS-um)RtNgF;{d zg?Mj?bTl~F?fue?H@@)3Ll=g9ZnueTKumabDiAiObN#aiA9>qH-j^C29d=V!mr4a<%Y~{I@G&JWRv7@1Au zcm3*n-}UZ8*BmlD=Ui9CGegxxFmy?KP}6s7oF~QK>WiYpnN$@R$LxTz7mbx1d2*w) zF8p0SSW)90Ct+AsPY`2_<|M7wIifE-_Q)E@v9*D$*MR~CD{vgCSBJJA-MMS#o%?o~ z^Q-K&z?p->+=&(izNQYc5>MMHAR;mk$;H7OetYYxTq>5z0dCDiPR-*(_cn?bUA33zVxxR1D{56LUUOKnbYqxl|*Iijz zIq~}IBbRr(UGM#9G)lUi+wQ;5EY1VC`RGk>9)k>{%p}G+9}15&+R;!k#z$vLHww=( zg^+*vq^KgfMepNV>i29j;!|S@_1OKD&emi)!*uCCt;5srQB_s;tj<%xD(k(EhP-%q zF~Wu{M4`DQhVu#wWkalfL@Z!fmagFs-+J4%-ENxYwt#jTV*rq0wdrQs`}Wf>JaO{O zsMQ{VE8ks_Q_<*MoQ|u3H;fl@Ds`5vq<{6r;@~V+xrk)WIZoF(IVt?jS6}}>{p&yY z=Rf$#vqQO%%&#Q9tTjt%$B~LU5n*O+E)Rs$by^U(Hm#u$AjjG=!FXaK%?TmCY00)t zt34ndwbG@T&Z(sH;Dz=7@hji^e}4XJ51zSr%A}WUPhg;?&__x|n70E^O$Go$az=eP zt5q{4JQgA=#erhVF2_Qx(@alzA<^J@^<*;B`u>YA{_NtpEVW2d5ebF*;x{}XM2Z&B z&a&VB_=o57Eb(5YGOdJzs;R01aVjMuc&m)ou~W@M{Na83x;9}65TFn&5vK9YJDCMm zpV^-u6lc5j^Ou7EDVE1Luhit)X21xnfB>&f0U_}!)>yE@*xmaNc#><6T|4UcjWKLf z9*z3H{FzVndfkoT+Pm(1XEqw--re!eyKcGVHd*Vx@YGZ2=VTMi&G+_R6JF+(0RtgZ zBD-U~P*eq-BW5-t!J|o_T6$fg^I0GvgPx2p(Y83?V-&WO}QEE@7 zk%F+3fe)nus0sY43-S2ha+?*&nbi%G$pH{RB+%8s6A=jl!rg#KygDyXMEtst5@`~P z4Ji>DRR*V|tO{7u$~F#n=vVGI)-^eM70m@sL=0Jm=E_5xUJ~%_PS2TC3BE*D zl3r?W7PwaAnk(JDJB(r2|gtefnmq&eeT=nx6N6YZ=PQH&%XGrfBfg) z{lWU+oNWyZk%fek&gBsR^~_3K!GVMTjtR8YA`DQMLeGs9Th$sSA~FRFeDohOghGA; zgy02UNJUj4qO?VKZD^KZu}tZbn9n`>#98S$+Y<*d)-i~P1q2qOOlrK>(eS{0>pe%0 z%&f0-NCrZVK)3@y;<~6E|6_IJIAn?yAu$_JvkX%8R+AeJ?Ax1A3LwC3l@#njA_^uC z4F~jAUX*HxCM~4;ZM7<0^(woupaNK7gYFc5iVtH+3`Kcc{Jb6mpb7P~-u*H?cPmL# zmLZthyTG_rUUlFpZ+N}21wZT2joYRsA2Lf0GOGXz4rlcSL8=j z8afd-V!JhISfH-K%t93X&h=nV{Zg$!DPw=1qI_>B(68P)+ew;O{pPykbt_{?V|EnL z#G0tjZdLrU8XnaezM}SunNfcKEw}#hPoG;I4yoOi)RIu@XL`sp9Cq@a1?8!j7Oo35rGRjQ`qyYz_+BBS)C{%O^ zM@D3arkM393}eJOC}q};(wS9G9(v=_|MIo3y)^V|-I)PfPFwJ~_c^htEeQcYFjpDQ zY0i<>ir2$b4y7PpxRp96f%*w6+YR{doOt7TQl>s^SkExr4uJsmsfY~UhMUHer@=~ z6F=)MF3j!Pg;UGO^|lY*FMGSYj_!x`er2V9eknA?7*p0=8uJAe_)|0CBpDC-QPA6K zr&-;V*07GRJWXb+^s6bOybb<$dtCqaS!oT4UrCT63?3GAxjD7*%uow++-(}w=J>Bb zHb6wI25SB2`aQefbN$h|;lPSW?mWDb+Jr=M06DiW+3ue_^yteQeVt#(0eC6v5Re!3 z!k{X3Hk<*`Oe2s9sW>HlL_J}cLt)QzFp4 z>_Ql45h)u!mLF5dpPDU25^I&Ap<_Wcm}TLK;YR}9J_k!#)#cupRpu}KYPiG1SoPE)ECw4Ef0oJIAx&h)D?D|T>=0jcXn z;XR3urP>8ll+#fT+zl^3_p)TT_S$RHPP^OdoVj?OJDk|m{?70G&ap$+xwZbjy$62r z10Q$(r=CDN&Pbe`v*}Nsc*T@th{hlUtciz&B8X`D{Ns1t zdDvJp%B(S}YK&2F1PAh*qyr=u#QxJi{NuBU$!!8yCXQ9{5YjI-7J(#aQL#BTBkaZi z>?qw~v1oHipMt{CQF;7hCShy91QUuw5~~qn&vu%mpaPB*>d8=oo6kp(&W*l!54FH*_>y=duO_&ZT&*=!Y69Uwv4?OeSk58QP?VfTXs;b@>ES_OJ ziHMv_-QaKj(k~pM+-5m569?Yxsz5mkccRi|MtEW;GC5d6TSs>uJG8soVkZEccu1sy z3DUV3p&c-zT?q^{mNu#GA5&J|!s}|DHWtGlj~H$HE{d_hP<=c2qSc7K{>b&+Ztv{* zbG`Y6PIt!c-oJ2R`O?4sfBjy6n1RgRz59Onz3*rJO`j-Kp8DY}O`15h z%t2eJB3IM!8xfio4BqApmB083I>5mjz9ld&e#=+5j$(isa<-v%PuKUa=Bj)4OQ*-= zJk>$;6O)PUfy4C!Z~QzhLn8&kgs7s;A`rx=qVY5qNAJ7WtgduCpk54gl|u`M>bguPy-1QhL*5)aaHmP;%WlQ2k~Qx~$8 z8ATx)5{ksjg{E`m`*v)d7*H%YRaoDRFe6~eiU_=bMZ^KhHi6Y#`OFAkIey{?ue|)J zyWa7e_q_eqPPZpw;fSE(2@Dao38JQ7OkW{w$ClMtBT2(_7>HM9)zEhkky2?=M8i=< z$}@x^#0(8$xDT33mYL&>`5>kBYrEo&p9Y>;z+IJ?q`oZPThpaoH_MI zuCQr0&0bx-G;-#!;MNhly+qd))n|dqUgb$C>sQ-yR#}#UDNHPTjYGbH39mac>l-0v4!ub zz&>p^&6hQ$gU+iQO}x0Z(DH7P&4=&4{Rd}GuMajjZ98H^#^)JZf;DQ839b6>pZxHV zJNE4P=&@sVb2$lse`cTifXRX?M-NY!J_@a>HgjzjbOJ=A@Ui|$Y-)90gf+=T;&Mo! zoAG2)X?NGTwUKn5yKwQp|NQ5F+8?fVdY&yMsfhT|&=4`mvl=E=a#Gjkgo&i2-?N+Cw@T$RLVnZC#fDL@El&48wX@l*bJ^R87FTeD+-h2PMZ@hMQE140W zfK?nA&JBquR#C4cqbXE}VkI+`16zcYnN`8D=c)lfS4i35#g3pRu!&3_WB}rzzA!UM znv*UIzVYOf&#kO(w!2=$SgSxZ?3lt1CLrFDEUE@YZ{33v?=fFc;^GIP2kG)|!(1TWlW%)_13 z*ZjF%^^kH?m@s11ZeM*;3@OKRO^6J{*9=AoGZU%H-H_*Y&keJYjPiD;%g)-}HyykC zlOH{B^vJLO=C6-7*ZAV?+~ILO+)Zax~8!)>j(yj?Ty z8Zrp6siNNO_$6A+bRA5#1>0ox;F>e%tpsL`!S-wJtoRE}P!)xZv4x>=dG)^;?HRjY zRYhGfP|bFVV{vx0ar-rk?>w}BHXD)~g$h5+46ugWePCehdVBW2{leE?7VURul&pHd zivjVJJwX6N1GJE!hQx$YK&04W0f?#te1IC2-tFLH5X(VjXJeTXmQJqSP3pcVt^ey+ zzV)B{!Jqtekgum*aH0_PnODcwcqLCPfhs_$pp2IyPiG80Ay;Q{Po1wZ_bvyBDhC=h z1{jVZVg;$SUd3?Y4POF3wmJHz-}>Hv{l#zobUj~@PVQ2YlpH0>9B8Q36KMpTgi+E8 zVO3kh1dc8jrN~tmT|i-x6iJ0l4f-bUy{LmIyr>s(3S|#o=k&pI7yjhYCoU-?X_2ww zyn(5(S%IAeE_K-qarQ!38|a2guXTQZKi^{=JHI08Px!<$r|WQLXQB$)=Nhy%BGw#7ZoH5(!DD zDx?}6`_l#ME?BbJU z3iZm>7QJ1$ZiuhQqFI%-?LdtJ-L;S8NG=xw>DLwTnN(#-};`Su`Ywv#dL6|hl z!^k*h1shJnaBGq-0k5aYKl!)+<}9Rdd8EG5H#r_`6brOb;ZwE>8x8{mT_a9G)bTYI zhYl=rTB=%OcwEXwG9{tRX&=1S?!4;d&z^trAOG>c{nGONOwLq;1pQcnjh|LlE-!t6^RJ-)mSb$!)yrz@#J$-M`|e%PBMqowhSp4 z!;~+zI7CpJiK%5JU_zr*WUY{q`#VYS3qki zqG`jg%!*JYDDWK+ft6{Pwtn!^D^H!ikQ?KSB}$kR4^K=?nDzb>x7>Kce9t;%6%Z>G zyv1A-B3c-6Xm0WCVL9QJE1HK%^}qOi%ja-(ej`}7rJ>0cxkBTCW5`jC6}z<_nXGxD zOQ%c3l1Dc^k-}RxV2I4k%`9)MKfkm(q|`ARWmT_6NR*9LFD~I6v_8u6EW7<}*QeRA z1*l>5j+BBUK?opa37MFN$Wwf20sD!03-3&j-}n~~KJ;%Nd+f!b43pFwLnWV#kRc<5TY(Btic+XdOGJi<0Yhr4gR9|jir7_B zzHIPk5`k78T}_;uQI${>IaH8gW=~Lff{NzBMMD6Qs^tOcO70$c{p1s`9Y1{V$b7fM z>RSp;j7rXIe2Cx%N}XV~2cW+&IGngl1cfDLg;y0W7ZL?TXmH&XNoSQMEu{Z~Mt0U}Wbdy}$Aul3NYCpU;aKx))08_O!;92EA3 zY&l=Wi$YT{iL>0op-=T6YW@>dRB|Wc;pY@BsCA)~7KZBs8Y*$rZ!AAoRiUt13a_H7 zl4WbuvTN~?V@J=PIb*D8wG+E*->$^!h12IOB_I0LkH7HpvnQW;`mu)}GwS?Ee*EMk z=nueTj1g-k6Sk>{WZnTN75NgcTHj1n+&}K&nCKgvQH`-{ILt%wl3E~P=V86+NQ1xJ zt3+~ykzoxIaI9710QuT?x4&XcrSSXK5(I?2!Wi~>&+)Il_dVZu`Ss;&D7G!uh8+YV z@4c}`Rq`ZVBl^;l&mCNx`}LczxB0LY&RUJka8cPxjPuU0%Ipu0=7Oq5 z1r;Kq3+#yT#X9|-Psc23b_zp8zc#dK|PH?IZt@QYTxI>jkW*kPrvY~ zx8M4!_ug|b<;0I>Y>Ok&fv6Y6l!N&7K*S%(S0da~8#+*ZNz{G|QvfK8g(?%l^Dy&h z%`B7t+sA(Lm1mwBbb6Y$gscy#zhOL~U?bvSJo~JZt^A$8@|goRnek5ZT*Z=Ms0V?H zsuT4nsupKaIh5t@4l!EpdJz24hyzvK1Sj$06= z2~xwi-f{DXe&v@9Cw^sP=trH!SruBm=HM^~X_%G$8nbBM!_g0%lgGk?EdE zXa7GxeB>J^POkKN!_ogbN=A%&F~lt4^}fiM4ecR_31^#xvh^wi$0{UVl%NbH zI3)*&@wV$a^D8q8FOvOdk3ad3{^Ya&-H#vs*~l$(+b7+Tv6!fHIAjqPcH?LA+EV&o!EDx`fh7Q@8F{LNK7^>q`wiZmoOy{`&rV#AvUe$v{k;6L@5Am^_%LF`8S=6#;corCqP}sm&>_;R% z1luW{SxVE-Kl{}0fBy&P+np@!s#pl8XeF@XL^$Y-LZtA%>j(GmnfvsON6m%v-0z!! z;NoCViHMtuwk&`iUe#AvzO8V5adIeO3n3Qae6%+)AAb9ti-5^Rh`jeiM9iY8d}dYhX+JHK?=btMnD{3T7zIQDPberb@mT)^a1Lv<*yM=Lq7`cE-ZeYBu(+$AIs4j~Q|ak5Sl?7j!(so%W7ognA95RmW7l2(^3mg`zWp#-Hc8q! z)U)wEH-JE$c&{-l7(^;kC8|UWK}3?H1QJI7wYMBxMqFUL3NS7zb6bPBp>P;bk^dM6 zf@*_u**X4875ZDvO0ams_SjKz@aEUGTAWc47&|HeWfKVJ4GUD$eDjlc-|_HsFZ}4@ zYTgn?5;S`6y&4j)OyZ#%Y)(t-cfa*RC-kc~-LNa`r~QqVja<>>g^1O8z{24oc#B*O@i0*D$r|rzlW8+;eeLO||JU#S;H+iW>I$bujVOe~609YkfaZtJoEVL zr!td>u?XH7H@0hEvuX4<`d2M<5;>hX0|P)meVa^?_F zWMvRjC|lcCdgiqgX=1NGb~MTIRFs(@QQ`+bdmM_&<_`#CVoE6DCPB<_&Z`I+Qe~Tp zNk{FO&H07rM}t3m`sx4qfp2`{r59iBZ)~<&+Ulw@N4tzg_qz{#=QX1~X^CyIGAoNUUIoOWfj5{yFnKGTx#iL>YX1a%||h$kju)BqNv3vI%yz$sgdwcWW{dcdR*FJRg+NBfc2j`bM2lixxp=%ASC+~7{B0kF!jzE6z zOPEBg2Pp*yA(UWhqs?5i!KtVp*+2(X$A-{^-=lQ)VdCh!tOM@xs1QuukR?&F!3Jhh7{IWAvsNoJe&qe?g{4zp z{pNcP?)~L^?zwT_?%7t)inL9Vc(s8L#FC09BB%+jqEgEaK6fz`wwQs5x#6LFBeNsx zC_NAR^{1ZxXW#nfDb}ph4bA~35>@9iV-xk>u!S%uq$+LA?%B8aTle3)Yi+HYiFzd% ztAYr!B4RdVYGK`wl@b;QQ9(TvJyB?2|LZ1u_{Bt2KA;L~R9t(sasLh19)9}imHuF0 zZ5~ebN<@UxIu%hz;r+jyKcKA8`R1FCWj zzikBno$q|7+v|0A&246x8D0O5mtT10mFHhJh8J(S_Ba39U;o~pKLF-W{q;7x}gO-`yGY+W@ganchSMr2?-~Nb|xnSD>G!KR$lyr^iob zCQ+LR1r;!;R66VOU%vVJ8y04bb0rFhD?wM~376cW+mGf|p2$Ve;)>P(X|J3LFRAVe z;XG=7b0RA^2D`!+V(x*|^qR2I1RypnssvA@Z1~#ijz04ElNUCIJ_*iEgTfRfLVDCU{7AyOwqmQ4b(LWyhl`mN6E zN$WeOPX6=n|L|Y_@P`i^KYq&lO`B@cg>8?7m=Y1F4M1;^$hyW61*H_=rLnYDSU*%) z0C5boD7?i%LvC_UOwP-8YG4Bc8h>;Q9TNG}Lhf!nfl&%gh@fA!Fh&RUGxEg0*K^)Q~uGa)e&;lxm8sIxvhl*m8%>z}z1 z(plR~oQv3`K!{;1yr>rCA0yJI6!0-1TT{EH!c@wY&rz=tAD-+9hM6Tx=GxNfKKaZu z8$!7?0rwSSYJY7;4V2z=24cD2Cs!sFG1R&w zDK81@m8pspgj`MrEZ8!3&ka*EziU28?Q5^R(qFo?@92>W%NMMw&vK8EO1C{XGymB? z{3Ec!>vw+}%Kf8qaJ^)Oe^ed623%N=agJFTx=)+mG1vrL`=0J%QHY7^Q5H^mHGb@`5ULHGr>QDaSM~|F2 zyE4H(F=V+#viC1f4f45@T zEk1b*#le+6?9J0iOsfAy1Z6w_hZH&@;vpQW+bI+I5QQbW<(_+JSND;}9~0;I@7kSj z^k?ViaLQY;dc$ouE>imZYcKVeE}cDhmPMtXufBQ?-eIPz&VxPCK5+kS^Rs(huA`wPQ6VBLN}433OEYwbMf_Cl-wE1-{xNX80-pm!`l@!KJ6z)*27=Vy39{o|Yy5qEemv#J|nth+Mr zyEksciF5YZz%UcXFv3 zy?FYyFO0u{ivg`=djNfW2@24fUV4So*HVE-Rs|Su8{OsfJ_=kV=waYBr z?;BW*JqMTp5|MCb1U1fWEZS=}JT%5F-paO`@dw6=WG%jII@RF&PL{illyqkLyObGY zr9?sq9$Grbz78BKvRi-O($2=_cfa#&J+4>QR@aUmw$rD6`%7O!*>j^;&iux!zhvVI z9--I2a&2e-Eyv$@=@p}sVSVf6tIzg@Oax$*g&CQ8RSh993y}bI3_a1zSf|~8q_7N4 z4Iw6Zwbiw{;608Am9Dy-+(S75q!B9iw&?N3j=jNSZ^71^xzHZnhx4WPkO6ydK_UVq zlB_Vp6il!n^Mr%PR+0msVGnoa6P4t?kNR8xF6HCljNQS9RrtNQ=Hn$%L9HWr`Um z+a!sSb^p1ZK6H0+62LeYbg zDm(=PgIN=RRg8#**$~v5)qT{D4&=Z3iO=0S4lCm^*FGSSvna!oTiRd<%Ifaf zSa~uVrkv~mkQq4U<%&U2ulnJSJ@&{O=PrEl&9h#66M|PWmx`4OF*xFtg^5&s-agGT zE%d5hmqeyfzYE??o%4P1`|sh(PSFa=vy=vtG7;k(#^ji2IjC0b3hzCIpa>{zaA+Sd z_phD1uyXh?2$lH@S1xl|Xst_2OHQ3b@K8H&?B+?WZ@zMJdsJ<_`67DJJ8adcLciz8 z*;vVYiow7xpCZa}usQi<+Clgv(BI>3U)y&Kk>ocqOUPPQQo2aU>pr9SqZ5A#{)tZe?4<|2= zE_~zM#iiN&Q902m^X*F0F22Ku7IR&GlT)_?FIhIho3z4 zZ@&2L&28=VD;6Oa3qvzZ&0FLr(Tkv#Ffe^XdveblT^(+5QCXOC365#5H=7fkb|<325ONzW<&~tkgx@alc2!>$E?A? z0t*IK3z7=>C~*w2#^=mx2QV)T-d^Nyk6%rt6wQ1>=z5KS$ajb#FjWExG`c!0)(L{L z7!k3PV&J{{5C9b-Xb9$sh{43pK(&FWP)=b3A|^AdjY!liaDT)65Y#V3tC~hTA%s}! zmAIHN1z`m-JDAjJ#0=6(P1zyFSDY|3R*_RRgZ919A-O`xdq$v` zAiyqIrKzqDF*!AF>F>r|n_9=WL?M69NE1$}97JXzGQRdZYY~FkXVQMDUGYQ=fVr@s z;vAe;3Wx5v#g~Nv((7Mdzb3shfC`8O4EoE#R}Q1ekrT)6d*q>i@DKjqesl83!;h6k zDZO&ATy9^vxc&Nw$Pp20Wf6FlPQgxQj-DxlQm$`4bxdork^5&C5E?XBm%R&dlRN%4 z=KXu;`!{Mc-dJA7^e?{WJ!3J)-==`13vWXtQrc8 zGE0#+frX%?q@gZ^TwRx;c2ZWMqOxa#npoA?G-(%_P5Rx7`803-{6a*FvY2M;3pF}r z1gKgxb&Z{HsfN+0Cq1g+UB52-qfjGwc0xw6@w|voa)vM>pf^}-L}sQYj5^c?nAE^f z6-CJ+W=7_T*tt>x4<#}+2oc8$ffPH%2$1W;D?_*rHCx^H)g2}OH-GJ~Jh8U4f6L3H zhOd>u1p^8-CsBKwK_*6Iq~N;8oV$FHPbMbCq~0HSwhd+)ypXJ-dg7+Fzxt66{Jr1$ zooYC)mV1F&g%oPR*n)wAYD^v>B4!6DnKUWFjSGUN)QDY<&dT11q42#>UGj6x&W3Uh z1vhu!+y1o-1~De{feZ0A0i$>HrkeT0k)vnNUAVTf0Y^`~`yEUB)?R$|~u&@${KzzV?mDa4dmIt*R%Zo$cqo{#7Y@wpEk2a%IH_Z*`!;l`OKobEvL=`lBD| zjdluO$9gT1dXaI#$)JlX0OS&0Jnr>>|K(SoJAcVbFR(-`xuroqphNzWA14UJxoG6q72Mdr`of5_z8$|&i=3r(_UX2}rSYaeR0~<*b z2QpKF3W``MlZYd{J9cgXf};ZJ3t#ni)(?7r+tTuiVN}(%S`hX@nT%sQ8i>uN+Z%QEhpB3QN_dKeP>Cc;tqk_zsJs(}04v6$$3#*z}j%FTUVTwLe>PaTU zg@AhMh~z~=qDgvbU=cYEY$?42W~RhokO@;T^3}~p4!g4d%4@Gr41r^w6Bz(WY2YEb z6(dMJHAo}lmiuI8bOX9VoBl!Iyh{j& zaOC=Kb8}rcw;uW6`-=YZ(|`D9WxtqQxKu3nilzR}m909|MUu4K3|UQ?1i2BEQ3o!a z>p&563?!xAAh8glYR;vP#H5sp0ql0P-lQ(&{V$#I+qkygx*f3Kcw5HAO!29VvuXq7 zIYWj3SO^Saoh%EVK=s!@{SgKK`t#3hkknk(N+j&PcTy;Wj4gmjnZ*!PYhs4M2V-F) zg|HgODhy;Hwjcttc||0QSdxmMAQ-7?>>n3M3!8}d%XQf2$L~II;&V?udH-Gap1Zd7 zU;h98DU?Ujn^?{4iOdHRip5Og(QWXcL^5O7@2#vY{Pu0w4V<;r_96rj0tGT+D~O0= z17t$!Scap0lbxUb;D>+qQ=i(|_RpTV^h>|MfRwvufJTtL#ae#7T33VKxm5gjf8}Q% z9t@T@hy5|&Jvjz>q12^Ig)J4|d<)ZM(-!~r1#`{^(AaG>iGTod7NBNgIxrxNKKsr` zyy`#v<8NH?*d_|r7@5|!#!~M#O(>u!sNae&;m%c=hKgen4 zOw8>4HWjq*=+TuUht8ilAGRkGf9=r0Lq##D!|<7BzI}P~3XGgX>B^f9u8BPIlqY=p z^PfGxe&zC|D-YcN(5X{zn&W#OeROMm^UABIDAc;Mts27ETQCCkN8=+Q{Pf2@QtoW` zGyotq1(6V#VVV&G1^^-yvnQDevg69%edU$!p1)d&Q!XPA5Tqc$^pJ%gd-T!cjyMD| z0|}wgL8vK3Lmj9r?^ffp!~WjtYD^24{CNkJH2Y@i;1;bjpHV#bi6!7eu5)Zt+h)NQ z>VOod@oKqxP@5ebed(HNsx_`Q^ZUp@a{t|{>`tA%SQ$vfds77g0TT#CmJ|@o0wER{ zRKQ>$I15CnIZX637)&h~bL=cga3Trqg3(ZRsE-pi-n+y)1sn#@#tBmoL2QDj~O5QD8#_Px6$-Uc_{<4NVY zXK)}68Y>)75JL#WrmWCFM$BTd2tEs#V`C4qAYdU@4IDcnn8ba@QRosOC`bM$J`jVr zP?f4a?1zu6<6e#LHhjMzH4v8vN+cAHOC!QO=fX!O5zUf_6rP&5a?EkLi=>o1SS3bn&Wk39a^ zWAA#$JHGkNZ!1GYL?3u(5PJIg=eKtzR@=t4YxmxH=jDw{Z+`3P%j+8~rN4R&0|!s$ z!Tp|9elV#HhwxJ${cyRnT?8Ma!K{=Mn;I980niYLC}nViNB#cqzw+v{XD;}nR4HP1 zV6>vibWQ8eJ@Uwj(s2l>8hq{?Ksn#7p~74OWWmw$g12q>PhWbb+wh*!C9N9f8TZ0Z zW*iKuS9O*Lv)7)=sa`ztoRHrPk|gzxhz$*iiAh&U?zrW)L0P`~`fFR`30%rHB?8DL z-w}g|1x~04sAIg1z&W1ZJTDUoftVc;GmB;brXMq23SNwz~4zL-#GS zojLicA8iwYz_AIG)YM6lnnN--VKTCU;bIz)F_Dl)F6<=c9*ejb5@af3>$J2-)Zhby zWK#FWJBKm;?4$Sn*MH?_ZZ%!r-YKhEm{g4=bE3?hiBb|`Lr0w%?gYtaYZs(NvDlP; zp;YefEHEgRm&l485}t};{~yGVG&we>^)1Jb3(?E3ovc;K6@kDIh7gp&IqM`9rVW(w zk^piuT40LD!z{Wx+u{xu9es`4C4yAB!JO~~v zE?vH~y|wL%a(g&bnBbB3sFT&To1D4PnJYIPIXvFpSbyV9^ov9H-E-^l6W6X?m0oGC z?75N+I-V?7)lm&U`LPd|J6mPs?+FiUDFY_WwUL2ijCwjefWV}dL@TI&J22G%~X}w~0#~ru7e(vmFhd zLoCWjeu6OEN7XxSKK#-9?mpHl*DqZfjwS&v#*FI#12Z`on=z3a(VUY*2ZK!GFWe^ zcmF#c+<)}Yg-e(BukG*k`{z!dUSHqv8Y=Hy(UXV%>fb2K-p~H*fAH*gUl^@#f96Mj zWP5x2;>lBd^w>Kde&pMK^kte%f@3!5xP}iLtUn$fR{g1uez+WNmmwfwH4@%9sq@)O zoq}Zm2@}hx?ES&3Z#;YULXa{@nI%}9a9s=ak3Rg+iN2HIVLtA+Od^;Q0K6Aw=LQ8p zx1i?y&v$`arV|#rfT$bT={oRm;>hyK zz4zQT+1gsac**;!M2-Vj8sfRI+pt1CC3xb3^IytZCf zzU&1vN3jzvo5ki5fKj5EZi(!-i0zy4?aW+filkz>jO5NpW;?P08h0`?D~wsp6hw|B z)V|cwO}6>-KlI5@-*)@jXe?D_L5ZLO)*z-mR7+YNDvEnf9J&AQJEZp8m#^4p*h3&+ zm4$>l6_w!FZXwo{CMI$qGGc&11;|A$i6JZwsS1KYA*#;Xz}HLl&dpSR{N7vti=Y0n zAG-HMf8*LxHKE!=;han8qnPf?>3L&W0)pSarwP4D3V6v?TP- zyY7DI#I3S3+Pb#GP?7@70uE;Cg9aZV>FLf)vC2|G60CN_q!1FoL_;wdEroD%z4OTO z;79Md<8OWBsh@iEk;e}1KM>3f#}0MW6TH_5)rcg+P?#QETD|MeyPtpK&5gPSV%3^d z4Ysj-sNugIe0mcUSr`fEo;LI6Jgc(Bzh#bIkt}vK>V7P-VuhKB6s#aM@%4T`{Pf+& z|Mn+8wldr)CSwx7RMp3_YmucBlZ8-lINB@Ud)sXvy7%6DR+o5tDAkAn5Q_+r5VH?H zTG6p14s)uY%Vr=)>_9?rJ_Hj7fR#*ztQNn0&~LqSU-_T?@JIjZQ}4ac5ntOFc+V5h z%w!tDRz?NI7UIlpr<4(b5I7smv-3P3Yu#No=Os|f@Ep#rk+>+FE1yQMhayeB zv1vcp9S07+^Pc;v_04PR>qdma87u_PY8qfL2ZkCUW*QO2o)L+gM;Dnyt!BzRpZ507 zJ&t9^eVn+)ASPY z_m19j?Bd1qJ3E_RDpzH_b%a^w%<4M^s#kqrr4m|vhht}6Od=(s(?&iZsUjF7Y zyxiB)6)Q{EE?!(&S*h3d4KJLc`smRUw{2X#R9{`MDnq5tInq?mM?{DX0n>b$fk0jC zZK7NSrJk`J^B`scOA~X?J^u_yK(D6m{P)ql+g;Du-Y+xS{bE*2*Z-v15*?TQZrfw# z^G4t;xW!vV95n#Dcnw3$+XKjg!Z8ZwNp-j=*TfFreapiqj(_FUxi5bGtKU9#X2fOX zc&S%vuN>8$g7+bS$tz+@2MbEX1?tiQdo4+z^%C3d2ln58%kfV>@WZBPyY1b z@u-~CqOsz&F|)8UUl{|z1LVZH;0Mq()?@qn_dou~#~yv;={HaR{&&Coozv&9Zf)-< z)rFg|nL_}15pS_yk1$gJ5qTqxm`kVT>cG{cv>@KEI=%bm{Xg=~Cx7t4dybP1hMUE> zj>t@no&1DTgR`lgx?K;=>{R5!?hEL4>O`|#D{R;0n9ir;6u@p)3LuhP6VS(CdlVA| z7tYsx#@fE6gP;8H?a#gNYk&4jsF}Fk0*w6b0)@^s({*DJ8I)Cxv*M9%Y{_^JbruzN+>&+b{bATTdMJ+lRk-=0G3{)*u z!6g7ZS}0nhD9K)XvOYB%QeMN(1j7$1z#)fAqw;6163P?!kD1Fdc$_hWY*`q*usdHKTC zuYLE`Z@hN$jh&sXQMJt!3ie=7QNhT_nIV9nebp652YYh+!2?g+bKAS_x$};L`}dd9 zuj``rJlqJu7Z6hqD6x4BP9mZ)RW%M8gi;Q;)>})^Puy|z@_Qct#XtGxh2hBeUCoeK z*fu_pl5uP|-)0m7063T8pJh5-Uca0FCr#SSXSu0iXQCt3X{+J? zeD&E^Pd$6)^r_32E>(UA8RcR z;*1&?l8TxT$7&levS5Uo$^sUs-nvcP&%NWpPuzLO?>_g^?>zVHo5OV?H-b#q0vuCe zCYqQk=k<)YPBDCDj^M47WjQ@qGjv2k)>F}zDYSekV5%M6?t7mXeW$T} z00+yh(^X+2t~B-{RS32qyHyt^A`qFv0VrA_hIZ>vE9(l)Lu>8f}`~q9WQU+g9 z2-hxNUB7f~v^_>WT3%gsJ3HHd_2>VFfq&t@|9`zW-g)5OyI=b5v-QRX5B5{9a6**@ z>pK<2FjT>u8Z&$EE#?EaC9$N5WORtY%q$igi@QriilSg@SLA$8LV_?YCbD{^hGzpL_H4$xD|nY_6|w?~FpI4Z?1zEN)ucfBeYddv7^< z{LtF%2lw4vy5-5F9FJtU!#f^99RSr-IwGS*Br+XpA5zK!bqqL|2zq{aT;wMnd-(Fk z=-)i|+7%eZqmi&UDT3sC9K_KlQ$*zNPqPg4!JF8M{T1dIzQt9sZc zDyqqG*8lxq{tFK;_gCwhLtstFDyV@KOwhoh8k|yaL=K`J4hpVH)zW%UTQxrB+?^+G zdiRMVLw^63*~RVc*Dqdr;nMk2S1(_>wz;)4-X43;%sf=VU@$new0!5$+aI|7_(Lad zx_xc+5b4rnyH{;sXf9TrASe-sg4on*tL(14;Cn`a>|z^+GR2&$?DJ7YR^`gCvvrvCEM(!TxYFJ1id?|*q^ zc`zv5+WwWQRup{u&Bq>p?6K#bfAKrtd`1Wab>p4zXMg(btLs-lD9iF2U;Fx{*Uwq6 z?+&j%_UPlUKKC4rwy?QbmP-@!p`H{Xb!z{}9Ae{YCTeNM9t;SqxGY0WVfwx&l%l1U$JC?B4!-|%ZBp|d0FTBX;9jB<|85! zD1-_lO$Byh+GGu=W0R)n63wkqNl1kd zII5Nejy?nS>$F2=7bn(jtGH_j95#Xta^I9Cl9>yzMp;K!84n~ zXHT7e{_NRP7ccJ^dG9s*tcb*dHeTqs;?W@yj~WUfG*8D_8BOz$vA=nPT4V(o43zWq zk}hxRUS8h6 z->obSUVHK7Lx&Id4jlSRKl9U9uU@WrZr&}3l)!sB(h#f1_c?^8<`dasmHe# zy}$78$G3d=((^As29v^xh=&%gu&OpjOfUve2hSjJY{56!3?R3XwAetHp-FWBEGHN@ zs%roV0Ll0%(>B>DiiD(nK!PTqbMUou#FL3=Sn5d*c`0BGSZ=b!|-@o$Z-~RGY$9F&a=*g2OCuc8@g)yEms2D6y zMl}(7h4}F>@{VSXx-R?13EQ%#ph}8@!D?n=y5vf$yel~{1t_s2bFf;?8~L(yWMEAd zM#<3jmYKzCeIKeY?0M|TUKdE341j7J2puurTF;1=1J~1%=E6=@n83Uz)!IcJqQ0g5-&Xol58W5sy$!BGavihB~ zFcNYj=164L->eaGMvH-rL4u^(fk3H7i4lcZeO}GfR8*CGwM_b;qo4omk9_jpJJv?q zG9F7%5L@k0v_%qWFW;tiiK@*xAdvYCMr@0#H*V|L2o#zgl*wR5Mr=OG2+CpIoMALi z7+psV15+fIT5vYx*U1o=u~RMLBz10U*h0wATcuvo46d|*2*8gc3(967A6sFdnvfrt zEKc0A#6WlJp^7k5YKqdw&$*F;vEf+rfZ8~+Vl5NP1&*3?6jNo1Q=+dmXadsdup;vy z4<@PqaiitfXVi?;tV#oRa3bFOtDzp9J1rUp{lU;zW>ysCC`^XKZ9mewo(%Ty4+PhD zy+Lv5%0*%hDhCc7dDl}P{?o62Z^C zezNEnH?8f{;gFeE53Q|l=!s)T-+cM?{>mz-9$(vJ)kg7e+p9Lai{>lc|Z7ph#q^?E&o>;;V7!V))Jq)OsCYWVkj?7Cx9I{U2qE-kasbt1si z3V7Q+EmPf%S2bux_!eRzt@%mwmH~hO{hHhC zI)F5XYzU^QSr(9b5(G05DMb5P-Q{|J@%`@#BERPWSyUuS zj{4Trd^Wx^3#oiV>)0@hJ*e_0IrYbwC`9dDqXEhQ6WAzPDd?92f}$E8b$su><^S@t zpMLzv!F|K6qOP=3FoW4>p`|##g5Ba)*$Sg8l`JQw|3wG~y$Y{YQ^F1SzNAvrktZA}9C>5cPnz#~?)t;CQ(H_;pD+gz0kOn?X zFoSYDza4UR>uMfk{qgsHCJxRlrrj%mO>E>j{Zn-MA)&3xvwo<;{PSiZ2ca*)D zEtO@TorMWOz!tscS$|m^jhTUXHy?g~2=02vRL0hfIX_)7II-GPp*-pJP3POGqk~hK z`TRyro+)hWE~i9K&UD7-^cm4y2<;_vz(4u$VPqa~abFUrJn*E7noKpZix7{F$*5UG zMx4KC;DVKk-*T_Q3}Z z4*FYL5-I?25#bYz$ZXGcMg*gT&dLK0X-rM;*VwBkR^R|sRTwc%(oDI*sKmfJViAp0 zEi3xG;sp~CPLo(eaeHCU-r7Moth>C`Bl5(k2<9HKEofQ@Y@sG13TA4`-pn{O7FcH2 z)@Ib_3scH1Oy;Ofa}R51rL)B&^fo2LwdlN@)R0e;X3i7ku}jFuCd0{y3>rGV6BngA z1yj;Mj+h-wEVV)i%0fzH=1E)#UYT84jE5DRY_E?lUR?Je7TkK<@uDwZ`|6i{HCC*U&CSs6-1|LXXK$C5=Z=|kjM!)wZ&arkdeMZFi40jMT7yUQ6N@ufHm%>Y-Ufp3k+(|(47j^h;I&x zU}T00S``vjP~GA5vk%?#w?6riha3&ATwbny89>Iy64VHiRQrNrH=+5bYGU%~)f!AG zbeHULAbPL@!$?2{NMd&aGb0OZ+&Au5)e+Ul*Ovd2PkrR?{N!gJJG8X2v*qd`#oo3Q ziNb`Z=6Sw7C~{Hh&e|zxuKjgPh+-yYVWe7@L@@-?S#G0Tio{rqT2B`;-&;g1+$J~e zNi#P>c#K$yr}c5qJx9BV_TE{GC^iP8xcMzIPMq06iBXU;0vG~~EisZK+2+^<5j7U! zjK`mH7>4Eq+RWgwORw^1N+zsI=ViH>O7fy-1VqjtBaSAt8q^rXA_m03aSgAet{`Tz z;GJ{AR1OA3uPg>jAR3gtx*Cs$!w{^h!?kNyPM&;y{p#h-&CL+PxTk?uiOJ=Mvr@!SZ#B!-aq=VOZ)YA- zLk5mwqBh6TGn+q{zo|I_8rVl{BsB zLQ}I&OoFHhA7_iFI|j{OY-9~R(h1B67BV)E+9{%3GHChT-y^-rJvYW?lX*A~KLQWfQrJ9O*umtKDPyU)M4y4-v4vHPF@&Np9t)o z!hxelFI>7>ESL4j(b2HIFt)+CwPjL3o$rr`He`0SJK|IVdre|mAd zmco<6WHPYvZN2iR-t~l3wFQN9gxF$F_aMh+BQ2!qw{+}Aj&JEwlxaPBt~iy=CB&k} z9Lq3tciMF>x7Rrf>lDpjdCy&D*3}U^=Njh%joHd>9<4hkOwd@BfD-68vQcYRjoUk= zEoX>MB*0=lh2~<+RtFMSPyrv5$&oV+M6OUO#SbjJ|JcDJpZmdwo_+Np5z3=fK ze8;`_^jWqq>SS1g)DsD*)+7!FRggDQHr^xfJB5eI#N3Gkr_gzvbY}>yfJ%x{3=e0m zi<^Af2B)l+(1}Pqah7Ii5i9(@9x3WR<*l8GJTD-?U6N*^kWLI*%~JL09G%v*DHO&c z7L-#BpE!n0&c>iHclT&cHM}bmFl7eU5Y>WbbM#U+1kOiyB>JQE3cfncJ=DPwF7tF zbKA)`FPuGh@z&dJyZf&DUwrzzJCn`*Yb&GCb`ZMbp1a-3+JW_R7j!aNI(*Oi`K|9h z{j!ygJVg3lCznpXZgs^l>zC}sMq?t1P2|QoP_MThq~ z{;xj&g0Oic)Qj7$HwF=NK?=ncelZ ze(b`~E%vL?%<^VDPBVx-@E%ful8>x&Qx*WCY!*^sPSS*OO{%oTdIsI=#5lnm&i}@C zY1hmJ$HTaJI{XMx#v%9kB0re_6l#o1q9RB z6c@t5d`ZTtfk{0f(O8z!Kx9E7fuO-0aHPsj@`M3w$btnW>d1nI1@@Lv+CB|Y_@>< z5PjLH%zi@eNZXq;nKl_>ZVfCd{S9hG%MFp)bt$mpGS45(+S%0h3}56(X|YL(J}o8( zC8%i(d^BcJdO`w;8W1d#;>;?vrRQ{k z85uTb4z}ul>ztER1UuKp4zZbvQE==D6E!ErY5>OLvFnM};moTii$PDAgVw>j<^I4z z0M#P$z`YNiee+VGWl^jiIdtUw+N)ceqocRnd13P!wAk3$aWWXtzE!cM`yYPjzB}&w z|NO!)1m&Z596$f!%Mi0z?q4Y=NH`_iNFjxqnFhctSHPg8Pj(@X$@(T(fGO03n4m$OD%*H&zG5?FaWAFJ&;=$p){bRuYyn11Y0!tYcn~%~^;Yy4joQPtjLi z=EWyZeZsq5rZsg5(fW+vM#o~KX{T_b5=q2@MF;Q9-QR8`W0$;}8@^cMHd%nM>J~-m z$bXujf$DVrE6ZhWvyWjcl zS6+Jg&CBCauRkfc=Ug3(Sn4{ku#cHZP7r*WYE{ZH3o``aphg5|AmX|XAR}h3l(g#k zx`g$uKE7}D!;ie@BM&|Jo}1TJG%OFdX}m2#i6s^(Gd2*h#@4iE@X@t}4iB7}VZR>!<~y4E`zjK>Us63lxq%?U}FzQ z2$q||GczL~0>eAt;Auiv1_z@63uGFrsTORaTWj^syY~P*JP#3z(QWNq(nvJpOV57af5DRYF@kn%_X&EPza9&XbW1I8B#o*W;0yUaN z`JJ(8^ka0g+wuvToYq8YY-(UJXmIe(CNA&?5A+`Y==;vRmo@#G)A_WBF!>ld{) z$F(Uj05*dvp$g1m8lQ-T5waJ;c>$p6Yf~Z-5>rzHhsnTKM@zcz(7q4ccjr?N+;`W( zoA#5+c!MS*Hf0NDYPE7)C&%4@D<~LAlm%_;m>Zt_hAc90#b)-^JVCZ7rUuWG{d?1# zHCI(z$e?!7e)ia3yFJFtpR_0%af7olU7)j#=Tpb@%oLTyBTKWNo4L%`wRGi$k7G%G zUFZ@Az2&EF^n#g+ZbJKI+$ zlhMt`@7x{@z4!M%eE-g5`@(CZ{_;=MJg2$KhHmpW#OX(Ft)9X08RGJun)Fk;id!S z{ZGB)!%sZ;%*FG6^7@%)-#mG7xU=c&P?R;qqzJ^Ca!a7G<_#Gen1xlD3~CP0x)Sq! z!oFVb=Hmw*zUAnX_ulp3k+s|VZndgx>#{R%0ghoIq*g`3FhbCn8jZwRlIzkaWY5|H zx?OloTeO<>kiFa^3n_Uw<7^QX&3(IwJg_^{#4f*_xmS*reh z#GiuU+MJ%c8>}2%Zq|m8_YB>UcF<*0c%d7#qOy*KHj+SK5mPOli)y!HG3K%qZ*WB^ zu>R6ud3FEgtJhYR_a8d6yt%V^S$Ejc%D&ZL71*RG#zb|kx7~Ti2j2JIFMjDum#%SH@wsD~3vKK;N5!Rpz zW)_wR$+852Z2YR!J_ZP+MiTK*L<}onfO;esrw6+fwo@N_nObTkM~w;;X{W)=WFlc!&2y}5JcWa`mwomm=zm)t(xU6Dgg#(9biMu9vFF{Ro7vH7UR?9-_J8%L`Z zH!8yzH@q45A~>4j)Yfw3>xUd{&T&iaWn(CKSFzNfCz@*^7G2;dX=2GzRt}&njg&do ztp`&~{2^s%q29EyzLnl>`IpEA>i!sChy zJdnVpQ44UGtrzk((KQLgIA51^*YoF`JirYyphe^{YwS+lWx+99YhreIvyhqVKa1rm z9I0LqEut$8Kvok|Kj3Lt&O&{E{*DV{1C2J*^wn<9X*mV}qFnaIZa~hpDU)67+ElZn zSUX6{-lVQWFb(it1BWnC>QP;xx4cvhx7F7U&}ujuaaE5_+;Y>Xs)O2KY5%cfYh}Op z!Ydc*NqzRs)4%fOsjbnlSn7kzrPUQj?0T;M_~TD}?KN;c6zxVrB zE?y!oY9G8-0NBZ7G>)Ys&5R(-Imto|(khsFwUkmc${48{8bAaw1h7J2P_^X4px99| z0!WD3;^WO+*mObKw*~dtWf$!tx2JrU3shaA78{U5zBv>HGgC`#(TP|rXwX1J9E%Eo z*j&Q#8B?szMgWBwpUoZ(!lnoI)?7Vk+%>7|DgjYDjz#t*>#-J5yt7l(!a(ElEu8Jy zy&5ATkvw8ipxIwpt0YEOuQdV#ikJ`ur3B848k6-=_k)=?DtkvuzU`h{pSocRX#!0rGHw%z2)!dNKwvX|Ki!Ll6Z z;LKQz4WSTLvJlhRqt_eU>6+by0gNrS#<@YN?L0{5S2Jk=3$8ZfZAiWsZt~Oaw8dt53T%?mDVW{f@D z5u9T3yW}XOi1ePvt|<xCTU~4Temk zlh+VZ2EH)VC?ElFy@Hg<5F2_Bz@$c85UH7pafcCPNH2ig)~8*pm?e=>(R=wryK6b8 z*T}*%(elUCpl%_R=QGn7h$yCV1d@u}w%XOouUF%px<0T}99~{IRQ7t~8tMrKu>ccu z+0Gys?^QWq5q<0Q4$8K|jS7$Hd7F;d;y z{yc(kDs|v39t&YV>Ao!(VX zo_znezx&G9zxhq8>z7}A$&E&%i64LFsVBeq#V-b5T-jK^=aGkYhFfP|dga8v+b)`6 zIPzS8J)l(d*n3lAaUld_BN#MygRxw?l9eI=F4gdpw3xBt9V8<;1*GVp0jc5sZ8=Hn zrGfMUfS?qQhD^i&m7lu$9 z&Z+&|Gv|KgD__5`GaS`^nTt<7diUS{@COe`Ro3Rp9s+~|$e_)=(1e1~lzq0`sEA+) ztWy$;GeUfNL-5oyPJc3YawiGM+6Ft8IxeI?rgi>3_CR~3#hk9e08O(HwvIU;*Shf% zq;^yiH*|VxrQCu7Fc;Ycv&CL{mK?`|fK=s0iK1)!iDli?d@Pnnqse&|O&vA%xow|@PPFdpA_^G$c&b<5Ri z{I#e5488ts_uu1;m4tuuH-Bp~nJB`=YZo85@1YmJ^9*Wx@w+b-WudMF*jE+QBvyAO z(tM;m085z*-I*kt5uVrg(oAFEG8fqsr`*a|y52Up-n|!oylb98lZS~68Hxxp^%E&h zP3m9y#69L?|UcPhKu^0CPtT=8*~-=PIQ_$SLl=* zRjKwgy}ysgb*=dC&(ScN=pJp-WYSYAIF;*aRY+AzRXn>$`Cw+T*mx7XOViaVt!+MF zI+{-Dov2UF32lj>JKog#<@-Df5oI_V3AjP-zsEIakL#mWuH%5ZQM+Us+`ad&uA_;o z&Y$p>&74N7pW0 z;(jUOH0XE|zWm2u5y9cZhtFQP!ptwd@M>8)R}@w4zx=0Pf9>pj!26&7&a;>p^~&Ydr6)i5eu$Y>&cQjTO@fcPVA8aL*_&2co^ErK zv8NxiIp!B~+C|g({CY*0XYaDt$=vjfntzt~Mf1h|C9@Z4H43}ZZa|m4H8vzmH$IV2 z=U8~D8)V|MbTW5M>#o^bnvi%s!*yn+`;&$YIFuLkjkI5bWNJPPcB9O4cysDIi zs)kUjdRVAOTa)pS;+fU(+Len}uU*{Q+C(7`_pNU}`=@{UjAy_VJLCG*S59qj?+jL! z%YOgz<;#4_9k>3>&;7-TuVHj>|G|6jzNfCk>VZS%{p#xK)6YB$Cm>U(54G3Y6(WwJ z<-!J*8;T1p5S`Zezuwx?&Mf3SeazS0-1C?lfLZ&x-rW;ZfVOw^8l?D>S6@BPQuTYa zz&R=eY^Fv30*YV!o!=j#C$48cG}Wd6ASX9MiKeC{JBXAL!1)GIA4K1a`hT4imAA{# z-|{e`uEeRnX1lAW{laEp0bw+XdJGRFwX0>&?e$xYY&v|hi%PLOS6ZN{e9uP&AYM1F z!&6Gb4I+teY_#3EHKhdAPEV}YAKTQG`Gz2pbN5|5!s+zGxo>y*NoRv!@6f#^$Be4F zc$^j@eP$n&F1!%BDyc=i!k{4{0P0}GF+pBMNbAb=N+WRL+|sgeK}1C%5aOP5j_Z0N zB3k>BibBewD9Fk3>b`?Vj*1kcamCK5DnoeUYgdXBcjn~j|LK4HpNBi++6>gYbo$c9 zl?|%`SXf-)`lvNFD(glzV&BV|W2UtFLQY|3^K-G=ncYSo-KhHR$J^D6ZZLk83(2;F zjKaX7CL1Y~3s9*hD&k?4^CN^E;-_Cb^}^Y6JD}RYAXA^3@!s6KD9_x5xu=;NHP+~NxhZN6#p3pmuYx>3o@NVS# z76~}9Hfk?OGGjFCcCT!)%+AjqsZs!@>u=BHJhxg%Yq?VAwdbR}6`j4y6DT|Drxk*? zjb2^!!>H8S{AHUO70GC554F_GY}S#mN3IevOciF#602J?nV}Y;vMfDF%E7*aH?6L% z#ox%Fci`}mqbF`#T3IcaC{+8G%7^d1{ou+zGw;RFE9xqs3f06920gxfZS$Y~@~@00 z6D5c^UkBIg_f`htBy4Z81|lXiKdC|$9G9^jm@7DygHF(wUDs*XMsx_fDGs|`Y<3ko zi0QfLZZ`_bv{>Wl?133|U{BqdI^WJCqkUBugoFKFuLD$2gaVF8&>vj$;S1mR#&FPA z7qcj4F#l;gQM+w0yH~rT{qvCl{|l)0^Rv3!F*P|eb2YO$ufN0h)Kz!+c@uL5H5r1} zx!QH{{+(kFHRVPw@>aB*u7KQy$mc@ne9I*voI9Ao7D1%|`z~Qp&D{&P( zg57`r{U814$5)nD$K%R-ed1m3{PCaoymJoTPpXMXaqy-?ZfS)`s1CfoxicJ%*%d6Z zvbOJu_rH6vTz=*=Kh!G>VhRrm=Etb3R8C7xRYM4=9JHolgG2<*WsEw;&|8R`+z{Vs zMebtH(lqz)ewZZz6G=pW?p|VHA;IkGsq7vCAq@{4Ki2mZg_=RZS1zL3#EnAOk@8Po zJ@w+XO<(qX%%tupWw8JnfElf7p2%+K`1fHVTvwyNp;?WBup9er3rKKw<`7M(w;h~pA~cU; zN?(@I4=<3=TyxkX{xu;iJ1hE6nlry}Kx9?TqQ~5+?9%p;4z{`Zn`sejr^g z_g0;LnTeSxL1%g_H^5w|c_GA_CF+gMnbBl2F*B|Gsn=iI+}w0h8XP>{c8ug&1*X&xE|C!aR0*}`S`~MtAn+*wM(bY%Dzv2c4@F$jp_pj*WUMm z_k8Dt?_9flwVG5LSFiffkOn;pCNSc{Dwvv0s^vNyjMuwxTGGT&ed$Yw{CUL1RQS5P0Uet!}az? z{@mam=wOlG=5fP6-c_1*MR+W3pLkOjA{B#di+mL6BAwZ2@qZL8#)J#vX*oeO0}Hq5 zK9RF1wQ0~xmlTO|ZF*WTwD0LKUK~@L>es$tuG5@M&$%Wvm2@TB^tupc3AB6g+%tAY z+THSVXGOD}J2#+3a<}VkM%$Xv+UVIF_n(3w($p0eklgIgGy0V<^VEANf5{KBP?c$z zM(5AO$|y6*q{^U89Vbed<5UY8+hzaPzw`VhC1a_A`4C*Mbp2ixyqbCMxhNq{gY0aL z*EhE|cXoQczSpp|wRQQ*wHi_u{h)gF%9Zav^Yqr%#&{gWm>+%o_K$z+gVoOF#?@vhJ(%*mMf?M}$=M+2Y*jep+=50^qo298nF=;^(_1?KHq@}d+SpSuF-ytM=}EJD zx{cLG6ez2k=&f$)J-6K1*I?#J%}@lCP6irCM6SSn<+al%H`ld4Q0uWLig9E$idJW= ze`W?HjrgB5hS0Q{Q{Bb?&}?;^Zlkt$)NA_NxE7k#H%;0p&;Hx$ckQcH-mmwH;Y1h% zGWB^^BTkx8Li)zgOVp-gehE7KcV?xmv+mYTh!DH9W!0hrobD17YVd#u0wECT)M#6$ z6K7fmlGdEv+U=h#V40ek4lUsZjs;HeJ;JBfB86ttUm8ukNqdYrMkEADRfc9!Gz^y5 zya7z{ed>N1_5Ji}`MP$H@W}Jh3KeUgC@VE7>nxU|nmOhzp!i9X0nts2j{6~}Xjm>F z|F*>K6n>;BNz!~fC8D|oF`F&f{F%PWLt41>T?^8>9Ix4r7U8XIijoTWFdXl=fuJbJ z6~Qaa;6w&XU@;;HqsKld!Kn}xPQf)4;K`^W*w)U*sP?Fc>iY1lM|d*!zx%ttch}wb z^n1Neef*R6-~TWm_dM|6p|wMpgflO``og!K36n`RnFIxkq%0tbu>kJwy2bOki-Ej1 z;&+$u+5!PPAA-+U=;%A0f8&zOV^ZG&h3sj%3b~%)K+q@ezWYE~7QrYaT)9HoNnN_S zC@yRG7k~7J!+rZg!C=xQD*zKjfSv124H-@4EfIhaXrwyf&^YDtYbj z%85H~8T1GU%S*j`?!I$nWd%a2_14Z7fB83m|MHt>g)y28fBDyb{mS~*(!oP7z543L z);2Mt^6)h~=X!-XE|-=g7B$8I(o;oiWdXYD215J`ptYQPTDtjmJ{ zW+hTfWdqbqHSgQFb7D8BMO7l+u9v^7uoIFd7?nZO*{!$`;UmctPBgvGIk~E5etz9~%XHhQ|6Lu>=IC~Yf2Wbm04t}`3Z{b}H*nCj=t^4l zA}I*O{KW;=@1EaHUAUnU%wZ_H?*4Smxha~p;`8A5cuo*8lq}Gqq9fKIDZ7~*d(T+= z+6~t{9gn#lAD;mt&q(MS`4uqZ5N1`7QV8a2H5QV>dlf=a6e<)zM^79-b?S8K3LTHN zp4@TUt?zsHqoo8)EL7nKKKbz{-~GtWWZM;V;LzF+{J^L7uP!5~I2jbB9Qc9H_4@sd zYga8OJ6HAxJHxT-mHSp#>&cD=6?W=t^C}Qud#mdHq&lqh;~)CqV6;_+8X%YwyX-YH zpwpfrJw`WuvE%ugUKh-%Rjs>rUZI;_(bjX0UE*DH?K&eJcIMl<&brQ^rfzDpcc&&O z06mtOcFu;Z@lu_2OfEF8IY_ZNWA-#sljvuEi+3%jqiTZ!c7?v z*-ti~c{D%T#e>)D3e;p(#u!k95?qfmF|Ts&0GdE$zjXs@-*ihJ=1=D%=~J`1D7yAm zbG4_q*p2-}cl-GbVshq=`G>S*PVMSxKu0xf#x(=SYFU4~FLZ|E?lNt&oPAERwpLDa zTEo6t3QYgBF#zOhV?-HFnXScqV`Ac+a`1&0UO7Ff#=!$r40={L4>PmP?d|b!$FYlz zlMwv5^KV|ce6{v~>Zy}2zj^W`yIx&|@p$Ljwe|ID8-7xUI)KTIHQavtZA;EwI(uO!r*(~)X!B)g6~S9Ib)AOVFw*PnXe-amW# zrK=n3!4wc>AmrA4W&g6-x36vff4}@kKmW0h9S)(aC&U)amjHM^iTdw$Cbn-iLj%|_FE zM`uSkGf2I`CIr_#V|N;xIl*$BoW<9l_8HDxx#7!ZhPK8|`!reI;F)S2HV_*=OfiC$$)!dg`fjm(E?;xN`RF*|mN9H?Ca%)qnXb zre46C>l?vqe`!TTtg2%Lh>5Rj4XTK(H*;C0_9>{%C|RJe?B!3~B{u*q8)_157U_C4 z6|qU)?M0JLytiGJoI>59UT4`GHQWr09_)^O`SAzuS*2h)Q3X@a6fq*Cs_M!O?)~aF zp8JE7Z*CTYAUzFmpqzU+6+4PZ7`CQz-GT|sA4yvi&>zA+EgRzaj8cn1PwWL~}8oX8s#B>$=Ac zp7xfG8;OXti#Rv2m?r7&dQC4yZtxd(cM}fTCE0ti)zop{?GeshNEmxD6bR-`8K-m{hO4^2$~|{O!9_u}no^8+iwHR( zvRLL5W{Ybebu?D$cxrY%X~8#`@da6@PhO*2QR8aNTa@w%vozH34sC16Ejp}g3Up7& zoj>P)qUIcF;7|>eKKaDMzxv8|uU^~Pv0e=nQbZt=5DYvfIw!sV+i(2Vp`ZMTCvIBX z??YLQAq~3Qj1klB7we@&$W?=9$vsxfG3=VAPqTQ(?kZ3wO5&_OQc8nLPtVB~-%Tj) zO77jyyQc1plcbxHn)pr=>B)OIu0U+mLS(5mH7&e*TQ6vVR@hGLLc_>9=t+v;PVPXQ z<8P(`sn^YD+07tWG;e#sw>szNCd{>SFoZ>9Xrw zuJb057XVmT==9T^wA3BIUJkQeSU={ByzBbAat1)UVZ+d@>Ac-}A|FBs#$YH_q3R8m zDyw_FIc{a-uY1WA7h~G|xF3joIZ|7Ju+Bj&R@kdvSL< zWsQ3YGc*$fORJH`hmD z;;B|0-g3+Fr#|?BwSxynqlx$R$3OAm2OfENb#*nED(U@?Jap5sBR;{)jaT*!>UzRu z?`l1ff=0`pwPr7ojrTnt&Mn6}VE062cL%9(%DkVumn8RY3r8nq(qk z*5uEf!!kG6>I6ig?7*dz_+u2|!K6UQRnlTj^aPAe`o0+yi1gadGMiDY@KI@v_6i}o zH1ZzERIagjN=y}2CS|g;Sv!MfvkVwV^hhYNnJA=D4;d^$3T7xXp#-#$;ZHvD;74!2 zc^}n5hsp}_8YV(cgv^ok+>Z30h5R3X1b6&vQ5Pz0VDQ_4hjoRhU_#=$-R0#P6|Oez{naU{Dl2%XDVFt8;oP~#^| zoZ7mKI}o%X4N8w}ks=7jVl1gcWqySCXg1?Y;_JPH^YV4V<6Y2wSF@~4=GgKy zLb!dgnJjoEcVBx2%hgrW`ch{eOj1GsSRiiU_)R+ZosoH+54$eGg~BFv0G%>@(|aY> z^z*n7@x2(F!KDR=ik%UR0taPH1aA^i6h=^DBPQd_uVDmbm~&)Gq+pG;EgHR+K-Bbt z?#Rr@?7BXQhO>7EpZ&TTo0}34>4U)uZq*tP6fIs-j=TrKYp4|_{j%Thn-O^v5fxxE z+CF(|a_*vxCN!xHvb3^#?BLPS){Y-d3NGud;6UiX{IFCRURnRZ2j8=Qe~&}0-o!or z)NnF>^^MmC%PZdd(QvZ8zE$s3emI7RGt#<(DZD3kq`-LGuft8=e(ED1UK(wcJ{X83 zH_t*>h0mb-UF_U8#yBtGr^50{V{HU%*XCl19z;%s>0?h7<|blH1ZIi+A$u)!*5*yN zWt1kCfj99hNfLsiG*gOhc1k@A4MZ_thRB#$1qM@3M8xKVdni^`_kH8_lcQlRR2mrK zlqirfJD9?`ig@hx#g|@iWqI?7+eCVXx@1Ps*ejk4ieQooF`5}O2QwmK5mgUTqDbD& zQyG{+foR(EmJo048zak)JDVxma;YFI5 zQF^J-%9*Ki_Y^G1{`KaBlry(L$+e_G%xJ#R$=<~z(F{Fmr$~Y+6QlWgvsDw^vm2NrV%SupSZgIVIx_WHAjP zVdSV?oUBSk*k^YIVzNuiO(WIB#q0#iWjo3U=Nj9#ByX+?>$e`RCim6u-J+T2*~_p4C9^7_j!zW72l zQC0Y>8|N;bf9;J|)~{Z*;Y3_d{KN0|8V1WtM~~kOPzbucv2As*e(#-6z3=SVGpH+a zAcBsSCZ5gu6Mvu%KmODQS03dBxbGG}WGrB$} zV*-Oyu5nDHRt=_+gD31@+~v82aU@DbRCx^4AfS!Gk+C9f5z0z4MHq<2w6azaC6LCf z&*&8sq7V+O?f2wfeC^Z~pe_n9!?ee&8j07aaNcCS4&OR;_JuQNZ@KTT)jpNAI*>uk z7z$#?f#cm(Q)ECjz!XfBt0Wl!(ilEWJJKlHM2X)=ZG|N{TR_GPit$yZyy0g}w>{Wa zbQ*NSr!D(xTB*jh=xBu$M`($M#(fS>r!`le>wl8ygp$V5*-?v+lt*kzw7@g3(|!XZ zwb-Mw<))p7BH|{o%c$i+u@nzYXZ({AqcuATmTlcVUX)KxT#q}$XuO!M?3sPFKX3Rq z?L9rsHtM*`8e=8%;l$HpZaQ+P&l>5_(o5U6tU1Fpn{6|`-1q|nrOng4l3F0a~{pbJY7r*%1+nYN+c(C}YLgB6rH+y9tK7@KA#-wCbVAXP59SZpTQ%~)y$F82h zrG`nZ0GNZ)??HjowzV@eiwb>5{!@FAtYbCUUP{s6DH_dlE+8`tM6uj*T)bFcn-fjM z%c_BtV)gCR(6!NCa-Xl1>XKsJrfLXCB?-8-*a$|(rVO&6m4UwKYeO_ zJ-EV1#4*%EEHHs;hA||#-VXTe`uaDXd#3XKz>#C+V7X%F)rdg|97kx%i6DuIV1XS| z)0VkSB9Hq@8vC{DWTa-x#@wL1@w(p1u4X#x%}6gXG~k&jr8qM2hW8;yk}WoZ&VnJS zYKvO|DiWbZKF6SI|80aj4MRC~r=p#cXW)=oQw_Quxd-*f++?|aX?zV-CC{mw*-(ygrRU){g&@Bh93dsTT%{PK~*!Te~X zG?|PqjA*-}x-R;CAP|%HV;?&DV`d`bMnF=N$2%41AS}?n;#W;*r85(e&$cIV z_s?lPBD$4fiH{-fMFg!yz+?zY0Za~RAWKFUQ`VuI&caQNPN10*(M2|0x= znY)PhRy7tR4+KNaSYW0e40Ec7>+%2kM?ZVvU;e9aUESKEVh1nHY7?c?NXBF^x+w(lCcqSOuaUXQNuQ^FM|VPUKoRfipIFi zqKXY(nHg$^Ue-y>f;|W7PHQFT8x__1FAx z3}LM*_n!B?@408c`{-kjpFMpJK|lRNKXCrynJ<0u3m8wFNT|H9t4BlBMnS9)Rwbf3 zcn|Y15>Af(CV!{VLlG@S+ii}3DUHAqdrGt1DswqNmwCigBp~{bW85_V7yv}zy$6em zt3V*9(bvM1X?HLi1<#2BcK}_I8F?q0h)5)Mb&B+isv{6;NSR0vvn{9I*Pq zgl_4%|J4uw;D7x~zx>L0?0bE6r8jbp#$m+KIl$(Pt-mx;Ka_IA*C)UF&ENjkb5EQ& z_Ji+y?BSaauQ8j2e(5X(E=bK_CNu~+nJ^D1H0(HT)D}d`E##nCf_B=?V$+GXyg|#y zIwado`dE-=pX*JCPL#D5v_aPPN zFfwlNpkb;Oe8V4g@jlJ7)b8rgFn(>wDGF$tI&)?QQjk7>Xx(qAlLEQ0=J_tcwy94V z?BQ-W#f9lLUHwekD8#IP!EqBDI@X2_TyI!m0LzX)-sXs7S7+s9){I=yy3fhG8Q~ zrnR4G|2JDup99f698KzA0uCWKqQdn;kGv@uLK>dx)nEA+|9mtWp$f(U?4%T5)o}q$$+@x~ z?-)}ccnwMjW(?y+`a_d^h*8azK5 zzx?W{fAjjO!~NpEV@Kb2#~sHH9a`@9q!be7KxC+_&N?$Ac&J6Umr>fT({+R*@8d=p z=}u_~HBt1om?8iOFaW;A?zT!_LI>@#?u+svSrI59BBtazbwLyzPQf`9qpNI2%unZ@ zjR4Seax;d8`F2=dKr7y8x*!cV6l)#QT|}0e+ct0)&XDC(!_G95W9TrfJ+S0u^RN`_ z!b&DlOY0aj8*>q=hEb-aL}S2p1!v1oqlplv2VhNe_9h8&ej9cc*K8_#tGNpsB;~D4 zHT#Vk`V_Ho8fxP==w2<_+vt24uL;DEERk^PN#0nBy#k#jW@aJB%nW0Jb505&W@Cy) z%<8(XgAXBa0Ff}P4An}{ZZ2R|#p9i_V0blEDjcac4n`s@NKKzRW{AK2lX;=c*fmj{ zNyM45D+>jC)q@8QZj5%!!IWItqr!P#^;Y{1HXLpTvvOtiz^#W5?!W1~Prp2wOdMq8 zz^V^4v_dy4V(u5cQSD(0B6riFn=f6t{LOECW3aTev%M)`c;lM5=z9(97?m<92nu*4 z*p!)7L8HGur7_Gs|IDMzdV!{&bz$-}I;tz?q(zLUGy=0R(xdu0Dr6+?YcId@g>QW8 z%xH9VRM*6n*ASA2(Q^JyiUKFIl$tJs(PN>GSu+}+K#Y42 z{YE36;(*zh$oxJc?aT-gKhWt~0;--Hh&?4MiY>Z0Lf*sDNDpSDD;RfIv(YHEHA(^i z$&i@lE8_kxio&mWjqhp)iXV$^{6F5PV93 z?#wC!VC6(j5u;2=tzo!8P*ntG5Ln4s8Q})a&-va>>clV z_o*`{AA0!ygRA?`ojUi;uRrZ4I|pw*wtewRRgGABa9|5sRSX4bMPX~QLp{eavTE8i zFEk4&YL!@W=kbz{I@e|F?uRJPRc%@l&Uh*s`b`%BU?I{NC;*@k0tH{J=z)D-{Py$z z&wul!Yej!V5~ycJl|Th%CL&QMYSfTm%pwux(C{W9QWi54V(%-C*|nyo#!w5X)Ra*y z4@oGNKzf?q~nOZ++p*7cO60 z>Tg&eX76ieHWN=&N>QWoP!Xn}!NJ%_c&M&YXEAb2MrJWB+!PF$1|WfJk{dLYkc%FP zhJ2%nn!V}>ne24N3{Q-JIr9!Rqd0$nkMhQ6bWQ7J%InoQmo;GC zb?w49ys4(hWC+>AqaAga^7C43UcF#*x`Ty7?`4|Kvrvj5a0k~n3|Xs1ek`Py>r9jm6tAj<>k|V;}i0e58Zb_L-3wZ za{#DF4XGmD5slulxR%kvPtw|13O<=Cu|)ROM*N{!*o4#z43Y}=iiB}BVT`OA?C(v2 zH&s!1Quum!^{N$AIEVfK#$W%vKNKm-K{=XKT7^IPoj>HIJ}H+gD?joRKlS4C-+A-g z`P$5pa~CdOTwYl{f9}H0`u4ZK@y&NU@%Rg`zG(G`#1%#Fz=1;-H#UR!2yl=XW&sck zqj6<}!sZO*#G1Jqz)DtLBA4lQ4ZCeEbkw3o9yZJ0?hQ=^GA&nWVInK!)YkBqzVM~f zO7&ph5TrqvT~Jfe?kxbs;H_E=HM6lqQ@RNf7>t>B`$Ht=9*81%G)_D?^O2@YOtM!R{z96>PuJgS(+2nqXYV?)vM_DgQ2)gyv_=#V5L3sbl3dh?2q>Ic6=K2!KNj>{B5SGqxl= zlF??-d}N03)Z`e)`KN5cbSElX`9#wecH;PKW|DuEBnsz<2oVLON6W4)X?<&rt0(IX zNmMWeGq6#o+@xbRvzSFi?KZbynk1J&!3Y2e2_W#0agY%(tcJhzRvm-a(KHlCoU!Q*c%?X=LDnHrz#sB#iKwbe9<<6v%;$xq@=brn{zJ98@ zx>1xaz;Enqt5=BI+1lQ@wn0@zst8QATCEi7UO;a=`N$&=-?MMIS51&=0dexGleOIv zGZ9Yl`Z-5e*M&OcRctS_u$Er3Nh6&pWuUU}FMav7Q@``fiyP%?a7#g1!65`oSU`>m zN=y!5kV9@UMI=lD<)-jhjAE$;QwZpL^!x zAOCQ#u8X>6cv59z@{{Gde&XIcj|}=}-#ooB8P`I-U?0?ojD)BR!I&Kpn;L`}$$1l5 zq5-DC5G!cpBBR*}7R_H`LeNI>h_DEbaK{$4n-PQtRw9$!ASPXoiA*g)EkP!%ASEcs z$S9<%!5}H86BN<)F-tlkYm72#D#p8!apuNIW>k|EDI{bhDMK=~!(>ahgn|(Pd7K_N zU64|?Mo!m^f3mgzQocwYb1bsKWW+|4yIytMqfkouijwz=o1zzCF|k z%+3PXECep$-+#x=cO2M9^+bY#O8^Gnh@wQZ(RrK$!ZRYF>#>;UY5A|z_K;c>kfA*@ zQim#oT<>Zz_*dWl&NXsTY+0{VFcqbih4!R(U_bYJS_KLa|7$-}iQSYOdE)fSCGsBe0 zbHNktU8v;gOPgPR{oKS%>k1lR!KikH?W5`zKmPnX9{4YQ><2$^u0>`d;63dZ3dujrPw%zs2n7>dzi9px=gC;$m)KwyKYMfz^XY4!T~!g)3JFciwZ? z{{8zQ1%P7E_rWH<9;^;l4y-zz)UUntvIx~4fB5A;A))7gEC>lyc$ zy_6&Krp81LTx$@bpvk}vYFw+Cd6x_rCIbS9)D&bE45}%z7(h|OiWyir?JL^8N=gJR z$Rju-VzNNSgOT;V`s@o&e&~I{fT&~%*c7F(g%uk=d1UR;pZNSAz4Xeje)H*Pw}+RV z?Wj@7-ZW?|1w_^7xlJLEEILU5gK^TiEn^ib7!fpn8_AiIzyuSkDG^)B7D%aZ zDR4%0^1hi0D7s`hhG0xJ7I=u3SByGjWKj&2Hp>@sr6+|+G*2|jRI#kR$1nq#axTb` z0WBJ>uB1_$GK?&%nn24*!JrI;Hy{-uiFTn9adZJqc^3^DeMwz%Yyu(Z+Uyb1oW7>y z(NRW-Us0A=l$`aJ&bQDwf14V%k#txSs609TlTRhVWQm%ojo(_blp>s>sYMzYYi#ds zk^%T21fpzwlNmycSCJ}6p-NCoA#=-9L`I&$8a-#-$!+4y5X7WFHJ=o&*49sKzb1)^+=JuA8x5Z#) zOl*KO42j6tGb=F*OE3e7ofAi@>&vC=TQ01gM2lMyNLo~Sw3QqCxD^c3@^b2!l7hRJ zt4hz?3{pV?f|-RtXU?7X8c<*od@v*EU@#b($D|_CH=lprQ*gb~tX~`Md*Hqgee9#Z z@=yQO&Xt}1+UigK%ujyd3t#&BwQF3+r+(-MpMU=O*I#~RxV7neefRu#zH0$adi|x| zWHdf_=n%W&+}ZP76s45aWCSNRsln_D@xBhG#8f*ucjXG4kT9Zu1hH~)gvl@RfaW(y zo}t;syj?Y9;pgo{&3q8e!VH;akn{eY1BVXydShqXR!Cv$I0PdYdu1~VAwt>M3<#=P zhBl&=VwL72)=gDO#BzKyA;is^&>R4;v9JLo9L&VI;C(?<3M%p&)lTwUYhZTj1I0WV zM+32e>VB<$^r8D7y6g5|{noet-E%LU8IFb{nXsv24+_K(HZZ}2eD;v4MbJ`=x@(#` zMhd_sRfR&r;87<=2W^om(I=jSoQavLGBc^huOcQ?QWTd+ErCE{kU17YBfw%ztl43$ zIw_RgO_ZW}mI^OXXp)qpSf#`$VTKswh@3E%)=7!j8bDNJ#+1X?oa(T4R4Wa}S@2M_ zL6W|bE*{|_3(;sz$aqbVLBxMV3~?K2zqOrnaH(eqDQQA-#9Y}Owp9& z&^rDCQTEuhZsat{4iw8>nHgzJagBphf>=~s*a&b&6lJN!VG}H{5u1`m-$pxTX&6>g zQDa)1nVFb15LiOQ=EvWwWi(V48*SoYd;pC&UNhn7R!yOJGJ-{anJFomkSd#zDk+&6 ziJ8KzN=e2RQ3xcYM5Lsuny4w=t{{Ld7+XpMiq#`c$Y4RCaKSr}t0w(Py{wamjvjjO z_$|D>;RrBeVOWUXvTPh#h9cWcyODVrkeHP=PM(Ft)_&5$XP7`{M1i=1Y>pV4u~A*$zqaz1 zKKy}S_@h5QKT_=vDld**UDqY4Xb2kAhg9Y%#T6snrV>#?7D8a=q;WGLmWV-$BC@fn zGRLw!Fvs4T3XR7EauaxCVgdyv8?P*t6uc0EnVRR4EO?Dx(OP2#o?bP)&lP|Bz3=&{ z2OfRqi-2oeA(=7#5Sb^uGsDKrD*Z#Z#OzpVYU z@hqYJ5WzGx2uPX2%{75t@~5Zy#SGVoGM6otz)0?G(iCDaZ3DtUR>1)}GaHkU21)^Tvs7ge@xwL^v-NmnP;V~ps07O9uURUGN?llO$C_{4Xp}>C z=s4&Aa4KY&^msL8A|ME2g^mP5U|**dG-8WDe}m=x5;kp1{}X^Yjwm% z?4Ty7rJnTa@$r87%>a7>^hVMvT`*s-a|Fq&$FykU7jKtWh?0IT)p~Q@ffA z(}yWM^F-vuH%||J0N8s^g#<$(F6JvQA3M5qU}dzuwY{~4zB_#EL{RrffAAGuAB*v1 zYxLDW{EEmjCZ;z2wSWDu#P!rMls1PW=a%*tz5eFr=Jv+ba;g8N-}~a$#wKwvD_(u| zbyg1+l3*SdKwLm-A{S_K;p)}Mcq9c8$*g1GSOZzklhgrGbe*|-?2Y+pnt@dr0bY>p z4@go$ID}OWpLo|JO9u}8{4uMiQ59d-)mM5O0>LQF)C z*boYFWE$P#P3B8hjkU4>r@G?9<2N7gGimU` zVnkJ_ONmu|0|6QoQS8@fUbTGukZPj|};w2L&A_l8f z$u#=yQMx+#<&}Njdu$J(R=~aUk$3;e=J?{Zv;C$1I8*>nf{BzGs-lp!LkBNky%va$ z-F%x1gD|RwU-{#&0B$_?XHUK0`m;hZqF$P!ozwuf6<`EJsEiO4I4tyo_uui6 z2ksnst1|YqEPDcqxxSJzPPIv8%3hHvr7T8@F&)jP6uV2Gd(${H?}}RK`I^En|&bBEc&vW?>|>1xbgS+k6c zc^>gAWs1f@Wz%9Se^Adq+7t*tDtU%uiuci1@)+o(d}La2>x?A*)eE*w96v?OAk#9~Y- zk)cCx+1KCYB~#6^vFPY-lBu6GidR&zI>_vqgK5GlMF`6rOKnQ@+#*y^Lk+W#?a)9? zLQ}LYIz0?H7aD4Lhb22M`c;xZ(&Qr&S1R#|{cG<&c(9^J3qj$>T#X1yF^k<21Ujm( z%rU>jK!%_R$N??WC_DE7Xj>T+Nt>dkXi1}tU^*q8 za%uAJzi8%wkP$(MJh3%tXMWPuCd-#?EVd*C7&)=0%dlcmDaDkEKtvRL$4@wYWRjTD z!uGJLZP(h3GsFio5I6bZnGWOQ`|e{-U%&aVEi7o{nqf`-MkA0v1}U=h*JfK*H4ijh zvLXjXq2^lMW!{n18XV1XX%byLwJsV-oVzm_v6R}WSwBCw3p(&{-k9+hmaP-*2-ogp zXfuo{CqYaZu|P?O@bG!w(w*CoZdx_YfROeJ%3i##-W~UN%-bOYLiqnv_TSNxT-SXt zy2CkD)#>&PlV>myz#wOU06|1%Ld;1d6|F#p&yp<3vL#uzf+hQXl4XC(?^#~Jet1zRf8O_*EgN29#^>L`Hjw@G&$1c- zsKcNVfI|dD8krLu+Uz@Rh#=;7Z3Hsb-G%BSp&B!xQ7AQfUTQQqs{)QCsDa9AtVIgI zpc+i=3{kr!>MkP@108MHxQip~MF>bhu(s#hRxg##yz&_0k8@I=2W@Ds8;0J7OtG;F zYa>g6W}$Fu_WE2XpgBU^-l#3RuXk5%U0A%}Is%@|kAt zXZiN#e`h+W{!(2q{B=UBa}(A2B{Rjpt+e&x{iUxY3lJO2x2eLNeIl>_t8)@7-jN!# zPb0Tx5unE3YS@gRjcIIu`PtyMB+#~vb8bH4)ns*|XR4O*2nH zbgsf%Km#yA047%P@m!O%sIW#TZ%+ zVjVCe07YW7)aiZt>1S4nH8lcih=G{U1cizex*atvOu!Vp#|$A!G_eBgL)qzN-}PPJ z`6nO!crlrRy0^Ultp|@C`gi~4KY$8$%<<#LP0Go+GiUzWZ~XtfcOv@i^Un>3!?m^b zmrlO)!VAw40x^s8%q$R8l?;1_!Jtpl&pr9{^sQNDlb`{EN18>i+APv6lpo*L*|Be- z;j<(03PNFu17K5pRafC+%`Oum zcC|GdAZ|lK^%V(Zk&VC$83qjsF=N?scFy7{%>zcnvY;v`Hw;V=`6~cOuof`0Z9E%l z6Ps>v3UU6qvB+8hAv%-y(Sp~OLt(4zP|`-g#1Q;8!u>`_1ORAZ=JoW}D3sYC=NEOW zXqf$u7kxfZyEmF+QU{=*U6&@C92?5CwWr_c)owpTmB<5J^9rcy*Yi0pO7ua~C1~z( znkA}L)LbcV{C*7&iqb1~!^XK~X)iaMs~GNYM$*K}YFHC&d+PaM-TZ4?2BwaoM#@Qu zg;UK3YBPzpUbb;5>NLP4WrN+zlijFdKWE4BjMk!Imj|+;@D+Q-go*9qzTHz_`Kt6~vAkpDrrT~k zabf+5#~#-_my92N_)8bo*C=Hb{foc+_+?M-^Pz%-A+Nsp#V;BbIDhJ_q5=~TudYuP z7Z;?gz)w)9lXXf_ab6T4vr<%|0LUJtOzgAc$y1x_*Jf#`rly$~+!6-Vd5a!#4(K`` z{a0n>gMSSK z5}#sgq}F$9?4}?@1|s(k7_5Q%p@y==Kq1wUT!aW?s!auKjvYo^Lv3FR)~534{s25raow<-VHMiJ$4C-;T?r@!+K>!34 ziM%OI{&}{lJZqydZwk!qlP#8hm`VAVwXE-=)IL8*6HEnAtuARUv=60o+I{vUT3ebF zK~o~Sl|CBhcsH?G^!Y!}_AN${cL{-{EqE6YRY9mpEwo||A0uY4iT2+=x({<=6&r=M3qs^SEtVXNP^xw%#^5%?B=8k~Rl83kJeP_pd8Cnh1%` z2>F|&NL${fB;EEK~%2YFTQ+6D+eOwWctzn`BAT!q(i?^t)E-ZC%Ma~z@WyO zB;#=7-AS5x2}!5Z>-Dc`g`c z?ZC8=ji61gc0S%VWw|1Xv{rzTF;;exoAVAYK2e(*{h!E-=Xw)sU z@+@px3*ww;h*X-<4WwCwwzkmYA%6|0NAXswj3IWL<(pTIBo_6Zf(H! zQD82H@k*)6nK){W;&y!F7KBede!$jJ+9u9Iv>fWQCRB2|o@D>wqaS-@G}VQH17c1@ zMM)G)K!DVM;qJm5{7f~r%yun++H*5;E@=^d(e(_WHv z3|K6RNxIPAw`bS;-}kP6|L_0(G@k%)vOKu*%Ht=Vdui>ZGbn@-LKTd)K`I9DDkb75 zO+NADW2f%9x0_{HRD?hc*jiL-!wP31p80v3Z3WmC_m4z!myYq*lI!n+pEU}KO~fgx zr3O#Ys`)kv3>b-aYRNsncd_X#Q7>4-`9%$KIxc2=&B#QERNIkp`%1$h9PJU6I%8Ff zEsnA<))zO|sq=D;Xv0{*S>xH~%F7$$h_`IEIm+3sJF+2V*qW7Z1P0Dd6nH?Mv&v^{ zIHP&39kBMsb5ZVgjijZ4Ab=2&PS9wJmMny%p^e)XAliZ$gNE;>?(Kt!q3v|0s;VsX zb&l)&!Z!cP-0xd|xx`Luwxxcy!7ekPLyfpYW`c~ECC>}39>O@t4VqCt4~5FvHgK#aaHLy za~cpN#&r52I5M1evn;XI=43n_qk;ZlP!)LqKNutiEg!x7TDIoc!2_pWJed%q0wkRD z(xXR@t}HBn;^bdSQ33*VGVdx(jFcXb1JGFH%(VKw|NXyy=#6j6%26%WMFEIsL=DY4 zH^75!VFZ9J$E-=seFMt_A{rOq6_r7(n!G5;2=mELjorL?PPQSmQ9{HB&}qh4hiq&2 zQns*)s@8$|7=jShxXPtkda;AfgB!wM6)xPyNkVX6h|E1+d1UfMq}n zj5bREk$@D1EC~bI)b`TBLPszgO(z89Ew|pXbLY;-zxMbGr!I)EI=y7E--9eWccv^? zS1%wM@5|jQ%b)%9!_PkQL}y@@ckk>4J6;=2*25qD{-4RX1V;c27=Q`6OQ8&q00M+c z0IF>8-yeSTb$7q!`d&8~ZvdhPBy`AvYD5|53Lg~b!8C7&U*5rKYW~tc`yrb5eM2X;c-_G$Q_@D2g_A+?+U=TUnW#oaVV( z>Dum}7>L-4iJa4OTQYk9gkrwVxtmIscIOKkDDIs?hK z>%R5H_43tnd1i^zEbVe>Xv%D3Nb?}CB$(>zU*7HjZTooZEp5>aVKxAw`bp+AaVz0L z6`N~rHT>I7K03<*v^U?Pdcs^7Ak3u69EfA9Mj_aus;IF#4M?;hX?BYiRMs5wdI69w zZQRW4zq!fRnmBT{>t^9)j<|C^E06r`%V#Fb!`vtk zp&@02j@i9u*O?3J-C=)oZCw-Y@7NiT5qVlpj^BLjs_U-*%fI-XyHIpETzlkl?$|Gj z)9C~dY`^!a`|p1EZ@*k^Y=GfT$1>gh+LNbGqoVkrs7cz{98H0#+v)T=nR*GOLr}w{ z!5dbQFGuPXK+-(D{ zv#&^4M+~ zUp07XKHL^F4Kxon*z)@;q8eJ?_!Xyc_OP!Y<*`nqT>5XbG2<&g*d>(|&QhA&9X^{6Vsmee*a~A?|Jpz`FQ%mOE0SM z&9AzX_1agdcUiY12wp>}O4f>@s>sXf)K?XVAOVp|aH;_U1m{5&t%*$~$3dp!obt)7 z*IbjhV0=LFN~#iwwoFaUJqYu$hh_!ZeAU)TZ&X4}85YAbGpqm77R?ruv)O9cNRauH zHlJ;4g~hB)5gP&b%D@ttJ2+8=5Z1SAd3v=0kkr{Qj0`q)!nD|WSXm?@!%2}>1b)!#rpA_KVT@tIkmsPFL75nQ05V7vf+`vn zJ^AAE`*!TSY-O1%kKh>ziLusltw@@guGkb5)r-2#0Hv+j^h@Z4c0w;+Fx>vpxfJcz z{WPm~L26(wtp!>q18VHKVzX^=UI1;k3$~gVGxY!%n!C5>x%~y^0-al~1^`%yf!*p+HnESjdPpYo|B=7(CE(i zr=~EWmejMQ`+BEz+f-co)hZ0cOFYzUY^{Ec9n@yYTNIr~D{Y^mizI$?>gOKf?^c$) zn3=$cTk^9qJ010Vako&t?znx& z)^-ExD?RCajYa!|6zTac+6;~P)#whu);RdO3Sj<-DQ+?I>$yePuBM3^swV&dH7F72 zqf*;Ogx~`qs){O26MKryfBW>`{@F_>E_Avf?IM!|37|-nWSI}*i&7a;0Q#Mclc!Id zJ<;iQhdXyvu97N<1Pvk0Qc!TEGX@EevM#YLHr7K~h7iCQ3E1fj7;e3L|B<6N-+c3A zef`@%^u8CKdHUqZv%r>(K6u}~M-Cr4fA+#;VRG8Y$nFI@U{7Px~A;Kkf#i*4Up<`Ze?S_1P8YcIV96m{E+> z`BZ3D@Dd^^`t0>Vi6Xux?1GsntJiuiSkZ0{g;1NoAT>)hDxzSdSOIL|I?cb-+*jwu zGRt&rd$a}~7h#2sEU2?d+hrLd#6C#TW4Jh`+c*vZS|d94(Suf1X!{hG{MlX`HdBO6 zYYw0pWEQZo#Agy@t3%x$%huFtJ*U>6m@^|Te$C8F+;*$xN9G-w29_Fc39ZvmtD@Q7 zXyg0s4I+h3gBm2MHO8FVQAap%8+JDC9zpzi6B`@c%t4lG_p$YynkA;V?Y5ZKTX)zt zjk|TW;^61pg6&rHCHB?acrNK%iwDH; z(hJW&_tevy>zhCZ39F(iCzIHIfsoKzVh-ZKiCT1Gd4fZL-4v+%{t2gR)w!rI84HitYH4cNXp8ltC0IJMr~HcxLJvbkj= z)>yRG25%}mkek7``zDG{y3`ox62rU14&U~emwL@t619<{xan~&v@&nC;hf#{bq(oy z4BO_R*4(l^OultI-{7Ki2YkB)H>U>X#(c?BcgeqQ9YC9k**@9ZLkJgrQFLB{Hv_xK znVny@nKIj|UH&e!+l&G(^`sa3if<$vnP$Ii`%`^bw#@xZ&ddcx5$F7!`B>1!mSy{| z#QIc$n;l5A2sCm{s4bNBK!$FG=hhD#D*RE%td%he`cdjh%*}Z#V zxHuV&GsBb7hMAtZ@bt!5(K?hcggP0>GL0kc83cf-n z=os%RlhJwfU){)m{ty4)C*S{$YkNI2a$RBcGDBFRv^~4$_ZMPKzjg5jYJ7zTDFg`f zNyb)vw9>fdo!uHGn7`zfEpbuNN{y|UzYev_uT47Z>q$jq1c;~$MAW($w$`OpGh9O? zR=6a#>{4FNtSdFmFjDjLFn1~4QY=caRqA4+I90d9rP{gDZC`95DD&1+gJVJ5dX>72 zMH(HmZGJAv^#rf{YMY8gaQZ8REWRQ1b8eJd5sl6D>-VUE$s*1%g#Z9zTO(Zyj`;@e zvnjgdA~qMlNIT4VWtSit`o@o=Fcy6nflQmn z!Z6!ZvycE_?5BgVbf95ZR+<%Ew$9yCve&pY|8hrjrHU;gshZbt`;MWK`;CmCnPhhVZy z5y_BL<#cmndFPIe_0_-uQe)AU&QGbzdi{mv-k{&T{)QXQoIYQcWj6#y9gjAO%_)R% z__BS+uekD0|LkMa3+u^nXjKuu_n!cB)*Dup?=SSyPBxn4#dJ!BjvUzAx9Ky_JU1Rs zfd$F}UC@aK0QQ0U-KBo_?8_%XI#7A2a?6I-#>>GS#p-9@^RDX_hMnoCUsl9z*au^e zHfUz2o=}`49sK$y`68+>Y+Y=;NZf4O#4~)o&bA2R{D&LKh=`HUURy%oxHD!6t{Sj7 zhcce~SdCb0x=+AdtC{9J#M6Rx$;_5#r}|G@x6Z86HrCy{=D!iGn@e7& zhRw}uzOT!8&NL%(XcuCBRB`g#ZeiOLz~9Lq*tQ3@S>kiIYcGEBUYZX;UwX!9`~PA? zPtIbY^bOzeHzt$ow2g zQ~iT&>Z|?6TnC6brV~-5fB@h?7%(Unh9``Vt>=IE@R$DNg%f8DSN%-VE}<>T9MUZ7 zr6gfGElf8HP!OPGDYNy}loxvY4;?tT|3E&Gho5|kt-bP^gKKBb6dNN(v$mEC%0eG0 z;Mz1GK_>~lUXL(6^TJEkSWSrGjtBZn{cEnh{`9%C$BylP@c#S0@aZq8E7L*ew%cwU zEOyUaIGt~f6JlW@i&`ll0&|#5r<)@bw2Y#Ptjrt;ra?Cxec~%$S>CmB+3sCfOfxhH z4K6ss!B8Vv7)G9B6rfrxO6+ACPibvoC?ZIVw-NxNfW)RH2nvjKs2LOxn{MTxphz@# z=9&eTCLI@}(iCZ;2|!y&f33n5k*Kb0gxVp%8n4ns^swa&uF)*@tr^?Go#-(j#mHa% zIDh~c02M%*BrU|tDoJePH@CAJG6+y$P>sBM0@Pw>w2eUr5G1ULgHVy`U$lU0OfdkTcm$;*5;&iw1PlmMLg8lahMTK z)chCF&h{)P%!&-LmIN^F4XI}jq)rjfmT191b_Zb`1%j9@%{afGp&w1x(gDj>QfJ(nh*IQ{ak zed==`Kk@uVHw#(UbK>LkrzwF~g-{g|askJkZcs5MMK6M?-RzFrZ$EzgsDOLw3tv$U zzMO2VZcfG}Cs`Q^g-|+i%CSjvVr(bvxXR~MG1#$~JI~pJZ(3YhB9pCNSTD=*3(vpk zJb2Ln`0R_%G2`l)Gu6fkm9}g;0?` z_W0u`&#qm6_0>WN0cp+DtDq2Rga82RX(d2b0K^ud2(^Pe+l&;2Y23J6b165e#U>nE zb1XOZvueW|QlF1`mX;v>QmcM^L8N;ABNYbDM%WhCVIywqb&Sr_tZ=u5fw%e?0p>0>!=uh6Rpyg1wft!r z$%yKGrBFL(P=6stT~fOS%EmKV4r@)x)Mr~ZtRYTSvS_1@eX){md7 z`*OAnk9EEpsik3$y>U>GD#X&*JndY_vPU*Y|KriG{?|`^<|}129S+K@CrRcM$XWtc zgrwIAAxIEV0W?{!pALqHt~k1OVO2xO78Vfkv9Emfe3&X`L zjvjpaYhOi&Ns>JJ^pnS~xN>yrLNS_@l}98>5+&HR@4%?2Ab23GLJ&~_3f7K-yfoVU zt4F?k_?oMih6@ITTxDZNJ|S!<6=swMs%_aSC}^bTliJ{hz#s^MC^a8c6X*A!5`Pc@ zRWQQGgMvh~9)N;|ctJ!o(Vo-zry0gU8;R18chzSY8UYQU)R?C=HH6N6K8^w-@w-MI z2a4}QTW}tq3+wplTv|*)V`mA8A3_BbsecquLi{}%vl0S`A{bE6AXpp5b6LiS`T>Lx zZ9B!d|0LD}1GJ!w+S#gqFz)yohKC?nTT!T>h$1Sr6(d13BEnQvL_+Pf)kjg9W1*lD zumS5M5D1_~6r47bDUHOeZNnK*R_dP?=+ydPQKdy?&`_U7mQAgDmp8IHij=uiAG>r# z_KT*GRwOoVJHIN$D0DNt##DehqXUi65eYF>YBEHP`v3$~R0Tm)12&V4pdf;(9;K$h zYkKu6ilSg3tov!|{JMZN1;Y(5U{nPvIG+o+BudyZ5DJ693&8;a|;k;Zg<5Qn!Qs`q}28nh{nN42r7zF zw={$LSb%D%LsiWgM2#Jg;=YZIifeXP6s)O7q~UCrNF@sp3@F7us?npUpNJX{zsUg9 zO1kFpA_audI3d~?rLpe`u_{FU6x+6eC?cs+KoT-wteA9aJFB+yrP26*Jo)5*`TWCw z@$%`DoKDlObox#x0C6`giuwTg+3)V5R$cNe;^7FbmGVAf=3EK=TDucY8m&Ikf}POMyjf9J98wEP|GQ9xqr8Z-RtG6d#}|mic?B}F54CR8hbSUINU>`T z0Alkjb@L71tT@aydPE4#9U;u`mUej1_*8Lv2_&}3Z3kQ<9~-}G9?PV24+Yz(3uM;e zt7&>g^XnLO6(RzPs=$1yIo3;qShrcORfcRwQsJyYP(uJd$1yZJ2->W$5@-M*kVs4t z8yzCX4%N{=irJ&sSD;qPQ7db!g)a`=OPvVUHi4%a0EEcYvmVuG8$03{hTF0wKopth zqGmIKISN(OgrVs*8_y;*+k0+P!7Qt%N@&{A22kCWfdEj6fbIYQ%r<}$QHYHt2qWP~ zKp<;9QKjUq$%EU>!_%whzwqLTPe1pK%X_~s=H8F-jEAPC6ZYLS^ zybAJk@vE~I5? zyo>urqa>T$1ptVVOiSX1NFnBCsdl(QR3T7o4-+J|j*{TwR%pAYQk~=EHi<`6 zOYr7|bE9A|a;T{S%@Z|i>>gJ)3a{lRw_UA4Rg;EKf5rmYG$Wlg)0p+qmA3nAv#qIq zNMoa%YaRA_km?&oBUHlJpiyuJ;71Ly>u;1?7y>Ck(@7P8h{#JFe{D75tnV?@+&DC+ zfk0x*#zyp_03wl5il%k|08x*qA|VP*(6*6m99SJ)nY*J`)t22}LrjLpZtk-=XEs~d zftq30)RKhSG~tcs*f7>>8f65>ARbC+_-aH!1Bjvo1R#~@z(^ZXXc8b%I7X{aAu!H& zjf;UFi->jWM`I#aceFTSCu20sDqF_i3XxXhZXvJVVxT~n|1Rr{zpvgBq$=vM8L zS=t-5Ul#;KMy#0#W1HVr2dF9r74c1+lABG(k(&;YRRc=wqg!`7h(L+?F_Qw%C-`d) zqFGvaw)%~*Rth8YC)`OO)Q-P6&)06S@7cM;U;`5K5 zI{Vz(g;l3xL7(=VwMl1Cg@6e%GZR(LsUbwvAb^tYT-kf*(DP3{&l=wHzPJ4C-+un| zv!^(-54`z43HaBa_^dD7V7a%lb9r&Nb#uJuhLJPZ1dn@7=femK$$6`O;Zq%ojfYS--jo z#@OZIjW=BPl`nkJZRX%SB`l&$C?zQ>8B^sIs#21eESqkw@o+G;)rJcfs_@LmKk=VG z_7?-yz01QRJ9gZBfJOD7WwKl#PLI3~{+UzHtKOvRhav(z-gNm#B$jE#QQB@f! zgrJQ%6Zf}>C`56q3`j&wKtv2^*v3R<+t)iP&JY5iFfkx7a~uOAs&M@vBC09@RHW%c z9%A=LK~zy!#i zkq~CmQ$(szIp;*g1rGp0C3YeM0IEsTYL|$Jkb#+_N`)rZM1Y18HpHj|$VfoQ%xq1Z z!WhKyD$yMg@zI(!b>Q37W`&4|cnRKX2%#!+=Um~c;5CGxs?NEfjX5&Mk{@qjk=miQ zJ*bH&K!^(g08tDxMv{0N(Og6Z01R?8oLL1CWoACt2sCNxCt6JqXUSn4Oue6pfSH-u zG<$%ER4ebj4=N%~1yBeR1xIQXT;$5SQ@R?de$Hjd(o=xXuH&pj3zEM z5wVGG97VSS37IX$PTH+v22%@NW<)f`Fe(ul#wN@a+UFIKI2+pTin=ABdXaWrT9G0s zv4K*(dxI9vg%G?1AyU-{6S2gINO86t0eDd#ujWIT6s2=6uiV;nd?BBl+Soj`v3Y8= zzM(n^eq__qTJ2_l)^>WRn6QC>Rq)0V0a7KQ3dwM>zqqivz8*rzvIHa$!_!YcS^A2s z1%PLteYVJ*m(U$9^m>^F=`Zy1rzb8iufG2H%8unn9{KV+-uX6T%~tzsN!3~^jY?-Q z4szDZGr9ZB3#WenxfALggwi0A`qbLWdt@V^LD5BD3mM2T29XAbxfREa&4yzn2-3hV z%*?7KC8D@vGzbwnS4J@;A`k=-BN|g7M0rYRw3eHuwML0ZL)$7SM3F>9ZA3*?Q`?Oj zh?$*pt!(w)$B2+Lf#i9));ggxYa-g#itR~mo|mI}UW?b-VZhd;5{)KPbHGH2D1^XB zt>YYGbW633hqMtM1!3m+bVT!RFG{FKE^VCw5~C6EQ8c!JvaR+kNkdJ8$W3&9OYd0# z;97jw6Al1~Bq9cFdr(Crwi;{04%lLL8ylI>sA>?0h!CP%8CnO5h*dR;5GDc;(HLsb z(DEur*ea1~EYR2J2Uy~0WXI@Hlx0!Gh=@`|cqk~a+?foVUA`Pb%HYR=j zd!r0uN{8ySXdUUNIZ<2hqS|&w#F~evwzWBWq7q=v;HZ0!G=;g1R)B`^vYyg9Y6^1-;Cix zYz>rl+qCXGbJL}x@r>Fd6ZdfI$CE;6Zc#6Lv#;utC8H1@I?No@SHUUT1mVt_NTJsz3eHKYr=-NwfypcDwzx^Bbu2 zg}?p$%E}UgNAd>j+PU+eJ^B2g*N?_ubmDR47yje4sE%E6^s8TexZlr?9y)yTi@As`}H9bED1xN^jg2{+P*4F@l0!!f_G3Y90J4icg7uI6f0)i((DGC5$6=rm0 zu0^n><5gKG1V;d&@B}L20X#uK2}qWj8pY7!>6lbeMVcr{^zSrUB2Xi05RpYBu57d9 z3;-dxW^)4wFmvU~Xg;V0@u6AWdiO|UFRLoDX{`x@S~5a(0Hf$c0_)HZ}^6iJjO7zhYZLg3g|NTY6UIMbNsqjsu6C7G}BX(Q=%??%K{ z(??~=jj)S5B$9yFJ61_Ro4v#`*T8ZXzHdZQtN&Wb+fHMApJMd}YK%9=P!(w#+-RB0 zn$Z(R=x}W@$ZV=-{y`iL%%{_~7$Q)6A&mfyZZQM|tKdkCfDJW zKW32HifB}3)ID*pMiZo2S&kDM$1k&uSEqRpQ6P|}&HkzH5+uZidb2!3(;gMuYDXfh z`@cf$-5vKs^noIMOLLr!qB`eH!akaJbu?2$2+XKH7-M31Kvl^LA*hDh1BosU84W^7 zT8SqjLq-L`azF^c%4E_c+7jRcDiDzahnC5hPMhRAJW8>RRJmxJRlgZ zV>KvPV;JfdupB~&0x^V8Tev}DywX=LhGN=78;#azdQ)up4IttXtT85tBbdc5n1q_1Hp?sC6M*368)qIFJ|0p`t8iswAW(3n}FjSed|agwMDCA2Dyh)_kM zb#CesKxL#DPZIzl6Pi{dI_n^|wYI47)n=M-B{w>`3O!(rD{$llyIZco8vx zGB+*U-USo@m_S4rDFlbnQESYUn1!xs)3gRW$3#fIKv4!sT(StF+2V8(k!)>%N<=fL^I=5YM&Uw{{NomZ-8LYe2PYTketOz(B=9*@H4u27^8&rtMTK3*I z+DefLBxr;Fg&-n=3h^Xj0|bwW3bu(CK_(^kA*dk$Kx;JwPOS!2K@9;^yS+}SDwQ|G zL3gka6c!eTC!c!`MU=I-V+Yw}GTqpM`AS*F;-2L_p|QiXqs;p&`Wy@J=PoVB5O1uP7|)7GuCE3 zkDgd<%0U8>O}gDHuDxP7T-Z3j5vuBr*W9st@1Bz{ohH>H&sW#iQ30GsA~viDd-m)u z#^Z=mRw4w{5QvQd=1vw=Q3MT>F+K>A0R_VenZ(EBi!s2aW+NjO0AvD+2u~#98gktr z34xHAg2ZlQqyT~_K#C}UgdA#wn+XvJ7yFzbDN;niDI$=G=tn`+$kKrn$-5Ov1&|n# ziH#zKw)muJCmV+p$BD@CGh_@jR{%m~P^!PuRDT(T*i{$7P>2Od ziG&E41zC~m@JOSB=BAFxrBHP$BoowP&!5SBh6=FnUPNED^5rbryf(BsQU0CY( z`;&YG2#1ay8IMMWXzD7%R@C2q_p6g$_uQ#dH{Np7Wd{yD@x)WbbUd1@7iA@45O{H= zw>VrNwV{%3mu|T3`Z6!7vgr1bTW-E32)d~;Qj~h`RJeh zxlE>%n9u$7r_h2TaR@ScY9XG)Frj2!Mf_ zR+{yL2+RdcB04;ZMQx1eh!qzR33U~assc4&l9;oppAcivp;kYlzJ4-nz+t2)0TI9u zD*$+jsOT8}5?~e)f{0d~Ooj`pk-tmSMR-6RbOjLarCC;`81auDC5Z@7 zWZB~QGyy^DEHMd6n_`)xsZg(g$bb-{b4;wNO1?Hbn3@*GgoFW6*#JPSAOHbX6)$RR z@ID%vRn&e=s(~22h%lqEf)G@NkeOq3gb-4iMSFx45LtX3w({VNC9;er9aS#zCD>F6 zpL_1PswhWm8<3c*Z@6(Y-#mZn%;^gk0P(Y*`t+j@KZ-$x{lty-AKZK4Y?bHvtM9pM zxU~5Dzwt*1aB%f=Kq=hc7#@xxN;svdF97{;$6^9&IQm zS_mz5`-}a}msY)qSy@0qqgbtv1R(%`MPimoi9`ZIS)vF^BpwOSi33A`#7F^2LjXY{ zVryJkMR5ii0JZL(Sf31J;1B|Uf?*YP5>y35i3pjaEQn%*jTMmy8ICblip|NR5CJ8g zwIhet&z+0aB2z>ziu!m3RTUqMF{pxA_dJepHf_iNR8<5eC`WHSN$Qj}G7?Cv>Qyz& zpct_tI!6(-<%KmZwql+2s)J1R7H5?ZwMD~r-Gq{<`OKV{Yl+OXNx{vZ6XdpdlB4Y` z4P%5!Y~$&-XjhrxSv540WfnDx`S{r64-rBv@uvnEu5o<7Lg72P6X@ES1cSq86p#%IT4YXJOnwKo<3A*Y6Fmxf>KZcafNe$s7O`eARt+TKrfy= z;iR05H=%L>=<~3iSKRFZ$c~-+@45fJ|M`1=*~v14=H(YpAfkA`xwiS`FMVY^9=kGs z>G>Ca`!|2<#HsTJv6@a!oZtM@kNnyBGpC&sQYqI*zQ~b&@L!5^5D+?c$KL++H{Sfe zKleyA$_)v#Hw?}>R}v91xGFEeGbi?%Tdy1pdY}5s&tTA7Z@;cL=za1}KPjUrBsR&i zsw}_QSI#QD z6*PqUf4!11NE$+jxa)wASkn3b5#Las97$x@glDr%c_ z&fbq&A%wuDrRA!{?v{(xW2jRLMC5%{+hReWN!3P4G?x&b!?4fmEJRQez=-XOBaaDy z7W^t(i;}dhMT%9R?RB6@br@DvV_6Z)vSNO(MeB zR@l^LUzFfA6s_Jr+!Pj|fqFrq0XmC_1`+Rp2)Ukz_*bu@HT72j@iEUF(Sre0MOuas zW_DEt0HA@GMbT!dC!UJMh&V_x=NvEJ_l_bjy2It(!eG~q<+at-_0d?o zNbm~2DsyYC_o3VA?O0w4U>pS7O|HD^*v9(C`O~Y$Fd2RB$)~i?5EKj@z2fpKue$2g zsZ&zqVqnj{{p0DRvpC$ld(YrDv2%uGOpB6Zi7I$7IvoJ7*-g@P&?69JDJ0Fj9?%3w0gY>^^o5wdBv zGZQr_tZ3WLkoys?z>x_l+RM=7h#Qs@t>fN+KwLNeRa9;+5+8?S&>7JX14c0OOdiK4 zorON4n8YY9TIsw+zs@?SA;v0!hR7yYfyee=z`e74eP!tNHg7_RNK*tDohRe z2Ld8d-31q7>H#p;GXX-)dqGIV#Dq|Hg$wa3H5E`yix9G*m`I|?5){{z5eY>ldaFT# zN+2ev9gp})FowA12%?&ii9SfA6eX#h#FEw5)orenx=KYN&^uY zV-RZs@<>n8>XG4av8jXsVvZj+#z=#%!B}UY5{IgaA~VcVqrXAKA(1f#Pzf;>62@H= zz3!&1Ud>s7!~h^65L4|GM>iLR2&o=ZZA%eq%^d-t>Lt=G)fEP85(5Be2oPfC5PU+H@@{Ppa1;d_LqkH_w5@D zhbn$yadG!$2a)!^X+8)hG{Qfx&jtPN(z4SHBiU6JWHd;a=v0RI8)SwGBcd z3zQ`)0St;rnNN$+gwvD^i}S!F63`%+$XVDBg2u95M#DBpP>B^=z6L{!gb+YHNQjUL z)PPLJG7&=ErU*sF2XCykNy63`Ympg|>tFye5>w+7Gywx8fSBH^JABOo9WiQ2HM)Z$ zTO`ITG%_1&5SfiZCSpRwni>Z&sD=n5WyaV6jmR()$J{B*dK!`c+D#XmD35TZh? z(^OQgZxSzo1Wm(2j-hF6;udu{8@Or2QIqXy^RPgO#B5YSRG7%H)!OeSAdHF7n7^fn z>~EsRabS@OEHa4_p%O4t+lxFZi5k2_)DTnFf;;*$^FNXERYGVJ$qjLn5+V~aku`D3 z2}By+H6a9OZyUSqMWLW^;@L!14~=ZHm1ggfP!iWfh{%hNmK|#3>1?(bq9~>c-ky4#fI&M zjHtNy@&bTqn(f%VGs&`iGEqdSDlfrRrHVxGA(KhdEM})Kzv2pHoQ_9e?DaR?yuLXx zY>!=j#i^4gebCD;zkFkDef9L&qL_}$eE0sn`FL{r<&)FR>A1*O_Uw7<+uwQm^og%M z`slOIJkM-Ln_~dndt{F!(CPFkVLO1Vo1`Y)v1{ef;eD=}E)2RyjvPw5S%5HH*|j#_ zG=ynFc|Hl76HV-0wA)D3}!)R?jfQgNC+NDQR|RGOoFyos38Iom?%vY0$U6<5>bmU; zaU?GRLS)w9;uYFrh!BF-&MFuA)L2VITBA1Gy24n3JKNiHWzuc5lS6RU+BiQ=-9|jB zXzFTnKt$(UdlF|`z1iCg(KG@d$vz_eUl2q@5>BkD`Vit4Y7o*GVg9~y-uq6PMqKqQ z16b=Km3c=zTv_B`}h3e@BPm1l^w5r?dyNzcYkkORn~M*o_Kk<)ID}!|5J}Y zF>`ELYwE;)p1y=dL+)kdW%Hq-$M-Dyx*y9^#*40kP`G+)v>`FUUT>6>e!FRkTUjC^^USsnUMgB z9NB_!tniaih00NqP?EYZ1yN2}#e))977gl2)r(ZV$|19gq^z78Ku{*pV6!9yU?wzx z%wk9&AX-(_P!OZdI>A@U7@Pnb0X2kz9#XPNA}rRhi3iwl1m+nevPOaFp^`8WlQn6Aq!J`CsS>IoW~)#E8Zv5M4WKZRYETkoHP%Xm z&aolHpvsm-RgFb91`Jv(RZN;V?*TMPlS;gz1kz~9qe-2L6Ys&1WncuBrb^SWQssk3 z4dP<5Sb&I(G7@5}oeAhX1VikD_uvtOLQn=}1m*zh#T!eATKN!wxu2NCl-`@9lW>;1 z3JoCyL}f-Lf*1}WG{yv}GbaiGNs$Q%$QVKtg)~izvLv#sCivj0637P$*0N1IUcG7% z1tAcJ!V)Gzn4&;;qF10*odh z%c(#JA*88gHR=#8fvR^7O<=|}O__`fm4H`_<%fift_sWm;F`Xp3|J{5C4`DWgf)<; zi#`F9;gppnIDvpB;!RNzgMy+bw8oMu8q=^!FeXXjy`3==UqMdt*V$e6*PFDl+S zw#HZ^q=Xa#1qn5u2S;{43LfgnE~qg~ghbTqbb|&}ql#IU5E&6w4akVhNRXyhK!}+Q z2M{s|f>ypDqX7b9O2!Zhi6}ZkWlo$AHnEBlRDw8ElXTOxn^rEbT#+X1Lx4K4PenC$ z;&$%cvoW3+O0A(3nG)+NDT|5?vJQsJstVMIxFQSQs*y zP*rN#WL7g9@*rjIU8%uAW^K}sTps7w&z?DZjtY~3x$>H$&pq+A`os^C4H^6Hh(6@6ds9SutFD7cgf1es|E{wP)9r zS6=nR<4;%9TolLC@zYO!?cCWjL?jBm9m{Wi>)Q_<*#G>=7yHA(?YH0g=vN<0df9_- zctE_q{M<=DodCh{8?U|h^$)&q>g7u1nrm)+;n@=&kA?z*64)f^^gBU>)_i(_L!Ll6MMpyD-%h%yBY9za6y!6~!X;C%?k&s*^wfBTlLBWHS;2SjYG*Py7*mB8pj1&G3+Mm-1-i&9_^WMu0_0;mfW8y3f%jEeR;3pg#0!C-R^=23oP$74*$2fCacks7QBjE?_z+z}&xnSrs&IKFA_2Vv zrv|~83N%Di0t7H7r~-*1cy(0}1QdeOO_^<3jH@aDdO;0E?tO6TJb1N<8m*)fWv}AZ zDY6S#I`3jM$^=5FDi@J29>lp42#A@Gf`}(k3f?;}A?c)tj~yC~C$22S)QX%8$J|=4 z-^n@|5LUs3ASO$|sv>G|i#wM*1d-sup~XtPC#WhXfYjkqDnL|JA1Z6DR|zN{ymLiB z4-|mwwmd-<8H};c7e0u97eEi>#FgHQXwZrcD`J2ufR?UO!m=uev&wtL;COnK`)iLqu7;L&?-)0~l33s(g}Yh6{GJ9YBh>9b$`>SLp| zwav*Wc+YUh1IMnp^7gy$eE#H#3jCEIAQ0745z)c(@Qxq zWB}Myp-MWbfF@~j=+I@syAV7X8Y~Pf(`3Bqe1(Qe@5<7Z)1uSuUVrmVkl4-1WbeVt z-t@-TojrekoKHDp+f91I-pa}n5$D01G+EraCT9lk$GhHn?%bKx%~gjX7_+ixF941=$14X8 zzVXd(dez-`KKabk)>7<-4?OZCV%7yB#2VQgMJ$EXJ z>6rD&DC>0Zy!-Y;M~+-rTX(_RZq^%iY}dZ-jSuYHf8f-GGdJCSD<>%>wx~*B-FtBV zq$~o<@}3=PV6fEB`dzV@^tvnib}sJT#kT7tP}d&2=ICH)VU$mYJ9fSIgCE*`aPQh= zbo*;w?O1KUUq~1i(_M%5eABzWX>GDDNW&dFxRc!V!2NgKd#`6Yx4KSg5)4x}`~H9M zVQ<{IQ=3l2E@YP-J?N{VGw4>R%PT9u00tL#?dUA_d);n-(4Pc%%?&rc@vRT#uG-9} zfk7E>xb2qLf73&!&YkhjQ-aHTmiHdqOU5FhAfg4^?~AJG_Q05Ip?Cf5x1L>FUszdr z&-*`gW^HX$l|rmcS+9HW*umUQLQs>XgN218%e*);q=tqoOCF^}NP0`dol9^0rnjI8 zn?>o_-uIR_40kTAk0*gx(C^-VAnOjMZjzV;m=;!sZ++)Gg8I$zYTEA{I(Fsig>#DL z(2*myla=DBliqvpeOF&~?fLaJVcC6XUuU7;?G1MB-5(Iwry~_e`@OW&$vU0GM-Cr5 zdTd(ci@R2i9X+yh_wH$tE26L&EDhiHp%0iWJ#pc}Ti^M%W0xO#=GkYQ;L^&jH^2Rx zJo?qm&86kVn{K;#dDn2VF&ZokuD|W(Bgc>3aMKO@5A9ps*vNeV;;XJb{>>kF@3AY7 zJn{IG-s{3};qQI-w?FjITc3XV$@R%-$KG9g5AB<}BI$Gl?LY9?V_)+gJH1|!yM-N# z@A=RN9)IE4-ooIrgNII>IeXh3ulkm6|KRh_KfAWJv9x2?5C4N79xN?A_0%(YFoF z`8WNA{(Ii{mbHy@kALL}b&gUH=U7uj>@F@S_$;+MS9UU6%2Kf8d^Oov-x!T1(?Yy3 zLpMtqZ+~#_{(Zal?0fR*r-Q&{2M(S&b!s{pk+loM&Ke~u)1Us#`q>M4Q9kkH z(=HTuz2=^0o`3%IGtZwneY!vBoj&nmQ2#C8@-3hL!sqhobZu>|$fs=Z#L1VKaL3N2 zd@_FN#TN-Vp^`s(+*=RK()a`de>2a`_c6J^P#n z@5SYlQISuBcs2y;T{ZPpPJ}i|*cebz0Grs8r%#?acebirSynH-^m1NS5i>*9zUH2L zuReawi!Z)7ndT-*?s?$8SHI?-uYUD0K%|Z}Y3lN^1Xglyed`qSHv(I@c(@wuT zNIU&r>8kN)R21W)oB|;k`{*N&j3&7cfHqrPS-Rt{R~31_u`#N|Kl#*?U;gTsy{ocr zH=vI4aXuXrFvrxL<-E+3B(tWowmOoo=Vo=}#t`XV0B2ohRnS<>6pqFwG|q zzV3Bs(nlYA;@tVwTW-Cr$ji}WymQx%(PTmdY|H}>K5)~GH$L^m6VvfDsM>CJ--Gvh z@h4tBjVj$?cI4=x$!I*yt8Qn|?f1`~Kex8Ndh*1H%%+>0)6pas^7_T0J0pL%q2eT~WXy8SfkEG{iyd;K+p=84CiMMK@i?t`y=?fc&Ou225;Uyr9{ zmL^DAxZ=)xU;V~6zVUOP`RvBV1~A)B^7zxwO!DcZ$os<~CFa1PgC@1-*Vabst66vP zz>$OB`t9F-)z#O3_A{R^OZQFhd}!_bg$rlS^;d@X+<(uDFTOM>^OeQLG)bVP|p+6j;V41s((WXKe4EpbT-+QjO^71F1cv69F)?wS(v7_I~7S6ApOS8eG z7%eO;lI69{3;p4sJ6IBh)eGm>R?p?*d~tcWxMN668><_xblq-Rlv5BeSxSbTgwf{a zxpQYm)9GY7dHKW(XU?5wV_XQtl=V8RYwIV^oQj!_$#`RZeFK!UPWIf3&*kMLNjjs^ zXl;D%`0*>Af9lyuUXF_K6Hh+=_!D0}d-kMBGw(svA3A*5U;eL;FE0&Go;dG=Pg48B z^Ur?uk;f+aWO?TfAanBMSpm88Rd=plxUjLl`sgE%7KJ-=;oRrH`1w<(&Z-NG!$k*l z;K#J)|KJnCe zv~hm*{9k|SldEeJOb2P&xv+Zvu_wMZ+SsfBgD{bQ_~B1~^$TBEJ9W-i0Z`W0FZ|8_ z`m3j&c?QrXSt44UK5_DquYCEbXPzH#l%nCe=bt)r=Je`?k)Ygg`}N=Swzr;n>9jHS zrkidoit*Co!h7ERUI@CnzH#L8!+AMn)ywwn8!Rm3lkw3*`GBVFppahz$Cs5_A8`xr*t1B@}p&$*_!j6>#2M#>-=%ZnCQ$nC& zpDFc42>|LUK*@&7ZtB8x0wf5cUaYlCJC@I$KL_5MY#~ev4S^Fx6$m~x)|dnWICMoZ z?U1o-N>>pYUsh#V5+{fpoFHmyNyG~<87ty|$QM*PuPS4?+wD{#XBB3a04b-IlC{kZwY0Kl$Mo!U zW3mAxE4xRuZrb# zOubIOzmT6@XM_@cYEkma7*n~j*XwwflM*5aB+Akx%huM`vMjTdP9_r#5`x+!9SjDO z>DYU(0-a7L>!cxs@!FafJ$B8N*I$4Apa0<>=hG=b)fq0KbJHRRtd^Gdcarq{`8A}f z)9DFIp65xDFte{*1VW~r%uk$%cfpf20-ALf#7Xc{hC;mp`Yf}kT=-Hc7-Aco1gL91 z0W2q~5WErqC5!~!PR}Wo(^AW_EXLiHg=5E#JoDspRpm{>5ATP zVSV)iR1R$dOr#KsBIyp2B+IMP7%Hn0R1aN#Z1wc|XtWNf#;`b_q$Wucn^^!_-&|AG zrQvclt(V0R(=>IH0+B61KAi$( zi;D{z>#K>e0901a_Ii8vA2|8)b1@ld4U;e;dlj<2*Xd7-JkQ6B+)ER1npdTB0W2;p z?IFz;=YhSOr$^$*q>yi}fpbgy_BgM3?y}5CaQQ~Qykl2BnU-ahq*xRc zcRGomD%7xfhu1)l1MK2eL`ht-xc?-T5>RA)<0T5F&w~ic@f9@RfHC zzyOk}C!>}G83aTLjWyz?gT?Mu*IX%z)6G#;PY`T}MN3ROoMe%JB^#znh%SJN$$$-{i3#GFEJX%60t`L^uni@uAfl;>O`(-g z`-6;0tFl6cG>MQFdHri&f8pHPcx|)OO_GFDV-FoZNR(8QB1q96_BctW!6E47$BypX zv**IObBwq+9QOOYswhQ0lTD0WUfSusPV;GJknO*0|LVDos>}}@-k9;rKP0>2?0GY!@{7qbMI2OmrSQq@4Yb~5+;*z+D&|3GMQ06 zK700TnR_K2EcYddL8{8h(#~B+j~zaDX4ScZESVHR2~k!S7lQK|fRn^n+P!z>@^42{Q)m_0xr3FI1`kd-v_W>WZs2&R_5$EH5u9c=0YV*zI&&Uasuk z>3k7_-?e+UD50XuCy61O!p4Pj)6IzlId;X7b8F`y=(RWBc=qIp>11@v9XGE}MhbBB z$l?A%55Q;L-j&C%oQx+bA;~OT?Dl%S{&2E63F5oGB!sfJkX63y_OmQWbMIW0A3t_j zC5*>YU^EG5U4H0IuM>4=PMt@lZr3_jR;5eQ%oRRVZegKsS)DJDFzXH!2wD35&he|R zSYKT$U6m#Yprl!L_3_I?aIOphXjAKh^Tl*&W#{JFrZr~A?&ZnGI-@#QbvlC(loBq= z;^^W1zH-yCvq?%UgI=03cGJ#ulpC_c;X+mBZZhrl`k<^D2nnH3Z>&uewbSVqUIXa+ zKJ@<8a~IZ6t*5rLYtNpkuMQmA>nrEWfN0V*DXWR7OVeyR%F`t24~Av6At3}82vG^L zPEwSk8*jX+EXV7sqrtGR07;e{x#Gy?_=1LDC`Bn1%gQ^uG?#)>t@Acvr7MoYt` zBA=G7y7rpm>RdG`r7BY+%S!{a98^Sv5O(ceW&&51h#a)ovuD@tUCS;nyelLAV)vev z*T44uNj@HpCP+Z6y>17PMF2{lWt}9!EFo98ZZ}O5RDiN_;;Q|J_rLD7uYK~dXW7_Z zzvruB&)(&gmErp8$W^km+^?!KsOb+Df)ig=oh;k8_dt>kin6GDm8OOf_w3p|9ZkGb zP-M29ZhFhj*YDiDe{*x)YsH+DUcjcngydbRh}i9C)~JLs$9SzWoF5 zdGFib{JQ&HF$N0gLir7h-k;&9S5&CWINe+ zegB8|A3B(eyZ;TZSHgFH=>6~g)(=d|>WvRQw79gKbvpm_XMgPK8xGqps`3Bmr+?&@ z+ph~!UVpu$Lr?e!Vyd%yR4AA0D`&Q(P86F>12uYK@!<7si<1F!#~ANg^Ur2q8i|5=5?-SqA6 z{HA~SWB*{Zx!K9O-~V0T^{wyw=F!^NITbIz_zS=AfBox!@!og5rJQb977NwgcinXP zv3(t@#-{yl_o}N7?A^D+rpX<5-}_DPdWY@MV9?pWfA7!y^gr&UN##qi@V!6y;okC~ zkg&LC_d7rM{f zcfF+urJ}s)&9D8TAO5~KJoLJs`q`h_oKE@+3&W-5Ubp*8zwk3}d(#{8@n$#8n5oxE z-}d@@4)5A2g*Sjd`44~S*x_AE%l)^%{p|>L@9us7=pTMKG1a~Izv_(-Jy<#qMArBR z?!Enk?|r+tsYL=oMsxQacmDD({pdY+U0;=>B&ANuH{E#c!K0TA7rR7;i2n7j{PS=5 z;M)&hzWb+t?k6$f{_?WT(mnfk{fmG8Gdq?uU}rP7EDt{T>VNTzKhf_YKs8wCy!D-L z;g0S-w)c&1eXXjdnSIy0?!Wr@L4e|y{>9I||AXJWIa%j48!oN9^^Ff4J9G$K$T+?1 z$YrCq4&>z?gtJY+CwbNy>7pA%{7OA^Vfgk&G+9`PJNOYNa$O?os>e@W-#c>fij@FOKtKWo7Xfe*VW^|N2*t*46{)>)v$FfBG-~ z>c@WcyN?~&Tbw<;ve5hG|Hse0`Jo4=`D8jNcJ0~yvp@S!|KX4Ph&5bQJ^)r#_3n4S z^MfCH+xLIpw^ij-#HShk@Q;4~2fy{bHbYf^?H=s`6{Lqj5;E(+H4`NpK zmzI)V_K$!3$8Ns%+R12~WZfORcP#8${*_<-`S1DeZ=H@dlGMKAU2psDANbD8j_!N= zd*3>qP84bXp5br(&OiAtzx~fT!;%P8L#LDe>c9EffBcjGAb8+3Eydf^|MIVX|G)m1 zKe%(L8wyWE*Isk{hyVV!{NYFb-Te>V&WS2Y>56;qfAz2Z$N%RK|H$|4*gZ@#%Jcly zuetO8{FR^YcF1-OT1+hd>aYINum8t?{>C@lRgK4)F~9sPKlN+>?&l62-b1WKsFH>B zt?zs22Y%uQ`+HY>N|#-Jcwxuz-Z#DGJ3ss(C7<-{)i+WIMOqdFMlKd+1GXePh}+8oYqqc>VR)-+J?F?!71L zcK04Qyl2l|5In!S_E(?$>+`41iO4nAU4PHruVLzV;meO+@tIHkt;nWGpvhL!tvJrr^*HzZ6KU`SeEr#}9c93oF4X=M_*MWWSc;`D0 z967XO&t8)x-NC}U-t*1}Uw_Ym%MYDCcXqRwmKyGR;583J~o~ffA~NDM{n478h-0{fBXOZSHH|@BI1Aj*Z<$A zPoK6~r>sI5l?S1vV_vC(>cDf(_@P{v)IS-xQ>V^_5PID#%hEFV+?W69zx=1iAN%ss(y*wca*%ei_kZAh_uTWq z%F6C(S#}n>KlbB4^1uW46?v5;y$h@7ue$njPWr$*+ya)2zE=@1a8@ zUjpfyAA0NFJ^RznKnMebvuDr#;qU#)um6WXnT&8@VR2)$@#M4543?Jn?AfP^2v${B zb}y>>U3>Qu+CTV%k9_Q7AG`UMJJR05pMU(5N_K5JJ#h4}L%i(RksEHjrPJ%BS+=}$ zW#_&QkSeOx=I|jsN!5uRVI@@v9Z+6My|TANk{teEE?tJ@Mq%3}>j+@AO6FqaXd) z`o=^8udR-+IDYe?qsJe8^vTDcdU9cD2~3)H`b#Uj`&oL&Ew|bv14<4ZJaYX_H)s9c z!J`MAFZb@*bJI=77MHpo_`o;6=L7G_%c2OQfN;-!_ntj_y4PO_V9<7a(3f6*@l*f% zbBB*yVY1$&^g{gI-}m8nz3+WD+JR?Vd#}0f>e5wf8`BSc=-WQ|iHAp{JngPHB{uz+ zUOx95|LuRDIk%cE*iuB<+aWZ{D%KJ(HGzxM}!dF;w- z_U%9VlmGaa{^v(N+F2M1=p&DP?aPlo{`>#qBj?Vqncfm{`ZGWGiy!&3KVMkhfwoh5 z_{?WM_uIeyyP(*~1{w^J{q5iSz2E%J-~7@SzdBsnC6W+l!=;_Zb|}e+?BPSl_U_py zKzUvsI(lSb#|~jLSm+-*dgSS+pZt?Q`IF9|Kdp)+P08S2eC&Tc{^T>A-ZFyu-QWL% z-Fx=+hs#es_Y#;6p;=#>{K_x>*1!L^zr~y;9b%Jt^q>3N$N%_`|1=%2Fq*_}tQ9}} zeZTtmf9#jet#1xihM3Tcr%(UZ?|x);&0V-~!TF$Ig0QIF*x2~%zxkv|?3GtvgW5ZE z@bbqWd!pMPsObXrCu8@KKltN+|L^|WC;sMtn@%Tp?qeVO^S}Dbzk2!PxeIISKsL!z z0D1iJ$As8>{i8qnlb24O=%jr9?5XK!4MMdr?4Dn{kam&>U-v+-p9Wtn4Tse5xp&{b#l_|KzyAZ*+;Dw5=)LCN``+-Twt#{zk&;HwYyz@Pu{Pbr|KKI-WueyPVKL6Q=t3trbj^SX>!5!oA z_{A5`n_#QyILRzo3PHX1ssf3H>69xUG6T6MOAAXqL9=;!Ow57>a6;=UR_xM1$~{LRzbN|qd!^VYimpgzunQk=Q0x|(d9Sh!pQz;z)ku~BAphz_d63C{RlnyG7 zPkd113mHI?8*#HNW0f*cW`6@-F>I?GFw$+T}XX6`Ky zPn|f)cA!%1*fn70lh2>wv@_f>D9UMBI1JJ;DF79&Lc_g<-sa{8O{yf}q;EI#O_QZ* z+Hqr#4vEY--|P;O;c&RNHUf{{d+#fgsG_yDSC%CwDn2A8?IhWFGD@;+X=S;r-1)O> zs34*04Z79DTZ=_GL8i!?Js1o?WVE_&6Ppb>J_JI90A*Q*qEgA(T5E0LJP3693wfS* zJH6nftfoYe*`z2Oq4B;<(j-5oEFnY=KQ`QwJ77ObX>>Ob&^N z(}bD5LRIFGg9AD1^#(qa)A2;1U^Wsg0w*cvWx+O0vUGZ`>i4>$)pW8kSQ-w7{j+D! zix;-&MNLhifQd<~s`6!};sD8X7F_U_gZB>8WH4OXIK2iSoh-@68%UI888TNP_$t_h z*`imEkYqNiiV^`BeO@~8)aebER~Ap5IZ4L!2if|WNvD$-cBLg9|}&Bg@pxBDXQGL;6q?b zOjaSyIz7>9G8#iU0phH;B%nbHL{_oLCTL(4U*wD?lpYKNlt9ATgCqo37-KvlD0h-> zUKTJZ(57TChy!&%fNYQqA$ngKV~~*yTay4#nO82~1Y?q{TUAx$cQV#`)G#j7{=gcT zZjMC2X13Q)HYb~esn-Cmgv0_QX_5eGReGI{fQ;ducfoc|P!$&lNjwlCm0t5nKnI4> zem{_sx~dXTOuD_GOhn)+=Nwk5fTo|>1j}hDq2x{yM3B3R0KP;B8XR~q-A|cnRP9Eg$;A!+-n2GtYe6 zhraFEr=I`gk9}<4(fwCnb?wLh=N}EMz3Q6FAAR)EYFs!MaNjTfzb>qeDgvBVLZRF1 zm7^kf230T!;D87wfLWsXw4vK1@BoBhfy+v~Q)7TFkOGwm1}9cRMO{=w8kmWf_Uv0b zcREy~J%8WkE#NCe^f9+_5w+b2rJsg)HmzdR-9- z&ezqL3R*h#lBBGp8eHW)8XJOoR}P1RsuU3c!OA;D=ww~*BVanPwdrsupfZ`ds&YXH z&Em>#S4~Hg^PMEErU8MoVH*4UmeZVolPrzsYz@JcB~(63NYp7Qn-qXt={weVWoSwh z1&AX~sB!{9+1P9tLeP-As#3vDuMg--U;47@cL({TOw%;&B$cnoX<3#gO~|kcib$+k ztqPYV94SSElQd12mWMAr{~RH9yWPB~m{AEuF$74Ho`O^LpdnNNk$YLMDDxyS$i}Hu z-Z^p1XppL^YMYG54z#XtG5ElM4nk!4Hp(tkP2wO}%DiGqnHjL^^?H+Xii#jY45BnxSg3*$zX|{ ztE#D}Q(u87vNhJ4#0Di_`ot0-d7)l!;H#pT<|2YjB8J!+i@q#Gi7k@^UzJ)-AvGKr zEGh<*un;qvnkz+Zr{7c6&9zOekZqE6d(M@rQsp@iWxXzfROKX0r@&yv+;0Z!0 zgh*(7z~17520vO`$53&S+H?R+WjPU02}HzcH|tnaPR3Q?Bp?uWGV4Quocd5%LPV^P zO_DL-csxc2NoK6&$#kT_CrQF=vu>xVrl=G`08+9h>vmk}^Kl+~-K3pfo=*rwRb5b% z^}su1a``y5nFL~kMX_NzDaSSm5)e_rbhA!SKQ42qJfTUFu6F@gg7e}bNz(v%C++%8 z=c@oL%*X~!hrI(AJb@yzaMJ5$tg^Yjp^gI4!tyYPukxa*3RQ2?9ucrL#?WYE1A>pd zLN-lFw5keY$p>{zU-LaKs-56obg z0IjvI%mp+V^nACgt{A4K*XwO=R}|^bMU~0^XI1H$?oNygvrZi z&uWnF(!zB0JXBS`-*aW9Mdej^viFEDa!|y=s~1}!rbr;(HpizFeXMxQ){ZS07Gnv5iL<-Siq@zlUZc!bh0uZC!GYur%9rqWU15Z zm`=i}O*@^+RbDCpPsSvPalW!$TZIZJSc~0WXK|&QWH$IB>6ks2?KCN5mW%1M*SFnH z!UWDaPOS^BKkP2=83JlmGZmbJ69Bi0Xp4oXE^8%7b>ZMff8d(!lrZV+9SXL z6Ez95QcySC(K~$Q(cnV>F-c}HEQmwJPq2XPuYozxgi(o{+J9^31{4TgQ^O3Um*b>kg3F78>{yl}x9 z&iaGlaM(*yUzGxqB?*L(SndzI$mE=}on-&feI80SraxQ=pp?+cp5?(}k2nQNDt3pC z9_|mj!{wnG>@D=Kx#60$n{bj)VitA`7nX)VD5^F~dkg(+VA(>N+O%V{UJ}sT%=8xr zDwXZnPA4UU3yU2WDht@@*kmEywSPyNz>Zx*@x_s=k0h2zydm1LZ};(QuPKXiX?bC| zxPU~1#lexQj_g0Y$A`&aq2mb-9Nxcc*V3dKbE;{Upm;=06Wbm9|697RCOgi=yyI`` z+3EwfQ3fdbUedytiS?>mQXaN>)139F%;CMZy6RL z27+2*3=jskOB56a0T2>m7~ua4))HWZZI2*}*bv#kt2zp4U|=r@EZ^ahA`@B`QM@!> zrZllKEJ!&;NL!9YOhZ75G1HJ67}=Hy4Im^3NLjw`xE=)@`^cXPkbyA)mT5SS=~k+4 z#q&K^DXEkQDlWvbl$q@jr(!E8h>5FnRcPV);%z04mN4XevlF zHciX5>&-eMx-nC;9HKM~sui~$xK$Tf5Sth&;kzy}4a>q#UL7`rwxEy8qZxybh| zgb|0bGG+J`JI|u?*Dg#qYZ^+P=R!-nYB%R*A(XabIi6FNNv&p_IXja?i3ZAWP|i}n z=DHOZAicD-WEd*V#@Itn#nh5Ifn%745CR$!V?YdyAcROmBoR@5)kIipErY;C1ZgaY zjSb&%1JCtLg@Nfi{!~qv5V^Qf@degAmwuyen6^U5tv7&aBHLUzvotq1JLq(HLGyyL zQptkHgJJ);F9hIW91Vsv&6QM1QYfZalmMua17%r)ve2f*x&u`+vC=8;MSl9P&3OWFC}!_ zG2eOX!OF_=-rkO3Aj>rw5RPr%zI7`JLxJF#(@T!y2rV%pckkX6f~RFRH9hs(tFQLE z?F;8u=H{9gFI?!hdb2alYBlhKs$g>Y?8@n-CCRzu+;S`ep-ITwZ@*<&rVzANsZQ1F zuIHY=dJzH8K0ek;)~BadR#vuNY!*e~dB!)t`=;GGURgQErHa#-vEua7;@|%Mx4SzV z&Dn;6Ajxv6)uT^88xQ+R%ZDF*xcOo&9FJz_r{`vyS_p&y7sYVcd+^|`=Im_y;7}-T zIL7_g?zKA|Aw_k%L0Nh3-15TwT&?C0Mm<_&^QW4B{pIIVQ&YYdOePbI;kmO5m#_vi>Qv>8H{O_@Z9e|#C);;F{P=_0ckZsOuQSReWR2;X?|WCSULN#&-Cp;jPd}7E z{PgoDIyCdk^2cb^H3WC#&^(?UPSFo}ZsvT3p!K+P!-D>fB8A z=8YTc&z^tz#iPrYFKP%{+xwTVy|QxoVn);7|L)78NH(9ZUA_Lwm!Ez9Z1u5(`oI47 z|Mue#Kek&(pMLh?d+)zH9QJSAx&Gsi|GIy)^VMH}_Sxs3{jY!gr(3sfEuCK2*xq>m zqlbdiD;HMYdHW3^b(*HX`S$B}x9u3@y${~)4TmKwZry%meqlaMGXUX7AAZp5wWelj z-+uFrk_uxShN0`&m#<%GHfICRy?6IctyVogKKk;jN0%c51qoCt0Ify>j7lPk%YJH2>(!FP^PFiQ@5VuiyWx4<798?(>p8c>A?`_io#chYfh=wVQ@xv<{B_ zz&wB9%;n1~moBVaIKQ&Dy9*RpSemiyoynX~infB4SKbmQp{Kl^_0;QhDTos;_PjAfH6=a=u@y`84n zcs%;{w_njRQH*~0!TX1Y2U@Fl-h1=Xm5X}^`(>WJ_2Bgj7tTF<{P?p+pI(3E(yxzy zi3g$MxPINcc=7!Gd$;f3z5D&2|D4CsfBgHu`S9U;gZ?PVCeS7ovjY_3@ z|J7H^B0AjMv>L&~j~)^LzWMrVE@ZdYC5C?ejr;R+v(5RLR=4F>JlnVa!yo_d@|BBO zmbN@A|&G`(*XZ`Q>l_=C_ACJD0D#^2L{5 z>>eD%d3yi#SKoZ_X1~{7I*`w38mY1bmcD!cPM(ya{*9i;bN# zD`y#{?foOgrQ=o_)v2AWEzT(hs4+El(&zoUkrIRE)ckbND@o^SM=_u+Po~*4to132Pwz^QG;b73~_xhtg zG)>~#t?tRu{^lqgQl(tq`|y(we){#X>3NeRPSb?Z;$V08>CeCH9~|%Q?kSBZ;|B*v zYtPqN$&{9omFajg>GwJ(C-HcqwcOp_91Z$ul3{|jwlS zhsj_#91X{e@_wt|J?YLjrw08&XXkh{oCwLIB%SoerssD%z5TuIpy5YJeEjQ1wLU#J zKexHHS#qi&?00&DcB?(?c6;sPqoad^eF9M0>a@DOXdJh8I-R4|&hD0A1y!@>DOO<_3_WYM3b->hP_sc61Dti?mQ$Xox%g-c+MH?Dsa-H$cJ0llV{n^5@OnEz5Bg!a~aZgZPHyXIA@{>kB98i$1}!f?>u-PwP(zH@Tq zI1XjS-2D8hrNzzd4a3HN{No??kM~C7-r1|?9)10&(`_S*T)1|5_UzoK+dX$~c@#}< z+`V=2`jyT7-Nn;OMVjF&|M`E+EiT->edpQ6n(f$cz5RAHj?$zMS*D<-JU=0`U_ji-w1R#e~X+X%L8~|)v0pkJ)QEN6$)6S@bwqg5Dr8d<) z>6Jy z>p1{|$G`r9gj$@P8;`=?pbxR`93Rm*YBr`Y!W-)w)gS;!FP%QS`uy2=JZjF)jwg|# zyht+F_AXq!xccnb-od`$s-VjEs{Kyq@L(Sr2mp-~EVyv1lD)BH000aWNkl(&C%QnZuNuK1EY1qCeq$-NkBt|$G7n9IX+OafL->B~HZtv`D9~~V}Pt8!uHa0esEKZ|@G3NP|k}G1Mr%#^FHfFLk!3wdm zG!SN#CP`YUR-_h_;m~rNieCw%kQJH83)gXp=~zzieD!Hj(%xVqlypoh38SD^r=>y! zB20oH;9Ov2awaX)(2|*$NX}iy)?B`9fWCB;AVj|F7I}&^l0s^w0?$_(0Hw;Z5S&$f zFAhU3bTpngrnR)Zczk%wnXK0vey!Ra_G!tN&YW3UKDWEOy|cL?gjzUtx+wYT)2DHs zA%wSgcQwFXwVJb{)9RSmvP}m;Lun`#4@VQom229LYbmAc(^K8nNxyS^ynk4fa;h(4iejA4Mvtk6>Up4;hmh-q1tL#ecg$uS#`hpqjCa5xzc zMzx^Q?RS3o;oqXsxXg=6y)KnzWwE}tE~K;w;*2AV41%LD#sJqVb;$&Ps6I8-J!$Rl z?VRi%Cga3&y(kVhH#e131~HO2RS*=S*xcSM(o!oGCfQ&zMnLs?9jF1pIEg~el!2Wr ziM3>{gQM0#Yr0lP2pk+7oE#r;&fFl#@{WGJWIk{DpjdgfYN!MZmhph0#rP|%=40S(=_umb4}YM z1YyD%7fdBdJRJ28_V>rVvE+Pd`Alx;Zf4$DCwHYS6i;yCasWuE{1l+LJCP~cHCuY&P3PunjB$>hz zTF{U=PvQh30~-M^g%q%q(slxxb5TfSInCzMI7%^e49DY4A=5+}n6|C4H8ppt-e|af zB@7c{c?gpv&6Qw_^Ya?&@nm3X?R!qQ*Li7K3|d_q;puvVQkI1AbbSgTgIFYrLacS8 zQth<62q28HR}DsC#+jN7V+e>}sf1ye#R*p0aBN~)01;$b^{FWhq~sJMWZEV&OxyE_ zX%Iq?g>BDHlQ>T@%|t$lYOdF)RS3p|VThnGzRShf{ZYm9T#^Q4fj<9N0cPGSrYD|xkAg&2pE*tKg4 zKySLfySHyTuJ5?Jo4a|MfkK$rqyY50gLE7tY(fnoCdjl6%NC5YlDkgD^($P!k}=yd zff5)Y!!lgo1CZoduDOOBD28pbijf&di4rpKE0*mj4K;woAmJ!N0NJKR92X+2At0tz zpPd;;5hes8Trj~CN=I21l|mvfsEx;=${8lavMg-cX;~TuPSez~9m6EZFd1V(P(ex? z*f5Am45MUR6oTelGc6d70^SoqK2SG(CErbLB#Br!OFo?ss2H4O5c#cgBqY_j& z=eB7kN!pm635Sz#Fdp?rKw`fV2qDV6!~kJpYlWnSm0G>Xit%_t48w38(|1cjE!(cu z>RFVrf;zTi5@+0-gu}RZG*FyWDvdNrm4FCBH}HvRmRxE8^0f543NbBY*b<o`_G98C;KFK`GFABl91_)2p8wWdkahL#z=4Q`y+8xjJOv|)f>t%`CwmfX$EY1~G zt`%s69oG?z`o0H*bUa@|vt*JO1So9~3q$DmH2|?F3#f(bS*Ag3*9*sqiTo_dv!u`x zU|=dm>Xn&1=TK_Pbb?APPczf@xljN?$Mer!x!#XcC@?RSW!ObtX*(A^s zNv;SsUAL-$(Of*+Yxju+s-UJ5&{7iLxpeK7{pXv!WK6<#tE<8SFc}WRqM%q3S*o*V zm$%n9iNr26Ws=)m;kmcJqH#_fM@hiLv@97+s3I7qLII!(j!Fg#sSz-cspw0-47CM853Dr zDy~$)ZQl*kI2wdSoN0*4v`oWP2{0K>7~?dTQpzNWHDg+UGS4OBTI)P7IF~#xw9=AO zDLG414VB?lKmPQiw8)|;IyyO#S}U#7BC{}|McM0hAq0WzW?9Mv*HR5S0|=Gx*qjzo z9MO_02??>So{TNmWEvoZS)QAQndPOFO3+f2B?6k! zQc}*c9BQZ(EJ{Wx6HJ!0pn^)yWJzt?rc!9AxsXsJE|gS&GM=TmmI@-6rA3h!k_)JG zndgi$rF2=88t635FfxP?QcB8bl4ogACV4KEpn0A|aTdmuN}&}KywmMy1D+ z7%;|Z$&uE!?G%g)Ewduaiwr1$AOt2+N)U}~+btL`iZY91C72QlYRq%NbFKjtN@%4O z2M{Y_k&=tNESM4i;Gj~^ijpZo5Q0ES#tO&EBA1Vcvk|m{3LXk^iK@AY)yr4y1D9L7U6Yp#}keM<^(ryPDS<(FHH+m1`|^=foU8W3Y!dA9E=>h zmHa0yIbn3x&@I5gOJEY0LWhD{Wt_%A(L+JroLdqYoL6kwAd`MWmW#LK;M5=qt!b$Y zSxkb4KCHY+RjCJaFK^U&eP!j+V?L7=r?^c#6WlDo%loFXTg_Vhswt<0_fq`+$I%^aSx%nxXX_dG&INcO$2WnvOboFyt I=akR{0C6go&j0`b diff --git a/examples/single-page-app/myhub/images/users/user1.png b/examples/single-page-app/myhub/images/users/user1.png deleted file mode 100644 index 7f9d66598d946b46977ceae2c1287c67ef94c523..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 336915 zcmV)GK)%0;P)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 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 lnykz37Ztd3HxcCG>zmf7UB5srr2;NvtK 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

69#4syfK%Hi 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⩔|ul(@)uh(rGXM+;4t8%mZkfvN6 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

s;f0+WSls+*XP|s%q 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 N002ovPDHLkV1m{8`5*uQ diff --git a/examples/single-page-app/myhub/images/users/user2.png b/examples/single-page-app/myhub/images/users/user2.png deleted file mode 100644 index b5d5bf37eca851e06fd0bedbecbb25fdb0cebcf9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 233429 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�gapGi+J5V%rAx-QY})wu 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 z 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

  • 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 diff --git a/examples/single-page-app/myhub/images/users/user3.png b/examples/single-page-app/myhub/images/users/user3.png deleted file mode 100644 index 90491954d85e20d4e56838584df74cc24fc011b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 378384 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%0ADS}=8bKfAoVtRXRPJ`(ij&XZ;h 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%0DY7hs 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 d$L|*EcKHAO0)->UI~cx{s+UEd!zsW diff --git a/examples/single-page-app/myhub/myhub.py b/examples/single-page-app/myhub/myhub.py deleted file mode 100755 index 0f7d2d203d22..000000000000 --- a/examples/single-page-app/myhub/myhub.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python3 - -# Dummy Github-like repository website with OAuth - -# NOTE: This is a partial and insecure implementation for testing only - -# This is an implementation of a dummy OAuth provider for testing purposes only. -# Authorization is automatic on request - no real authorization or authentication -# is done. - -import logging -import os -import pathlib -import secrets -import urllib.parse - -from aiohttp import web - -from shared import Data, debug_request, TokenStorage - -# Demo envoy user, gets rid of rodents 8/ -DEMOUSER = "envoydemo" - -logger = logging.getLogger(__name__) - -# TODO: add to app -# Note: You should not persist data in this way for any production system! -token_storage = TokenStorage(pathlib.Path(os.environ["TOKEN_STORAGE_PATH"])) - - -async def authorize(request): - debug_request(request, "authorization") - # Generate a random authorization code - authorization_code = secrets.token_hex(16) - logger.debug(f"Generated authorization code: {authorization_code}") - # Store the authorization code - token_storage[authorization_code] = { - "user_id": DEMOUSER, - "client_id": request.query["client_id"] - } - # Redirect the user back to the client with the authorization code - state = urllib.parse.quote(request.query["state"], safe="") - redirect_uri = f"{request.query['redirect_uri']}?code={authorization_code}&state={state}" - logger.debug(f"Redirecting user: {redirect_uri}") - return web.HTTPFound(redirect_uri) - - -async def authenticate(request): - debug_request(request, "authentication") - # Extract the authorization code from the request - content = urllib.parse.parse_qs(await request.text()) - authorization_code = content.get("code")[0] - logger.debug(f"Extracted authorization code: {authorization_code}") - - # Verify the authorization code - if authorization_code not in token_storage: - logger.debug(f"Authentication failed") - return web.HTTPBadRequest(text="Invalid authorization code") - - # Generate an access token - access_token = secrets.token_hex(16) - logger.debug(f"Generated access token: {access_token}") - - # Store the access token and remove authorization code - token_storage[access_token] = token_storage.pop(authorization_code) - - # Return the access token as JSON - return web.json_response({"access_token": access_token, "token_type": "bearer"}) - - -async def repo(request): - debug_request(request, "repo") - _data = Data(pathlib.Path(os.environ["DATA_PATH"])) - user = _data["users"].get(request.match_info["user"]) - if not user: - raise web.HTTPNotFound() - repos = user.get("public_repos", {}) - if request.match_info["repo"] not in repos: - raise web.HTTPNotFound() - return web.Response( - body=f"Myhub repo: {request.match_info['user']}/{request.match_info['repo']}") - - -async def user(request): - debug_request(request, "user") - _data = Data(pathlib.Path(os.environ["DATA_PATH"])) - user = _data["users"].get(request.match_info["user"]) - if not user: - raise web.HTTPNotFound() - return web.Response(body=f"Myhub user: {request.match_info['user']}") - - -def main(): - logging.basicConfig(level=logging.DEBUG) - app = web.Application() - app.router.add_route("GET", '/authorize', authorize) - app.router.add_route("POST", '/authenticate', authenticate) - app.router.add_routes([web.static('/images', "/var/lib/myhub/images")]) - app.router.add_route("GET", '/users/{user}', user) - app.router.add_route("GET", '/{user}/{repo}', repo) - web.run_app(app, port=7000) - - -if __name__ == '__main__': - main() diff --git a/examples/single-page-app/myhub/shared.py b/examples/single-page-app/myhub/shared.py deleted file mode 100644 index 964abd09aa29..000000000000 --- a/examples/single-page-app/myhub/shared.py +++ /dev/null @@ -1,67 +0,0 @@ -import json -import logging -import pathlib -from functools import cached_property -from typing import Optional - -import yaml - -logger = logging.getLogger(__name__) - - -def debug_request(request, resource): - logger.debug( - f"Received {resource} request:\n {request.query}\n {request.cookies}\n {request.match_info}" - ) - - -class Data: - - def __init__(self, path: pathlib.Path) -> None: - self._path = path - - def __contains__(self, k: str) -> bool: - return k in self._data - - def __getitem__(self, k: str) -> str: - return self._data.__getitem__(k) - - def __str__(self) -> str: - return json.dumps(self._data) - - @property - def _data(self) -> dict: - return yaml.safe_load(self._storage.open()) - - @cached_property - def _storage(self) -> pathlib.Path: - return self._path - - def get(self, k: str, default: Optional[str] = None) -> str: - data = self._data - return data.get(k, default) - - -class TokenStorage(Data): - - @property - def _data(self): - return json.load(self._storage.open()) - - @cached_property - def _storage(self): - if not self._path.exists(): - self._path.parent.mkdir(exist_ok=True, parents=True) - self._path.write_text('{}') - return self._path - - def __setitem__(self, k: str, v: str) -> None: - data = self._data - data[k] = v - json.dump(data, self._storage.open("w")) - - def pop(self, k: str) -> str: - data = self._data - result = data.pop(k) - json.dump(data, self._storage.open("w")) - return result diff --git a/examples/single-page-app/secrets/myhub-token-secret.yml b/examples/single-page-app/secrets/myhub-token-secret.yml deleted file mode 100644 index fac6fbd56dcd..000000000000 --- a/examples/single-page-app/secrets/myhub-token-secret.yml +++ /dev/null @@ -1,6 +0,0 @@ -resources: -- "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret - name: token - generic_secret: - secret: - inline_string: VERY_SECRET_KEY diff --git a/examples/single-page-app/token-secret.tmpl.yml b/examples/single-page-app/token-secret.tmpl.yml deleted file mode 100644 index 16ca84f645d2..000000000000 --- a/examples/single-page-app/token-secret.tmpl.yml +++ /dev/null @@ -1,6 +0,0 @@ -resources: -- "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret - name: token - generic_secret: - secret: - inline_string: $TOKEN_SECRET diff --git a/examples/single-page-app/ui/.env b/examples/single-page-app/ui/.env deleted file mode 100644 index a2e84adc9e76..000000000000 --- a/examples/single-page-app/ui/.env +++ /dev/null @@ -1 +0,0 @@ -VITE_APP_AUTH_PROVIDER=myhub diff --git a/examples/single-page-app/ui/.env.development b/examples/single-page-app/ui/.env.development deleted file mode 100644 index 12bd20d336c9..000000000000 --- a/examples/single-page-app/ui/.env.development +++ /dev/null @@ -1 +0,0 @@ -VITE_APP_API_URL=http://localhost:10001 diff --git a/examples/single-page-app/ui/.env.production b/examples/single-page-app/ui/.env.production deleted file mode 100644 index 366c3c4a1ecd..000000000000 --- a/examples/single-page-app/ui/.env.production +++ /dev/null @@ -1 +0,0 @@ -VITE_APP_API_URL=https://localhost:10000 diff --git a/examples/single-page-app/ui/.eslintrc.yml b/examples/single-page-app/ui/.eslintrc.yml deleted file mode 100644 index c812102edd82..000000000000 --- a/examples/single-page-app/ui/.eslintrc.yml +++ /dev/null @@ -1,29 +0,0 @@ -root: true -env: - browser: true - es2021: true -extends: -- eslint:recommended -- plugin:@typescript-eslint/recommended -- plugin:react-hooks/recommended -- plugin:react/recommended -ignorePatterns: -- dist -parser: "@typescript-eslint/parser" -parserOptions: - ecmaFeatures: - jsx: true -plugins: -- react-refresh -- react -rules: - "react-refresh/only-export-components": - - warn - - allowConstantExport: true - object-curly-spacing: - - error - - never - "react/react-in-jsx-scope": off -settings: - react: - version: detect diff --git a/examples/single-page-app/ui/index.html b/examples/single-page-app/ui/index.html deleted file mode 100644 index 8910338f5f10..000000000000 --- a/examples/single-page-app/ui/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - Envoy single page app example - - -
    - - - diff --git a/examples/single-page-app/ui/package.json b/examples/single-page-app/ui/package.json deleted file mode 100644 index 82f7a868fd44..000000000000 --- a/examples/single-page-app/ui/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "app0", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" - }, - "dependencies": { - "@chakra-ui/icons": "^2.1.1", - "@chakra-ui/react": "^2.8.2", - "@emotion/react": "^11.13.0", - "@emotion/styled": "^11.13.0", - "framer-motion": "^11.3.19", - "mdi-react": "^9.3.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-router-dom": "^6.25.1" - }, - "devDependencies": { - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", - "@vitejs/plugin-react": "^4.3.1", - "eslint": "^8.57.0", - "eslint-config-standard-with-typescript": "^43.0.0", - "eslint-plugin-import": "^2.25.2", - "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", - "eslint-plugin-promise": "^6.6.0", - "eslint-plugin-react": "^7.35.0", - "eslint-plugin-react-hooks": "^4.6.2", - "eslint-plugin-react-refresh": "^0.4.9", - "typescript": "*", - "vite": "^5.3.5" - } -} diff --git a/examples/single-page-app/ui/public/envoy.svg b/examples/single-page-app/ui/public/envoy.svg deleted file mode 100644 index d7f5bcc45c11..000000000000 --- a/examples/single-page-app/ui/public/envoy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/examples/single-page-app/ui/public/myhub.svg b/examples/single-page-app/ui/public/myhub.svg deleted file mode 100644 index 7d65673545b4..000000000000 --- a/examples/single-page-app/ui/public/myhub.svg +++ /dev/null @@ -1,496 +0,0 @@ - - - - -Created by potrace 1.16, written by Peter Selinger 2001-2019 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/single-page-app/ui/src/@types/app.d.ts b/examples/single-page-app/ui/src/@types/app.d.ts deleted file mode 100644 index cecd3fb3f8c7..000000000000 --- a/examples/single-page-app/ui/src/@types/app.d.ts +++ /dev/null @@ -1,136 +0,0 @@ - -import {ReactNode} from 'react' - -// user - -export interface IAuthPayload { - isLoggedIn: boolean - user: IUser -} - -export interface IAuthAction { - email?: string - payload?: IAuthPayload - type: string -} - -export interface IUserLogin { - avatar_url: string - html_url: string - login: string - name: string -} - -export interface IUser { - avatar_url: string - email: string - followers: number - following: number - login: string - public_repos: string[] -} - -export interface IAuthProvider { - name: string - icon: T -} - -export interface IAuthProviders { - [key: string]: IAuthProvider -} - -export interface IAuthState { - authenticating: boolean - failed: boolean - isLoggedIn: boolean - user: IUser | null - provider: string - proxy_url: string -} - -export type TAuthContext = { - state: IAuthState - dispatch: React.Dispatch -} - -// data - -export interface IRepo { - html_url: string -} - -export interface IFollower { - avatar_url: string - html_url: string -} - -export interface IFollowing { - avatar_url: string - html_url: string -} - -export interface IDataPayload { - [key: string]: IRepo[] | IFollower[] | IFollowing[] | undefined - repos?: IRepo[] - followers?: IFollower[] - following?: IFollowing[] -} - -export interface IDataAction { - payload?: IDataPayload - type: string -} - -export interface IData { - repos?: IRepoInfo[] - followers?: IUserLogin[] - following?: IUserLogin[] -} - -export interface IDataState { - data: IData -} - -export type TDataContext = { - state: IDataState - dispatch: React.Dispatch -} - -// components - -interface IHeaderProps { -} - -interface IHomeProps { - isLoading?: boolean -} - -interface ILayoutProps { - children?: ReactNode -} - -interface IActionState { - isLoading: boolean - errorMessage: string -} - -interface IComponentWithUserProp { - user: TAuthContext -} - -interface IComponentWithDataProp { - data: TDataContext -} - -interface ITableResourceProps extends IComponentWithUserProp, IComponentWithDataProp { - headers: React.ComponentType - name: "repos" | "followers" | "following" - row: React.ComponentType<{resource: T}> - title: string -} - -interface IRepoInfo { - html_url: string - full_name: string - updated_at: string -} diff --git a/examples/single-page-app/ui/src/App.tsx b/examples/single-page-app/ui/src/App.tsx deleted file mode 100644 index c5590ce55f8f..000000000000 --- a/examples/single-page-app/ui/src/App.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import {ChakraProvider, extendTheme} from '@chakra-ui/react' -import {createContext, useReducer} from 'react' -import {BrowserRouter as Router, Route, Routes} from "react-router-dom" - -import Auth from "./components/Auth" -import Home from "./components/Home" -import Login from "./components/Login" -import Logout from "./components/Logout" -import {TAuthContext, TDataContext} from "./@types/app" -import {dataInitialState, dataReducer, userInitialState, userReducer} from "./store/reducer" - -export const AuthContext = createContext(null) -export const DataContext = createContext(null) - -const theme = extendTheme({ - colors: { - primary: { - 500: '#000', - }, - }, -}) - -function App() { - const [userState, userDispatch] = useReducer(userReducer, userInitialState) - const [dataState, dataDispatch] = useReducer(dataReducer, dataInitialState) - return ( - - - - - - }/> - }/> - }/> - }/> - - - - - - ) -} - -export default App diff --git a/examples/single-page-app/ui/src/components/Auth.tsx b/examples/single-page-app/ui/src/components/Auth.tsx deleted file mode 100644 index 935f5519d919..000000000000 --- a/examples/single-page-app/ui/src/components/Auth.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import {useEffect, useContext} from "react" -import {Navigate} from "react-router-dom" -import {AuthContext} from "../App" -import {TAuthContext} from "../@types/app" -import Home from "./Home" - -export default function Auth() { - const {dispatch, state} = useContext(AuthContext) as TAuthContext - useEffect(() => { - if (!state.isLoggedIn) { - // We cannot login here as Envoy requires a request be made after - // succesful authentication in order to trigger the completion of the - // OAuth flow - dispatch({type: "RESET"}) - window.location.href = '/login' - } - }, [state, dispatch]) - if (state.isLoggedIn) { - return - } - return -} diff --git a/examples/single-page-app/ui/src/components/Header.tsx b/examples/single-page-app/ui/src/components/Header.tsx deleted file mode 100644 index 0bfb0b63052d..000000000000 --- a/examples/single-page-app/ui/src/components/Header.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import {useState} from "react" -import {CloseIcon, ChevronUpIcon} from '@chakra-ui/icons' -import {Box, Flex} from '@chakra-ui/react' -import {IHeaderProps} from "../@types/app" -import {UserMenu} from "./User" - -export const Header = (props: IHeaderProps) => { - const [show, setShow] = useState(false) - const toggleMenu = () => setShow(!show) - return ( - - - - - {show ? : } - - - - - - - - ) -} diff --git a/examples/single-page-app/ui/src/components/Home.tsx b/examples/single-page-app/ui/src/components/Home.tsx deleted file mode 100644 index 87b5e82f636f..000000000000 --- a/examples/single-page-app/ui/src/components/Home.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { - Tab, - TabList, - TabPanel, - TabPanels, - Tabs, - Text, -} from '@chakra-ui/react' -import {useContext} from "react" - -import {AuthContext} from "../App" -import { - IHomeProps, - TAuthContext} from "../@types/app" -import Layout from "./Layout" -import {RepoTableHeaders, RepoTr} from "./Repos" -import {RelatedUserTableHeaders, RelatedUserTr} from "./Users" -import {Resources} from "./Resources" - -export const Content = () => { - const {state: userState} = useContext(AuthContext) as TAuthContext - const {user} = userState - if (!userState.isLoggedIn || !user) { - return Login to query APIs - } - return ( - - - Repos - Followers - Following - - - - - - - - - - - - - - ) -} - -export default function Home (props: IHomeProps) { - return ( - - - - ) -} diff --git a/examples/single-page-app/ui/src/components/Layout.tsx b/examples/single-page-app/ui/src/components/Layout.tsx deleted file mode 100644 index ea30635f8593..000000000000 --- a/examples/single-page-app/ui/src/components/Layout.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import {Flex} from '@chakra-ui/react' -import {ILayoutProps} from "../@types/app" -import {Header} from "./Header" - -export default function Layout (props: ILayoutProps) { - return ( - -
    - {props.children} - - ) -} diff --git a/examples/single-page-app/ui/src/components/Login.tsx b/examples/single-page-app/ui/src/components/Login.tsx deleted file mode 100644 index 7fa7b3c8d61d..000000000000 --- a/examples/single-page-app/ui/src/components/Login.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import {useToast} from '@chakra-ui/react' -import {useEffect, useContext} from "react" -import {Navigate} from "react-router-dom" -import {AuthContext} from "../App" -import {TAuthContext} from "../@types/app" -import Home from "./Home" - -/* -Note: Envoy's oAuth implementation requires that a page be requested *after* - a successful authorization/authentication. - - The consequence is that 2 pages are required to complete authentication - - this one and Auth.tsx which does a hard redirect here. - -*/ - -export default function Login() { - const {state, dispatch} = useContext(AuthContext) as TAuthContext - const {isLoggedIn} = state - const toast = useToast() - useEffect(() => { - const {authenticating, failed, isLoggedIn, proxy_url} = state - const fetchUser = async () => { - dispatch({type: "AUTH"}) - const response = await fetch(`${proxy_url}/user`) - const user = await response.json() - dispatch({ - type: "LOGIN", - payload: {user, isLoggedIn: true} - }) - } - if (!isLoggedIn && !authenticating && !failed) { - fetchUser().catch(error => { - dispatch({type: "ERROR"}) - toast({ - title: "Login failed.", - description: `${error.message}`, - status: "error", - duration: 9000, - isClosable: true, - }) - }) - } - }, [state, dispatch, toast]) - if (isLoggedIn) { - return - } - return -} diff --git a/examples/single-page-app/ui/src/components/Logout.tsx b/examples/single-page-app/ui/src/components/Logout.tsx deleted file mode 100644 index 26ebb5374899..000000000000 --- a/examples/single-page-app/ui/src/components/Logout.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import {Navigate} from "react-router-dom" - -export default function Logout() { - return ( - - ) -} diff --git a/examples/single-page-app/ui/src/components/Repos.tsx b/examples/single-page-app/ui/src/components/Repos.tsx deleted file mode 100644 index 93ea2b469288..000000000000 --- a/examples/single-page-app/ui/src/components/Repos.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { - Flex, - Link, - Td, - Th, -} from '@chakra-ui/react' -import React from "react" - -import { - IRepoInfo, - IUserLogin} from "../@types/app" - - -export const RepoTableHeaders = () => { - return ( - <> - Repo - Updated - ) -} - -export const RepoTr: React.FC<{resource: IRepoInfo | IUserLogin}> = ({resource}) => { - const repo = resource as IRepoInfo - return ( - <> - - - - {repo.full_name} - - - - {repo.updated_at} - ) -} diff --git a/examples/single-page-app/ui/src/components/Resources.tsx b/examples/single-page-app/ui/src/components/Resources.tsx deleted file mode 100644 index 388e5c51ac89..000000000000 --- a/examples/single-page-app/ui/src/components/Resources.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { - Button, - Table, - TableCaption, - TableContainer, - Tbody, - Thead, - Tr, -} from '@chakra-ui/react' -import React from "react" - -import {withAuth, withData} from "../hoc" -import { - IActionState, - IDataPayload, - IRepoInfo, - ITableResourceProps, - IUserLogin} from "../@types/app" - -class BaseResources extends React.Component, IActionState> { - - constructor(props: ITableResourceProps) { - super(props) - this.state = {errorMessage: '', isLoading: false} - } - - render() { - const {data, name, headers: Headers, row: Row, title, user} = this.props - const rows = data.state.data?.[name] - const {user: userData} = user.state || {} - const {login} = userData || {} - - if (!login) { - return '' - } - - if (!rows || !Array.isArray(rows)) { - return - } - - return ( - <> - - - - {title} - - - - - - - {rows.map((resource, index: number) => ( - - - - ))} - -
    -
    - - ) - } - - updateResources = async () => { - const {data, name, user} = this.props - const {dispatch} = data - const {proxy_url, user: userData} = user.state - if (!userData) { - return - } - const {login} = userData - try { - const response = await fetch(`${proxy_url}/users/${login}/${name}`) - const resources = await response.json() - const payload: IDataPayload = {} - payload[name] = resources - dispatch({ - type: 'UPDATE', - payload, - }) - } catch (error) { - const e = error as Record<'message', string> - this.setState({ - isLoading: false, - errorMessage: `Sorry! Fetching ${name} failed\n${e.message}`, - }) - } - } -} - -const BaseResourcesWithProps = (props: ITableResourceProps) => { - return -} - -export const Resources = withAuth(withData(BaseResourcesWithProps as React.ComponentType)) diff --git a/examples/single-page-app/ui/src/components/User.tsx b/examples/single-page-app/ui/src/components/User.tsx deleted file mode 100644 index 29e6b13ac78f..000000000000 --- a/examples/single-page-app/ui/src/components/User.tsx +++ /dev/null @@ -1,152 +0,0 @@ -import {useContext} from "react" -import {ChevronDownIcon} from '@chakra-ui/icons' -import { - Button, - Flex, - Image, - Menu, - MenuButton, - MenuList, - MenuItem, - Modal, - ModalBody, - ModalCloseButton, - ModalContent, - ModalFooter, - ModalHeader, - ModalOverlay, - Table, - TableCaption, - TableContainer, - Tbody, - Td, - Thead, - Th, - Tr, - Text, - useDisclosure, - useToast, -} from '@chakra-ui/react' -import {AuthContext} from "../App" -import {IUser, TAuthContext} from "../@types/app" -import {AuthProviders} from "../providers.tsx" - -export const UserMenu = () => { - const {state, dispatch} = useContext(AuthContext) as TAuthContext - const {isOpen, onOpen, onClose} = useDisclosure() - const {authenticating, isLoggedIn, provider, user} = state - const authProvider = AuthProviders[provider] - const toast = useToast() - const handleLogin = async () => { - // This is intercepted and redirected by Envoy - window.location.href = '/login' - } - const handleLogout = async () => { - const response = await fetch('/logout') - if (response.status === 200) { - await dispatch({ - type: "LOGOUT", - }) - } else { - toast({ - title: "Logout failed.", - description: `${response.statusText}`, - status: "error", - duration: 9000, - isClosable: true, - }) - } - } - const { - avatar_url = '...', - login = '...', - public_repos = '0', - followers = '0', - following = '0'} = user as IUser || {} - if (!isLoggedIn) { - const {icon: Icon, name: providerName} = authProvider - const loginText = authenticating ? 'Logging in' : `Login to ${providerName}`; - return ( - - handleLogin()}> - - - {loginText} - - - ) - } - return ( - - }> - - Avatar - {login} - - - - Info - - - - - - Avatar - {login} - - - - - - - Github user metrics - - - - - - - - - - - - - - - - - - - - -
    metriccount
    Repos{public_repos}
    Followers{followers}
    Following{following}
    -
    -
    - - - -
    -
    - handleLogout()}>Logout -
    -
    ) -} diff --git a/examples/single-page-app/ui/src/components/Users.tsx b/examples/single-page-app/ui/src/components/Users.tsx deleted file mode 100644 index d71eb8f8ae06..000000000000 --- a/examples/single-page-app/ui/src/components/Users.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { - Flex, - Image, - Link, - Td, - Th, -} from '@chakra-ui/react' -import React from "react" - -import { - IRepoInfo, - IUserLogin} from "../@types/app" - - -export const RelatedUserTableHeaders = () => { - return ( - <> - User - Full name - ) -} - -export const RelatedUserTr: React.FC<{resource: IRepoInfo | IUserLogin}> = ({resource}) => { - const user = resource as IUserLogin - return ( - <> - - - Avatar - - {user.login} - - - - - {user.name} - - ) -} diff --git a/examples/single-page-app/ui/src/hoc.tsx b/examples/single-page-app/ui/src/hoc.tsx deleted file mode 100644 index 1747d14e3535..000000000000 --- a/examples/single-page-app/ui/src/hoc.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React, {useContext} from "react" - -import {AuthContext, DataContext} from "./App" -import { - IComponentWithDataProp, - IComponentWithUserProp, - TAuthContext, - TDataContext} from "./@types/app" - - -function getDisplayName(WrappedComponent: React.ComponentType): string { - return WrappedComponent.displayName || WrappedComponent.name || 'Component' -} - -export function withAuth(WrappedComponent: React.ComponentType>) { - function WithAuth>(props: TProps) { - return ( - ) - } - WithAuth.displayName = `WithAuth(${getDisplayName(WrappedComponent)})` - return WithAuth -} - -export function withData(WrappedComponent: React.ComponentType>) { - function WithData>(props: TProps) { - return ( - ) - } - WithData.displayName = `WithData(${getDisplayName(WrappedComponent)})` - return WithData -} diff --git a/examples/single-page-app/ui/src/main.tsx b/examples/single-page-app/ui/src/main.tsx deleted file mode 100644 index b176ead5a5a1..000000000000 --- a/examples/single-page-app/ui/src/main.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import ReactDOM from 'react-dom/client' -import App from './App.tsx' - -ReactDOM.createRoot(document.getElementById('root')!).render( - -) diff --git a/examples/single-page-app/ui/src/myhub.tsx b/examples/single-page-app/ui/src/myhub.tsx deleted file mode 100644 index a84e788f4334..000000000000 --- a/examples/single-page-app/ui/src/myhub.tsx +++ /dev/null @@ -1,14 +0,0 @@ - -import {Image} from '@chakra-ui/react' - - -export const MyhubIcon = () => { - return ( - Myhub) -} diff --git a/examples/single-page-app/ui/src/providers.tsx b/examples/single-page-app/ui/src/providers.tsx deleted file mode 100644 index aa4983e9af05..000000000000 --- a/examples/single-page-app/ui/src/providers.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import GithubIcon from "mdi-react/GithubIcon" - -import {IAuthProviders} from "./@types/app" -import {MyhubIcon} from "./myhub" - - -export const AuthProviders: IAuthProviders = { - "myhub": { - "name": "Myhub", - "icon": MyhubIcon}, - "github": { - "name": "Github", - "icon": GithubIcon}} diff --git a/examples/single-page-app/ui/src/store/reducer/index.tsx b/examples/single-page-app/ui/src/store/reducer/index.tsx deleted file mode 100644 index 493e9ff5dd82..000000000000 --- a/examples/single-page-app/ui/src/store/reducer/index.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import {IAuthAction, IAuthState, IDataAction, IDataState} from "../../@types/app" - -export const userInitialState: IAuthState = { - authenticating: false, - failed: false, - isLoggedIn: JSON.parse(localStorage.getItem("isLoggedIn") || 'false'), - user: JSON.parse(localStorage.getItem("user") || 'null'), - proxy_url: `${import.meta.env.VITE_APP_API_URL}/hub`, - provider: import.meta.env.VITE_APP_AUTH_PROVIDER -} - -export const userReducer = (state: IAuthState, action: IAuthAction): IAuthState => { - switch (action.type) { - case "AUTH": { - return { - ...state, - authenticating: true - } - } - case "ERROR": { - return { - ...state, - authenticating: false, - failed: true - } - } - case "LOGIN": { - if (!action.payload) { - throw new Error('LOGIN called without payload') - } - localStorage.setItem("isLoggedIn", JSON.stringify(action.payload.isLoggedIn)) - localStorage.setItem("user", JSON.stringify(action.payload.user)) - return { - ...state, - authenticating: false, - isLoggedIn: action.payload.isLoggedIn, - user: action.payload.user - } - } - case "LOGOUT": { - localStorage.clear() - return { - ...state, - authenticating: false, - isLoggedIn: false, - user: null - } - } - case "RESET": { - return { - ...state, - failed: false - } - } - default: - return state - } -} - -export const dataInitialState: IDataState = { - data: JSON.parse(localStorage.getItem("data") || 'null'), -} - -export const dataReducer = (state: IDataState, action: IDataAction): IDataState => { - switch (action.type) { - case "UPDATE": { - if (!action.payload) { - throw new Error('UPDATE called without payload') - } - const data = { - ...JSON.parse(localStorage.getItem("data") || '{}'), - ...action.payload} - localStorage.setItem("data", JSON.stringify(data)) - return { - ...state, - data - } - } - default: - return state - } -} diff --git a/examples/single-page-app/ui/src/vite-env.d.ts b/examples/single-page-app/ui/src/vite-env.d.ts deleted file mode 100644 index 11f02fe2a006..000000000000 --- a/examples/single-page-app/ui/src/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/examples/single-page-app/ui/tsconfig.json b/examples/single-page-app/ui/tsconfig.json deleted file mode 100644 index a7fc6fbf23de..000000000000 --- a/examples/single-page-app/ui/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["src"], - "references": [{ "path": "./tsconfig.node.json" }] -} diff --git a/examples/single-page-app/ui/tsconfig.node.json b/examples/single-page-app/ui/tsconfig.node.json deleted file mode 100644 index 42872c59f5b0..000000000000 --- a/examples/single-page-app/ui/tsconfig.node.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "skipLibCheck": true, - "module": "ESNext", - "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true - }, - "include": ["vite.config.ts"] -} diff --git a/examples/single-page-app/ui/vite.config.ts b/examples/single-page-app/ui/vite.config.ts deleted file mode 100644 index 3cd02eb20298..000000000000 --- a/examples/single-page-app/ui/vite.config.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {defineConfig} from 'vite' -import react from '@vitejs/plugin-react' - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], - server: { - host: '0.0.0.0', - }, -}) diff --git a/examples/single-page-app/ui/yarn.lock b/examples/single-page-app/ui/yarn.lock deleted file mode 100644 index 3eeb27064e4d..000000000000 --- a/examples/single-page-app/ui/yarn.lock +++ /dev/null @@ -1,4382 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@aashutoshrathi/word-wrap@^1.2.3": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" - integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== - -"@ampproject/remapping@^2.2.0": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" - integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== - dependencies: - "@jridgewell/gen-mapping" "^0.3.0" - "@jridgewell/trace-mapping" "^0.3.9" - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": - version "7.23.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" - integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== - dependencies: - "@babel/highlight" "^7.23.4" - chalk "^2.4.2" - -"@babel/code-frame@^7.24.2": - version "7.24.2" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" - integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ== - dependencies: - "@babel/highlight" "^7.24.2" - picocolors "^1.0.0" - -"@babel/compat-data@^7.23.5": - version "7.23.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" - integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== - -"@babel/core@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.5.tgz#15ab5b98e101972d171aeef92ac70d8d6718f06a" - integrity sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.24.2" - "@babel/generator" "^7.24.5" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-module-transforms" "^7.24.5" - "@babel/helpers" "^7.24.5" - "@babel/parser" "^7.24.5" - "@babel/template" "^7.24.0" - "@babel/traverse" "^7.24.5" - "@babel/types" "^7.24.5" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/generator@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.5.tgz#e5afc068f932f05616b66713e28d0f04e99daeb3" - integrity sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA== - dependencies: - "@babel/types" "^7.24.5" - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.25" - jsesc "^2.5.1" - -"@babel/helper-compilation-targets@^7.23.6": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" - integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== - dependencies: - "@babel/compat-data" "^7.23.5" - "@babel/helper-validator-option" "^7.23.5" - browserslist "^4.22.2" - lru-cache "^5.1.1" - semver "^6.3.1" - -"@babel/helper-environment-visitor@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" - integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== - -"@babel/helper-function-name@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" - integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== - dependencies: - "@babel/template" "^7.22.15" - "@babel/types" "^7.23.0" - -"@babel/helper-hoist-variables@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" - integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-module-imports@^7.16.7": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" - integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== - dependencies: - "@babel/types" "^7.22.15" - -"@babel/helper-module-imports@^7.24.3": - version "7.24.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz#6ac476e6d168c7c23ff3ba3cf4f7841d46ac8128" - integrity sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg== - dependencies: - "@babel/types" "^7.24.0" - -"@babel/helper-module-transforms@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz#ea6c5e33f7b262a0ae762fd5986355c45f54a545" - integrity sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A== - dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-module-imports" "^7.24.3" - "@babel/helper-simple-access" "^7.24.5" - "@babel/helper-split-export-declaration" "^7.24.5" - "@babel/helper-validator-identifier" "^7.24.5" - -"@babel/helper-plugin-utils@^7.24.0", "@babel/helper-plugin-utils@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz#a924607dd254a65695e5bd209b98b902b3b2f11a" - integrity sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ== - -"@babel/helper-simple-access@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz#50da5b72f58c16b07fbd992810be6049478e85ba" - integrity sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ== - dependencies: - "@babel/types" "^7.24.5" - -"@babel/helper-split-export-declaration@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz#b9a67f06a46b0b339323617c8c6213b9055a78b6" - integrity sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q== - dependencies: - "@babel/types" "^7.24.5" - -"@babel/helper-string-parser@^7.23.4": - version "7.23.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" - integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== - -"@babel/helper-string-parser@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz#f99c36d3593db9540705d0739a1f10b5e20c696e" - integrity sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ== - -"@babel/helper-validator-identifier@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" - integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== - -"@babel/helper-validator-identifier@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz#918b1a7fa23056603506370089bd990d8720db62" - integrity sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA== - -"@babel/helper-validator-option@^7.23.5": - version "7.23.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" - integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== - -"@babel/helpers@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.5.tgz#fedeb87eeafa62b621160402181ad8585a22a40a" - integrity sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q== - dependencies: - "@babel/template" "^7.24.0" - "@babel/traverse" "^7.24.5" - "@babel/types" "^7.24.5" - -"@babel/highlight@^7.23.4": - version "7.23.4" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" - integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== - dependencies: - "@babel/helper-validator-identifier" "^7.22.20" - chalk "^2.4.2" - js-tokens "^4.0.0" - -"@babel/highlight@^7.24.2": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.5.tgz#bc0613f98e1dd0720e99b2a9ee3760194a704b6e" - integrity sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw== - dependencies: - "@babel/helper-validator-identifier" "^7.24.5" - chalk "^2.4.2" - js-tokens "^4.0.0" - picocolors "^1.0.0" - -"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b" - integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== - -"@babel/parser@^7.24.0", "@babel/parser@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.5.tgz#4a4d5ab4315579e5398a82dcf636ca80c3392790" - integrity sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg== - -"@babel/plugin-transform-react-jsx-self@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.5.tgz#22cc7572947895c8e4cd034462e65d8ecf857756" - integrity sha512-RtCJoUO2oYrYwFPtR1/jkoBEcFuI1ae9a9IMxeyAVa3a1Ap4AnxmyIKG2b2FaJKqkidw/0cxRbWN+HOs6ZWd1w== - dependencies: - "@babel/helper-plugin-utils" "^7.24.5" - -"@babel/plugin-transform-react-jsx-source@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.1.tgz#a2dedb12b09532846721b5df99e52ef8dc3351d0" - integrity sha512-1v202n7aUq4uXAieRTKcwPzNyphlCuqHHDcdSNc+vdhoTEZcFMh+L5yZuCmGaIO7bs1nJUNfHB89TZyoL48xNA== - dependencies: - "@babel/helper-plugin-utils" "^7.24.0" - -"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3": - version "7.23.8" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.8.tgz#8ee6fe1ac47add7122902f257b8ddf55c898f650" - integrity sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw== - dependencies: - regenerator-runtime "^0.14.0" - -"@babel/template@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" - integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== - dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/parser" "^7.22.15" - "@babel/types" "^7.22.15" - -"@babel/template@^7.24.0": - version "7.24.0" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50" - integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA== - dependencies: - "@babel/code-frame" "^7.23.5" - "@babel/parser" "^7.24.0" - "@babel/types" "^7.24.0" - -"@babel/traverse@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.5.tgz#972aa0bc45f16983bf64aa1f877b2dd0eea7e6f8" - integrity sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA== - dependencies: - "@babel/code-frame" "^7.24.2" - "@babel/generator" "^7.24.5" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.24.5" - "@babel/parser" "^7.24.5" - "@babel/types" "^7.24.5" - debug "^4.3.1" - globals "^11.1.0" - -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.6.tgz#be33fdb151e1f5a56877d704492c240fc71c7ccd" - integrity sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg== - dependencies: - "@babel/helper-string-parser" "^7.23.4" - "@babel/helper-validator-identifier" "^7.22.20" - to-fast-properties "^2.0.0" - -"@babel/types@^7.24.0", "@babel/types@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.5.tgz#7661930afc638a5383eb0c4aee59b74f38db84d7" - integrity sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ== - dependencies: - "@babel/helper-string-parser" "^7.24.1" - "@babel/helper-validator-identifier" "^7.24.5" - to-fast-properties "^2.0.0" - -"@chakra-ui/accordion@2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@chakra-ui/accordion/-/accordion-2.3.1.tgz#a326509e286a5c4e8478de9bc2b4b05017039e6b" - integrity sha512-FSXRm8iClFyU+gVaXisOSEw0/4Q+qZbFRiuhIAkVU6Boj0FxAMrlo9a8AV5TuF77rgaHytCdHk0Ng+cyUijrag== - dependencies: - "@chakra-ui/descendant" "3.1.0" - "@chakra-ui/icon" "3.2.0" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/react-use-controllable-state" "2.1.0" - "@chakra-ui/react-use-merge-refs" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - "@chakra-ui/transition" "2.1.0" - -"@chakra-ui/alert@2.2.2": - version "2.2.2" - resolved "https://registry.yarnpkg.com/@chakra-ui/alert/-/alert-2.2.2.tgz#aeba951d120c7c6e69d5f515a695ad6e4db43ffe" - integrity sha512-jHg4LYMRNOJH830ViLuicjb3F+v6iriE/2G5T+Sd0Hna04nukNJ1MxUmBPE+vI22me2dIflfelu2v9wdB6Pojw== - dependencies: - "@chakra-ui/icon" "3.2.0" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - "@chakra-ui/spinner" "2.1.0" - -"@chakra-ui/anatomy@2.2.2": - version "2.2.2" - resolved "https://registry.yarnpkg.com/@chakra-ui/anatomy/-/anatomy-2.2.2.tgz#2d0e14cba2534d92077ca28abf8c183b6e27897b" - integrity sha512-MV6D4VLRIHr4PkW4zMyqfrNS1mPlCTiCXwvYGtDFQYr+xHFfonhAuf9WjsSc0nyp2m0OdkSLnzmVKkZFLo25Tg== - -"@chakra-ui/avatar@2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/avatar/-/avatar-2.3.0.tgz#f018a2714d1e3ba5970bcf66558887925fdfccf4" - integrity sha512-8gKSyLfygnaotbJbDMHDiJoF38OHXUYVme4gGxZ1fLnQEdPVEaIWfH+NndIjOM0z8S+YEFnT9KyGMUtvPrBk3g== - dependencies: - "@chakra-ui/image" "2.1.0" - "@chakra-ui/react-children-utils" "2.0.6" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/breadcrumb@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/breadcrumb/-/breadcrumb-2.2.0.tgz#751bc48498f3c403f97b5d9aae528ebfd405ef48" - integrity sha512-4cWCG24flYBxjruRi4RJREWTGF74L/KzI2CognAW/d/zWR0CjiScuJhf37Am3LFbCySP6WSoyBOtTIoTA4yLEA== - dependencies: - "@chakra-ui/react-children-utils" "2.0.6" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/breakpoint-utils@2.0.8": - version "2.0.8" - resolved "https://registry.yarnpkg.com/@chakra-ui/breakpoint-utils/-/breakpoint-utils-2.0.8.tgz#750d3712668b69f6e8917b45915cee0e08688eed" - integrity sha512-Pq32MlEX9fwb5j5xx8s18zJMARNHlQZH2VH1RZgfgRDpp7DcEgtRW5AInfN5CfqdHLO1dGxA7I3MqEuL5JnIsA== - dependencies: - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/button@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/button/-/button-2.1.0.tgz#623ed32cc92fc8e52492923e9924791fc6f25447" - integrity sha512-95CplwlRKmmUXkdEp/21VkEWgnwcx2TOBG6NfYlsuLBDHSLlo5FKIiE2oSi4zXc4TLcopGcWPNcm/NDaSC5pvA== - dependencies: - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/react-use-merge-refs" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - "@chakra-ui/spinner" "2.1.0" - -"@chakra-ui/card@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/card/-/card-2.2.0.tgz#b5e59dc51c171fced76ea76bf26088803b8bc184" - integrity sha512-xUB/k5MURj4CtPAhdSoXZidUbm8j3hci9vnc+eZJVDqhDOShNlD6QeniQNRPRys4lWAQLCbFcrwL29C8naDi6g== - dependencies: - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/checkbox@2.3.2": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@chakra-ui/checkbox/-/checkbox-2.3.2.tgz#4ecb14a2f57b7470d1a58542ca4691c3b105bfa1" - integrity sha512-85g38JIXMEv6M+AcyIGLh7igNtfpAN6KGQFYxY9tBj0eWvWk4NKQxvqqyVta0bSAyIl1rixNIIezNpNWk2iO4g== - dependencies: - "@chakra-ui/form-control" "2.2.0" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/react-types" "2.0.7" - "@chakra-ui/react-use-callback-ref" "2.1.0" - "@chakra-ui/react-use-controllable-state" "2.1.0" - "@chakra-ui/react-use-merge-refs" "2.1.0" - "@chakra-ui/react-use-safe-layout-effect" "2.1.0" - "@chakra-ui/react-use-update-effect" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - "@chakra-ui/visually-hidden" "2.2.0" - "@zag-js/focus-visible" "0.16.0" - -"@chakra-ui/clickable@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/clickable/-/clickable-2.1.0.tgz#800fa8d10cf45a41fc50a3df32c679a3ce1921c3" - integrity sha512-flRA/ClPUGPYabu+/GLREZVZr9j2uyyazCAUHAdrTUEdDYCr31SVGhgh7dgKdtq23bOvAQJpIJjw/0Bs0WvbXw== - dependencies: - "@chakra-ui/react-use-merge-refs" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/close-button@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@chakra-ui/close-button/-/close-button-2.1.1.tgz#995b245c56eb41465a71d8667840c238618a7b66" - integrity sha512-gnpENKOanKexswSVpVz7ojZEALl2x5qjLYNqSQGbxz+aP9sOXPfUS56ebyBrre7T7exuWGiFeRwnM0oVeGPaiw== - dependencies: - "@chakra-ui/icon" "3.2.0" - -"@chakra-ui/color-mode@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/color-mode/-/color-mode-2.2.0.tgz#828d47234c74ba2fb4c5dd63a63331aead20b9f6" - integrity sha512-niTEA8PALtMWRI9wJ4LL0CSBDo8NBfLNp4GD6/0hstcm3IlbBHTVKxN6HwSaoNYfphDQLxCjT4yG+0BJA5tFpg== - dependencies: - "@chakra-ui/react-use-safe-layout-effect" "2.1.0" - -"@chakra-ui/control-box@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/control-box/-/control-box-2.1.0.tgz#0f4586797b3154c02463bc5c106782e70c88f04f" - integrity sha512-gVrRDyXFdMd8E7rulL0SKeoljkLQiPITFnsyMO8EFHNZ+AHt5wK4LIguYVEq88APqAGZGfHFWXr79RYrNiE3Mg== - -"@chakra-ui/counter@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/counter/-/counter-2.1.0.tgz#e413a2f1093a18f847bb7aa240117fde788a59e6" - integrity sha512-s6hZAEcWT5zzjNz2JIWUBzRubo9la/oof1W7EKZVVfPYHERnl5e16FmBC79Yfq8p09LQ+aqFKm/etYoJMMgghw== - dependencies: - "@chakra-ui/number-utils" "2.0.7" - "@chakra-ui/react-use-callback-ref" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/css-reset@2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/css-reset/-/css-reset-2.3.0.tgz#83e3160a9c2a12431cad0ee27ebfbf3aedc5c9c7" - integrity sha512-cQwwBy5O0jzvl0K7PLTLgp8ijqLPKyuEMiDXwYzl95seD3AoeuoCLyzZcJtVqaUZ573PiBdAbY/IlZcwDOItWg== - -"@chakra-ui/descendant@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/descendant/-/descendant-3.1.0.tgz#f3b80ed13ffc4bf1d615b3ed5541bd0905375cca" - integrity sha512-VxCIAir08g5w27klLyi7PVo8BxhW4tgU/lxQyujkmi4zx7hT9ZdrcQLAted/dAa+aSIZ14S1oV0Q9lGjsAdxUQ== - dependencies: - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/react-use-merge-refs" "2.1.0" - -"@chakra-ui/dom-utils@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/dom-utils/-/dom-utils-2.1.0.tgz#d15df89e458ef19756db04c7cfd084eb552454f0" - integrity sha512-ZmF2qRa1QZ0CMLU8M1zCfmw29DmPNtfjR9iTo74U5FPr3i1aoAh7fbJ4qAlZ197Xw9eAW28tvzQuoVWeL5C7fQ== - -"@chakra-ui/editable@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/editable/-/editable-3.1.0.tgz#065783c2e3389c3bb9ab0582cb50d38e1dc00fa1" - integrity sha512-j2JLrUL9wgg4YA6jLlbU88370eCRyor7DZQD9lzpY95tSOXpTljeg3uF9eOmDnCs6fxp3zDWIfkgMm/ExhcGTg== - dependencies: - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/react-types" "2.0.7" - "@chakra-ui/react-use-callback-ref" "2.1.0" - "@chakra-ui/react-use-controllable-state" "2.1.0" - "@chakra-ui/react-use-focus-on-pointer-down" "2.1.0" - "@chakra-ui/react-use-merge-refs" "2.1.0" - "@chakra-ui/react-use-safe-layout-effect" "2.1.0" - "@chakra-ui/react-use-update-effect" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/event-utils@2.0.8": - version "2.0.8" - resolved "https://registry.yarnpkg.com/@chakra-ui/event-utils/-/event-utils-2.0.8.tgz#e6439ba200825a2f15d8f1973d267d1c00a6d1b4" - integrity sha512-IGM/yGUHS+8TOQrZGpAKOJl/xGBrmRYJrmbHfUE7zrG3PpQyXvbLDP1M+RggkCFVgHlJi2wpYIf0QtQlU0XZfw== - -"@chakra-ui/focus-lock@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/focus-lock/-/focus-lock-2.1.0.tgz#580e5450fe85356987b9a246abaff8333369c667" - integrity sha512-EmGx4PhWGjm4dpjRqM4Aa+rCWBxP+Rq8Uc/nAVnD4YVqkEhBkrPTpui2lnjsuxqNaZ24fIAZ10cF1hlpemte/w== - dependencies: - "@chakra-ui/dom-utils" "2.1.0" - react-focus-lock "^2.9.4" - -"@chakra-ui/form-control@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/form-control/-/form-control-2.2.0.tgz#69c771d6406ddac8ab357ae88446cc11827656a4" - integrity sha512-wehLC1t4fafCVJ2RvJQT2jyqsAwX7KymmiGqBu7nQoQz8ApTkGABWpo/QwDh3F/dBLrouHDoOvGmYTqft3Mirw== - dependencies: - "@chakra-ui/icon" "3.2.0" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/react-types" "2.0.7" - "@chakra-ui/react-use-merge-refs" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/hooks@2.2.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@chakra-ui/hooks/-/hooks-2.2.1.tgz#b86ce5eeaaab877ddcb11a50842d1227306ace28" - integrity sha512-RQbTnzl6b1tBjbDPf9zGRo9rf/pQMholsOudTxjy4i9GfTfz6kgp5ValGjQm2z7ng6Z31N1cnjZ1AlSzQ//ZfQ== - dependencies: - "@chakra-ui/react-utils" "2.0.12" - "@chakra-ui/utils" "2.0.15" - compute-scroll-into-view "3.0.3" - copy-to-clipboard "3.3.3" - -"@chakra-ui/icon@3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/icon/-/icon-3.2.0.tgz#92b9454aa0d561b4994bcd6a1b3bb1fdd5c67bef" - integrity sha512-xxjGLvlX2Ys4H0iHrI16t74rG9EBcpFvJ3Y3B7KMQTrnW34Kf7Da/UC8J67Gtx85mTHW020ml85SVPKORWNNKQ== - dependencies: - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/icons@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@chakra-ui/icons/-/icons-2.1.1.tgz#58ff0f9e703f2f4f89debd600ce4e438f43f9c9a" - integrity sha512-3p30hdo4LlRZTT5CwoAJq3G9fHI0wDc0pBaMHj4SUn0yomO+RcDRlzhdXqdr5cVnzax44sqXJVnf3oQG0eI+4g== - dependencies: - "@chakra-ui/icon" "3.2.0" - -"@chakra-ui/image@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/image/-/image-2.1.0.tgz#6c205f1ca148e3bf58345b0b5d4eb3d959eb9f87" - integrity sha512-bskumBYKLiLMySIWDGcz0+D9Th0jPvmX6xnRMs4o92tT3Od/bW26lahmV2a2Op2ItXeCmRMY+XxJH5Gy1i46VA== - dependencies: - "@chakra-ui/react-use-safe-layout-effect" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/input@2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@chakra-ui/input/-/input-2.1.2.tgz#0cad49ec372f8f21f2f4f1db365f34b9a708ff9d" - integrity sha512-GiBbb3EqAA8Ph43yGa6Mc+kUPjh4Spmxp1Pkelr8qtudpc3p2PJOOebLpd90mcqw8UePPa+l6YhhPtp6o0irhw== - dependencies: - "@chakra-ui/form-control" "2.2.0" - "@chakra-ui/object-utils" "2.1.0" - "@chakra-ui/react-children-utils" "2.0.6" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/layout@2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@chakra-ui/layout/-/layout-2.3.1.tgz#0601c5eb91555d24a7015a7c9d4e01fed2698557" - integrity sha512-nXuZ6WRbq0WdgnRgLw+QuxWAHuhDtVX8ElWqcTK+cSMFg/52eVP47czYBE5F35YhnoW2XBwfNoNgZ7+e8Z01Rg== - dependencies: - "@chakra-ui/breakpoint-utils" "2.0.8" - "@chakra-ui/icon" "3.2.0" - "@chakra-ui/object-utils" "2.1.0" - "@chakra-ui/react-children-utils" "2.0.6" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/lazy-utils@2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@chakra-ui/lazy-utils/-/lazy-utils-2.0.5.tgz#363c3fa1d421362790b416ffa595acb835e1ae5b" - integrity sha512-UULqw7FBvcckQk2n3iPO56TMJvDsNv0FKZI6PlUNJVaGsPbsYxK/8IQ60vZgaTVPtVcjY6BE+y6zg8u9HOqpyg== - -"@chakra-ui/live-region@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/live-region/-/live-region-2.1.0.tgz#02b4b1d997075f19a7a9a87187e08c72e82ef0dd" - integrity sha512-ZOxFXwtaLIsXjqnszYYrVuswBhnIHHP+XIgK1vC6DePKtyK590Wg+0J0slDwThUAd4MSSIUa/nNX84x1GMphWw== - -"@chakra-ui/media-query@3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/media-query/-/media-query-3.3.0.tgz#40f9151dedb6a7af9df3be0474b59a799c92c619" - integrity sha512-IsTGgFLoICVoPRp9ykOgqmdMotJG0CnPsKvGQeSFOB/dZfIujdVb14TYxDU4+MURXry1MhJ7LzZhv+Ml7cr8/g== - dependencies: - "@chakra-ui/breakpoint-utils" "2.0.8" - "@chakra-ui/react-env" "3.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/menu@2.2.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@chakra-ui/menu/-/menu-2.2.1.tgz#7d9810d435f6b40fa72ed867a33b88a1ef75073f" - integrity sha512-lJS7XEObzJxsOwWQh7yfG4H8FzFPRP5hVPN/CL+JzytEINCSBvsCDHrYPQGp7jzpCi8vnTqQQGQe0f8dwnXd2g== - dependencies: - "@chakra-ui/clickable" "2.1.0" - "@chakra-ui/descendant" "3.1.0" - "@chakra-ui/lazy-utils" "2.0.5" - "@chakra-ui/popper" "3.1.0" - "@chakra-ui/react-children-utils" "2.0.6" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/react-use-animation-state" "2.1.0" - "@chakra-ui/react-use-controllable-state" "2.1.0" - "@chakra-ui/react-use-disclosure" "2.1.0" - "@chakra-ui/react-use-focus-effect" "2.1.0" - "@chakra-ui/react-use-merge-refs" "2.1.0" - "@chakra-ui/react-use-outside-click" "2.2.0" - "@chakra-ui/react-use-update-effect" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - "@chakra-ui/transition" "2.1.0" - -"@chakra-ui/modal@2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@chakra-ui/modal/-/modal-2.3.1.tgz#524dc32b6b4f545b54ae531dbf6c74e1052ee794" - integrity sha512-TQv1ZaiJMZN+rR9DK0snx/OPwmtaGH1HbZtlYt4W4s6CzyK541fxLRTjIXfEzIGpvNW+b6VFuFjbcR78p4DEoQ== - dependencies: - "@chakra-ui/close-button" "2.1.1" - "@chakra-ui/focus-lock" "2.1.0" - "@chakra-ui/portal" "2.1.0" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/react-types" "2.0.7" - "@chakra-ui/react-use-merge-refs" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - "@chakra-ui/transition" "2.1.0" - aria-hidden "^1.2.3" - react-remove-scroll "^2.5.6" - -"@chakra-ui/number-input@2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@chakra-ui/number-input/-/number-input-2.1.2.tgz#dda9095fba6a4b89212332db02831b94120da163" - integrity sha512-pfOdX02sqUN0qC2ysuvgVDiws7xZ20XDIlcNhva55Jgm095xjm8eVdIBfNm3SFbSUNxyXvLTW/YQanX74tKmuA== - dependencies: - "@chakra-ui/counter" "2.1.0" - "@chakra-ui/form-control" "2.2.0" - "@chakra-ui/icon" "3.2.0" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/react-types" "2.0.7" - "@chakra-ui/react-use-callback-ref" "2.1.0" - "@chakra-ui/react-use-event-listener" "2.1.0" - "@chakra-ui/react-use-interval" "2.1.0" - "@chakra-ui/react-use-merge-refs" "2.1.0" - "@chakra-ui/react-use-safe-layout-effect" "2.1.0" - "@chakra-ui/react-use-update-effect" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/number-utils@2.0.7": - version "2.0.7" - resolved "https://registry.yarnpkg.com/@chakra-ui/number-utils/-/number-utils-2.0.7.tgz#aaee979ca2fb1923a0373a91619473811315db11" - integrity sha512-yOGxBjXNvLTBvQyhMDqGU0Oj26s91mbAlqKHiuw737AXHt0aPllOthVUqQMeaYLwLCjGMg0jtI7JReRzyi94Dg== - -"@chakra-ui/object-utils@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/object-utils/-/object-utils-2.1.0.tgz#a4ecf9cea92f1de09f5531f53ffdc41e0b19b6c3" - integrity sha512-tgIZOgLHaoti5PYGPTwK3t/cqtcycW0owaiOXoZOcpwwX/vlVb+H1jFsQyWiiwQVPt9RkoSLtxzXamx+aHH+bQ== - -"@chakra-ui/pin-input@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/pin-input/-/pin-input-2.1.0.tgz#61e6bbf909ec510634307b2861c4f1891a9f8d81" - integrity sha512-x4vBqLStDxJFMt+jdAHHS8jbh294O53CPQJoL4g228P513rHylV/uPscYUHrVJXRxsHfRztQO9k45jjTYaPRMw== - dependencies: - "@chakra-ui/descendant" "3.1.0" - "@chakra-ui/react-children-utils" "2.0.6" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/react-use-controllable-state" "2.1.0" - "@chakra-ui/react-use-merge-refs" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/popover@2.2.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@chakra-ui/popover/-/popover-2.2.1.tgz#89cfd29817abcd204da570073c0f2b4d8072c3a3" - integrity sha512-K+2ai2dD0ljvJnlrzesCDT9mNzLifE3noGKZ3QwLqd/K34Ym1W/0aL1ERSynrcG78NKoXS54SdEzkhCZ4Gn/Zg== - dependencies: - "@chakra-ui/close-button" "2.1.1" - "@chakra-ui/lazy-utils" "2.0.5" - "@chakra-ui/popper" "3.1.0" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/react-types" "2.0.7" - "@chakra-ui/react-use-animation-state" "2.1.0" - "@chakra-ui/react-use-disclosure" "2.1.0" - "@chakra-ui/react-use-focus-effect" "2.1.0" - "@chakra-ui/react-use-focus-on-pointer-down" "2.1.0" - "@chakra-ui/react-use-merge-refs" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/popper@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/popper/-/popper-3.1.0.tgz#92a9180c6894763af3b22a6003f9a9d958fe2659" - integrity sha512-ciDdpdYbeFG7og6/6J8lkTFxsSvwTdMLFkpVylAF6VNC22jssiWfquj2eyD4rJnzkRFPvIWJq8hvbfhsm+AjSg== - dependencies: - "@chakra-ui/react-types" "2.0.7" - "@chakra-ui/react-use-merge-refs" "2.1.0" - "@popperjs/core" "^2.9.3" - -"@chakra-ui/portal@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/portal/-/portal-2.1.0.tgz#9e7f57424d7041738b6563cac80134561080bd27" - integrity sha512-9q9KWf6SArEcIq1gGofNcFPSWEyl+MfJjEUg/un1SMlQjaROOh3zYr+6JAwvcORiX7tyHosnmWC3d3wI2aPSQg== - dependencies: - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/react-use-safe-layout-effect" "2.1.0" - -"@chakra-ui/progress@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/progress/-/progress-2.2.0.tgz#67444ea9779631d7c8395b2c9c78e5634f994999" - integrity sha512-qUXuKbuhN60EzDD9mHR7B67D7p/ZqNS2Aze4Pbl1qGGZfulPW0PY8Rof32qDtttDQBkzQIzFGE8d9QpAemToIQ== - dependencies: - "@chakra-ui/react-context" "2.1.0" - -"@chakra-ui/provider@2.4.2": - version "2.4.2" - resolved "https://registry.yarnpkg.com/@chakra-ui/provider/-/provider-2.4.2.tgz#92cb10b6a7df0720e3fa62716dc7cd872ae3ea3d" - integrity sha512-w0Tef5ZCJK1mlJorcSjItCSbyvVuqpvyWdxZiVQmE6fvSJR83wZof42ux0+sfWD+I7rHSfj+f9nzhNaEWClysw== - dependencies: - "@chakra-ui/css-reset" "2.3.0" - "@chakra-ui/portal" "2.1.0" - "@chakra-ui/react-env" "3.1.0" - "@chakra-ui/system" "2.6.2" - "@chakra-ui/utils" "2.0.15" - -"@chakra-ui/radio@2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@chakra-ui/radio/-/radio-2.1.2.tgz#66db19c61a2e628aaf5e727027f7c3b4006ea898" - integrity sha512-n10M46wJrMGbonaghvSRnZ9ToTv/q76Szz284gv4QUWvyljQACcGrXIONUnQ3BIwbOfkRqSk7Xl/JgZtVfll+w== - dependencies: - "@chakra-ui/form-control" "2.2.0" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/react-types" "2.0.7" - "@chakra-ui/react-use-merge-refs" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - "@zag-js/focus-visible" "0.16.0" - -"@chakra-ui/react-children-utils@2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-children-utils/-/react-children-utils-2.0.6.tgz#6c480c6a60678fcb75cb7d57107c7a79e5179b92" - integrity sha512-QVR2RC7QsOsbWwEnq9YduhpqSFnZGvjjGREV8ygKi8ADhXh93C8azLECCUVgRJF2Wc+So1fgxmjLcbZfY2VmBA== - -"@chakra-ui/react-context@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-context/-/react-context-2.1.0.tgz#4858be1d5ff1c8ac0a0ec088d93a3b7f1cbbff99" - integrity sha512-iahyStvzQ4AOwKwdPReLGfDesGG+vWJfEsn0X/NoGph/SkN+HXtv2sCfYFFR9k7bb+Kvc6YfpLlSuLvKMHi2+w== - -"@chakra-ui/react-env@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-env/-/react-env-3.1.0.tgz#7d3c1c05a501bb369524d9f3d38c9325eb16ab50" - integrity sha512-Vr96GV2LNBth3+IKzr/rq1IcnkXv+MLmwjQH6C8BRtn3sNskgDFD5vLkVXcEhagzZMCh8FR3V/bzZPojBOyNhw== - dependencies: - "@chakra-ui/react-use-safe-layout-effect" "2.1.0" - -"@chakra-ui/react-types@2.0.7": - version "2.0.7" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-types/-/react-types-2.0.7.tgz#799c166a44882b23059c8f510eac9bd5d0869ac4" - integrity sha512-12zv2qIZ8EHwiytggtGvo4iLT0APris7T0qaAWqzpUGS0cdUtR8W+V1BJ5Ocq+7tA6dzQ/7+w5hmXih61TuhWQ== - -"@chakra-ui/react-use-animation-state@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-animation-state/-/react-use-animation-state-2.1.0.tgz#eab661fbafd96804fe867b0df0c27e78feefe6e2" - integrity sha512-CFZkQU3gmDBwhqy0vC1ryf90BVHxVN8cTLpSyCpdmExUEtSEInSCGMydj2fvn7QXsz/za8JNdO2xxgJwxpLMtg== - dependencies: - "@chakra-ui/dom-utils" "2.1.0" - "@chakra-ui/react-use-event-listener" "2.1.0" - -"@chakra-ui/react-use-callback-ref@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-callback-ref/-/react-use-callback-ref-2.1.0.tgz#a508085f4d9e7d84d4ceffdf5f41745c9ac451d7" - integrity sha512-efnJrBtGDa4YaxDzDE90EnKD3Vkh5a1t3w7PhnRQmsphLy3g2UieasoKTlT2Hn118TwDjIv5ZjHJW6HbzXA9wQ== - -"@chakra-ui/react-use-controllable-state@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-controllable-state/-/react-use-controllable-state-2.1.0.tgz#8fb6fa2f45d0c04173582ae8297e604ffdb9c7d9" - integrity sha512-QR/8fKNokxZUs4PfxjXuwl0fj/d71WPrmLJvEpCTkHjnzu7LnYvzoe2wB867IdooQJL0G1zBxl0Dq+6W1P3jpg== - dependencies: - "@chakra-ui/react-use-callback-ref" "2.1.0" - -"@chakra-ui/react-use-disclosure@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-disclosure/-/react-use-disclosure-2.1.0.tgz#90093eaf45db1bea7a6851dd0ce5cdb3eb66f90a" - integrity sha512-Ax4pmxA9LBGMyEZJhhUZobg9C0t3qFE4jVF1tGBsrLDcdBeLR9fwOogIPY9Hf0/wqSlAryAimICbr5hkpa5GSw== - dependencies: - "@chakra-ui/react-use-callback-ref" "2.1.0" - -"@chakra-ui/react-use-event-listener@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-event-listener/-/react-use-event-listener-2.1.0.tgz#afea2645bd9b38f754fc2b8eb858f9bb22385ded" - integrity sha512-U5greryDLS8ISP69DKDsYcsXRtAdnTQT+jjIlRYZ49K/XhUR/AqVZCK5BkR1spTDmO9H8SPhgeNKI70ODuDU/Q== - dependencies: - "@chakra-ui/react-use-callback-ref" "2.1.0" - -"@chakra-ui/react-use-focus-effect@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-focus-effect/-/react-use-focus-effect-2.1.0.tgz#963fb790370dfadd51d12666ff2da60706f53a2a" - integrity sha512-xzVboNy7J64xveLcxTIJ3jv+lUJKDwRM7Szwn9tNzUIPD94O3qwjV7DDCUzN2490nSYDF4OBMt/wuDBtaR3kUQ== - dependencies: - "@chakra-ui/dom-utils" "2.1.0" - "@chakra-ui/react-use-event-listener" "2.1.0" - "@chakra-ui/react-use-safe-layout-effect" "2.1.0" - "@chakra-ui/react-use-update-effect" "2.1.0" - -"@chakra-ui/react-use-focus-on-pointer-down@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-focus-on-pointer-down/-/react-use-focus-on-pointer-down-2.1.0.tgz#2fbcf6bc7d06d97606747e231a908d5c387ca0cc" - integrity sha512-2jzrUZ+aiCG/cfanrolsnSMDykCAbv9EK/4iUyZno6BYb3vziucmvgKuoXbMPAzWNtwUwtuMhkby8rc61Ue+Lg== - dependencies: - "@chakra-ui/react-use-event-listener" "2.1.0" - -"@chakra-ui/react-use-interval@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-interval/-/react-use-interval-2.1.0.tgz#2602c097b3ab74b6644812e4f5efaad621218d98" - integrity sha512-8iWj+I/+A0J08pgEXP1J1flcvhLBHkk0ln7ZvGIyXiEyM6XagOTJpwNhiu+Bmk59t3HoV/VyvyJTa+44sEApuw== - dependencies: - "@chakra-ui/react-use-callback-ref" "2.1.0" - -"@chakra-ui/react-use-latest-ref@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-latest-ref/-/react-use-latest-ref-2.1.0.tgz#d1e926130102566ece1d39f8a48ed125e0c8441a" - integrity sha512-m0kxuIYqoYB0va9Z2aW4xP/5b7BzlDeWwyXCH6QpT2PpW3/281L3hLCm1G0eOUcdVlayqrQqOeD6Mglq+5/xoQ== - -"@chakra-ui/react-use-merge-refs@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-merge-refs/-/react-use-merge-refs-2.1.0.tgz#c0c233527abdbea9a1348269c192012205762314" - integrity sha512-lERa6AWF1cjEtWSGjxWTaSMvneccnAVH4V4ozh8SYiN9fSPZLlSG3kNxfNzdFvMEhM7dnP60vynF7WjGdTgQbQ== - -"@chakra-ui/react-use-outside-click@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-outside-click/-/react-use-outside-click-2.2.0.tgz#5570b772a255f6f02b69e967127397c1b5fa3d3c" - integrity sha512-PNX+s/JEaMneijbgAM4iFL+f3m1ga9+6QK0E5Yh4s8KZJQ/bLwZzdhMz8J/+mL+XEXQ5J0N8ivZN28B82N1kNw== - dependencies: - "@chakra-ui/react-use-callback-ref" "2.1.0" - -"@chakra-ui/react-use-pan-event@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-pan-event/-/react-use-pan-event-2.1.0.tgz#51c21bc3c0e9e73d1faef5ea4f7e3c3d071a2758" - integrity sha512-xmL2qOHiXqfcj0q7ZK5s9UjTh4Gz0/gL9jcWPA6GVf+A0Od5imEDa/Vz+533yQKWiNSm1QGrIj0eJAokc7O4fg== - dependencies: - "@chakra-ui/event-utils" "2.0.8" - "@chakra-ui/react-use-latest-ref" "2.1.0" - framesync "6.1.2" - -"@chakra-ui/react-use-previous@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-previous/-/react-use-previous-2.1.0.tgz#f6046e6f7398b1e8d7e66ff7ebb8d61c92a2d3d0" - integrity sha512-pjxGwue1hX8AFcmjZ2XfrQtIJgqbTF3Qs1Dy3d1krC77dEsiCUbQ9GzOBfDc8pfd60DrB5N2tg5JyHbypqh0Sg== - -"@chakra-ui/react-use-safe-layout-effect@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-safe-layout-effect/-/react-use-safe-layout-effect-2.1.0.tgz#3a95f0ba6fd5d2d0aa14919160f2c825f13e686f" - integrity sha512-Knbrrx/bcPwVS1TorFdzrK/zWA8yuU/eaXDkNj24IrKoRlQrSBFarcgAEzlCHtzuhufP3OULPkELTzz91b0tCw== - -"@chakra-ui/react-use-size@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-size/-/react-use-size-2.1.0.tgz#fcf3070eaade8b4a84af8ce5341c4d5ca0a42bec" - integrity sha512-tbLqrQhbnqOjzTaMlYytp7wY8BW1JpL78iG7Ru1DlV4EWGiAmXFGvtnEt9HftU0NJ0aJyjgymkxfVGI55/1Z4A== - dependencies: - "@zag-js/element-size" "0.10.5" - -"@chakra-ui/react-use-timeout@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-timeout/-/react-use-timeout-2.1.0.tgz#24415f54267d7241a3c1d36a5cae4d472834cef7" - integrity sha512-cFN0sobKMM9hXUhyCofx3/Mjlzah6ADaEl/AXl5Y+GawB5rgedgAcu2ErAgarEkwvsKdP6c68CKjQ9dmTQlJxQ== - dependencies: - "@chakra-ui/react-use-callback-ref" "2.1.0" - -"@chakra-ui/react-use-update-effect@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-update-effect/-/react-use-update-effect-2.1.0.tgz#5c57cd1f50c2a6a8119e0f57f69510723d69884b" - integrity sha512-ND4Q23tETaR2Qd3zwCKYOOS1dfssojPLJMLvUtUbW5M9uW1ejYWgGUobeAiOVfSplownG8QYMmHTP86p/v0lbA== - -"@chakra-ui/react-utils@2.0.12": - version "2.0.12" - resolved "https://registry.yarnpkg.com/@chakra-ui/react-utils/-/react-utils-2.0.12.tgz#d6b773b9a5b2e51dce61f51ac8a0e9a0f534f479" - integrity sha512-GbSfVb283+YA3kA8w8xWmzbjNWk14uhNpntnipHCftBibl0lxtQ9YqMFQLwuFOO0U2gYVocszqqDWX+XNKq9hw== - dependencies: - "@chakra-ui/utils" "2.0.15" - -"@chakra-ui/react@^2.8.2": - version "2.8.2" - resolved "https://registry.yarnpkg.com/@chakra-ui/react/-/react-2.8.2.tgz#94d692fb35e4447748c5bfd73d8d38a746193c7d" - integrity sha512-Hn0moyxxyCDKuR9ywYpqgX8dvjqwu9ArwpIb9wHNYjnODETjLwazgNIliCVBRcJvysGRiV51U2/JtJVrpeCjUQ== - dependencies: - "@chakra-ui/accordion" "2.3.1" - "@chakra-ui/alert" "2.2.2" - "@chakra-ui/avatar" "2.3.0" - "@chakra-ui/breadcrumb" "2.2.0" - "@chakra-ui/button" "2.1.0" - "@chakra-ui/card" "2.2.0" - "@chakra-ui/checkbox" "2.3.2" - "@chakra-ui/close-button" "2.1.1" - "@chakra-ui/control-box" "2.1.0" - "@chakra-ui/counter" "2.1.0" - "@chakra-ui/css-reset" "2.3.0" - "@chakra-ui/editable" "3.1.0" - "@chakra-ui/focus-lock" "2.1.0" - "@chakra-ui/form-control" "2.2.0" - "@chakra-ui/hooks" "2.2.1" - "@chakra-ui/icon" "3.2.0" - "@chakra-ui/image" "2.1.0" - "@chakra-ui/input" "2.1.2" - "@chakra-ui/layout" "2.3.1" - "@chakra-ui/live-region" "2.1.0" - "@chakra-ui/media-query" "3.3.0" - "@chakra-ui/menu" "2.2.1" - "@chakra-ui/modal" "2.3.1" - "@chakra-ui/number-input" "2.1.2" - "@chakra-ui/pin-input" "2.1.0" - "@chakra-ui/popover" "2.2.1" - "@chakra-ui/popper" "3.1.0" - "@chakra-ui/portal" "2.1.0" - "@chakra-ui/progress" "2.2.0" - "@chakra-ui/provider" "2.4.2" - "@chakra-ui/radio" "2.1.2" - "@chakra-ui/react-env" "3.1.0" - "@chakra-ui/select" "2.1.2" - "@chakra-ui/skeleton" "2.1.0" - "@chakra-ui/skip-nav" "2.1.0" - "@chakra-ui/slider" "2.1.0" - "@chakra-ui/spinner" "2.1.0" - "@chakra-ui/stat" "2.1.1" - "@chakra-ui/stepper" "2.3.1" - "@chakra-ui/styled-system" "2.9.2" - "@chakra-ui/switch" "2.1.2" - "@chakra-ui/system" "2.6.2" - "@chakra-ui/table" "2.1.0" - "@chakra-ui/tabs" "3.0.0" - "@chakra-ui/tag" "3.1.1" - "@chakra-ui/textarea" "2.1.2" - "@chakra-ui/theme" "3.3.1" - "@chakra-ui/theme-utils" "2.0.21" - "@chakra-ui/toast" "7.0.2" - "@chakra-ui/tooltip" "2.3.1" - "@chakra-ui/transition" "2.1.0" - "@chakra-ui/utils" "2.0.15" - "@chakra-ui/visually-hidden" "2.2.0" - -"@chakra-ui/select@2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@chakra-ui/select/-/select-2.1.2.tgz#f57d6cec0559373c32094fd4a5abd32855829264" - integrity sha512-ZwCb7LqKCVLJhru3DXvKXpZ7Pbu1TDZ7N0PdQ0Zj1oyVLJyrpef1u9HR5u0amOpqcH++Ugt0f5JSmirjNlctjA== - dependencies: - "@chakra-ui/form-control" "2.2.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/shared-utils@2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@chakra-ui/shared-utils/-/shared-utils-2.0.5.tgz#cb2b49705e113853647f1822142619570feba081" - integrity sha512-4/Wur0FqDov7Y0nCXl7HbHzCg4aq86h+SXdoUeuCMD3dSj7dpsVnStLYhng1vxvlbUnLpdF4oz5Myt3i/a7N3Q== - -"@chakra-ui/skeleton@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/skeleton/-/skeleton-2.1.0.tgz#e3b25dd3afa330029d6d63be0f7cb8d44ad25531" - integrity sha512-JNRuMPpdZGd6zFVKjVQ0iusu3tXAdI29n4ZENYwAJEMf/fN0l12sVeirOxkJ7oEL0yOx2AgEYFSKdbcAgfUsAQ== - dependencies: - "@chakra-ui/media-query" "3.3.0" - "@chakra-ui/react-use-previous" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/skip-nav@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/skip-nav/-/skip-nav-2.1.0.tgz#cac27eecc6eded1e83c8f0cf7445d727739cb325" - integrity sha512-Hk+FG+vadBSH0/7hwp9LJnLjkO0RPGnx7gBJWI4/SpoJf3e4tZlWYtwGj0toYY4aGKl93jVghuwGbDBEMoHDug== - -"@chakra-ui/slider@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/slider/-/slider-2.1.0.tgz#1caeed18761ba2a390777418cc9389ba25e39bce" - integrity sha512-lUOBcLMCnFZiA/s2NONXhELJh6sY5WtbRykPtclGfynqqOo47lwWJx+VP7xaeuhDOPcWSSecWc9Y1BfPOCz9cQ== - dependencies: - "@chakra-ui/number-utils" "2.0.7" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/react-types" "2.0.7" - "@chakra-ui/react-use-callback-ref" "2.1.0" - "@chakra-ui/react-use-controllable-state" "2.1.0" - "@chakra-ui/react-use-latest-ref" "2.1.0" - "@chakra-ui/react-use-merge-refs" "2.1.0" - "@chakra-ui/react-use-pan-event" "2.1.0" - "@chakra-ui/react-use-size" "2.1.0" - "@chakra-ui/react-use-update-effect" "2.1.0" - -"@chakra-ui/spinner@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/spinner/-/spinner-2.1.0.tgz#aa24a3d692c6ac90714e0f0f82c76c12c78c8e60" - integrity sha512-hczbnoXt+MMv/d3gE+hjQhmkzLiKuoTo42YhUG7Bs9OSv2lg1fZHW1fGNRFP3wTi6OIbD044U1P9HK+AOgFH3g== - dependencies: - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/stat@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@chakra-ui/stat/-/stat-2.1.1.tgz#a204ba915795345996a16c79794d84826d7dcc2d" - integrity sha512-LDn0d/LXQNbAn2KaR3F1zivsZCewY4Jsy1qShmfBMKwn6rI8yVlbvu6SiA3OpHS0FhxbsZxQI6HefEoIgtqY6Q== - dependencies: - "@chakra-ui/icon" "3.2.0" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/stepper@2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@chakra-ui/stepper/-/stepper-2.3.1.tgz#a0a0b73e147f202ab4e51cae55dad45489cc89fd" - integrity sha512-ky77lZbW60zYkSXhYz7kbItUpAQfEdycT0Q4bkHLxfqbuiGMf8OmgZOQkOB9uM4v0zPwy2HXhe0vq4Dd0xa55Q== - dependencies: - "@chakra-ui/icon" "3.2.0" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/styled-system@2.9.2": - version "2.9.2" - resolved "https://registry.yarnpkg.com/@chakra-ui/styled-system/-/styled-system-2.9.2.tgz#898ab63da560a4a014f7b05fa7767e8c76da6d2f" - integrity sha512-To/Z92oHpIE+4nk11uVMWqo2GGRS86coeMmjxtpnErmWRdLcp1WVCVRAvn+ZwpLiNR+reWFr2FFqJRsREuZdAg== - dependencies: - "@chakra-ui/shared-utils" "2.0.5" - csstype "^3.1.2" - lodash.mergewith "4.6.2" - -"@chakra-ui/switch@2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@chakra-ui/switch/-/switch-2.1.2.tgz#f7c6878d8126bfac8fa3b939079f1017c21b7479" - integrity sha512-pgmi/CC+E1v31FcnQhsSGjJnOE2OcND4cKPyTE+0F+bmGm48Q/b5UmKD9Y+CmZsrt/7V3h8KNczowupfuBfIHA== - dependencies: - "@chakra-ui/checkbox" "2.3.2" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/system@2.6.2": - version "2.6.2" - resolved "https://registry.yarnpkg.com/@chakra-ui/system/-/system-2.6.2.tgz#528ec955bd6a7f74da46470ee8225b1e2c80a78b" - integrity sha512-EGtpoEjLrUu4W1fHD+a62XR+hzC5YfsWm+6lO0Kybcga3yYEij9beegO0jZgug27V+Rf7vns95VPVP6mFd/DEQ== - dependencies: - "@chakra-ui/color-mode" "2.2.0" - "@chakra-ui/object-utils" "2.1.0" - "@chakra-ui/react-utils" "2.0.12" - "@chakra-ui/styled-system" "2.9.2" - "@chakra-ui/theme-utils" "2.0.21" - "@chakra-ui/utils" "2.0.15" - react-fast-compare "3.2.2" - -"@chakra-ui/table@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/table/-/table-2.1.0.tgz#20dce14c5e4d70dc7c6c0e87cce9b05907ff8c50" - integrity sha512-o5OrjoHCh5uCLdiUb0Oc0vq9rIAeHSIRScc2ExTC9Qg/uVZl2ygLrjToCaKfaaKl1oQexIeAcZDKvPG8tVkHyQ== - dependencies: - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/tabs@3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/tabs/-/tabs-3.0.0.tgz#854c06880af26158d7c72881c4b5e0453f6c485d" - integrity sha512-6Mlclp8L9lqXmsGWF5q5gmemZXOiOYuh0SGT/7PgJVNPz3LXREXlXg2an4MBUD8W5oTkduCX+3KTMCwRrVrDYw== - dependencies: - "@chakra-ui/clickable" "2.1.0" - "@chakra-ui/descendant" "3.1.0" - "@chakra-ui/lazy-utils" "2.0.5" - "@chakra-ui/react-children-utils" "2.0.6" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/react-use-controllable-state" "2.1.0" - "@chakra-ui/react-use-merge-refs" "2.1.0" - "@chakra-ui/react-use-safe-layout-effect" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/tag@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@chakra-ui/tag/-/tag-3.1.1.tgz#d05284b6549a84d3a08e57eec57df3ad0eebd882" - integrity sha512-Bdel79Dv86Hnge2PKOU+t8H28nm/7Y3cKd4Kfk9k3lOpUh4+nkSGe58dhRzht59lEqa4N9waCgQiBdkydjvBXQ== - dependencies: - "@chakra-ui/icon" "3.2.0" - "@chakra-ui/react-context" "2.1.0" - -"@chakra-ui/textarea@2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@chakra-ui/textarea/-/textarea-2.1.2.tgz#30f8af0e233cec2dee79d527450c6586e7122eff" - integrity sha512-ip7tvklVCZUb2fOHDb23qPy/Fr2mzDOGdkrpbNi50hDCiV4hFX02jdQJdi3ydHZUyVgZVBKPOJ+lT9i7sKA2wA== - dependencies: - "@chakra-ui/form-control" "2.2.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/theme-tools@2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@chakra-ui/theme-tools/-/theme-tools-2.1.2.tgz#913be05879cd816c546993ccb9ff7615f85ff69f" - integrity sha512-Qdj8ajF9kxY4gLrq7gA+Azp8CtFHGO9tWMN2wfF9aQNgG9AuMhPrUzMq9AMQ0MXiYcgNq/FD3eegB43nHVmXVA== - dependencies: - "@chakra-ui/anatomy" "2.2.2" - "@chakra-ui/shared-utils" "2.0.5" - color2k "^2.0.2" - -"@chakra-ui/theme-utils@2.0.21": - version "2.0.21" - resolved "https://registry.yarnpkg.com/@chakra-ui/theme-utils/-/theme-utils-2.0.21.tgz#da7ed541a5241a8ed0384eb14f37fa9b998382cf" - integrity sha512-FjH5LJbT794r0+VSCXB3lT4aubI24bLLRWB+CuRKHijRvsOg717bRdUN/N1fEmEpFnRVrbewttWh/OQs0EWpWw== - dependencies: - "@chakra-ui/shared-utils" "2.0.5" - "@chakra-ui/styled-system" "2.9.2" - "@chakra-ui/theme" "3.3.1" - lodash.mergewith "4.6.2" - -"@chakra-ui/theme@3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@chakra-ui/theme/-/theme-3.3.1.tgz#75c6cd0b5c70c0aa955068274ee4780f299bd8a4" - integrity sha512-Hft/VaT8GYnItGCBbgWd75ICrIrIFrR7lVOhV/dQnqtfGqsVDlrztbSErvMkoPKt0UgAkd9/o44jmZ6X4U2nZQ== - dependencies: - "@chakra-ui/anatomy" "2.2.2" - "@chakra-ui/shared-utils" "2.0.5" - "@chakra-ui/theme-tools" "2.1.2" - -"@chakra-ui/toast@7.0.2": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@chakra-ui/toast/-/toast-7.0.2.tgz#d1c396bbfced12e22b010899731fd8cc294d53ec" - integrity sha512-yvRP8jFKRs/YnkuE41BVTq9nB2v/KDRmje9u6dgDmE5+1bFt3bwjdf9gVbif4u5Ve7F7BGk5E093ARRVtvLvXA== - dependencies: - "@chakra-ui/alert" "2.2.2" - "@chakra-ui/close-button" "2.1.1" - "@chakra-ui/portal" "2.1.0" - "@chakra-ui/react-context" "2.1.0" - "@chakra-ui/react-use-timeout" "2.1.0" - "@chakra-ui/react-use-update-effect" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - "@chakra-ui/styled-system" "2.9.2" - "@chakra-ui/theme" "3.3.1" - -"@chakra-ui/tooltip@2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@chakra-ui/tooltip/-/tooltip-2.3.1.tgz#29fb8508a37bb6b20ab8dbb32bca6cd59b098796" - integrity sha512-Rh39GBn/bL4kZpuEMPPRwYNnccRCL+w9OqamWHIB3Qboxs6h8cOyXfIdGxjo72lvhu1QI/a4KFqkM3St+WfC0A== - dependencies: - "@chakra-ui/dom-utils" "2.1.0" - "@chakra-ui/popper" "3.1.0" - "@chakra-ui/portal" "2.1.0" - "@chakra-ui/react-types" "2.0.7" - "@chakra-ui/react-use-disclosure" "2.1.0" - "@chakra-ui/react-use-event-listener" "2.1.0" - "@chakra-ui/react-use-merge-refs" "2.1.0" - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/transition@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/transition/-/transition-2.1.0.tgz#c8e95564f7ab356e78119780037bae5ad150c7b3" - integrity sha512-orkT6T/Dt+/+kVwJNy7zwJ+U2xAZ3EU7M3XCs45RBvUnZDr/u9vdmaM/3D/rOpmQJWgQBwKPJleUXrYWUagEDQ== - dependencies: - "@chakra-ui/shared-utils" "2.0.5" - -"@chakra-ui/utils@2.0.15": - version "2.0.15" - resolved "https://registry.yarnpkg.com/@chakra-ui/utils/-/utils-2.0.15.tgz#bd800b1cff30eb5a5e8c36fa039f49984b4c5e4a" - integrity sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA== - dependencies: - "@types/lodash.mergewith" "4.6.7" - css-box-model "1.2.1" - framesync "6.1.2" - lodash.mergewith "4.6.2" - -"@chakra-ui/visually-hidden@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@chakra-ui/visually-hidden/-/visually-hidden-2.2.0.tgz#9b0ecef8f01263ab808ba3bda7b36a0d91b4d5c1" - integrity sha512-KmKDg01SrQ7VbTD3+cPWf/UfpF5MSwm3v7MWi0n5t8HnnadT13MF0MJCDSXbBWnzLv1ZKJ6zlyAOeARWX+DpjQ== - -"@emotion/babel-plugin@^11.12.0": - version "11.12.0" - resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz#7b43debb250c313101b3f885eba634f1d723fcc2" - integrity sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw== - dependencies: - "@babel/helper-module-imports" "^7.16.7" - "@babel/runtime" "^7.18.3" - "@emotion/hash" "^0.9.2" - "@emotion/memoize" "^0.9.0" - "@emotion/serialize" "^1.2.0" - babel-plugin-macros "^3.1.0" - convert-source-map "^1.5.0" - escape-string-regexp "^4.0.0" - find-root "^1.1.0" - source-map "^0.5.7" - stylis "4.2.0" - -"@emotion/cache@^11.13.0": - version "11.13.0" - resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.13.0.tgz#8f51748b8116691dee0408b08ad758b8d246b097" - integrity sha512-hPV345J/tH0Cwk2wnU/3PBzORQ9HeX+kQSbwI+jslzpRCHE6fSGTohswksA/Ensr8znPzwfzKZCmAM9Lmlhp7g== - dependencies: - "@emotion/memoize" "^0.9.0" - "@emotion/sheet" "^1.4.0" - "@emotion/utils" "^1.4.0" - "@emotion/weak-memoize" "^0.4.0" - stylis "4.2.0" - -"@emotion/hash@^0.9.2": - version "0.9.2" - resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.2.tgz#ff9221b9f58b4dfe61e619a7788734bd63f6898b" - integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g== - -"@emotion/is-prop-valid@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.3.0.tgz#bd84ba972195e8a2d42462387581560ef780e4e2" - integrity sha512-SHetuSLvJDzuNbOdtPVbq6yMMMlLoW5Q94uDqJZqy50gcmAjxFkVqmzqSGEFq9gT2iMuIeKV1PXVWmvUhuZLlQ== - dependencies: - "@emotion/memoize" "^0.9.0" - -"@emotion/memoize@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102" - integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== - -"@emotion/react@^11.13.0": - version "11.13.0" - resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.13.0.tgz#a9ebf827b98220255e5760dac89fa2d38ca7b43d" - integrity sha512-WkL+bw1REC2VNV1goQyfxjx1GYJkcc23CRQkXX+vZNLINyfI7o+uUn/rTGPt/xJ3bJHd5GcljgnxHf4wRw5VWQ== - dependencies: - "@babel/runtime" "^7.18.3" - "@emotion/babel-plugin" "^11.12.0" - "@emotion/cache" "^11.13.0" - "@emotion/serialize" "^1.3.0" - "@emotion/use-insertion-effect-with-fallbacks" "^1.1.0" - "@emotion/utils" "^1.4.0" - "@emotion/weak-memoize" "^0.4.0" - hoist-non-react-statics "^3.3.1" - -"@emotion/serialize@^1.2.0", "@emotion/serialize@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.3.0.tgz#e07cadfc967a4e7816e0c3ffaff4c6ce05cb598d" - integrity sha512-jACuBa9SlYajnpIVXB+XOXnfJHyckDfe6fOpORIM6yhBDlqGuExvDdZYHDQGoDf3bZXGv7tNr+LpLjJqiEQ6EA== - dependencies: - "@emotion/hash" "^0.9.2" - "@emotion/memoize" "^0.9.0" - "@emotion/unitless" "^0.9.0" - "@emotion/utils" "^1.4.0" - csstype "^3.0.2" - -"@emotion/sheet@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.4.0.tgz#c9299c34d248bc26e82563735f78953d2efca83c" - integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== - -"@emotion/styled@^11.13.0": - version "11.13.0" - resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.13.0.tgz#633fd700db701472c7a5dbef54d6f9834e9fb190" - integrity sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA== - dependencies: - "@babel/runtime" "^7.18.3" - "@emotion/babel-plugin" "^11.12.0" - "@emotion/is-prop-valid" "^1.3.0" - "@emotion/serialize" "^1.3.0" - "@emotion/use-insertion-effect-with-fallbacks" "^1.1.0" - "@emotion/utils" "^1.4.0" - -"@emotion/unitless@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.9.0.tgz#8e5548f072bd67b8271877e51c0f95c76a66cbe2" - integrity sha512-TP6GgNZtmtFaFcsOgExdnfxLLpRDla4Q66tnenA9CktvVSdNKDvMVuUah4QvWPIpNjrWsGg3qeGo9a43QooGZQ== - -"@emotion/use-insertion-effect-with-fallbacks@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz#1a818a0b2c481efba0cf34e5ab1e0cb2dcb9dfaf" - integrity sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw== - -"@emotion/utils@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.4.0.tgz#262f1d02aaedb2ec91c83a0955dd47822ad5fbdd" - integrity sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ== - -"@emotion/weak-memoize@^0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6" - integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== - -"@esbuild/aix-ppc64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" - integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== - -"@esbuild/android-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" - integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== - -"@esbuild/android-arm@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" - integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== - -"@esbuild/android-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" - integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== - -"@esbuild/darwin-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" - integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== - -"@esbuild/darwin-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" - integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== - -"@esbuild/freebsd-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" - integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== - -"@esbuild/freebsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" - integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== - -"@esbuild/linux-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" - integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== - -"@esbuild/linux-arm@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" - integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== - -"@esbuild/linux-ia32@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" - integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== - -"@esbuild/linux-loong64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" - integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== - -"@esbuild/linux-mips64el@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" - integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== - -"@esbuild/linux-ppc64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" - integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== - -"@esbuild/linux-riscv64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" - integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== - -"@esbuild/linux-s390x@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" - integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== - -"@esbuild/linux-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" - integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== - -"@esbuild/netbsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" - integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== - -"@esbuild/openbsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" - integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== - -"@esbuild/sunos-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" - integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== - -"@esbuild/win32-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" - integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== - -"@esbuild/win32-ia32@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" - integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== - -"@esbuild/win32-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" - integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== - -"@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" - integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== - dependencies: - eslint-visitor-keys "^3.3.0" - -"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.0", "@eslint-community/regexpp@^4.6.1": - version "4.10.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" - integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== - -"@eslint/eslintrc@^2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" - integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== - dependencies: - ajv "^6.12.4" - debug "^4.3.2" - espree "^9.6.0" - globals "^13.19.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.0" - minimatch "^3.1.2" - strip-json-comments "^3.1.1" - -"@eslint/js@8.57.0": - version "8.57.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" - integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== - -"@humanwhocodes/config-array@^0.11.14": - version "0.11.14" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" - integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== - dependencies: - "@humanwhocodes/object-schema" "^2.0.2" - debug "^4.3.1" - minimatch "^3.0.5" - -"@humanwhocodes/module-importer@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" - integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== - -"@humanwhocodes/object-schema@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" - integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== - -"@jridgewell/gen-mapping@^0.3.0": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" - integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== - dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/gen-mapping@^0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" - integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== - dependencies: - "@jridgewell/set-array" "^1.2.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.24" - -"@jridgewell/resolve-uri@^3.1.0": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" - integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== - -"@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== - -"@jridgewell/set-array@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" - integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== - -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": - version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" - integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== - -"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": - version "0.3.25" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" - integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@jridgewell/trace-mapping@^0.3.9": - version "0.3.22" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" - integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - -"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - -"@popperjs/core@^2.9.3": - version "2.11.8" - resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" - integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== - -"@remix-run/router@1.18.0": - version "1.18.0" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.18.0.tgz#20b033d1f542a100c1d57cfd18ecf442d1784732" - integrity sha512-L3jkqmqoSVBVKHfpGZmLrex0lxR5SucGA0sUfFzGctehw+S/ggL9L/0NnC5mw6P8HUWpFZ3nQw3cRApjjWx9Sw== - -"@rollup/rollup-android-arm-eabi@4.13.1": - version "4.13.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.1.tgz#88ba199f996e0000689130ed69e47df8b0dfbc70" - integrity sha512-4C4UERETjXpC4WpBXDbkgNVgHyWfG3B/NKY46e7w5H134UDOFqUJKpsLm0UYmuupW+aJmRgeScrDNfvZ5WV80A== - -"@rollup/rollup-android-arm64@4.13.1": - version "4.13.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.1.tgz#c89a55670e1179ed7ba3db06cee0d7da7b3d35ce" - integrity sha512-TrTaFJ9pXgfXEiJKQ3yQRelpQFqgRzVR9it8DbeRzG0RX7mKUy0bqhCFsgevwXLJepQKTnLl95TnPGf9T9AMOA== - -"@rollup/rollup-darwin-arm64@4.13.1": - version "4.13.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.1.tgz#52e3496fa66d761833df23a9b4860e517efc7d1d" - integrity sha512-fz7jN6ahTI3cKzDO2otQuybts5cyu0feymg0bjvYCBrZQ8tSgE8pc0sSNEuGvifrQJWiwx9F05BowihmLxeQKw== - -"@rollup/rollup-darwin-x64@4.13.1": - version "4.13.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.1.tgz#7678922773a8b53d8b4b3c3cc3e77b65fc71b489" - integrity sha512-WTvdz7SLMlJpektdrnWRUN9C0N2qNHwNbWpNo0a3Tod3gb9leX+yrYdCeB7VV36OtoyiPAivl7/xZ3G1z5h20g== - -"@rollup/rollup-linux-arm-gnueabihf@4.13.1": - version "4.13.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.1.tgz#69c3b896e3ee1c3487492323a02c2a3ae0d4b2e7" - integrity sha512-dBHQl+7wZzBYcIF6o4k2XkAfwP2ks1mYW2q/Gzv9n39uDcDiAGDqEyml08OdY0BIct0yLSPkDTqn4i6czpBLLw== - -"@rollup/rollup-linux-arm64-gnu@4.13.1": - version "4.13.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.1.tgz#13353f0ab65f4add0241f97f7ccc640b3a2b5cf2" - integrity sha512-bur4JOxvYxfrAmocRJIW0SADs3QdEYK6TQ7dTNz6Z4/lySeu3Z1H/+tl0a4qDYv0bCdBpUYM0sYa/X+9ZqgfSQ== - -"@rollup/rollup-linux-arm64-musl@4.13.1": - version "4.13.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.1.tgz#bf64eaa29b2b1e6bc9195f04bb30b2a4ffdc25ae" - integrity sha512-ssp77SjcDIUSoUyj7DU7/5iwM4ZEluY+N8umtCT9nBRs3u045t0KkW02LTyHouHDomnMXaXSZcCSr2bdMK63kA== - -"@rollup/rollup-linux-riscv64-gnu@4.13.1": - version "4.13.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.1.tgz#ec05966a4ed1b3338c8842108353ac6d3443dc6a" - integrity sha512-Jv1DkIvwEPAb+v25/Unrnnq9BO3F5cbFPT821n3S5litkz+O5NuXuNhqtPx5KtcwOTtaqkTsO+IVzJOsxd11aQ== - -"@rollup/rollup-linux-s390x-gnu@4.13.1": - version "4.13.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.13.1.tgz#c10a1f1522f0c9191ee45f677bd08763ddfdc039" - integrity sha512-U564BrhEfaNChdATQaEODtquCC7Ez+8Hxz1h5MAdMYj0AqD0GA9rHCpElajb/sQcaFL6NXmHc5O+7FXpWMa73Q== - -"@rollup/rollup-linux-x64-gnu@4.13.1": - version "4.13.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.1.tgz#836f948b6efc53f05f57d1d9ba92e90d629b3f22" - integrity sha512-zGRDulLTeDemR8DFYyFIQ8kMP02xpUsX4IBikc7lwL9PrwR3gWmX2NopqiGlI2ZVWMl15qZeUjumTwpv18N7sQ== - -"@rollup/rollup-linux-x64-musl@4.13.1": - version "4.13.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.1.tgz#07e0351cc18eeef026f903189d8312833cb6bd1f" - integrity sha512-VTk/MveyPdMFkYJJPCkYBw07KcTkGU2hLEyqYMsU4NjiOfzoaDTW9PWGRsNwiOA3qI0k/JQPjkl/4FCK1smskQ== - -"@rollup/rollup-win32-arm64-msvc@4.13.1": - version "4.13.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.1.tgz#6f9359bbec6cb4a2c002642c63e3704b0b5e68b7" - integrity sha512-L+hX8Dtibb02r/OYCsp4sQQIi3ldZkFI0EUkMTDwRfFykXBPptoz/tuuGqEd3bThBSLRWPR6wsixDSgOx/U3Zw== - -"@rollup/rollup-win32-ia32-msvc@4.13.1": - version "4.13.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.1.tgz#d6545a29ac9dd8b39a9161b87924f13471eb992e" - integrity sha512-+dI2jVPfM5A8zme8riEoNC7UKk0Lzc7jCj/U89cQIrOjrZTCWZl/+IXUeRT2rEZ5j25lnSA9G9H1Ob9azaF/KQ== - -"@rollup/rollup-win32-x64-msvc@4.13.1": - version "4.13.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.1.tgz#d1b221daca9afca1885b91a311c6f4a04b0deeb5" - integrity sha512-YY1Exxo2viZ/O2dMHuwQvimJ0SqvL+OAWQLLY6rvXavgQKjhQUzn7nc1Dd29gjB5Fqi00nrBWctJBOyfVMIVxw== - -"@types/babel__core@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" - integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== - dependencies: - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - -"@types/babel__generator@*": - version "7.6.8" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" - integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== - dependencies: - "@babel/types" "^7.0.0" - -"@types/babel__template@*": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" - integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - -"@types/babel__traverse@*": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.5.tgz#7b7502be0aa80cc4ef22978846b983edaafcd4dd" - integrity sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ== - dependencies: - "@babel/types" "^7.20.7" - -"@types/estree@1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" - integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== - -"@types/json-schema@^7.0.12": - version "7.0.15" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" - integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== - -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== - -"@types/lodash.mergewith@4.6.7": - version "4.6.7" - resolved "https://registry.yarnpkg.com/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz#eaa65aa5872abdd282f271eae447b115b2757212" - integrity sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A== - dependencies: - "@types/lodash" "*" - -"@types/lodash@*": - version "4.14.202" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.202.tgz#f09dbd2fb082d507178b2f2a5c7e74bd72ff98f8" - integrity sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ== - -"@types/parse-json@^4.0.0": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" - integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== - -"@types/prop-types@*": - version "15.7.11" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563" - integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng== - -"@types/react-dom@^18.3.0": - version "18.3.0" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0" - integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg== - dependencies: - "@types/react" "*" - -"@types/react@*", "@types/react@^18.3.3": - version "18.3.3" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.3.tgz#9679020895318b0915d7a3ab004d92d33375c45f" - integrity sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw== - dependencies: - "@types/prop-types" "*" - csstype "^3.0.2" - -"@types/semver@^7.5.0": - version "7.5.6" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339" - integrity sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A== - -"@typescript-eslint/eslint-plugin@^6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3" - integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA== - dependencies: - "@eslint-community/regexpp" "^4.5.1" - "@typescript-eslint/scope-manager" "6.21.0" - "@typescript-eslint/type-utils" "6.21.0" - "@typescript-eslint/utils" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" - debug "^4.3.4" - graphemer "^1.4.0" - ignore "^5.2.4" - natural-compare "^1.4.0" - semver "^7.5.4" - ts-api-utils "^1.0.1" - -"@typescript-eslint/parser@^6.21.0", "@typescript-eslint/parser@^6.4.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" - integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== - dependencies: - "@typescript-eslint/scope-manager" "6.21.0" - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/typescript-estree" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" - debug "^4.3.4" - -"@typescript-eslint/scope-manager@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" - integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg== - dependencies: - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" - -"@typescript-eslint/type-utils@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e" - integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag== - dependencies: - "@typescript-eslint/typescript-estree" "6.21.0" - "@typescript-eslint/utils" "6.21.0" - debug "^4.3.4" - ts-api-utils "^1.0.1" - -"@typescript-eslint/types@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" - integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== - -"@typescript-eslint/typescript-estree@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" - integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ== - dependencies: - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - minimatch "9.0.3" - semver "^7.5.4" - ts-api-utils "^1.0.1" - -"@typescript-eslint/utils@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134" - integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ== - dependencies: - "@eslint-community/eslint-utils" "^4.4.0" - "@types/json-schema" "^7.0.12" - "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "6.21.0" - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/typescript-estree" "6.21.0" - semver "^7.5.4" - -"@typescript-eslint/visitor-keys@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47" - integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A== - dependencies: - "@typescript-eslint/types" "6.21.0" - eslint-visitor-keys "^3.4.1" - -"@ungap/structured-clone@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" - integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== - -"@vitejs/plugin-react@^4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz#d0be6594051ded8957df555ff07a991fb618b48e" - integrity sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg== - dependencies: - "@babel/core" "^7.24.5" - "@babel/plugin-transform-react-jsx-self" "^7.24.5" - "@babel/plugin-transform-react-jsx-source" "^7.24.1" - "@types/babel__core" "^7.20.5" - react-refresh "^0.14.2" - -"@zag-js/dom-query@0.16.0": - version "0.16.0" - resolved "https://registry.yarnpkg.com/@zag-js/dom-query/-/dom-query-0.16.0.tgz#bca46bcd78f78c900064478646d95f9781ed098e" - integrity sha512-Oqhd6+biWyKnhKwFFuZrrf6lxBz2tX2pRQe6grUnYwO6HJ8BcbqZomy2lpOdr+3itlaUqx+Ywj5E5ZZDr/LBfQ== - -"@zag-js/element-size@0.10.5": - version "0.10.5" - resolved "https://registry.yarnpkg.com/@zag-js/element-size/-/element-size-0.10.5.tgz#a24bad2eeb7e2c8709e32be5336e158e1a1a174f" - integrity sha512-uQre5IidULANvVkNOBQ1tfgwTQcGl4hliPSe69Fct1VfYb2Fd0jdAcGzqQgPhfrXFpR62MxLPB7erxJ/ngtL8w== - -"@zag-js/focus-visible@0.16.0": - version "0.16.0" - resolved "https://registry.yarnpkg.com/@zag-js/focus-visible/-/focus-visible-0.16.0.tgz#c9e53e3dbab0f2649d04a489bb379f5800f4f069" - integrity sha512-a7U/HSopvQbrDU4GLerpqiMcHKEkQkNPeDZJWz38cw/6Upunh41GjHetq5TB84hxyCaDzJ6q2nEdNoBQfC0FKA== - dependencies: - "@zag-js/dom-query" "0.16.0" - -acorn-jsx@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - -acorn@^8.9.0: - version "8.11.3" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" - integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== - -ajv@^6.12.4: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -aria-hidden@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.3.tgz#14aeb7fb692bbb72d69bebfa47279c1fd725e954" - integrity sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ== - dependencies: - tslib "^2.0.0" - -array-buffer-byte-length@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" - integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== - dependencies: - call-bind "^1.0.2" - is-array-buffer "^3.0.1" - -array-buffer-byte-length@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" - integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== - dependencies: - call-bind "^1.0.5" - is-array-buffer "^3.0.4" - -array-includes@^3.1.6, array-includes@^3.1.7, array-includes@^3.1.8: - version "3.1.8" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" - integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-object-atoms "^1.0.0" - get-intrinsic "^1.2.4" - is-string "^1.0.7" - -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - -array.prototype.findlast@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" - integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - es-shim-unscopables "^1.0.2" - -array.prototype.findlastindex@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" - integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" - get-intrinsic "^1.2.1" - -array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" - integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" - -array.prototype.flatmap@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" - integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" - -array.prototype.tosorted@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" - integrity sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.3" - es-errors "^1.3.0" - es-shim-unscopables "^1.0.2" - -arraybuffer.prototype.slice@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" - integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== - dependencies: - array-buffer-byte-length "^1.0.0" - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - get-intrinsic "^1.2.1" - is-array-buffer "^3.0.2" - is-shared-array-buffer "^1.0.2" - -arraybuffer.prototype.slice@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" - integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== - dependencies: - array-buffer-byte-length "^1.0.1" - call-bind "^1.0.5" - define-properties "^1.2.1" - es-abstract "^1.22.3" - es-errors "^1.2.1" - get-intrinsic "^1.2.3" - is-array-buffer "^3.0.4" - is-shared-array-buffer "^1.0.2" - -available-typed-arrays@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" - integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== - -available-typed-arrays@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" - integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== - dependencies: - possible-typed-array-names "^1.0.0" - -babel-plugin-macros@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" - integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== - dependencies: - "@babel/runtime" "^7.12.5" - cosmiconfig "^7.0.0" - resolve "^1.19.0" - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -braces@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" - integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== - dependencies: - fill-range "^7.1.1" - -browserslist@^4.22.2: - version "4.22.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b" - integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== - dependencies: - caniuse-lite "^1.0.30001565" - electron-to-chromium "^1.4.601" - node-releases "^2.0.14" - update-browserslist-db "^1.0.13" - -builtin-modules@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" - integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== - -builtins@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" - integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== - dependencies: - semver "^7.0.0" - -call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" - integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== - dependencies: - function-bind "^1.1.2" - get-intrinsic "^1.2.1" - set-function-length "^1.1.1" - -call-bind@^1.0.6, call-bind@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - set-function-length "^1.2.1" - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -caniuse-lite@^1.0.30001565: - version "1.0.30001579" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz#45c065216110f46d6274311a4b3fcf6278e0852a" - integrity sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA== - -chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color2k@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/color2k/-/color2k-2.0.3.tgz#a771244f6b6285541c82aa65ff0a0c624046e533" - integrity sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog== - -compute-scroll-into-view@3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-3.0.3.tgz#c418900a5c56e2b04b885b54995df164535962b1" - integrity sha512-nadqwNxghAGTamwIqQSG433W6OADZx2vCo3UXHNrzTRHK/htu+7+L0zhjEoaeaQVNAi3YgqWDv8+tzf0hRfR+A== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -convert-source-map@^1.5.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" - integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== - -convert-source-map@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" - integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== - -copy-to-clipboard@3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" - integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== - dependencies: - toggle-selection "^1.0.6" - -cosmiconfig@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" - integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== - dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.2.1" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.10.0" - -cross-spawn@^7.0.2: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -css-box-model@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1" - integrity sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw== - dependencies: - tiny-invariant "^1.0.6" - -csstype@^3.0.2, csstype@^3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" - integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== - -data-view-buffer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" - integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== - dependencies: - call-bind "^1.0.6" - es-errors "^1.3.0" - is-data-view "^1.0.1" - -data-view-byte-length@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" - integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== - dependencies: - call-bind "^1.0.7" - es-errors "^1.3.0" - is-data-view "^1.0.1" - -data-view-byte-offset@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" - integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== - dependencies: - call-bind "^1.0.6" - es-errors "^1.3.0" - is-data-view "^1.0.1" - -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -deep-is@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -define-data-property@^1.0.1, define-data-property@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" - integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== - dependencies: - get-intrinsic "^1.2.1" - gopd "^1.0.1" - has-property-descriptors "^1.0.0" - -define-data-property@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - -define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" - integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== - dependencies: - define-data-property "^1.0.1" - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -detect-node-es@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" - integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== - -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -electron-to-chromium@^1.4.601: - version "1.4.643" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.643.tgz#081a20c5534db91e66ef094f68624960f674768f" - integrity sha512-QHscvvS7gt155PtoRC0dR2ilhL8E9LHhfTQEq1uD5AL0524rBLAwpAREFH06f87/e45B9XkR6Ki5dbhbCsVEIg== - -error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -es-abstract@^1.17.5, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2, es-abstract@^1.23.3: - version "1.23.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" - integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== - dependencies: - array-buffer-byte-length "^1.0.1" - arraybuffer.prototype.slice "^1.0.3" - available-typed-arrays "^1.0.7" - call-bind "^1.0.7" - data-view-buffer "^1.0.1" - data-view-byte-length "^1.0.1" - data-view-byte-offset "^1.0.0" - es-define-property "^1.0.0" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - es-set-tostringtag "^2.0.3" - es-to-primitive "^1.2.1" - function.prototype.name "^1.1.6" - get-intrinsic "^1.2.4" - get-symbol-description "^1.0.2" - globalthis "^1.0.3" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" - has-proto "^1.0.3" - has-symbols "^1.0.3" - hasown "^2.0.2" - internal-slot "^1.0.7" - is-array-buffer "^3.0.4" - is-callable "^1.2.7" - is-data-view "^1.0.1" - is-negative-zero "^2.0.3" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.3" - is-string "^1.0.7" - is-typed-array "^1.1.13" - is-weakref "^1.0.2" - object-inspect "^1.13.1" - object-keys "^1.1.1" - object.assign "^4.1.5" - regexp.prototype.flags "^1.5.2" - safe-array-concat "^1.1.2" - safe-regex-test "^1.0.3" - string.prototype.trim "^1.2.9" - string.prototype.trimend "^1.0.8" - string.prototype.trimstart "^1.0.8" - typed-array-buffer "^1.0.2" - typed-array-byte-length "^1.0.1" - typed-array-byte-offset "^1.0.2" - typed-array-length "^1.0.6" - unbox-primitive "^1.0.2" - which-typed-array "^1.1.15" - -es-abstract@^1.22.1: - version "1.22.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" - integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== - dependencies: - array-buffer-byte-length "^1.0.0" - arraybuffer.prototype.slice "^1.0.2" - available-typed-arrays "^1.0.5" - call-bind "^1.0.5" - es-set-tostringtag "^2.0.1" - es-to-primitive "^1.2.1" - function.prototype.name "^1.1.6" - get-intrinsic "^1.2.2" - get-symbol-description "^1.0.0" - globalthis "^1.0.3" - gopd "^1.0.1" - has-property-descriptors "^1.0.0" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" - internal-slot "^1.0.5" - is-array-buffer "^3.0.2" - is-callable "^1.2.7" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-typed-array "^1.1.12" - is-weakref "^1.0.2" - object-inspect "^1.13.1" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.5.1" - safe-array-concat "^1.0.1" - safe-regex-test "^1.0.0" - string.prototype.trim "^1.2.8" - string.prototype.trimend "^1.0.7" - string.prototype.trimstart "^1.0.7" - typed-array-buffer "^1.0.0" - typed-array-byte-length "^1.0.0" - typed-array-byte-offset "^1.0.0" - typed-array-length "^1.0.4" - unbox-primitive "^1.0.2" - which-typed-array "^1.1.13" - -es-define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" - integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== - dependencies: - get-intrinsic "^1.2.4" - -es-errors@^1.2.1, es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -es-iterator-helpers@^1.0.19: - version "1.0.19" - resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz#117003d0e5fec237b4b5c08aded722e0c6d50ca8" - integrity sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.3" - es-errors "^1.3.0" - es-set-tostringtag "^2.0.3" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - globalthis "^1.0.3" - has-property-descriptors "^1.0.2" - has-proto "^1.0.3" - has-symbols "^1.0.3" - internal-slot "^1.0.7" - iterator.prototype "^1.1.2" - safe-array-concat "^1.1.2" - -es-object-atoms@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" - integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== - dependencies: - es-errors "^1.3.0" - -es-set-tostringtag@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz#11f7cc9f63376930a5f20be4915834f4bc74f9c9" - integrity sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q== - dependencies: - get-intrinsic "^1.2.2" - has-tostringtag "^1.0.0" - hasown "^2.0.0" - -es-set-tostringtag@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" - integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== - dependencies: - get-intrinsic "^1.2.4" - has-tostringtag "^1.0.2" - hasown "^2.0.1" - -es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" - integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== - dependencies: - hasown "^2.0.0" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -esbuild@^0.21.3: - version "0.21.5" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" - integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== - optionalDependencies: - "@esbuild/aix-ppc64" "0.21.5" - "@esbuild/android-arm" "0.21.5" - "@esbuild/android-arm64" "0.21.5" - "@esbuild/android-x64" "0.21.5" - "@esbuild/darwin-arm64" "0.21.5" - "@esbuild/darwin-x64" "0.21.5" - "@esbuild/freebsd-arm64" "0.21.5" - "@esbuild/freebsd-x64" "0.21.5" - "@esbuild/linux-arm" "0.21.5" - "@esbuild/linux-arm64" "0.21.5" - "@esbuild/linux-ia32" "0.21.5" - "@esbuild/linux-loong64" "0.21.5" - "@esbuild/linux-mips64el" "0.21.5" - "@esbuild/linux-ppc64" "0.21.5" - "@esbuild/linux-riscv64" "0.21.5" - "@esbuild/linux-s390x" "0.21.5" - "@esbuild/linux-x64" "0.21.5" - "@esbuild/netbsd-x64" "0.21.5" - "@esbuild/openbsd-x64" "0.21.5" - "@esbuild/sunos-x64" "0.21.5" - "@esbuild/win32-arm64" "0.21.5" - "@esbuild/win32-ia32" "0.21.5" - "@esbuild/win32-x64" "0.21.5" - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -eslint-compat-utils@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz#f45e3b5ced4c746c127cf724fb074cd4e730d653" - integrity sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg== - -eslint-config-standard-with-typescript@^43.0.0: - version "43.0.1" - resolved "https://registry.yarnpkg.com/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-43.0.1.tgz#977862d7d41b0e1f27f399137bbf7b2e017037ff" - integrity sha512-WfZ986+qzIzX6dcr4yGUyVb/l9N3Z8wPXCc5z/70fljs3UbWhhV+WxrfgsqMToRzuuyX9MqZ974pq2UPhDTOcA== - dependencies: - "@typescript-eslint/parser" "^6.4.0" - eslint-config-standard "17.1.0" - -eslint-config-standard@17.1.0: - version "17.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz#40ffb8595d47a6b242e07cbfd49dc211ed128975" - integrity sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q== - -eslint-import-resolver-node@^0.3.9: - version "0.3.9" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" - integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== - dependencies: - debug "^3.2.7" - is-core-module "^2.13.0" - resolve "^1.22.4" - -eslint-module-utils@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" - integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== - dependencies: - debug "^3.2.7" - -eslint-plugin-es-x@^7.5.0: - version "7.5.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.5.0.tgz#d08d9cd155383e35156c48f736eb06561d07ba92" - integrity sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ== - dependencies: - "@eslint-community/eslint-utils" "^4.1.2" - "@eslint-community/regexpp" "^4.6.0" - eslint-compat-utils "^0.1.2" - -eslint-plugin-import@^2.25.2: - version "2.29.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz#d45b37b5ef5901d639c15270d74d46d161150643" - integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== - dependencies: - array-includes "^3.1.7" - array.prototype.findlastindex "^1.2.3" - array.prototype.flat "^1.3.2" - array.prototype.flatmap "^1.3.2" - debug "^3.2.7" - doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.9" - eslint-module-utils "^2.8.0" - hasown "^2.0.0" - is-core-module "^2.13.1" - is-glob "^4.0.3" - minimatch "^3.1.2" - object.fromentries "^2.0.7" - object.groupby "^1.0.1" - object.values "^1.1.7" - semver "^6.3.1" - tsconfig-paths "^3.15.0" - -"eslint-plugin-n@^15.0.0 || ^16.0.0 ": - version "16.6.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz#6a60a1a376870064c906742272074d5d0b412b0b" - integrity sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ== - dependencies: - "@eslint-community/eslint-utils" "^4.4.0" - builtins "^5.0.1" - eslint-plugin-es-x "^7.5.0" - get-tsconfig "^4.7.0" - globals "^13.24.0" - ignore "^5.2.4" - is-builtin-module "^3.2.1" - is-core-module "^2.12.1" - minimatch "^3.1.2" - resolve "^1.22.2" - semver "^7.5.3" - -eslint-plugin-promise@^6.6.0: - version "6.6.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz#acd3fd7d55cead7a10f92cf698f36c0aafcd717a" - integrity sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ== - -eslint-plugin-react-hooks@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz#c829eb06c0e6f484b3fbb85a97e57784f328c596" - integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ== - -eslint-plugin-react-refresh@^0.4.9: - version "0.4.9" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.9.tgz#bf870372b353b12e1e6fb7fc41b282d9cbc8d93d" - integrity sha512-QK49YrBAo5CLNLseZ7sZgvgTy21E6NEw22eZqc4teZfH8pxV3yXc9XXOYfUI6JNpw7mfHNkAeWtBxrTyykB6HA== - -eslint-plugin-react@^7.35.0: - version "7.35.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz#00b1e4559896710e58af6358898f2ff917ea4c41" - integrity sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA== - dependencies: - array-includes "^3.1.8" - array.prototype.findlast "^1.2.5" - array.prototype.flatmap "^1.3.2" - array.prototype.tosorted "^1.1.4" - doctrine "^2.1.0" - es-iterator-helpers "^1.0.19" - estraverse "^5.3.0" - hasown "^2.0.2" - jsx-ast-utils "^2.4.1 || ^3.0.0" - minimatch "^3.1.2" - object.entries "^1.1.8" - object.fromentries "^2.0.8" - object.values "^1.2.0" - prop-types "^15.8.1" - resolve "^2.0.0-next.5" - semver "^6.3.1" - string.prototype.matchall "^4.0.11" - string.prototype.repeat "^1.0.0" - -eslint-scope@^7.2.2: - version "7.2.2" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" - integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== - dependencies: - esrecurse "^4.3.0" - estraverse "^5.2.0" - -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: - version "3.4.3" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" - integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== - -eslint@^8.57.0: - version "8.57.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" - integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^2.1.4" - "@eslint/js" "8.57.0" - "@humanwhocodes/config-array" "^0.11.14" - "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - "@ungap/structured-clone" "^1.2.0" - ajv "^6.12.4" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.2.2" - eslint-visitor-keys "^3.4.3" - espree "^9.6.1" - esquery "^1.4.2" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - find-up "^5.0.0" - glob-parent "^6.0.2" - globals "^13.19.0" - graphemer "^1.4.0" - ignore "^5.2.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.3" - strip-ansi "^6.0.1" - text-table "^0.2.0" - -espree@^9.6.0, espree@^9.6.1: - version "9.6.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" - integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== - dependencies: - acorn "^8.9.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.1" - -esquery@^1.4.2: - version "1.5.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" - integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-glob@^3.2.9: - version "3.3.2" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" - integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== - -fastq@^1.6.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.16.0.tgz#83b9a9375692db77a822df081edb6a9cf6839320" - integrity sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA== - dependencies: - reusify "^1.0.4" - -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== - dependencies: - flat-cache "^3.0.4" - -fill-range@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" - integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== - dependencies: - to-regex-range "^5.0.1" - -find-root@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" - integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== - -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -flat-cache@^3.0.4: - version "3.2.0" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" - integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== - dependencies: - flatted "^3.2.9" - keyv "^4.5.3" - rimraf "^3.0.2" - -flatted@^3.2.9: - version "3.2.9" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" - integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== - -focus-lock@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/focus-lock/-/focus-lock-1.0.0.tgz#2c50d8ce59d3d6608cda2672be9e65812459206c" - integrity sha512-a8Ge6cdKh9za/GZR/qtigTAk7SrGore56EFcoMshClsh7FLk1zwszc/ltuMfKhx56qeuyL/jWQ4J4axou0iJ9w== - dependencies: - tslib "^2.0.3" - -for-each@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== - dependencies: - is-callable "^1.1.3" - -framer-motion@^11.3.19: - version "11.3.19" - resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-11.3.19.tgz#7a2bd72ed1dd04a47d8ecb8444447cfc47c70984" - integrity sha512-+luuQdx4AsamyMcvzW7jUAJYIKvQs1KE7oHvKkW3eNzmo0S+3PSDWjBuQkuIP9WyneGnKGMLUSuHs8OP7jKpQg== - dependencies: - tslib "^2.4.0" - -framesync@6.1.2: - version "6.1.2" - resolved "https://registry.yarnpkg.com/framesync/-/framesync-6.1.2.tgz#755eff2fb5b8f3b4d2b266dd18121b300aefea27" - integrity sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g== - dependencies: - tslib "2.4.0" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -fsevents@~2.3.2, fsevents@~2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" - integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - functions-have-names "^1.2.3" - -functions-have-names@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" - integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== - -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" - integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== - dependencies: - function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" - -get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" - integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" - -get-nonce@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3" - integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q== - -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - -get-symbol-description@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" - integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== - dependencies: - call-bind "^1.0.5" - es-errors "^1.3.0" - get-intrinsic "^1.2.4" - -get-tsconfig@^4.7.0: - version "4.7.2" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.2.tgz#0dcd6fb330391d46332f4c6c1bf89a6514c2ddce" - integrity sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A== - dependencies: - resolve-pkg-maps "^1.0.0" - -glob-parent@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob-parent@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== - dependencies: - is-glob "^4.0.3" - -glob@^7.1.3: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globals@^13.19.0, globals@^13.24.0: - version "13.24.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" - integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== - dependencies: - type-fest "^0.20.2" - -globalthis@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" - integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== - dependencies: - define-properties "^1.1.3" - -globby@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" - -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== - dependencies: - get-intrinsic "^1.1.3" - -graphemer@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" - integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== - -has-bigints@^1.0.1, has-bigints@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" - integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" - integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== - dependencies: - get-intrinsic "^1.2.2" - -has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - -has-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" - integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== - -has-proto@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" - integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== - -has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== - dependencies: - has-symbols "^1.0.2" - -has-tostringtag@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" - integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== - dependencies: - has-symbols "^1.0.3" - -hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== - dependencies: - function-bind "^1.1.2" - -hoist-non-react-statics@^3.3.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" - integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== - dependencies: - react-is "^16.7.0" - -ignore@^5.2.0, ignore@^5.2.4: - version "5.3.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" - integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== - -import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -internal-slot@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930" - integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== - dependencies: - get-intrinsic "^1.2.2" - hasown "^2.0.0" - side-channel "^1.0.4" - -internal-slot@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" - integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== - dependencies: - es-errors "^1.3.0" - hasown "^2.0.0" - side-channel "^1.0.4" - -invariant@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" - integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== - dependencies: - loose-envify "^1.0.0" - -is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" - integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.0" - is-typed-array "^1.1.10" - -is-array-buffer@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" - integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.1" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== - -is-async-function@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" - integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== - dependencies: - has-tostringtag "^1.0.0" - -is-bigint@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" - integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== - dependencies: - has-bigints "^1.0.1" - -is-boolean-object@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" - integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-builtin-module@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" - integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== - dependencies: - builtin-modules "^3.3.0" - -is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" - integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== - -is-core-module@^2.12.1, is-core-module@^2.13.0, is-core-module@^2.13.1: - version "2.13.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" - integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== - dependencies: - hasown "^2.0.0" - -is-data-view@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" - integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== - dependencies: - is-typed-array "^1.1.13" - -is-date-object@^1.0.1, is-date-object@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== - dependencies: - has-tostringtag "^1.0.0" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-finalizationregistry@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz#c8749b65f17c133313e661b1289b95ad3dbd62e6" - integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw== - dependencies: - call-bind "^1.0.2" - -is-generator-function@^1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" - integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== - dependencies: - has-tostringtag "^1.0.0" - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-map@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" - integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== - -is-negative-zero@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" - integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== - -is-negative-zero@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" - integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== - -is-number-object@^1.0.4: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" - integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== - dependencies: - has-tostringtag "^1.0.0" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-set@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" - integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== - -is-shared-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" - integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== - dependencies: - call-bind "^1.0.2" - -is-shared-array-buffer@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" - integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== - dependencies: - call-bind "^1.0.7" - -is-string@^1.0.5, is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== - dependencies: - has-tostringtag "^1.0.0" - -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== - dependencies: - has-symbols "^1.0.2" - -is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: - version "1.1.12" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" - integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== - dependencies: - which-typed-array "^1.1.11" - -is-typed-array@^1.1.13: - version "1.1.13" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" - integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== - dependencies: - which-typed-array "^1.1.14" - -is-weakmap@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" - integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== - -is-weakref@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" - integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== - dependencies: - call-bind "^1.0.2" - -is-weakset@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" - integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - -isarray@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -iterator.prototype@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0" - integrity sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w== - dependencies: - define-properties "^1.2.1" - get-intrinsic "^1.2.1" - has-symbols "^1.0.3" - reflect.getprototypeof "^1.0.4" - set-function-name "^2.0.1" - -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -json-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== - -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== - -json5@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" - integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== - dependencies: - minimist "^1.2.0" - -json5@^2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - -"jsx-ast-utils@^2.4.1 || ^3.0.0": - version "3.3.5" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" - integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== - dependencies: - array-includes "^3.1.6" - array.prototype.flat "^1.3.1" - object.assign "^4.1.4" - object.values "^1.1.6" - -keyv@^4.5.3: - version "4.5.4" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" - integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== - dependencies: - json-buffer "3.0.1" - -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -lines-and-columns@^1.1.6: - version "1.2.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" - integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -lodash.mergewith@4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" - integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== - -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -mdi-react@^9.3.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/mdi-react/-/mdi-react-9.4.0.tgz#b1f886681084fc85dabbcbc31b39b518f5030ced" - integrity sha512-3McdJimHT2CO+bSDGgJ1SSmU6HvXLEwLdfFi3M/nQT4aauvVxIbIGTCI8eOCcPtkyVyVuZRcCZ7Gw0oaGldYLw== - -merge2@^1.3.0, merge2@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - -micromatch@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== - dependencies: - braces "^3.0.2" - picomatch "^2.3.1" - -minimatch@9.0.3: - version "9.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.0, minimist@^1.2.6: - version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -nanoid@^3.3.7: - version "3.3.7" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" - integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== - -node-releases@^2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" - integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== - -object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - -object-inspect@^1.13.1, object-inspect@^1.9.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" - integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.4, object.assign@^4.1.5: - version "4.1.5" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" - integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== - dependencies: - call-bind "^1.0.5" - define-properties "^1.2.1" - has-symbols "^1.0.3" - object-keys "^1.1.1" - -object.entries@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41" - integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - -object.fromentries@^2.0.7, object.fromentries@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" - integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-object-atoms "^1.0.0" - -object.groupby@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" - integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - get-intrinsic "^1.2.1" - -object.values@^1.1.6, object.values@^1.1.7, object.values@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" - integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -optionator@^0.9.3: - version "0.9.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" - integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== - dependencies: - "@aashutoshrathi/word-wrap" "^1.2.3" - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parse-json@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== - dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== - -picocolors@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" - integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== - -picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -possible-typed-array-names@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" - integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== - -postcss@^8.4.39: - version "8.4.39" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.39.tgz#aa3c94998b61d3a9c259efa51db4b392e1bde0e3" - integrity sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw== - dependencies: - nanoid "^3.3.7" - picocolors "^1.0.1" - source-map-js "^1.2.0" - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -prop-types@^15.6.2, prop-types@^15.8.1: - version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" - integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.13.1" - -punycode@^2.1.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - -react-clientside-effect@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz#29f9b14e944a376b03fb650eed2a754dd128ea3a" - integrity sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg== - dependencies: - "@babel/runtime" "^7.12.13" - -react-dom@^18.3.1: - version "18.3.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" - integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== - dependencies: - loose-envify "^1.1.0" - scheduler "^0.23.2" - -react-fast-compare@3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" - integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== - -react-focus-lock@^2.9.4: - version "2.9.7" - resolved "https://registry.yarnpkg.com/react-focus-lock/-/react-focus-lock-2.9.7.tgz#778358691b55db38a9954a989341048bd4065935" - integrity sha512-EfhX040SELLqnQ9JftqsmQCG49iByg8F5X5m19Er+n371OaETZ35dlNPZrLOOTlnnwD4c2Zv0KDgabDTc7dPHw== - dependencies: - "@babel/runtime" "^7.0.0" - focus-lock "^1.0.0" - prop-types "^15.6.2" - react-clientside-effect "^1.2.6" - use-callback-ref "^1.3.0" - use-sidecar "^1.1.2" - -react-is@^16.13.1, react-is@^16.7.0: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - -react-refresh@^0.14.2: - version "0.14.2" - resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9" - integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA== - -react-remove-scroll-bar@^2.3.4: - version "2.3.4" - resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz#53e272d7a5cb8242990c7f144c44d8bd8ab5afd9" - integrity sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A== - dependencies: - react-style-singleton "^2.2.1" - tslib "^2.0.0" - -react-remove-scroll@^2.5.6: - version "2.5.7" - resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz#15a1fd038e8497f65a695bf26a4a57970cac1ccb" - integrity sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA== - dependencies: - react-remove-scroll-bar "^2.3.4" - react-style-singleton "^2.2.1" - tslib "^2.1.0" - use-callback-ref "^1.3.0" - use-sidecar "^1.1.2" - -react-router-dom@^6.25.1: - version "6.25.1" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.25.1.tgz#b89f8d63fc8383ea4e89c44bf31c5843e1f7afa0" - integrity sha512-0tUDpbFvk35iv+N89dWNrJp+afLgd+y4VtorJZuOCXK0kkCWjEvb3vTJM++SYvMEpbVwXKf3FjeVveVEb6JpDQ== - dependencies: - "@remix-run/router" "1.18.0" - react-router "6.25.1" - -react-router@6.25.1: - version "6.25.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.25.1.tgz#70b4f1af79954cfcfd23f6ddf5c883e8c904203e" - integrity sha512-u8ELFr5Z6g02nUtpPAggP73Jigj1mRePSwhS/2nkTrlPU5yEkH1vYzWNyvSnSzeeE2DNqWdH+P8OhIh9wuXhTw== - dependencies: - "@remix-run/router" "1.18.0" - -react-style-singleton@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4" - integrity sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g== - dependencies: - get-nonce "^1.0.0" - invariant "^2.2.4" - tslib "^2.0.0" - -react@^18.3.1: - version "18.3.1" - resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" - integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== - dependencies: - loose-envify "^1.1.0" - -reflect.getprototypeof@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz#aaccbf41aca3821b87bb71d9dcbc7ad0ba50a3f3" - integrity sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - get-intrinsic "^1.2.1" - globalthis "^1.0.3" - which-builtin-type "^1.1.3" - -regenerator-runtime@^0.14.0: - version "0.14.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" - integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== - -regexp.prototype.flags@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" - integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - set-function-name "^2.0.0" - -regexp.prototype.flags@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" - integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== - dependencies: - call-bind "^1.0.6" - define-properties "^1.2.1" - es-errors "^1.3.0" - set-function-name "^2.0.1" - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve-pkg-maps@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" - integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== - -resolve@^1.19.0, resolve@^1.22.2, resolve@^1.22.4: - version "1.22.8" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" - integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== - dependencies: - is-core-module "^2.13.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -resolve@^2.0.0-next.5: - version "2.0.0-next.5" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" - integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== - dependencies: - is-core-module "^2.13.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -rollup@^4.13.0: - version "4.13.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.13.1.tgz#5bd6d84eafd60280487085b8bf9c91679571005a" - integrity sha512-hFi+fU132IvJ2ZuihN56dwgpltpmLZHZWsx27rMCTZ2sYwrqlgL5sECGy1eeV2lAihD8EzChBVVhsXci0wD4Tg== - dependencies: - "@types/estree" "1.0.5" - optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.13.1" - "@rollup/rollup-android-arm64" "4.13.1" - "@rollup/rollup-darwin-arm64" "4.13.1" - "@rollup/rollup-darwin-x64" "4.13.1" - "@rollup/rollup-linux-arm-gnueabihf" "4.13.1" - "@rollup/rollup-linux-arm64-gnu" "4.13.1" - "@rollup/rollup-linux-arm64-musl" "4.13.1" - "@rollup/rollup-linux-riscv64-gnu" "4.13.1" - "@rollup/rollup-linux-s390x-gnu" "4.13.1" - "@rollup/rollup-linux-x64-gnu" "4.13.1" - "@rollup/rollup-linux-x64-musl" "4.13.1" - "@rollup/rollup-win32-arm64-msvc" "4.13.1" - "@rollup/rollup-win32-ia32-msvc" "4.13.1" - "@rollup/rollup-win32-x64-msvc" "4.13.1" - fsevents "~2.3.2" - -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - -safe-array-concat@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.0.tgz#8d0cae9cb806d6d1c06e08ab13d847293ebe0692" - integrity sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg== - dependencies: - call-bind "^1.0.5" - get-intrinsic "^1.2.2" - has-symbols "^1.0.3" - isarray "^2.0.5" - -safe-array-concat@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" - integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== - dependencies: - call-bind "^1.0.7" - get-intrinsic "^1.2.4" - has-symbols "^1.0.3" - isarray "^2.0.5" - -safe-regex-test@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.2.tgz#3ba32bdb3ea35f940ee87e5087c60ee786c3f6c5" - integrity sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ== - dependencies: - call-bind "^1.0.5" - get-intrinsic "^1.2.2" - is-regex "^1.1.4" - -safe-regex-test@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" - integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== - dependencies: - call-bind "^1.0.6" - es-errors "^1.3.0" - is-regex "^1.1.4" - -scheduler@^0.23.2: - version "0.23.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" - integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== - dependencies: - loose-envify "^1.1.0" - -semver@^6.3.1: - version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^7.0.0, semver@^7.5.3, semver@^7.5.4: - version "7.5.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" - integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== - dependencies: - lru-cache "^6.0.0" - -set-function-length@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.0.tgz#2f81dc6c16c7059bda5ab7c82c11f03a515ed8e1" - integrity sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w== - dependencies: - define-data-property "^1.1.1" - function-bind "^1.1.2" - get-intrinsic "^1.2.2" - gopd "^1.0.1" - has-property-descriptors "^1.0.1" - -set-function-length@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" - integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" - -set-function-name@^2.0.0, set-function-name@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" - integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== - dependencies: - define-data-property "^1.0.1" - functions-have-names "^1.2.3" - has-property-descriptors "^1.0.0" - -set-function-name@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" - integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - functions-have-names "^1.2.3" - has-property-descriptors "^1.0.2" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - -side-channel@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" - integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== - dependencies: - call-bind "^1.0.7" - es-errors "^1.3.0" - get-intrinsic "^1.2.4" - object-inspect "^1.13.1" - -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -source-map-js@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" - integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== - -source-map@^0.5.7: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== - -string.prototype.matchall@^4.0.11: - version "4.0.11" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a" - integrity sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-symbols "^1.0.3" - internal-slot "^1.0.7" - regexp.prototype.flags "^1.5.2" - set-function-name "^2.0.2" - side-channel "^1.0.6" - -string.prototype.repeat@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz#e90872ee0308b29435aa26275f6e1b762daee01a" - integrity sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" - -string.prototype.trim@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" - integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - -string.prototype.trim@^1.2.9: - version "1.2.9" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" - integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.0" - es-object-atoms "^1.0.0" - -string.prototype.trimend@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" - integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - -string.prototype.trimend@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" - integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - -string.prototype.trimstart@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" - integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - -string.prototype.trimstart@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" - integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - -strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== - -strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -stylis@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" - integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - -tiny-invariant@^1.0.6: - version "1.3.1" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" - integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -toggle-selection@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" - integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== - -ts-api-utils@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" - integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== - -tsconfig-paths@^3.15.0: - version "3.15.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" - integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.2" - minimist "^1.2.6" - strip-bom "^3.0.0" - -tslib@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== - -tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0: - version "2.6.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== - -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - -typed-array-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" - integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.1" - is-typed-array "^1.1.10" - -typed-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" - integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== - dependencies: - call-bind "^1.0.7" - es-errors "^1.3.0" - is-typed-array "^1.1.13" - -typed-array-byte-length@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" - integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== - dependencies: - call-bind "^1.0.2" - for-each "^0.3.3" - has-proto "^1.0.1" - is-typed-array "^1.1.10" - -typed-array-byte-length@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" - integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== - dependencies: - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - has-proto "^1.0.3" - is-typed-array "^1.1.13" - -typed-array-byte-offset@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" - integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - has-proto "^1.0.1" - is-typed-array "^1.1.10" - -typed-array-byte-offset@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" - integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== - dependencies: - available-typed-arrays "^1.0.7" - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - has-proto "^1.0.3" - is-typed-array "^1.1.13" - -typed-array-length@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" - integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== - dependencies: - call-bind "^1.0.2" - for-each "^0.3.3" - is-typed-array "^1.1.9" - -typed-array-length@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" - integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== - dependencies: - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - has-proto "^1.0.3" - is-typed-array "^1.1.13" - possible-typed-array-names "^1.0.0" - -typescript@*: - version "5.5.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" - integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== - -unbox-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" - integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== - dependencies: - call-bind "^1.0.2" - has-bigints "^1.0.2" - has-symbols "^1.0.3" - which-boxed-primitive "^1.0.2" - -update-browserslist-db@^1.0.13: - version "1.0.13" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" - integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -use-callback-ref@^1.3.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.1.tgz#9be64c3902cbd72b07fe55e56408ae3a26036fd0" - integrity sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ== - dependencies: - tslib "^2.0.0" - -use-sidecar@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.2.tgz#2f43126ba2d7d7e117aa5855e5d8f0276dfe73c2" - integrity sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw== - dependencies: - detect-node-es "^1.1.0" - tslib "^2.0.0" - -vite@^5.3.5: - version "5.3.5" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.3.5.tgz#b847f846fb2b6cb6f6f4ed50a830186138cb83d8" - integrity sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA== - dependencies: - esbuild "^0.21.3" - postcss "^8.4.39" - rollup "^4.13.0" - optionalDependencies: - fsevents "~2.3.3" - -which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" - -which-builtin-type@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b" - integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== - dependencies: - function.prototype.name "^1.1.5" - has-tostringtag "^1.0.0" - is-async-function "^2.0.0" - is-date-object "^1.0.5" - is-finalizationregistry "^1.0.2" - is-generator-function "^1.0.10" - is-regex "^1.1.4" - is-weakref "^1.0.2" - isarray "^2.0.5" - which-boxed-primitive "^1.0.2" - which-collection "^1.0.1" - which-typed-array "^1.1.9" - -which-collection@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" - integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== - dependencies: - is-map "^2.0.1" - is-set "^2.0.1" - is-weakmap "^2.0.1" - is-weakset "^2.0.1" - -which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.9: - version "1.1.13" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" - integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.4" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" - -which-typed-array@^1.1.14, which-typed-array@^1.1.15: - version "1.1.15" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" - integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== - dependencies: - available-typed-arrays "^1.0.7" - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.2" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yaml@^1.10.0: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/examples/single-page-app/verify.sh b/examples/single-page-app/verify.sh deleted file mode 100755 index 2e1cb87bd37d..000000000000 --- a/examples/single-page-app/verify.sh +++ /dev/null @@ -1,304 +0,0 @@ -#!/bin/bash -e - -# Note: This uses the pip version of yq, which is a simple -# wrapper taking jq args - not the separate package installed -# with eg snap - -export NAME=single-page-app -export PORT_DEV_PROXY="${SPA_PORT_DEV_PROXY:-11901}" -export PORT_PROXY="${SPA_PORT_PROXY:-11900}" -export PORT_MYHUB="${SPA_PORT_MYHUB:-11902}" -export MANUAL=true - - -BACKUP_FILES=( - "envoy.yml" -) - - -finally () { - rm -rf .local.ci - for file in "${BACKUP_FILES[@]}"; do - move_if_exists "${file}.bak" "${file}" - done -} - -export -f finally - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -# As much of the logic is implemented in js, not everything can get tested, at least without using -# eg selenium or similar. -# Everything else should be tested. - - -EXPECTED_USER_JQ=$( -cat << 'EOF' -{"avatar_url": "http://localhost:\($port)/images/users/envoy.svg", - "followers": 3, - "following": 2, - "name": "Envoy Demo", - "login": "envoydemo", - "public_repos": 3} -EOF -) -EXPECTED_USER="$( - yq -c \ - --arg port "$PORT_MYHUB" \ - "$EXPECTED_USER_JQ" \ - < myhub/data.yml)" - -EXPECTED_REPOS_JQ=$( -cat << 'EOF' -.users.envoydemo.public_repos as $user_repos -| .repos as $repos -| $user_repos -| map({ - "html_url": "http://localhost:\($port)/envoydemo/\(.)", - "updated_at": $repos[.].updated_at, - "full_name": "envoydemo/\(.)"}) -EOF -) -EXPECTED_REPOS="$( - yq -c \ - --arg port "$PORT_MYHUB" \ - "$EXPECTED_REPOS_JQ" \ - < myhub/data.yml)" - -EXPECTED_FOLLOWERS_JQ=$( -cat << 'EOF' -.users.envoydemo.followers as $followers -| .users as $users -| $followers -| map({ - "avatar_url": "http://localhost:\($port)/images/users/\(.).png", - "name": $users[.].name, - "html_url": "http://localhost:\($port)/users/\(.)", - "login": .}) -EOF -) -EXPECTED_FOLLOWING="$( - yq -c \ - --arg port "$PORT_MYHUB" \ - "$EXPECTED_FOLLOWERS_JQ" \ - < myhub/data.yml)" - -EXPECTED_FOLLOWING_JQ=$( -cat << 'EOF' -.users.envoydemo.following as $following -| .users as $users -| $following -| map({ - "avatar_url": "http://localhost:\($port)/images/users/\(.).png", - "name": $users[.].name, - "html_url": "http://localhost:\($port)/users/\(.)", - "login": .}) -EOF -) -EXPECTED_FOLLOWING="$( - yq -c \ - --arg port "$PORT_MYHUB" \ - "$EXPECTED_FOLLOWING_JQ" \ - < myhub/data.yml)" - - -test_auth () { - local proxy_port - proxy_scheme=$1 - proxy_port=$2 - curl_args=() - if [[ "$proxy_scheme" == "https" ]]; then - curl_args=(-k) - fi - - run_log "Fetch the app page" - responds_with \ - "Envoy single page app example" \ - "${proxy_scheme}://localhost:${proxy_port}" \ - "${curl_args[@]}" - - run_log "Inititiate login" - responds_with_header \ - "HTTP/1.1 302 Found" \ - "${proxy_scheme}://localhost:${proxy_port}/login" \ - "${curl_args[@]}" - responds_with_header \ - "location: http://localhost:${PORT_MYHUB}/authorize?client_id=0123456789&redirect_uri=${proxy_scheme}%3A%2F%2Flocalhost%3A${proxy_port}%2Fauthorize&response_type=code&scope=user%3Aemail&state=${proxy_scheme}%3A%2F%2Flocalhost%3A${proxy_port}%2Flogin" \ - "${proxy_scheme}://localhost:${proxy_port}/login" \ - "${curl_args[@]}" - - run_log "Fetch the myhub authorization page" - responds_with_header \ - "HTTP/1.1 302 Found" \ - "http://localhost:${PORT_MYHUB}/authorize?client_id=0123456789&redirect_uri=${proxy_scheme}%3A%2F%2Flocalhost%3A${proxy_port}%2Fauthorize&response_type=code&scope=user%3Aemail&state=${proxy_scheme}%3A%2F%2Flocalhost%3A${proxy_port}%2Flogin" \ - "${curl_args[@]}" - responds_with_header \ - "Location: ${proxy_scheme}://localhost:${proxy_port}/authorize?code=" \ - "http://localhost:${PORT_MYHUB}/authorize?client_id=0123456789&redirect_uri=${proxy_scheme}%3A%2F%2Flocalhost%3A${proxy_port}%2Fauthorize&response_type=code&scope=user%3Aemail&state=${proxy_scheme}%3A%2F%2Flocalhost%3A${proxy_port}%2Flogin" \ - "${curl_args[@]}" - - run_log "Return to the app and receive creds" - CODE=$(_curl "${curl_args[@]}" --head "http://localhost:${PORT_MYHUB}/authorize?client_id=0123456789&redirect_uri=${proxy_scheme}%3A%2F%2Flocalhost%3A${proxy_port}%2Fauthorize&response_type=code&scope=user%3Aemail&state=${proxy_scheme}%3A%2F%2Flocalhost%3A${proxy_port}%2Flogin" | grep Location | cut -d= -f2 | cut -d\& -f1) - RESPONSE=$(_curl "${curl_args[@]}" --head "${proxy_scheme}://localhost:${proxy_port}/authorize?code=$CODE&state=${proxy_scheme}%3A%2F%2Flocalhost%3A${proxy_port}%2Flogin") - echo "$RESPONSE" | grep "HTTP/1.1 302 Found" - echo "$RESPONSE" | grep "location: ${proxy_scheme}://localhost:${proxy_port}/login" - echo "$RESPONSE" | grep "set-cookie: OauthHMAC=" - echo "$RESPONSE" | grep "set-cookie: OauthExpires=" - echo "$RESPONSE" | grep "set-cookie: BearerToken=" - - HMAC=$(echo "$RESPONSE" | grep "set-cookie: OauthHMAC=" | cut -d' ' -f2-) - OAUTH_EXPIRES=$(echo "$RESPONSE" | grep "set-cookie: OauthExpires=" | cut -d' ' -f2-) - TOKEN=$(echo "$RESPONSE" | grep "set-cookie: BearerToken=" | cut -d' ' -f2-) - COOKIES=( - --cookie "$HMAC" - --cookie "$OAUTH_EXPIRES" - --cookie "$TOKEN") - - endpoints=( - "Fetch user object|${EXPECTED_USER}|/hub/user" - "Fetch repos|${EXPECTED_REPOS}|/hub/users/envoydemo/repos" - "Fetch followers|${EXPECTED_FOLLOWERS}|/hub/users/envoydemo/followers" - "Fetch following|${EXPECTED_FOLLOWING}|/hub/users/envoydemo/following" - ) - - for endpoint in "${endpoints[@]}"; do - IFS='|' read -r log_message expected_response path <<< "$endpoint" - run_log "$log_message" - responds_with \ - "$expected_response" \ - "${proxy_scheme}://localhost:${proxy_port}${path}" \ - "${COOKIES[@]}" \ - "${curl_args[@]}" - done - - run_log "Log out of Myhub" - RESPONSE=$(_curl "${curl_args[@]}" --head "${proxy_scheme}://localhost:${proxy_port}/logout") - echo "$RESPONSE" | grep "HTTP/1.1 302 Found" - echo "$RESPONSE" | grep "location: ${proxy_scheme}://localhost:${proxy_port}/" - echo "$RESPONSE" | grep "set-cookie: OauthHMAC=deleted" - echo "$RESPONSE" | grep "set-cookie: BearerToken=deleted" -} - -get_js () { - _curl -k "https://localhost:${PORT_PROXY}" \ - | grep "assets/index" \ - | grep -oP '' \ - | grep -oP '/assets/[^"]+' \ - | sed 's/\/assets\///;s/".*//' -} - -run_log "Adjust environment for CI" -# This is specific to verify.sh script and so slightly adjust from docs. -rm -rf .local.ci -mkdir -p .local.ci -cp -a ui .local.ci/ -export UI_PATH=./.local.ci/ui -for file in "${BACKUP_FILES[@]}"; do - cp -a "${file}" "${file}.bak" -done -echo "VITE_APP_API_URL=https://localhost:${PORT_PROXY}" > ui/.env.production.local -echo "VITE_APP_API_URL=http://localhost:${PORT_DEV_PROXY}" > ui/.env.development.local -sed -i "s/localhost:7000/localhost:${PORT_MYHUB}/g" envoy.yml -export UID - -run_log "Generate an HMAC secret" -cp -a secrets/ .local.ci/ -export SECRETS_PATH=./.local.ci/secrets/ -HMAC_SECRET=$(echo "MY_HMAC_SECRET" | mkpasswd -s) -export HMAC_SECRET -envsubst < hmac-secret.tmpl.yml > .local.ci/secrets/hmac-secret.yml - -run_log "Start servers" -bring_up_example - -test_auth http "${PORT_DEV_PROXY}" - -run_log "Live reload dev app" -sed -i s/Envoy\ single\ page\ app\ example/CUSTOM\ APP/g .local.ci/ui/index.html -responds_with \ - "CUSTOM APP" \ - "http://localhost:${PORT_DEV_PROXY}" - -run_log "Run yarn lint" -docker compose run --rm ui yarn lint - -run_log "Build the production app" -mkdir -p .local.ci/production -cp -a xds .local.ci/production -cp -a ui/index.html .local.ci/ui/ -export XDS_PATH=./.local.ci/production/xds -docker compose up --build -d envoy -docker compose run --rm ui build.sh - -run_log "Check the created routes" -jq '.resources[0].filter_chains[0].filters[0].typed_config.route_config.virtual_hosts[0].routes' < .local.ci/production/xds/lds.yml - -test_auth https "${PORT_PROXY}" - -current_js=$(get_js) - -run_log "Ensure assets are served with compression" -responds_with_header \ - "content-encoding: gzip" \ - "https://localhost:${PORT_PROXY}/assets/${current_js}" \ - -k -i -H "Accept-Encoding: gzip" - -run_log "Rebuild production app" -sed -i s/Login\ to\ query\ APIs/LOGIN\ NOW/g .local.ci/ui/src/components/Home.tsx -docker compose run --rm ui build.sh -wait_for 5 \ - bash -c "\ - responds_without \ - \"$current_js\" \ - \"https://localhost:${PORT_PROXY}\" \ - \"-k\"" -responds_with \ - "Envoy single page app example" \ - "https://localhost:${PORT_PROXY}" \ - -k - -run_log "Update Envoy's configuration to use Github" -export TOKEN_SECRET=ZZZ -envsubst < token-secret.tmpl.yml > .local.ci/secrets/github-token-secret.yml -GITHUB_PROVIDED_CLIENT_ID=XXX -cp -a envoy.yml .local.ci/ -sed -i "s@cluster:\ hub@cluster:\ github@g" .local.ci/envoy.yml -sed -i "s@client_id:\ \"0123456789\"@client_id:\ \"$GITHUB_PROVIDED_CLIENT_ID\"@g" .local.ci/envoy.yml -sed -i "s@authorization_endpoint:\ http://localhost:${PORT_MYHUB}/authorize@authorization_endpoint:\ https://github.com/login/oauth/authorize@g" .local.ci/envoy.yml -sed -i "s@uri:\ http://myhub:${PORT_MYHUB}/authenticate@uri:\ https://github.com/login/oauth/access_token@g" .local.ci/envoy.yml -sed -i "s@path:\ /etc/envoy/secrets/myhub-token-secret.yml@path:\ /etc/envoy/secrets/github-token-secret.yml@g" .local.ci/envoy.yml -sed -i "s@host_rewrite_literal:\ api.myhub@host_rewrite_literal:\ api.github.com@g" .local.ci/envoy.yml -cat _github-clusters.yml >> .local.ci/envoy.yml - -run_log "Update the app configuration to use Github" -echo "VITE_APP_AUTH_PROVIDER=github" > .local.ci/ui/.env.local - -run_log "Rebuild the app and restart Envoy (Github)" -export ENVOY_CONFIG=.local.ci/envoy.yml -docker compose run --rm ui build.sh -docker compose up --build -d envoy - -run_log "Test dev app (Github)" -wait_for 5 \ - bash -c "\ - responds_with \ - \"Envoy single page app example\" \ - \"http://localhost:${PORT_DEV_PROXY}\"" -run_log "Inititiate dev login (Github)" -responds_with_header \ - "HTTP/1.1 302 Found" \ - "http://localhost:${PORT_DEV_PROXY}/login" -responds_with_header \ - "location: https://github.com/login/oauth/authorize?client_id=XXX&redirect_uri=http%3A%2F%2Flocalhost%3A${PORT_DEV_PROXY}%2Fauthorize&response_type=code&scope=user%3Aemail&state=http%3A%2F%2Flocalhost%3A${PORT_DEV_PROXY}%2Flogin" \ - "http://localhost:${PORT_DEV_PROXY}/login" - -run_log "Test production app (Github)" -responds_with \ - "Envoy single page app example" \ - "https://localhost:${PORT_PROXY}" \ - -k -responds_with_header \ - "location: https://github.com/login/oauth/authorize?client_id=XXX&redirect_uri=https%3A%2F%2Flocalhost%3A${PORT_PROXY}%2Fauthorize&response_type=code&scope=user%3Aemail&state=https%3A%2F%2Flocalhost%3A${PORT_PROXY}%2Flogin" \ - "https://localhost:${PORT_PROXY}/login" \ - -k diff --git a/examples/single-page-app/xds/lds.tmpl.yml b/examples/single-page-app/xds/lds.tmpl.yml deleted file mode 100644 index 298f054392b1..000000000000 --- a/examples/single-page-app/xds/lds.tmpl.yml +++ /dev/null @@ -1,28 +0,0 @@ -resources: -- "@type": type.googleapis.com/envoy.config.listener.v3.Listener - name: static_routes - address: - socket_address: - address: 0.0.0.0 - port_value: 10002 - access_log: - - name: envoy.access_loggers.stdout - typed_config: - "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StderrAccessLog - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_http - route_config: - name: static_route - max_direct_response_body_size_bytes: 640000 - virtual_hosts: - - name: static_service - domains: ["*"] - routes: - http_filters: - - name: envoy.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router diff --git a/examples/skywalking/Dockerfile-elasticsearch b/examples/skywalking/Dockerfile-elasticsearch deleted file mode 100644 index 5f1c4ac29279..000000000000 --- a/examples/skywalking/Dockerfile-elasticsearch +++ /dev/null @@ -1 +0,0 @@ -FROM elasticsearch:8.14.3@sha256:1ddbb1ae0754278f3ab53edc24fcc5c790ebc2422cc47abea760b24abee2d88a diff --git a/examples/skywalking/Dockerfile-skywalking-oap b/examples/skywalking/Dockerfile-skywalking-oap deleted file mode 100644 index 74f1a6bbd3d4..000000000000 --- a/examples/skywalking/Dockerfile-skywalking-oap +++ /dev/null @@ -1 +0,0 @@ -FROM apache/skywalking-oap-server:latest@sha256:9af311a030f2a106dceecffddef72038e8a7335b92e2c2bd3a30be5942d663ea diff --git a/examples/skywalking/Dockerfile-skywalking-ui b/examples/skywalking/Dockerfile-skywalking-ui deleted file mode 100644 index bcc776bd9b1e..000000000000 --- a/examples/skywalking/Dockerfile-skywalking-ui +++ /dev/null @@ -1 +0,0 @@ -FROM apache/skywalking-ui:latest@sha256:6e87fcdaa13ef3cac4d1fc482422987461c9d6b79b661f2fa80bf16a271022b8 diff --git a/examples/skywalking/README.md b/examples/skywalking/README.md deleted file mode 100644 index 24020c335c0a..000000000000 --- a/examples/skywalking/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/skywalking) diff --git a/examples/skywalking/_static/skywalking-services.png b/examples/skywalking/_static/skywalking-services.png deleted file mode 100644 index 901947fb1f142d572b1bcdc1949046cbd96f50fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 248896 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 z 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
    gU&KT!83*OnU{7)qI%Q8~t2GeUusR#SEaItK`ju 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 P{slXG{0Hg#>%aUbK!U*j diff --git a/examples/skywalking/_static/skywalking-topology.png b/examples/skywalking/_static/skywalking-topology.png deleted file mode 100644 index 2ea133f1cbc019e48f996e9b42e5f164bab21bdd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 327278 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�?eO zIy#S=`V>_;-l$vB`-tBUf!A+lWE=Q}NFv`$s|Hu&f>+!6UIh`gwS^xGQmR6O=QM5- z?KT!Wpr(-?jR$xGCs7(@@d>1N))k#=g%QinXSIU!kLu)9>yIuuR@>l4E7(nowX37n z76SUzz<7zBu+V*z1Dh-w6`KW5hYD1ZEW|kmQPTv%PQa#krID@V2&|l#RzG_#Am=rT zgH_M|c5(BjGsV?ER`iP!=BE5R`3=~nQ4=@0W9YZb!gLnCx_*zGu~CIY>&PFoE_e5) z5wX+4x(!}G62Q((OB;@flZe_bM<*%i*gRvlZe8&lsz@KSFMO7}!C>FieWd8RVI$&s;Z*4pFRddZtfmLSo z4_b*x;yQDNj^IOGz;ka?)^izBWcsoi+QY#Qs#Y~1ZfJ;Dc4~{)y;bvd(or3JMDVin z2wyUDWi!f6vQUsRbf-qb=2~<#Q$Ji$ z&Cswde~U>;j(ItadhoQYwAA0N;cl%6nZ&~V65!r&7dx5+Sv&{vqfDCe>30%6`_LI+ zP~_$m%PeTrq68QAU2pF-16~IoF{RL4*wXUt`}Y-A7cz8Rb6R=M@d@7p4jWI+FaNps z2=_GxGn*GJx7>Fal1)Y3`*a0rQzLI5?WUF|Lvg7QVgd zq+^|UXw~5EMdHEZKZvbTOR(`FNRZ&+sB?V?bt=+UxD||82PKnjn3%bjz}f2Eyaahls{cDkBidUnXKzkjk7 z;dt|A8re7ycS5-G3>u}JRk*XsdQ^gi7L041JI9-~sV=tXt`0_(OuIJRbx=LA`Tex? zwsQ^$o)O#*PfZ{;pn+_cx|Z?)0bKsphd3bB*t4UF%sOE<|4X%6I(iBTx5zRw%MBj2 zkU#OQwtM(uUioR8Fh+u6bzLN2<79sn*(|m@a*v07{AmPkn)C6;9QDN^KVj_VGd)df z&*;T;f|xSD8DlK>s@O{I*ni2`!Yjux7^Ci5bNdcOFaihd2po&A_x1Cq?MujJ#b{!F zMwEQjqO%FIw}HTy$T#Av%<#f8WaGb3-A`0-dI}7^d^0#!hd5xwC77L_YNnq%0SrKS zdHItQpgUEZ601}V+tHX+%xN2EB6=^sTx;0;1;g&FF8)vs(Quz-m7)FaNFCl{Vn?=BIRI*&9$*HXFPzqI)oB7_&a74wguj^U1%~^>(9w& zh}ULEIIabV61zTz+)$;fwVfIBml(E`6@~U>+J3b<-blu_b8`Si<*=NiiH$LqwgrBl z5a{*+I7J(=X+&c-INDuJB#*P*8Ri$9Y%;W^tDjo0PiCzlMI^t`Xf9?Jb8vy^Sakvu z7`r`lMIdYh;hi$)a^XUk@y6g(Jha_=;8nb`VGS%p+cl@y79u#?86D~rLSSyW!Iq1u zS=sI(9R@HnkrutvB_#+ws;o671jZ0Cu^KfZh+1GxaQc078|?uF5jIRaM1G#AmZM&V ze3WAvY!%YE32VedX)<|r`jdhGPV+})$HNN&(*#$bdPn$Yk(ZT0k_X4n2i@J_PlY1o z+F*SdgVf<+@>Xx<%rz;|A$6#EBfj-;!nBSy7<0QqqyeNYWmQJxbqoDLf#`yd0&P#@XT zyiUmv+rA^u&L=tEaBy&CW2woinQ>dwS{*SVGG$#@T6!0Gcb(35=4654)1o|aJlmC;Eo8p6)wz+j1OSQq6;R%I~XHS&f~)e)?I@`kd(`nuL(1K-JC+{|o1?ol9{{#QSV z2cx>D&vGZv>bN)dzj=_vo4KOK+@vvj=j_3${N?BZ`y1kYsmca^zCE5O@7${9>&v56 zK=-pj(=lcRs;G0RCD*GEt3Q8wAdFPx;+rxRWlPN8@#c}uG^}MZqRT$|WD0mKynG4C z>ut+ZkgGZS#r=JMOznMYRum&2Q)E-oeu>lI#eEVZDtqerwq^gwo&`!i$UbwO;y+rc zF5}A6HW|L6Uz{!nJ34>n5y9<@@QnBQu}TV^zQ*Q25!Y=IL@uZE(iZxbuOLN3~1A1ZZZCT`rTEKUnV5*AE<9nQpQ=(QE@S zg{F0}9aJ!#l$3<%cYxW3hB|0;Y*|UTYBcr}ZmdDRSupmLB1GNALrgWLVvPbLWk1Jk zEnqM06_ykh?hx-Y;{C#WJNc4hQ{5$-t|vtV-TWW#Tt*4=NrA^X(uNcWz6ULtB^bFG ztk-JF3T>xf(U**`=Ky9Oo(HzgHU$HYAFQHrbEN5IZ?8!!k0T6YPw}fhNZ3-&&!*i0 zOYsZOyGQR*k13SJg*rK5k&)Md((ji4{3Z>#s?c0HGXCDlX?cV12c_(j8@p);6@U3( zR=Vw#V+Qw>X+(7rV3z1|b38Jgb=>j%W5Cf4`OS>%JauKjdXia6nE6H;hoMt-`zWw0j*G7OIIB7lD499IOw|il{C%2WB@*uO4#JX_*x{-{j^-Zso ztHP~SjJhSz3*c1~(Lh3~`Wy}&iloht4~NfB2K0qkWBjgEyJt0bi3A72HlD+5anlne z)~OoS7+D8NjBj-T0}3$jMCOur27v@Fz(~B01ta;__^Jfdp|6Y$b=;e-Vj2QQ^kF7< zqxUIP9@2n(94II&FL&#Vlo|Z;dGOX%3LrBQ`$_=mi{hfsSg#k&yd7*EVntx3>wSkE z9ZWO<0=IQ_^*^e8D~k>D;C&tSblNPanZV=yqU6~tR4hz&3#lf6 zPsMHZ>ay+mNq!|0pe1eBIzz86yIXpE-a@J7Ad!)5GShNOWa?(*er|0RB4)|xS zqnuHNsdm7R(7$_|xMCK54^YSVSY#Y7L&weAb<|WQe6caU8|y|@?#;b`&cPJ6dGQY5 zb|x(VYn6@%OmKPuD%oVIqIJ7Ev-(RczP6?OTtWAfz5$=9l5W5rK5$gqqe6HU1C3Ix z5iO4ZLUXa*i)+fZ0L6HOv~9uy`^tiDfo?~q^Os%%31 zr(YtE>bd1D8u+pR2a=qm*NFiHX<$MgB#J}0p0i;Eh<88Cd{HKh{PTlN`9K`N-P3VV z*YMEv0oEO;{#+0QP7pXZEsvZ^^+bWy;x|6T zwBkNJaFm7CZT&`ga7xQ;b1}auc*2{K*NR+y3gQjCEztL3p#nSjz*L3-X7T)cpjIWo z_ybHE>!b`FeLxWsc$9hb&3=T;$w(hP9zXW_tWIz~ACi@B#%--0je`@I$)b->;6;F* zJRbe3vvnCO49w@hNvhUXya*9a?kr|000V+^vW?l|G&j^H$=z27elzAr3z!-#8qrGy z3=q;_k&UC1Q=`ahZT7thhht}rNwoKDZ*y*zO5{&;Myl+Ujp&y(zT|7`IEbXV;qC`> znuyosWO}N)j2pk479@CpZ=Iujft)S#8+A;_=?D*<$lLo+f!wfY5H|BZ(>T&Z_JR9f zL`|p7&dvfJL`iAs8Gvd1#~b`}4E^gwUcoB}pG_R?__uKhD>!vqTFhx#opN%o_6-mj zcK#n;BF_P4@Os5zmG(R;;pNL>dIWoqb-CF+plz>;?fx=JxBC~yYA-)rHBh&f9m6J@ zno}+t^w9?YA;4n6H3ZU(5o}6dR_)s;mneVxwaNC2y?`3+4&zj#Qy)L>wJQJ5?rWtV z8fp*R)h@{oyAjg9*yi+xifiKk-Vr9Wr7^P#lE<#fWX)e!gkbX17xD_npH09!bxZPN zZY0ov#U-pt18R?v3pB$Mj0&k4|Lb9ZV|U_0J{j=wDH2SXO{p2F1`_AV`IZ(u%+iRJz_64E!0JxC{lr?9^7tL z;q}j@XiHmWHdGbd$5&fC>VwE5n&DG5K6rU(vKZ&XkM^;nm)BP8cKmkm5m4kFmG1yc z3UIp%k0XY^P{@JAbv+TS{hY+>>2VlQ(zQ&Jk6;}u1K}HlWT{jpU#Mo%tXuKX&h}rGZ3yWM2`9Hc;tppwT{UdJ^G@VzCi(b;4 zVf)}aBwfd2-dzB+>=z2->;^~sUfFop)r~~uNGi`LH^8M3roBA2&S9)*VaAp$_RHX3eH?}O%a{;>!h2K2n3#Iu-V_u zlKbH^m$)fHXS>OxgD z5f7)TcpwV=I|Cnbq0Zu#u$z*2$*)gf#y2||@r&)&V)E6a*HIVjEjF)XW0wTivnR$z zb}OuDGS1Eltj2<6oE5`K7j{4y2Z|cjt(aXXx(te@L{|kc(USaAnxU7$B8c zwosaZh6qaltg#~j@h2&HgspvQ!YVzrbnv`=zA;$VU%{GvJQgq%hmow;@^Sv;klO_T z>92*{=NZ-*dMo8f~!}Hy7!nD|kW-R^X#+oR)r?u?cOJ@v(pjR(T0uHsZewoS5`i3_3Q4JI!BxgTW$L|PEVY^Q`>`Cw-vNo0^5 z5>O?GKJ_HFFKal;K(M*i%2Si}PwAnv+-y9HE7>*2QNx#%P47*FFAta>@U^D~dReiW z0^vQs(pbz_4D&36ye4uML|=w*$ZsNq2X_seSbUKFo`v;wN~5QA?txNO{5D2-3(wCD#gTz%C(SxXYyb>7e#rly>qsQ(=Lm3mY;`6(OJX1?a`Zc3{ z>uF_<0zBKIJ3p5Zhsq51_$@9n$ysOLF(A4*;?jFUldF z0;*&LucM_?wHX6+bV=c&QgI}me;=9=H^U%<`5kp{#^eD)-7j!p#9Lphr`AB%b2V{2 zZk8{azf~s^xBCwUK*bw#G+eKjmnQS6@`0g{zTly#Oz==MLA?s4n!$jL>q4X~G*iM~ z9;;|U*@-q?l}C{wo*mreN$Ko-f0o7B!R<3vr~R)MDRwSK7t|d*FB*f}K`qxVMuBGZD1H=i#Xq zjXO5P9!tgqkWE*2&lihcr$329hOwF%rXBK}9B@oz?=v;=NwL;w>6yQWMb5c~^sg8G zFAkynwqN+HK7tV;r#xZEXx6C|?@2{r5jwRnyeFRCAvmnudfQgN_*muEkAA-H$6 z*m$>-m_mOqrB_`}7D2c68J5M1W`C7m-zw20K=c*uZn=qDMzAKgQ;r74TJbtHnNQ-% z9XlOEEFci#mg5XtJzE%QC6Y{vR+&qZ!PPCWMPl4uSUhYR{> zQ#kV@qOldpS;{rIL$42Rnhq%Xpp)_~I57yT4jfr$$;Ka#x5J#SU*9-Q?De6#1ZTDF zs%i_(f4zXX^deqK%$y(|JKu5QZU^WfEPCE5j=YO?_wK0y4+T7>+|7Y@>YFnGSc;cY z(d}&qb*z}~jR2W9Y7b9>%@hM%y5F$@1Pbo6cxOI1{w{8~|iJ=Hp*m}?AO zb1(7QI!3ts7&G62;-w@mjo-EkA#$F#L1f6&pI4qs4s+f2%dyC8p1m;5VAOC|bRclb)p${oQS0k102sHGD? z3q~!@ds!75MQ27_fN(n)1455W6MEju#v_W5ybGr`eH$6L>_wZ@)!a(kxZSD@gI7Ko zyVd5mEt#VI2n~%?F;)*gW=^zpYw}3)!Wf=Ciw8d^-oA9Saw;S6u78FuG|4Bp*Fqqw z3`r=rboFRF>bLu?XWz>&sFQ{lhXOk!Jw4sVVW(9FBHxC+%IH%ihWaF%&MXm}7$Unw zcAY!=S*P{2$SKg}iRpYZi-4X>YV3lP)COz)tvAQ-byW%O<$taxr{+}1avZ`F%ym-x zD*{7GbQfpa5B*);?W#q&{CP?{ht%VcQXReuXi4wcLhGkDKkSUEo2?SzZMrDqHA&*K zB1Dta{~UQ+R2R}^xfk)uBG>3+Wi!u;%yJLOuF9?Sjj^t12%a!r9dEK-#_UsFbLFiS z65LXM<4&IfCoN&eV<&>^N370%zFYN>x?D#;5horJZEHegwtfDby{Qh-oGa?m^%wiM zc|BCrf^B)<;Gh=r_i4vK6u}#@KKwd}0HC$d0K%AbhY3^QZlgtHe%Q zjD{KWeq0~oZN!iZF=WO@Frminse!0s>`p@sMBQle{mb1xD>jD>)~XbT8scaxHQg5U zc9-@qPm_cNVbf|>^97A~WpqYNpr3c2=Fw1iEfSJ>2tkB86>GJql-CiH^T`X3OtgE{%m zjr$_10@k$jSVixJ-rci>%bkAnAfZM!($S87YtMqZ0aqH)NozZUwg|juYNSWKe<#uR zRgHZY`+D1Hh~Pb7T`zO2F`ph#@?Nwm|3~v3YXQBc*|8N9vo#i(zU&@;mOAXenHVoy zFF{Mvy$Wc4jC#pw04Hn&EL_NSOH1a%Gf{v|K2dW}ry`yZE_#c+Yr9Uh_O)2Q*Cnoj z?SQQ7-gcV2n1CQQ=O0#9_6ofu+4Y@k-iN;3`FOnLiZqB;uVSzLfQEB4kL(E;4AoWR#?Q%I58Lt#42z1R`$ab-9eKt3852IME`-eVIrC$ zDL$czeALH=_hAFXn-qhXj|0`n?&L!xYs-;($j-vtQ@0a;lmDyY7uGC!E;EKzye$A^I-0@N~LfF8_FMU4N1~v3b;I~yK0{22PpqZ zj~>;G_|pil^#gBsGHhi3V#a(cLoI_l-B!Efxc6|EMZ5EIhHh1Swnv?}H6ZeOsh$;^JJSjL-c=AZGeOa`hSGQ>y)m&sYk9w| z8sNJE3CDc_3V;z=50UP`+>@xy&!5W+E{bsC$ENz%JcRh<|$B}K4XRU8IJLh;Mn}72MB1EYK2msh1Cvy@>125kKe+u{a(U?M8 zN|q&Ey8rt7>$is*NT0q4^oS9p1EEe-*l0F``s_7o)U3Io!B7Sa9Rh~V+;qM6xQMxT zsE9zg#GZ_x{wi8~lLSPeTP=G38%zGifw=v0 zKBK}f1=#hW;1bF{W_45cx&eSCY&rl(SpRhm$mNF1wRN33b;W06tkz_!^wb}mUea++ zOAA=u17Z*MReRWv?2@CL!UzDGjop%YbikwnPP1L#Sm+!rAfidIe?!(pQ7ZE2XM>yXGcEyt4VTl0Mr(KU(x^sxh^EC zhF<5i`F-EP!xPt7vA}FR!0k5XcZ1&%G~oM3j-(5iNH!Y|02c|0J3vCp3Nd!&`STPe zrJZYSY3Xyyc^49pt6V^wxR1X8FqCWtNX)0u{kwJlfGd6q-My8&czOT-@cjQpE&dmq z`M)^=bq2ryj|~AxIuIX_paJPqg`hMn4k3V`KrXOEFyvkDF2~;=kHwo^R6}ii0f4Wm zg@D3&c!(Z&%4t`8%MG&u0TMFR7LmZ(T%vpJb@_+vGJMB2m75y4jX)yV3Ldj4zg0Q8 z3Q$&p97+kAZpO${iJf#2G}jsRqseKH!rXvVtz|8&yf{bi}=6H z4FU~tK{7(1rX}u~lve-W$*ukaWdGsPGeF*v9ru)?y$};=6DxhXSH8ND?}`d2WJ;8L zUR7^hA9{f6&y|E=Q8QHpm?u`zZ|6sh^;iylaBo zsvfE2>=`Yt-^p*9L;*_-0*^!V24A*BXRq;Iau-pvYk^D@0Hw(v&w(D(Zi$y-dAy`c zeUEXe-k!zj7TJ(I6jBvS_&t>+%ujpkhK8o%+gyF%^K`anPd&8*`v~XE|3_fF(q`FK zuET|;_@3|#K*AR-n}Z)!)tZ-6h{owKAF=zCd>U}LzOO8Oyh z-d)c+e*stcy6qU=ZP@>Zw1=AkB~}rNyG;A)Tp4s~Z>2&UUiar~|N2Jcj(l@T+PVuRp`Qyb- zLowN#p~$YM$FTAok-Mr2$N-GyrFaaUCK2OPDG;<_J+bYzm3uMow?y`fsqrGGz6^hQ zLx@H8jf_V=T;`@Gm%5rlz$&_I>Ftux?Y+()_iX$r6h`l+tdmlC8y}w|+rvG2dwYjo zT82h)#!gPPtpJ-%z8sn6SK_c4C5b6l0f+k-xtYb9Z*=flM>gl*&}^(1Tf4BSxzkz8 zaFl+vtvKjuWXPCgz$5m3%1~nq=RY|3XgSn4a76dS*7ha4GjU%lM#jK<<) zk>;m~#B%%bQQ>-hj)Jju1;%!;e12lFPWn@;pnqtc?c%39x42I*>X13lFX7~n1L6e! zVwSrf8w(UwFi#vvq4pe!m3Fn~ih>%_NI(*3v8tMouU?|Ggc`@KVf{!-Qc>@~r;ptkpy zTGw~t>8&$AtN9Ul9#+Y^izso-lH3o}jl1OWHLAXB0*2n#$(J zB$564>vGX2$5x*s^rm%#m&sK5YAN_FRynoy@VUVk(G%f7=0v!^6Qqo$(GA_0GiNL> z5k%<)xw%{#gqGhF_!#58l}3=PBq@tWe~E1(jnX@ZLg)pB$Tn=8lj%)u;c{RI@?Jbj z;2>~zz{KWA3E!IA6Sv}vZ&K_X>Z|GyKnMmADjtS6qIC%^hD9X|+@p&1e}vIhe*IbT z1@4%|H+%O@VN&hBjZDXN%+rYEfy6`|emvkZ;CJRCWCXZ_<0m;yOBHF7vtBk85CDCT zm7oPYd&<@KNHOAC=26#so`{5ov0C?JS-Gk&9V!zLU9K>T917M459rMog{t`pz~Kce zywuJEbL=u@r1*4ec+avRdiqBjQyc0Mn*Mb=xLQ-i*J6ty>?7md>uOSKaj8c)>$(es?*V z%OjcJqaI!qN<*$Pf|u3FQB>?{QXgDr{6+UVqs*q+z8R-YlJ=x1O;sX5upQa(Jp*)LHf&#jozc?OJCZ=CuT`8m_e1w;f^N z%$(WaU9~%kFseY^Z=C7+DFH#z)4S^9!|YYkGrT>ZqxA%*1lpLngNz>Ya++?htJA-t zMgv71YHjbO#_ISI(%q?IhnOt^&Lfm}N?G8Gv;Z1LCwNW6IKI^mE+E#3|vxT zZ(vXRw>+s@T8;LwW1udrnypQ1g}u&)J;J5V5AoN>(lh6an~!=A+qS9YUvBaMRXZQ4 z_f*b0Am3THk%vp^gK=Xc?>w8-o(SrgS828o%^x4uwzlXSqCxAtMS1Exi(VeO)o`yT zF4sq^_t1o1>>6EJ4O>smF9jlQ1ysJ=D?r(l`-|lGM zzv4DRHH*S0&9tEI7Ray=V6TJpE#gkja`~6HQsS8-$77?pa0-cPlbzYihxLjFA_eq;_aHNuf$4fsf7dXu|E`17*Cf=Jch$!`rD;7e0m$(>avEw_p@ z90cA-7>LhkU4~PAE+Hm0+E*sMrwF6h$i1miVt7VW>3St!(|i5Ra*mNwN(#t^tvgZV zjJY0X*bFg=GcmhPNr(bB<}u#(U(Yp0z4n>PuY;$|4!d@hD`E-8Vv2<(Q1qHROwh_& zyB)m+|71Zu`=&G2(Vp8p_}R|l%qbTn7l?>HIVN8EoR*f1(k#gv)jM*;3G%3|ja;Qo z&GJ(v>Cm89gJiRBz=t@w_0@z^_MPo#m{pmMNf`Pw^`AKIers6^7T$k_4=S|!5tAirH z>?juN{hbt@C)K$=jHD299C{IFkYe)nROF-v+1RaGa%@*?S2{Mz$hGR_%djF%BO=r^ zq;X5t-BcIazS24=bTT=)lN+*?Sfm_^_O}voI|P(6BE>1L$#!a((wq!7h9-JoO8hiQ zzVyn)H}a1aNsWB98%(hZD1@Nfh5xw>NF+2;4J7z9O-H|GHUJ83@$qQ<`O{2Zs2%wU8BSeQy1ABzu$aagwONPAXa77)vpTGP2qQr^fyr z^y~^XBR$=P{~4^pcgmg$Y`*u6i&XOFxWkS2iLBTxwsd1z6!GECxWc$!`)E?gsC~k| z3XsiH)5eObHXHV2oedhk#J?m>GTb`jWL}-rRIt*qc_sR2N>EZ`^ONj>HUC z;#jBKS+Bfyn4knW8K(E`*TKv9uF73n0EnLRuZ8lvA6~Y!UDFi+Rv411Q@^@pRyc%bkohPe5A12ujX@rIy?v2e zLT#+}+ZvDAUFdUv^Ic5TC*cKFmS=URV;=+p=EI%Ftq|B?$OFELaZncE0yqb9Bm76- zS!dUM+?kkaD{@W8O=?UuRyTia(@xdU*%5URgxm+2RqlSv$F%6h6f6J9me3r-ki3Tx zzV!PYuSe5_To$s~De-&tFVrrp-6y$`@L$J+_bLjQUWW4y8BWb;!d* zw>(Jv<~{|Q_`<5qHD`Zr1gEN7KV0;4-$Mr21Oj%x|{WZ#Mj8#=kmPmw?y(ur|N3r$u8F7b4MkQkZFS}s~rSs zNDlLvTAW@V!JXf3B4q7pT?LAmJyrK1EHX>Frh<(7z-@`|7=}?y|E7Wa3iO!67uBL) z#RE_0Iv;~*S_4SEoyFR=)3~%hzQn~J3OG`NzD;zGC$znCP_jHsh(ZXTqNy9|)^jTn z(=SAg(%%H5U415Ptsz4z13bUhEsVJC_hu7tibCvpsb>)B7nC&Z5D{fZaS+Jb78!)W6K~z;Jo`xR#l=Ay>N-V59|5x9;r1?7kJS)z&-eHmmr=-_q!7gWEEu}6Z+ zHD`J}n_Jw6HX{2~o9DWWbFqh1PG=jL;n20Ox6Py3NhYE^sN>BF+a1x`R(PC$v{mP3 zyoRmVun{z^?Y=r-GDRFRwKT%?19=e)L3O{#bb2?w@Z!~%v441X6*4DMN`MtQCg!gv z23Fexw*C#ij2#Sw`JLC+c(XBT$Y`wU+I3G=qnJi4tlff~^_tj2VrsJ35Q}e5rrTMT*;)5z`;L{gi7=V}N&YGSI6>oV$; zEWLtXlunqt8Ap|YyE=?D3=~)M_W5JLJ6lv$YhOsbK8wZ1zCnfo+zK!^W(gv}i2dlM)~>AhvJfy_VX9!$ z^z?Kf$=t1qtvE^a+gcho(MyRx?bV$uoaJq;PEpjw zqYW*5I<7f9fM{tvRS3wl(&xR3eU8o+CQK$%8NzBcUrSNqah{lHjln}KzKXa)+2Q)t z+gGa?ZlT#8t6d< z{Z-rFGE<5Fk=nIElsszuDHby*dTqqea4s0EW+ys>t7>G}IK^V4)LOpo=-%l005Lto zm}m`dx@!m->g#3~X>v#}%y1OqvTEAgQPn(f+|H9~bBNBVYKyb-kTIQ(7;b8Qvo3{| z3$m|&DabZ%)lTiz@Lf5O!o(+H$-meWN0oLt_skiiYfTJ)NpEIc{pMoC_Y~2hKL8lq zpa__sA_KO6T~bY`wjn)g<+8j6&yj3gR57?oX+*xg!p--A1u%~)Hj4`TNF*V7EFXzT zkZl&lQ9`r&vr`LH{jur-_F4sq*1IK@NFW{^Pye1P=ih68;}=G*j+a5(BLhat)7=^a zih#7UGKYdLCA4$*bb^wI*Mwy*drQ^JTF33<6DCdOIcfG-k0~$hI~!Wnk8!la!8;z= z!iSSs;X@DXWn%#9I!5oj!f&{Sx!$|%^^W?t7?xXp0{|MDr1zQP;%^pT6=$U7*H3QGRfFHDc2UeKA)DJUgDLQKe~;& z*4+VYYV-I8LBO(^ssLhXGVEZ5`_8#qg*FKKW#=fF@!p@XD?{iwButv^cE?elWM z=2m!a&j+ggqZ5~FJwF{t?d>%HoDY-5U%ZIhD>HXed=^fnu$T@7sp@etC!Ti#8&d$H z+G&~y_#>Gfk_mI8*zYmk)j*Q&?-Fq4uXy;ea2p`ZMf+&G-IXo*5pUWVrMeXA9p3=& zZkPy5YM3O12K47)Q#6wMwDiEKzws6ON=Y~ue&Ijnq;d1yeLIYqG&0Jq;S6PLFjRS` zr>5w~d9%dH{NGXz_PL5FJ$rozYF&7=q`C`8-+tRT{pkP@1Z&-{({gU}1-gm9As5Gf)}D!~;}zD0 z%^2)9DOMrCnJ+g|R9P96K57r3u~$-ef0T1-e7$d=;y;F&$tb@QcDu&1+|7}FDi<}K z6mx;)$+R`~-B&I;1r6*8cY4dpywXvZtob+b%gL3$jEqqHAwT4JGy<>&TGH{TfbE_L zivzpw)`Y2q%Zb-ZKbZIoFD$9@`SG$J zfh3zAM>-{Aa@a{k91e{sVfNq7JMiw=hZZ(%;mh5v848vN6^x%4H~ pzoq2;##j6Q-1I@#sEe<+rFXBNJ+@gdjFsJ6TiSh(|L*qB{|8gFIY^{yy9St3fjcpvwY@I+*009UHA_z%Q zVHLNuqZQXAmFo zGUgOfLq@94pLq~NltkZ}OPTr`Kb(xT`HpZTG1^ zje!uaSBNAc{okQvl1eL>8UUAfRh9$IN2>M!C)`$e9$dc!vYCRFw^{Je zIN|93wgQ=?vm$iSd#4I7Y3Pj2>Up^(S|GLIvqp?cYROJwX;3<(h_FxyYPNz`A*z#x zxY?)izb_f1awA$2lxbM4PI8ME8PCuk+Pl%S!91T&TUTrrp_ishe`S|3@wuU+juLYFr^O# z2vQ=Zqiv{WcqTYg`a4iqhzkzxn;#Kgka`&(6F#&*vDqu4N(@Opq-SpSY%=FeP)(SH ztJ9saehhn0tA!;Gor8{_L#qUI0dIi()Anx%l4+!DA|klA0o|dUj$Cl)D6r7NNp^h5 z>rB}SLZ}J^J{-{h;F!2OL<`-c_WPgj>8YuoaPrACMe4|;%A7p;Fd0FZ_@=vZU@pxR zoR0wmhFW=ozoD+%aiM*S2nY=z%@;K6LHE7&v%}sv%X+*whSPk-&w~gFFy)w$AUq!i z>g=YbfQ=1<6b?(Vr3PzB%P2Xr_mVE{F=WSIBqibR+?&hv02$U=jWO?~{o`o#X5j;g zFiGLQO|}MQlK-6>#S9@m8}XiX_%i0d-fFy}7}+;kolSM| ziCe6WOA5R%Z;u=HM>@&-t|C3bp91XjJs|ZXU8Rj8mvwyMukU#jP3tgd%N{e z!}>#dH_3eZB&2PVj#9`ZdB`RwaTD}FLlxekC&18Sm;d#O5T6_!ru1?SGfA{Wjb#kk zepHAkNl#Ao`60oX8wrH$@@efWuFs$e^AX5HcN&BefyJR`1g6A|PM5&v)mF%6=!M(L z9*oUlWh7%d28LSr)jIyJ43aSi5fDTZ(2L09F?oy~>U63fy;j}Zh5uncxNo5J!2d5j zC#sthdG&q?s}!#g?qdv@ceC4Z?B6$-*FsID6@t!*L;)6GSpR0vnFr;oSDn99-4MTG zJNR`cHztVJ_7y``JobP4SqNFH-&4K!gNwkO5qEFz=e)c;haR*Q^W`RH;jal=|Jc-h z4(&Ukerem_#^q)0RbOA?2{R18Z{I}zd@a{($dg}Nm8_79QE^r2IO#2Ienn&qvRZH% z)MczR$%rQIPIsB#29@X=vt-Od+hJ@qd^OQ2j|Tt{g(fg_xW|B*!)jS#$;H}ywN2yQ z7sl7~knP(!NxT`4qz^w`(kSjJWt-4$=I>d)g8aTH#@+5&cR%Pk-e&BW)2rBEWUQ#9 zR5R5ODR7^p-yMV}v+xgsVvrr#3H&0U0LjTM{mkUmP)n|r8T^m~tJUA@Gb@c(TCTR2 z5D*YTdJUk3?Y{CR{#~qpWHcX!8k}x(?~vla`^m-!5&M_zYDcae26m z`cNnBd9xQQ4>{g$Y0AL&vP`B%`bxUNksncnULLxC=oAF3cc3Fkh6!N|PzQWifb`bt zvDi}gzQ3ekb+oyAx;mVl(}rlV!`v)1Qd?fT<%q?63h@z7^^*&db$tG%b0gS2&F=oC z0i#C$ii4v}cdYAf%Ka|WH}9g?cYOhLBO{d#ku87|ZuLwDH2zi4?5H8<>v3c*qDm24 zP-*f3qBNO_G^{dX$_ogq!4sUk1hL3saJx?66LbXMXt!fqpeV#Iw)RNpnm$Rj@G7c+ zy(3RaAJlmAVYd*;qu10piQFMUIZMvL6VzOlKF_*ck{MeyaJj8yeI6>2*O~;h^X53x z_^XwGyDgod03k#ihUtq?cvMc?a8QHK>vFn?YkU3^acr&9{l#ZT zV+@-!LF^~e!0W8an&%J~3u?UNGop{(0y&bv$vl6=%h!qb1doTgR`X5peu*y^Tf~*s zHCY9&Nm^+ku&x#uyITzGST3vS3_3jM2;>nsVKdWbE~m4ptklpv8E)K(kydXz`&?Id zo>I(p%e_D7^pJA$_iczs96nh;Uv51;9>mz=3EpCRxPnCINIe{nqR_&*4^y?h51;s6 z4%Y5`fJWle-FTb^ROS#~BS$00p4eI^WowDe7 zwb)?Y-Qs*W2n15<%uTGsFtq!b8CXSndfemcB)I z_l6QOlzaH)x|iCJc(!gg+Q=>lQ4AH#TDA(kN)vveyW@obef+lGn+spuDCLXbSPLI0lP)CTmQ{h_%M!0bQO7qK2b)i)~^+H>Q;n}!#`{)+y41M|ry=B3(NNU)Dw zz9|H}j}RTe=5Z^uPrlf>cz7VpDQar+(#aM3X~Dm#vbiwmvLA{$hKRB_%k+RL93*`D-=l9x2D_z^0+#f$dg3(JC_@Wwd~#s9SM7bFXpHu4;L}^)d<~> zjzV6uGaF=C&axz^C%v&f?JtKPt3C2u#2FF|g}!UH-qzd-!h4>;GqHivMAE+WQuPSmyng%`8?&8&Yx}#d+R)AVG1c=3T8I15YxpPUv_atC{cKDg zi`wJqdHRWXuHuRNDOv$AMY+R9b%*by1w{Pg#}C(!4=c8M+O+Jds+*8|_B!a*qi%Q< z6$lsC2P>}9&qi9E{TjpqE0{S|Qj3n_iW{mLE@)-*3D^Ld)||@%S#-#~>FE@>?-iRr z;SnMCoVv_B-UK|bdWafA)U0}^-pB~er3BGyMTPYHc6lN?#A=T|n{}RsLxy6_N647a zm-|c){W0l}@9<^xcvvh%u^?(p$9{je03f3opByz~Kt=m^5OiZqSwIw#|2EL-r z$&Ksba;oXxRB)jf$DZxu_6aP3$xt26NK*PB;fZg^#zuNKFj3EidhK@cZM_adbjs@b z+x8#LmU5p_?`k*?&NjH;UfR}MA};^3&h={IR_qE|Moq`1e4iP~_=CwPtRvmg+_gInUoBq}fbYIZ1f#|n?u&bdD<;Ec?zZK8^sK*byT>HsrWFIhQf$!7 z38~!b;#(On@As?%+M|okhw81=!4kFCPDk^}KK+@%M&O?K#fs@L`j0v0fb*EU@YrMjCt!<&nHhrzzw z>O?Tmp5_A1XFxBh&X>RYUwq=Ec~mkqu2Z-}1=}uZgLdiGXH4}B7B{x(^v<}Klr*Q> zwNPNfn?t1gujfZC3uGW^SMO0?yqrH?B$kuX3I@*Vv`n<`5{*4wzeUo0f+*B|ppj*` zX9YfGsf`N4FMigjx~o64eRFz>8EmoBzm@*3H$X?g}Gl$=30g|V5fF^w&z{eZr`p`6~0V<3NY=^3L>kF>_$Cd?=I z%1}o0^SOM&nBAt>VO;tc2b(20S}|`5NcaG`XU7%2W3^N;Dm({p>Jn9jaKauPSArDa^msU_)d$D1Q08k_-SFKg zBV(3r?hv|R+smG7^LZxiU}X#Nigv!i=WW=I+iKES$hSfW0LgQ85c=W~@$ z?TnxYF`a#<q`Gjn-kid25tq=~e9D{xc&m6>ihZMXpBl>cQrl$RyB z9vkk)^tN--y`2Sn=HC3d)*gMzgl^Z=+vCWqN`8mQ5m87_49Qk0KPH9S6?ve+ecADt zzOAk$z(ZWOv)xq>_&8AnYC8GqMCVx6Akaqg{g2jdIPn92pKZlgMlMTk<~P)`-$yeLslHB;B56eG#1^qa^h&Vdl+Bqtk(OgG`&VoGo_@$?}QVm*| zbGvIwn&i?3ZBwhA)}X#!OC=zQbLUGyDyNF(YMfqmTfS4XG1T5{o`DTpf}6qAFxVI`CBBvG0KRYfuW^?cLtYH9x@q91J|yUvF^uTgO1M@XpNp}fzgvDs8~Ae3Bv7> zd7iZfv-EmH)4K$_`^{SF4=1FLZ9cpiPctD>I@VAgPRCNY4D?*~({@4E`!cBs^yVp= z1GDc+lLYsDA3eJ$jePF~eJ(m7GCHWgmvGPCF77t({D5@_pdPsLWPr3;5)3_^vb|a7 ztKFe!1=IS&^Xa*K5>Ht>c`zAm`k3|Fzg4~i$wt~(OAfDeQeNn8+RBITZl=XQ?z*Bd z?-6pc zILYX&=R^Gt=W>d-nfr#~#KV#N&fPNUegsO!dq+RT=@@vr-i^)f!G$SVg@f6gi5+;ZxxOX_sgs!8Iz^R6JUvtsu@v|7zz3x1#VRJ_S$ zQ~1?}R?Osb>$#V(JEqxaCj)36FY+t!Vuwi(29Gs>c$-qlR?L`fSCG-D+M*9>)~gaW zo#xp7s%)X`EtdrssisQ#_HST1RL!P0!R?(a--9_CwGMCx0DS#jU$W)E8~g>{f)_v@OZet z9w4p~<**y=_^{Udijq!)DcP5w|L2WhMJQ|;ls!1%h^i$`1ZycI6Afwi=>>s|&+f|k zwwkVQEO9@+_>IlXbMEU-0DBJIfc7kWivwIs&CvR88vc@HeIH9O8HA47XDk_`E-}CB zG`9Nz8m&WCuO1;193}&m_4D>eHV77;P#IrH%Gz% z!}*&LIr{?jfe|$>8@6|6pu*~8F{HK)n-iWwTI%@VfXMZcYJB&TV&FW>w)KvlGaXc^ z3&B`QSE^&K5SAR_T(Cm?XgW*K)Kw*6ElZ2v0_cPNVn~f6Mc*U$1=X8~k1PQR+pHo; z*vZ1NSi1TTH8?U$cFb4_oE$)dz9VA@JZ3Md;}USy=6ZtXw@m(cx664t-5)5*6R|la zs5=-{0Nc?zTe$LT3Vr93+&M#;U!orBw5J*V5zVBxi; zoQ*Dl+S+MpdxYgm$%mV#6PL-Uo5rV>SDg&hEqnL{ zX)!wX)5G;lukejHd@W_0ET&NZM4Afw3lbZiK#Z`ypLRi8y5yPe_54;yt}B9IA|ku- z0E+KGu`J~5`T6b5P41Y-C9dn66$tfT=kZ?%dfR+YPs_C7JWkU~$Cjuc+iW$ba>ctX z8o?^ZQJBa<9x@cpm}8MH;#eP%us^iG)C zAA>T#J|ads0D+9_W!d~@1dt)Itu72N&4=P?FS-44qJy zSgyXCAr`z6Hv-n$Iq0_yuE|^gqr_v}wdAzDjb%@~Lf29g*cCCR5-1^J#~r$yjWV5~ zvV{d9&miOlZa4yhC}A-VgOO<*jSsjE;1CI&#ZMaKy0Y}WtS^7CjYpB#doGJ zAlw?;{M3=jTGRV0P;*MNVU_w(I~#vDjh}7e%6-!z;rP;ahe!8$PRj~5CG-B1k2d)K z8o8^aIMW3w?@B)WDcL}BnPWxaNMgoDOva0BHmw(~gAAp!^xEg^#7j4QvZ?b`_7}uU zfWJ#Wk1-fNmcss`96*d2c3eYG5P6Z>>H~bq@D+fM|H5U2pJj`!G}2_FIOcA@3w^qc zesHmKcL$NDdQi|>!-k!%9M`0WnX#G%Yfl7SHe3!T#921&<^IrM<#AO)QQanpV9NTO zE?QDEYX4+bV~&pNisJ;`kN1`p>(f}BYtombUjt9b*ojhveM$Q7+#J0KcvUM%!u;9R z=9c*$AyBB(-$;|qO=F5t(uS49y^bEYO=O1wC+1YS)0_Sc$Orr{Glhv}`(Mgv|> zR7!v?eG6<)1U^^3=<2wo@|n^nzatYbDXd*#57~lPBizFUef~-VKhd2JoLb` z?p-RE91kGzW5-XV_zKiUmvJY1jZ|O0xn+w@PW8k5gspou{nINj`qPw~w}Swp$=IJ3 z%mf@&5^w6AV%ChE*!6N{VObZ!So)JvQ(9rg%UfUFB_0Y+;*LP=mPqwZ&DeM;KnU@_l-)$f2;=WKo06Nz1 zjS|#KJ8q6`C)%|V+ao-;>p2HTfs6pb+a|*so=WH8goirYSB3myx@*Dc?`QGn8s%GV zxc7IRHO^;lX#l=6d6H-hIRSkYr2{@ANHagka>K{F4~^^HdJfeo?<;} zO08K)o=5&`#0oeDsD;g(tp>3(D>Z_-d zKhy3eS1DvnyN*P}B6!B3*_`&L(QdI-!40^h_kIkE=V+`2#x-Bh#J$cJcsOj9hh~?( zz{i7BB`%TVWzSZh45~lD^5EGSx1tjpbDEAc#M7d$)Ed?{ZBL^&8QZqf(3s5m48bZW z#uB96jvit=Zz!C!oVe)fevz3C$4aVuR$;Kv(#f_xPkqrEOQ9M!U=wWG!!7~2>J*S~ z$`OuyA{;SzXtj-YA4LtykF(Kyz_ew6Ie52ThVDwJ2jV69z5{F%nq6!j(RhIJs1J5_ z+^Iq^V>4oUaxqs+8>&M4E}703nB&fx9Pj(FFz<7+peI3~&_%fZ<;w6y&CBlCb4c}R zb*j6M1_x@v+8o2~^mQvr`&^O)0`*L}USrIMKnVQ3ai3y_N2EAL-vyCzYt6=u zt_$GGVG}qv=%3+yM34x`v~4*#_@o0qT={QlpU2qjS_s_o7kqLqJaweP*ua|8{wyU$ z186_I*&4a=eTIW}$4B;3 zqUbj|S|1$*It=5?=+OM0oh(Tp+8HR(Vr)`7ETxaX*M4uL9Lf3$*x6#eDo@4;|0w&^ zj#8o8#D*rn*~eDVmVL`g!S~2};%(2YWhhicK+ofxD?O)sDPb#Q;YJFQ)l`=aJ`h1b zkjC0%-e|QFbZW3(QEizHs1SE$upMjFvTi;0>_7BIHRp{fWx(k=Y?RLpFs9R0g0n7# zFXZG;E*-NTy1D@x_9t*QT_p%S9*?muDBCm)!WTL|KGi1Gfm0urFl-J!v^_n3bMTp9 zP;Q_7BZ1QNuZiwKv&T1;MvR84CYV^6_~1es#a{&m1xLs^SPnPk*Y5$0f)7CBgJlvq zoIChh6Jx0TaO~Ag9hNC$hbc)(7Dlk7w(Gpvk+t0zK{iLH02)5yN^sXhU(?Bxc*$cYZj~Y-C@`>%tOYuWN(T=Wffp=D-)PTG9Qx};xwk2 z8uA_40++m%s@mpPAJeC$e)ZJUrnP*@cXQ3o2%EbYPDZ&dc4u@pSKcxgSkb z;qO>i^~HXf$?zA;(xea8uC3(e|PYr5?h{C!BeivIHTFDyVr z-j}B=1A__CigJ#g6uH7{S-<0|9oAV0ew4Dhr`a}*+R6NawDSwtTir%Y9y{`V?*|9y zDRrs1@}+aXgBC~eJ(tz;<@S9kpZiOP8k3d&c-O`)TL50egOJNpcAC2qD@A)vJaz1@ z0-G0faoNV`;IN-bn#ba}MWhM?pPyp)r2F=$_G%_;=A$x{NjqV1mS8vR$bue5;O!3m|iWDiEQVkkIw(gFZ;Y+$>-%Q6Trtk!<=#g%vYe|11}+#dDhk2xOO zytOdT+}ilybhx4M!+PB|iVJb#nd?*!-(3sjnWT+#Yj(7;m2On=r;ug%mFFKP7AC@E z@p48?wL59;1o^4sIY{$LxLT;x;%9NnWel`Y3EB2#)D)hw3VjmGTx9^lIix6xen5y5HLC-@^!&zRU5L7J9#Q{MT_(b?mG*NG742jrliRZ9!r>i$9J5X zrGmkqILssy``n&8GP;@E!R$f@S^7w;I(@f9;9Tc}jbPB)@|E{JJA}j9ZpYz?(Rm+P z0Go2lc>N~>`?1nZ4Yh|4ML*Q^d<`?ZfW|h>XE1WOyg_q)fWgvAfFj}tssB1Su(P8X z9$HlGz(WYU?PXW9-`U@sZ*WF*Z|2_G-LA1d#2n-E#^ZcC$AT7CI9^^emt=Wc18@(X!YaT`Uv5{bvPgxE*^P{ z`BS*7UB77T)M2>2aCP(Vqe?Ci^Qqzad7SQ6tlv(5^eEsj9L(s;e|4)ArOn!S(PQdk@&G#kY%<8m> zcLS-vdNl6nlG&2kg>I($CS4hW-$>xHrTs!9a%G5oB8}zCnu{W?TkKB5#KF0Q8c69P z1$sk6BjE`otz2^o3^>AmJNHF(aB#T3bsL%n-N~LbKb1NmL#3{+C+IzfzJ1`RhjHF4 zbA*jT!032^cI9uZS!D7Y?2FWBsBf@*aNzMVf>C?g`r!>u<(s$raGMS&)$ASz!au1e z|2-g)nagW7TJ#<_E{`;uX7)CeYYhNP2fil+XUXFZTXVkrP%YP_C?9LDuP9gioWDQm zA^bV%YQW^faB0o=a|U3%^%zQZy&a)a0j$9c+3GDE(MS|C<+y>~P9Dl`-mhYrR9(^a zqsHU}PZq<;QXDLu{@bMrgNGXF+Z}kQy;Z)E*%`uU?q|@W`(t5Tl*o&8#OcOJWI(DZNX{`hB|InBpgMvGtUrT-%GK?hoI!lG>cf<#oJq zhJmed(m?EJ^=Gcy{6~YJJG}XEMVt45_bzP z{!&A>b!q}z#8$2GriW_B3o;PE5j)c?)Si1gVavFibSe;sw?Fc>aqU+hZRK_*p{?P; z3SbL`o?z=fd$a^+Ii6MSu-g!YQGYRyQsTvqemsn|-({__kkT8DG!l{3*#%Qt5XP*( zDm(p}ukZ^@Kj9S-VxkQxEg~HLXo&t#SHb76K>WqY3jC_K3G(>BZ?%l3LfzQC#l?9? z*&_wteTJUqOwZcaI))SEGq-m+>o+Q0^@MKtWlQvrfzR+*00-_!dZ7N!li=18BB0l` zpI}%~ypEjQlAMqsWekw%mJzsQSy{kXEB5oF{rVikJjflEMf{bGJDu*xc$*eJEHxS* zWB4#AH`t5}QnKu9p%2ehK6qUbmsgf(z7xU%yHZaT1pY=_^$9}vvmHM!1%T3*D^dl8 zHy~>XGELlRyCR4EfY4AOvxBdF@h(iOtxjU;TuE{JQ&bEo-96^q;oSA>47nejPlOkD zhm+5?K*?uD@js&*@Lry9o6o)>f8lW2-Z*45IRIvCZGPKsyYG~3yBFF%*4ISlO8#|7 z$!zNFTpzgaJW<(9eu#YHvrp>cb;;tYeaC`Y;KNTvMR#bw>>tNEwlR<*E%a$haoG*@ zj;(bU$OkYZt$lXHs(pqEIGif-iA!5Hcn`QU@$w$viZ}o?DtN|~ud7~yqQ*QkZS@B) z)zn$MZ*p;+#(w9}R_-i`7o2#G?t9;z>2TKW|IlOgS=ns^x75uqeiJs=%~e?UEk$yt z3;XJd?8KS6qlMtPel%P{Op+@&l9Ac8WB}KFeZSnOKOI8AS69bYv+RjYnhG+1 z{FT8=&!vAP^ju@ca5tQ>4Z91F`mTV;@OT)jy_(x{X8dL`(}UD^xh9_Q*`1ho)ia>< z#H=?LKp=bg>9RdgC&1HgKfN*5_W|VYvF@VUeuWM2Ijl|(ng)5XwTmF$6z;cxs)_FB+@S7zeaDQgfH%@ZHyRR#P`%E}N*Owy;8LLXZGgmqIV^iAQu&gcH66kAu^7_ zdVj(f+f>&Lt?;Jo+0WdYVbk`7FJ<@niY^wj!f5m!Kdd6y3o5kJs4U z&L}9mBk;g8hn@a1_4<_fQqCwsbE!g7t*Nt+N~ZfQWv=SA4l={;OTG(_XD1V4T*0)_ zE$Uj0CIkFK&N9=Ld-^T0l<^83SRSW6iOh9VhhKyI<~1lf`K}Wr-}Ro>X=;4D7;76D zw}F7RpRG#u6C|CNIFWa+>C486}b<6eC|!< zUd6Mbl-Gxj`0Q~0tG1tXI^7^)^q!Kg(_Fk>~@bZx*(ymd&;>Gs@-~%Ceo#b z5?k4SPP7Q8rRDr!m>{Kb4k1|Pn1H-LW)HPCkCx(%vG{@vK5c={8r)+x!uF}qLg1(+ z#?7Kn=lSR}yZ)!>w_0vXo&|^5R*b(@ieN}y*|Zx(iwqmggvBgp1*|wnDI9a7bmUZr zewut^AQj!{=Nk)5+O^eobAx6ptvL#qta!3i%JYpfkt`g_OqSE}b~h)9qa@~76o>;t z$;z?83bDo32On2nTn_63&pC!chR?60h)gT|6U0=vdmNdjT{V2Gf9w(=bz`H5=0~7u zyd;?}9TlU$eOP(sgJkCZW)mhLRwv?G{8{{QbC7!Lz1_n>Q6YS;MlBXj6TA9-u=!(P4|U0Sthrsv%}Nmg;-~VaQ|a_} zv-lJjK5p+Iqa;}=@X#DWQxf$hBvDrjS?Gbe(9Y?nud@(rsZn3M_U+AaIM+5bOT@ld zVFSG+?ef46`JJ;aJp$v$A+9>U^wN%r+#SFj@v9BRSjhf_&6*-P-An3vN||a9PIJfF zJ$8ARM!Z02pxe*F0D8xt9YXN1oW$kE=tPv9O~5*8v^(L;GkPZ$mM*O3f!;1VUEJWP z17@XPfeOha*^9s^qe(1z0Z_+1rWRyNX4gEyV1@Y1{=-V}ohK-?jl-S-QLVywC5R3| z>ElF;G9gUn<(Ce}RUysbSe^V=xtS!7diI^8NiSj;0yPy?77@U2f-*hrUvN#M)nvZV zd-JPeJ%GTK(qVK=iii}M2pF>5t^bWk!X@;TWb^n#di;kt{~#<&bZ<&pU-JT4uc0~a z^|~=Crr2OC_Um!~=V#z!+fUhG#5GlNy+n-DfP4EZe&n^#zp;PMT;;Ye3&v;G^jz8O zTI$WDzM6U8?!)|B<(Nz34+w#9`r`k9c1pjYdgm7?67Y>^;R?KfMBulq$Ckz^kAFhD zDWS6!wvm&-I3X|&mKGzY?a*=KW75%&S4&AgkWwM~y%iQht%0f|+Yh7vgY_0#!18-N z%2}EO0v|fy4-z^lCML#cj!bfr7>rpI;liW9h|_%#36&dMkTR-hZLjpS`g-f(83y&BX&u4WFrmM8sG!>lq2xDL5bqRQe%y^7UsG{5)q!9 z_QN;S0Fh9JaTniLujla)zYloxO(DsSN{kE+JuI)vZJh)L&lS(y{9TMrCYdBGy`W%Z zFCtd7CS$w=DuuSkF(McR&lP&=$hYw2b)k3t85JzCZwfh3PF!H^3!I+Dz#PdGzvkc- zmX0}SII`B#VDcp>E~d^CRe}p9&BxE8;`bHv?-8nv1`Sf! zE#+EWj?Koa(4r;C+~cKU>LR72qy~l;et}V1^DXRQ)6*(SB-ElLXe7~0>qJC&$e{OL z|3tnA10+zP!LZe?^;0=q5n`~n$6r{mT1olLhe-*B*N63n^h-h)Y}#izX~g7dsuHL( zfHu;aTmru{WfO^#;bns%ETU2`L&9Y5De#aJgEPMqRXx0F6Oqj@N7wvTqk^M~6bk+$ zN+LW8%!v1)pF_lQdkpOj&altiyb8x_AZqs@y`i5CdT3pJVK(N$OpN2(UN@!@8OcTs z0ADS@AnO=GhX!8-$YR6L;l8!{2X6f!15OAPdRr&muo`<^A^?oM$3!Hb?cK~@-~BNL zjQ%%S=B|)C2O?uI z|6GHV<*vN3bmeQ7M$F}BgVWal{tj#ra4Ui${U6{^vKe^c8pNpAZXhPl+Zf`twu0}i zr0f4bc!nnF0;y#DHZX8&Ybz=II6!i zOLy+zNJT>@;llkN#^rU(ShU}>N!UtEnT$$G+p3rLU$a_C#?)2D++~2nKK|+-Hk=t@ z5kp$7)&w3M;SVxwMp-O3q0wuBG0`~p-?VNPEE(7nvy5S{34<5aK2eJV%u~c zd;vgcX2%l~?B{M-A0vyqlp1XId)U+gM3&F@OENW*|14b+ zHYA|O@X*#warbA_CDg&^|6+g^l92QymIM`slnea-NVJGUK<{6A;Z)@%2u_iP{{Ke( z|I;|p7@hydQI`hr?F9!%M`{`x5`sv|)-)bk420;E6e$l69@!K&$-gwUg!qpw|7SzU z=;Y+&R?AJ%U0q#9iRXc+k^R(kba_=(QQ(UWnvFKHWE;Mb{~kQ!{C9g*`nJCNsSrHw zCoU&NE7E_4KtTQX`d6_3@lqqMk3bIsBH}`aw`a}pzm0uT_}tbDdNP zdS}gc=K0;CGlY}bS_G+w)9ySZ6c~1kYQ0n^>5yMfML!9mF#~TfC}frQ?m=khY#ef0WYSYvCW*4XTb=7NszZ1hp-TuJ=D#I z9(HBgVU_8cG8t<yAfE3xjgJ+-SH)&~U*Xq;>BdHtkjfFO+6 zIHaPm5b)ycbb{$-*%3%I-@e-c+QrUODps#Cg4fsAhmc9*iYAv%2&rly0^gnb^Kq>! zDWP#WzDqS8R#Vc|wW%7-?;&F#r>M3B3PL2MCZF}?1cnoGVg`!X^9C=qkx9OYPg*z= zn~;(k&15Pn6%!E?nD@)U-ND3`(5Pr=Y~dmIZ#3^#H-QAgTUucrhg&~!re-eA4<65HadWZRCx&wkxiFQ7WcoTEV!|sny7e&g%T!Qs z1>Fb@4b6EK1p&cewavZU#X+}NrN-^dASX9hktY7881;WX z0S(m5%+>k9c5|nfmsb14+J=jD=5r&p_YV)X=2Hbr96`$B$Zy7LfqoRnH!dUk-)#sy zH~W4D1P7y|q4`VYzv-Nt!{X%Rq>xJ+2!KcLS+4lQuR8lt=P?qvu463wBW&B}!xx)- z;V-Pp$^s9!q%bHgP|y5PWZg3^kEkeA5fPEg*j@-$lMx~+Dha)PjbfGecz7~qW$HZjixKBvN~ z>X$SQ&25tRmeh+mBu0Z$64L%j7pR_SHICyn@k#l4U&&$>>0EzpKVX z;o_2-v#CqH|Ds9%g@6FNxtZ9}sVC1;8bB~vz`Csq!I(=Ax($Nxm|x(*Ed~$52h+#Umn-xKT-djY>y)bD!eh&=4>`%dOS`08~Rm+;_ zH_gCtAYW;4x!E!F$B#*XsiEQb%*;%m6Al`hA0Z)dU>A2~?d9#Q(P)$2F%bN(xvhi2 zn=RMS^x&aY_5G~v#kY@xBfHor9gOVedOY+kF{6K`H(&ChBYkLiIQivKx<{vFI`8TF zXf?gQRL#J^U^4G%6}GYEQG-##MiLI(ES(a$zc7SisG#apRjJC#JGG*J9y?7v2bC5GWry7(%wvx$_kqtPkYld)AR74~6I>&*!zg~~V@u%lc z;~*ATf6wjij&wAKWoWpWKp=LL9oElsT6)yeMbVs|VK4%1_b&uqm5 zreEfq?-H3II;2^4G27Lay_p%+uV25Y;-#KEeSCbN-@aW3f!2xyy1KhHs=h)}Q&ZpH z{9$B`k!0?J83Vt^WfnNdz@t-~uDGtTdkCtj%~`HmPM}`uIWlF|WjJ_Lksa`bdE308 zrc6!D@#9>(zvtrX?>MT&c9soGAF0k5xHs54vmx6R-1B^ZrC!!hay`FGfx`IutLhHK zR9vf$T$oSo>eaXS>FrnMfaaGJnZp)#`oBzT{PJIlAq#01FV$P6->p!r;=Ds=iNHO- zxahDofM#WFo$5_zO%LIHW#KCzAV8KSP_NbsX^IEM@Eyu^CtV%Z98(l4W*c|2jv4^&2IXH=x#$H)W~6il-2#WZVLuLM+x z_^W*RoKS#JF5-_DRi~-Ln~apKjm(lPz*b%)cbhU`6qk&>*<_7N;eB5}PH98)UQ;s2 z-JE6en)6i4BP7p@y{`b%D#={Dvz9P={~m&a0*0hU^WDH!QN6 z$q^()7)YSXGZdv!ev5(Q;MvBv&pmyVcE87o(fn-kim+G6#@0iWf*$VbTTq=G2ZzQ? zj$3TGeb)`rZ}^v2?49h!)jYCQBlG0_xHaAhX$LK z7R*$YisJJLjBI1D>@N4GK9iee&rD$G?DS+C_7v_$#m0W}DaJdzzMfxl%^L262)eS% zngF(2AT&80a8UwviMU(+a#-jKW{>U8_ap(tpZ3=lg8KNOsTET&2iHTem1-Ra2S;-c z<&75q!UBlaWTm%5UCR+%5(uQN;*Y%Jvoud?;0mnmk3`3D3@v*Ukt&g?YH-Xlyb(Ym z;0dWXwmnJ{kB<|D^i#y*Tj_}p|C?886jxVI66oZ0+>v>DU^N{x0=u#z6&i4&c3_3> zl;Lx4{&Z8p9AESF^t90K+1Rn+0w#Z|P~LQ|LZ{KNNr=0+-}OBvnzW_~1x_-~7KXoZ z>vdgYWBS2OO96SVv-9lg*tgC{dMlz*JjFasDnOB7cwybm$ z8uRlR_J^DTg!Txq9)fOC($@uslIpk?L8PUFBY9>0wT-zEAuOe<0AKDB8>Bt3@ zCA~)Akz4Q3Z+o8Hv^bWvzPOMr`7HG9j|{bdc1>fFG9@>XN%3$werm*e=r1XlG|{9R zrgGL?SW$d%<|rDFQjCs|>AX{3KO*ExUQoU_nEU#yp7Z+=*=NNoDXxYGHk*hy`P-3E zP9#D0+NVfXV?WZLo(*cU1_sW(9w_pBOCuUJf73y09SI4ztkl?Yoe%@C%{4VQN5{l; zfg3_#w@3qarJy(If9yRXlD?ja{b z&5Rn)^Ik)KK*swGYJW(1;_^M2A8hjMW3f8@Z6c@|TZ~Lis`I5-J;v!n8o4BvnJQHj z4Na8up5Fv%B+=m#3T$B7c@fpn_4D_gb7P~vJ_*fo256mj+vDeh$7U$?0a~IfwnI79 z(+fs`9dHlL>vmndO#6zw@^jxL^9{~9!Vr~0aaEmpaIGh0+WpT@)-(7#mw6kfY5|X@ zv}>7DStE?Om8BmpQ1EvBZ+t{o%-Mc$sPdNYtYz=m9bE3496p%Thnn@ztnWThhMTSr zGuEFLEk>+%2&k0IP$|d#N7+Dhx;ZZ%8||A(*QZ8w^tf_v8k(9c$LoVKo^C8_FPE(P ztv3ovnlxR&E}cK_?*7#A*cdbQ8#qrC78V{!<%}%M^hp8dAvrlYaEOSl>qC>}@lyE) zC(F%8i}liUiB!u`X#;`_nF^4YE7+cTwu>tUz9j>)k6@g7{y>%8aoE~;Mick^SEVm9 zKB_De;*kylg)uR4-H-QS_&~wq$WQ#+spK=<9Nw+bMIu5bf&D_hFgJT4<@V%nsj;{) zeSXBnI4DIvc6lr)^YIQMvVP}}M|*gPeDrC6VER>^NZ9S+`P<0Uzkw_>Z8{83&>|lnVL_FNyE-TzN_x<=l8xj#g!NZdRPQ`adQ|GK4TMzQ+A0KiFL+D|{m!?>M69nBj zbgy>s&x(D(*x8NE%=|ReDfH++|1UEUNmbTXdeccd1(nM8i1gLkdb9`) z60EmT8POA4Pn?v_Up|27I=tfz*L+-=JsV#9MJAdYH0Vz}^tVz@9qpM}2nl5X_XxdN zza%W!DL%eC9vPtu3ne}b+$|1)6SpxhMiE|HAe;WEFpsp+4jYN)a{#UB%#9-F;kSo~ zm3#%@cd7!K42Ada^L4^H212Vk$i3kI>aAOGa%eEQ$yP4At%ZOUS8KVuT(3NhJ^C>h z#k36}CnsQCfpA$!=-s(Lz&bkV`r_lAn!lzsaR4IKI` z%bOZ0paY{_e9vbg+WGG0qQ|h7ikg1=_xfo8K@L%!=AWc(4g^@^qfBQP-s==vVPTw@ zm~fSW_$c(RqGs7@O>lh-quoZpzmzrW!ZJAlykRW<9GwQ_W;w=7AqLU@;5>~Q_~Q7E#*xZ9n=7y+NT-q5KV-_kg~ zmkaQ}Hoj=93$H`Ngbd5*X^*Qe=xB}|_qPgCOZ^3Lu)ZwB7@0=rL#Xv7tR9>5wM@c+ zXuFe@iwl6^EUOx5KMEDYBIPLC^0iPA1+n!)@n8T$W4Pwbn;Nanhg(#7w^ zC)MJ{m)AFw+Be<_V#40u0zao&EH@e=FsLeZ0~*TO(%YZWSglVF(+KV8$l^#7lKe~Q z3M}5kdIp284>E-@ypy%cvB`9G(^bpzdgTS%iEkldf>P(2evQqQfHO`ojYey;fC(X=blMf3t-VTwKhW^3Qw8W zw*-jK*8w#|BCrwrrU4uFsh}$W`3AH`M#z%U=5au}xPVu^$5zdN%;=x$+Bz{+Bo@Cb z^ey)!Fze2c<2I8uhS=VhHeRY%9?l~6zM8k;6GUN56mzljLv!j{0WTUqDN<>_b=4J`BVaIo*n)`3PZD|p)v9$p{S_f!7NB}PR`P){&DT-C3kIy0x9Np^ak z_0e1mekb{-gbV|4&WYfwR(i4o>VFcs^V>o_*#9n%KfF5XUwpj2#*u2bGT*eGXyJe) zH0yTLA5zeC(x|K(1ew#lU!h!W?@v5E{$_9N zV+|&<{*}gQ5qx2nv~pD501AC5XlOepr~di*1Yq?P$fgy%V*nw~wdq`^4=DeS$1)ad zU0h6h{vh}=G6UPQR$&w%nam$`8Ygt}YLA1*2L|?2zdb};g29WEii@|)b0d*K)ts70 z$!y0dsstYdRsV5dOysE#eEGkEh=Ia&uOw|_Q6i^ny4#N>SFGkKOmBb8yDer=%WZbh zG9s^pb{87%FD|ohD0m~+{7fTHI_U?M&RgCGLK(oQf`y!jb1rhEd~8T0AS zq@td~!?Gu8F)YDMs+9wcF1j0D7yRzePqv3y%;!XGpDZ*CwauKWE%G+L_vG~ zyRf@^;(2~EMIuVN*9fn39yA12d^~Q8S*qA>vCO*k^BeM2$%R;SxU=xURAq~d+>ZyNik-s?2il2+aSk9T=e^d90Kz-J3Bl@XdA0&hA$=trM{osCc{mlfh)> z8|{vDyV%kG#3mlRpV@qaAW-L|0S3v=iG$JIX_~a!RPWfMC!I;yI_}h9;@D2wqwT$t z05TKH)En#j^R9`4ozcRo!{HiSma+?zz4{Y@|$fwvn*hoicixuktzd z&SppQbXwx2Z6nxQ2m8v3+gd4q=Yq0>o*#c%TCVkwwX&x*T0?jBtfiZw$#)bBL}u|( z{FAhm-ov&u%tsZtA@%#Q&aBpf4+nhWw;XPMN?u zl9P#97ccMaaN=4Vm z3^KLFYghj% zLepF;t1O~qNK03JtqBhPNF>s~^&s&6-GTy3tab}zb2S`jifHA&xytQVMBdApY?Egd z5!)V?(s)Y!IFLE^V2NV0zDUGtfFeqR{s*SLuViOX{eCRycKIFsO_je^3+qw$t(^hO z?Lu{_>K7#c_-BuU@5Juj1R$p8m}SkZe8uwnlC!K@N`;8Krq4RWD;!CuC@3hR`7``9 z0yZ0bfW}gZaKhn?y0EyIwBG7+(%HeIMD!nV_X{~WOoEiCsi~>I-!ZR{loVq2^Tryj zuCA{0`P#B&x1DT4!ILsef4{F&Euq-IqOxhfoVXa&_wCnBG>dII}^Fs_3YgZM53gx03=F$fo zfykvbVOnp;@3=g7M#RakXjoW#mg{XHn|trSlHYA1wi$Kwwt8S+>J6y&RTheX5_)t` zA{O1g89Cs4_u~@RSLLaVnQ*Mhd*PaMf8!;~Iu0+>R+_6;@lvF3VV5^&c7*}iag0=P zacM|hr|MDqW@c^~{MMO4eiVl=TXK397GY^=1VGX@-Xfzf3$nJhURnvw|6-jsGJ2|p zHP!;UxBbcyV3~kT2hZ7pMj*cFU~@gwe*Sg;9fE=-O}+ZqibwEw27q5Wd2$n5fR@r z^ReGDy{7?(mlghsrtE%oM49>xhDUC!gj|P=SPZ)J#DwGJVgKb78ne@a!P;y;q3;(xsg$vfJyY}4V|tgzo;6JnND?*#t<|I=r)SSn4AWJ>eRza&r1Vi(%>sc^GT@3BnFgGD3|2M9`E4|yLkWZUksO&vQ+otDhm1V)(;?jh)BqY~WJQP5QSP3h{vD|JK@ z{1;nS!|kO$*5tG|aa=Cf$koNmfCnM2l2)k3G^pLnBb^6LZ})>PVSh=*|Kdzz+3~e= z2ZHS1UjK_rkW1Fz-P*UYa;iB`GApN<53lBQ%}dPZJ0y=_Va9pRpIB6f3P99IWMYJIA9u50;J<9t=J_C_Zg z6-{@032X#po;6=O>fnIlS(XRK-N|+Z1kxq(#n#9|CF1=;MZh5W=he|S21$I|^|d$+ zPExe`bom+wUT!ctT{(DV3>_?&akMs@6-4#CqX2S%Rt_M~T$|hoHjQOt9UdDY?_7&_ zCepICyVJjXRm2085(Ngi{Hy)eF!EhFF)OBrg{-!RfZhZK9(Va@$1*N)S>qbBh$iL* z&lK#Sp1ncc-HOv%6Jv!ER-qZWWOC>0czm9ikp8W|bU`ma+;|2X-X}U7k|cf>L~H*N zg-1i<^AcyXI=1C$7&jsRPhHS7>e+11&D7Bny&W5S5}0iflvPg8@Mynxr&?ltCYK@L zFzOW_AAbq%cpdY)Mu6aHY34BRoHbdIfJi;2o%AJ-n@8D@`t<2D9v=tl0=6O|;$ z^Hnxk4iK(op7kh>XiZ1)i^dr0+ElQ7flYVyL=I6XtL&~$aguo5rX{Nz8vgRe{U@YG zlgH!3Lo^@)o12^066F?LG^o5bGboBy7<6MGsrytEO*~_n;UIwQV=fs4FL&7J9(6Aj z9~N-L60f;ij=70_*y10qH7=40KBXl|ou=5nJ)D0883kM^@%qwp@Uf@6fd&;l@1X7S z54bD%Ca>Cku6Gv69`r15ZBSb4Q(K!`-}^74Gya$;&AT^7*9I{<+)wFRKD>#moGtad zWZtDe6)AprzBJqSZcD3M(T+EY#cWQ?8(M#Pd7Y$xn|sqV7gx*o2!_%DkviXY*Lr|DW<%X+OfdE~5+;Vqj-DwF+TJZVFn9G8Pv zj6aqo<57b}e@AcNg zzd`Yhog9%Rv9&hDX1F9Co`&*hUeA0QmX_Y_Nl`>j<_IcoNBOe~na3L2>tnDpVS3H( z#IvcOgP#P5jZQqiW^9yG^&6dUp4YVw+^^vGCH$1Ij z|CS(ILYp=xjl=CwhOtAO)hhvRa6zU0UmGYD7wnzs^ebw1dKK0{&jzC&wutAB2$Wd_ zbui3#Q#D$Tl~)FTeTpIc`0EP&zVbPy2@mOV!G@lcn;ZTzPdsV`CspU+Az8?hRf=LR z-dFB*5A+gEK7Do7n6fkPY{R;c`JOJ|#iQ$aB^swh$3YHs6-C3a@%ZjDMg|9i4i670 zY4(4%nu>I8Npb27;n3ohGWFmdaV-1!wZ2mB+op!-Qu?-x%nckgOJ@J zy7oD&EZ*_{^_tY;zA)#aTt>3tXKQKCq{j7xwefrjipc(4W^l=8-u&p(c$mLr_x6vv zpqr6wiv#PhiI=`r4>^aN333%0NfveT{`Ac$AiVfo$eug3_KP(CE10pJoUhmaBsCS@ zIsn98n`hTF4bShMakmC04ttihf7|~0C8}z*;jWSoT00{KFxJuMRKk^a=FJU<^02FrJiw`1Vp9i1-KCm9h;RvRyv$ymbqI*x0fb8OwYZ0u}q^^VVg zhkPLty>7j0Z@p?YWFa*EWN8j$eEJ>PYzsN1Y~pO+rXwmL^zm4+b`Eq_1hK9a_xfF1 z`9MJbcFHBB45oRhWN{ zJ6t!x7mkc)7dp0xzFKi^ z+aJBcJC%Plf_y&7PJ9aKwYk^7ZlSmJ@#8N96=rn=&^WPmupVpq3>m^Dma+_3%*^s> zT{EoeD@hm?6E4@K4ngzdv1XI)T~TopWd*G)Wp2couvi7hb(dKiNg}imHsJx$j!QdU z{g)LiCe+?uyl@7@S9_duH*;)J(A!8cAxeUJ%*&P2*PwQImZGrqT}7(qWbMecg|w?E zJ_VbP4Ztkerv$8p2=&wM6@0pETxuq|%EFk$yuQ(vB;rh0{FkPH^anE(%^g}u?z5Ih zma#^*iiri&n_0=91L$t_fpa$@D5!<3TWAXvf`N$KDj7j61D#YvL{==tLsV?sslpyn zaj8CjRatLczq*m#=i>Y(I$hH}o3S$BV5eGZktQNJsHJWs!UO@MJWS{uclwhRT?T)q zeiwUxeqG)xQ4gmFTQqa-PBsh7gZt*(1Ytc8%fir4Bbr2}^PyjAn)xgF*dJ0u`FZxz zQM7xjuMx*GT4oCyafnW3e&X!v&6FG0Bh#&@ODd&s13D>=_Bv5@ZGHcUh;T7&(%j7; zH+Gz)+m(@X(0Sf0UEJYZgPhR+)GZA>Ivx*B=i+h#Go-6$a2%pz`qvbpyItfAlJwso zcyTUFm++m(&GIZNg_tZfy~t(dQIsR%wu$^A325qUSElaLNQGbQiP@^JW}^YxQ~;$D zj>q-^1%(0#fk(52|1@$fN;p9!fDW-gkr4&}J$eJ-crxirm-Z_zPypBiAVl0iE1m(2 zg^mvS)xdVm`~y=PTU;HOBl-vF&yV#fli06v&(^{pE=hW_{a%lbjMt0O8um;&x5~l2 zLot|j;1rR~yl%&{Ltis>?2$0>r4~D!tCT_Y&Qk)d_uA<0dhhW)#(TAdA-hZBfoP!) z$8)_XVGoPl{6|BYT<(weXg|64@=mpTkBQgS`8qnDt@;VI-gtgd7^!(6>!>q8tdZfIjGft(6{sC}(u2>*CY+yL^K!NY8+zI-;T z%b?E)7o4k0&-!wDP1&f(#dCy}_-yeQ&jjcDvecw}!PY`S(gpTE+1{45ZTrXD418}m z30c|B{8yl8MYtpxcm->#3v%=9d*^Qzz8 zIWWL(OP?M)5gYk%HcX)1gT>AMbL7MXpNHh|s-OSngZ0>p_}_-3KUy=t%dGfq-FR4Q zmY)QaOC_upxFukK30mB#U%rk`2m7$JklCvzdmce!M-5 zAxnEzlP%86!-Nor4?3yCgvlYTr*Z|P`7|Tejj8HC#$U9z1t)zZ3@LEIZXumLDz=rQ z2^h7c1_i7(Zw8($C#1if^Kw_-;3{p>#?azc=a0m^jtz94SRqjw)^0BNE!`bHI@iZG zLca{Kn(aH#w;x~ngZSkZ#LvD3a(wwO3t(olX$e@SaZ(!Z1>gi4k(6J|1rUJLDa%|$ z66C%F*Vdb;o5b8;P>yj6*^A40$1=cl*n608r9}7~ZS>3LdL7!yq$kX|y}z{67*b9~ zqdiDh_cTI4lXofWnZzTpuz*4Dn#_Vrm3R<`$#WoX@J&?Si*s=KQjt*E_keDdtI1v@ zBh5jF|9OY=3DUS-!DUP=@vOQLM)T%45)*rAd38}&U39-cnMQaBA&eAMMAl1*lAn>| zEQX4lddJF0)mVEJrD&)s8E?{W`alH)*Jn8Z29rZ2?PDN>skHI3qDEB--!@x*3 z1LAgmx1l}xX?xW5Vx?~(7?bu#91c!*1=qT>!D7r<3NIuY8#FI;cK+YCmdx;GER~4n zfvTDw{2_>u(G#R^g)#9(9`>`#wMc>X4@NA%PD4v!#x7GYzeYu#274!4pS#`>|9L2yvZ;rKb?iyYv(A9P6&}|NA+Gev{=k{GBIX7=Y zjMgSG=Hm|#a9V&p3l?wOxIH2q8OfUXp@2RpxFrR*cA#UTO2e5BLObl4TrceC*@3pI z)Z*gh+TNcJ6Ua@~R3UVmBgIoHX=ol3N^`k$V*)(mju5SOPjT{9}q>qqCvK?;hJvasg- z)EC<&zP0LxCb^Zj$Qt(c*N`}CYnZ-?$?x7H4O*}rmF>fZ9@#R(G3)QPx0bA_ksS{a z8NRxOnwpZQ&KlMFu;=IJ0RNJ!Q2YTI+3@jt50DjWC7(c5zo3zD80UP5Ffm1dzI@#6 z`id;Tjb`w={8cEnHe=-F<4f=9`R>druoe0n`223DVf{m)l;WUVmp+Z!?cY1>SF5X z=}bgNuh_K9I@HE98s!;7ueV^ zRZ91&J!GNey3_&fjC z&Q8d9apO`?gD!=zUL{cW)_uRC(>I}=%%W2(pft_;FK~WCa6gIXpWVOXB_VL^PtFJ$yo0r#w$*bv6AozsK-{QRAY$RB#e@BnKR2k4= zlf&NrNGL7+c$0QXfR4IH_5@}Onx6ZbK?cTj_R61&}ZW))gnd_giUzw6$ zH)nRntZf@tk@m-S@+_uCc){Cd3|j3KQueLGb3Pq};uox~QaBRZS)|x&WDT zg15u%Mgd}28QhFA_>uJzjMmsbAg@^Bp9*`xot5KZwsW`?o)BVKcat&3))7}ZTM;`} z3%}&T6_`%@&05Zb7Z27g-xP9$vHJBSp%0#~Yqy%eiru!ncwQIAd}=Q4pj_JT=Xg|- zmUUIh*HpdGG|U*yJYHX(Gl&xvN;b#s-tdbP(@k>a(S%=SrfXH< zx35DdtwoFyUnF7m{7i6!m91F0aGe7czHPB21_3gQ1&cHBP`iJWy|qb6cQ0nvnxtza zjM#e0KTXF+#FVpd=SW;KYoHWND)MMqRcK(KyQWLa5GFYv8DCmFbj6dnAt zl*PrKNpla|=`t~lq7zbJn-*6!{>M_A(!l2BCqPkhaWAt^O`ubWe`fcu`^xaJpcO^{ zjK{+H5M}`CFx?Xn=zd{IfjQ~8);A*=c6C1n6cI$g2A$JeZP_rop0aWcre0)UThzJzv zBu1+mJts|OfQotrI}XQEoUS-Ih$aBMB=TWL(6~1D>e_nNAc&|38i9(-bMqs7ul&a( zMZZ6R+wl_jmcfRTi9ep?-G3LFz|5f_K|lJu4`tD4Fyr-|HUrm4b?EG3YdGj_bFMOJ znAal`^JzxCa~`)eFqA0y`J<~3T0@YCunOyN{#1SD0m@30hkw*Qn3GGsAeZ8o!v~)O zsC{U+mt~L2N4|*Z2@}_KVCf?WM9Jh&>gw8ym3*dQfre|@c!2!3^`4rXP=Ef1j*TsM zg99ZXn?UiAn~W@XqI%`G-1)gyaD-KChyD(R+VxgY#6+Xhz3D5MghKMWF^Cu->b>9v{-}k>3ZOaGu*UfeM`$f zc`U!c723D=cUwo?@>{!ee&cCAWM5vGGcz;X^zV1tUbe%Qc#qD9EWYCJ+H>y=L=}D& zOorCnFsYh-HRX-?#u$fBsAIua7pnRb1i2MT-p{SGM5fc-rbrFZv(jt@HE9s3~y6XPB@is`!yxe0BK zrtIIJj|828st~M{ZI^u71O(pJ@z#zfaD;xNS64-NVzu;D`Uhb);!AN*)z#HM=r&}> zI2s0|eDM@@GsOqpd0F%~S1+#hF6q51smWMbu?uc1&V3-D`L-@ZxG?zAlGYY3eQM!$ z=JaUmQY8BO0=y=ff)tAT;v;(tt~U=mn$(4JSX1NBgFXnRiKXyFEwu|)@pzL*39$Gv zs;&<)vzI@*(fn;HVu5EYXxi7wOIfbx6c|Z3eZKOzCr|tx1N}-VYrK}txI`l*Dd-bR z7KG;TWRITOL*66YNlt3VBQJB`-7qnkhF)Nd_zgOtA5-~z@33ZeUNT)Tt7lOD7nYc> z5V?%5EqD)*Op39wNS||qborGV2}osA{vu_!x=-i(rSV4+@#@P^9=AEp&}R3dP#v#s zomwIvnGX$<;+Vf`ndB-RmuU04y8Y6$A;ES$HH8m>jW5iPiE8Ygv@*ByTJQXQ;?UI% z`Qtl)p5w2fsJ8#7(D+YVTH^<0N(#h+nkMJm1hIz)1UoD&MOVVWx(WaIWxb7q>uaYP zhb_g;vyz?bK&1TGj=+gM>YE35&obcimhCQ+@RIkCRTUGUMf&jY@DUeR;@2-y0uD=nCaSSs=drXp>HN+qlCQwx za<28NRSHCvG!CmCz!bq9xM9A$vcl@~=K0&-A0YMhw|W9N32Ai!vK&}z5CB^99{A2^ z9f^L{7RWMefg6`uLe5DVyCaTzH7O__JgVx?lQ!E4d!r*oF}i^tW>7xXlh3tI>*#uy z1Flylyj^Q`|21Mn%gSh1jLZY-x{%^kRUHc{2zO*Pnz~}h>`UD0}gz3xg8;v;6)oiq0w3(aviqFgn z1==X`S@+GGKkk{B_cQW(QK6j0YKk3ptTbZXm6OB8BLJY=6`ztu#aF;xX*j=iWM7V5 zE70ylY8C;9S^kV#E=D)lMo-+|-9P3f82-V^XWQWZu+awjHNkC?ukc+%yIAwe9e}M* zm#Y0aL9YHaH44Tsaz+ceY_2C6K3hAh@V)@54#W?{0H|`6urjIf_!Rl;QZXTKIk+?J z&=?tlCI}1-tx|+<@-6aUB`4VrJlA7*`{Qmk|BUjB@Nefwy_bET6BY6c{nID%x(Ij< zdUH|u9_`uDTwH)`C68@3vXuwzVqkL)Ag2w-r>D?8nnw%V=A4L^{f>zlkE6d zDN|kvHEo6O$wED2K@p{yZeWR9gP* zp`0SA;(Zq^_tFzu^ps^fm8kL_prZ7{E?2lnT-l8AXasY{0g+k>OF>J=PeWTuZ2UV_ zeUja$f45KIT_eW1&I10&H=Rsu^djSCl_kvzi|Jpf(+fe5{*?!jTPMej zXK?Kjm!-liq3@ycoW|LQ&C#5NT;Eq*JZrUK&u<<#$E#h3e1ilw-+RE!RE`8E4IRTp z;zX$jI~*7Q4iHJqFQuoK-2ox0rnZElYK4k~RH|sIKU*N%NYk|X^bbK>RmBWQNy|Xh z^etC-s-DgiT2==+@24TFT%WGb7&gR1+{pw1@WI>BE=Y&v- zR>|twHOJe<9vrQXYKh~Gv;vKhk)Ev^qZN1a@Kv9-6eSr|)LI*q6di5xP^f6RMHfRk zrVgRJr_;1Bj;gAEY4kdeG(Ce~`U>63zbH*z89HPyg8I84gq&z*F((9ylkij*kUm4K z_Ex0BM}ENFfFbMPf4V>GZRl(OO$h{jiL5d{bk|490&><8Np_GJ~elo38Ssa z<1opGVIq#;l$Yo@Vb;RYA|M|V5{rzoqdg!I9o}}NW#`Chj^>2}h|0s1zfLJwu z!0Vb(z^NY@9&eT#O>^N^NBCAKM?CI~EM$rT_`iezZXJS@lvI}EO0zazZ=B`Nej8kSr zC^uk<-MqN4vCRU+;zW9Nzz(F80p_$^vhDqKOKD^%-iUzoFgKps2zrjgl$3pW6V8+I z%5z6$4J)(MRxJLS6hAZIXs#zv$`>G<=JV?$bB7$WDD4_!E}u4UpQ>KFHBDPl_CM8B z&e_rVdIIXigrT8vjr|?I7%cK&i(kHDi-(KRyNybEBR17`lfW&77Uq>ap?5Kp}) z-oHOC{(vIM3*yb&wk`NxLhA=LlM_K<`Xh48bucsR3T(3du2*R*T)GkNOYsg*Q+(T`-|CM4q64d z6DzuBw8R>%*CplT_WXh<71e*rX}D9qK0REn)lOb7ol;fT)XaD>)qMgSU37HR3^aWe z?PReq__A1(l&X8!N*Y?qh-!;>Hn$qe@Aif5Fygjq03dn+V zzdP)HM8awQ=eF~<@_e3m1gtR-5H&J@1iETr^?9tt)QTAoh@B1zq|Pz9(LFCGaYaaWIF!K z7-#F`P${v*sp+?-1@-U)`(+Gq8nEOTOmCXHTIYTV$#aBIQZ{2t46kNJ!HNn3}gqSby}g|--CtupmQm5X-=!Z5Eu0Rh*2tff9IGIF7_ zBPHS5`~riB$ZK)3er084p}33)3rkc_?{h;#!^7j_*3J&}50iOeVQj`Z<#oWm`BiU^ zE#Zp)ZFlwNo3U~E7vw}W4G36YaGYoD=@vmJtgdHdw2QzdQ$Bup#v!lwS7;B{>G@fr zLT3)O)HHoUjGj1Y7X|xLlD-}-L9~RlwA^>he}N(we>aEBxq}T3%axusZg~#LPT)Dr zDN9Fkd$VY#_v`^Luc2}z*2w|)wWms<#gR0u}B=Hm4==GRvi40JC+5;U~oc96paSc}u{9Q-Vh zbrI3!^`P6>GxH_nk0F_6!$ps&3@IgL;0#5WgC0nyjaI2^g@=7!?<>Bfq+IvfTDT%r zWbH#zR`JhV$^&60V8l|-Dm1)N*_XICe7v!-WR^4O2^iwfGr26oSMXQXOPCGgegzl~ zXKQ;KnL{6ZRu5J#yPq#04gX|EW$`79w!6FYS#6p4rfzUvU?P6t%RGAqHS8GkTr3!b z@RiS$3W$q|86&?4gld9g=%f+es1maE`{%-VXJdp3n+Fknb08r_pwyIdH3HJ2G!vMz z65TrbLNHR~Q4Nou6vQGQ;^@8|y;&fvwZ=1vcSeVDlCP*c~?OB`LCF~*|X zhV9};C2Da74-`N^&Af%tD3)s^`suN?>d%?2rv21?8w)f@`mlTrJBh^(%~)&PhuN^J zL~%Ma6kb9Lu#YZQhC#8}1A zlD6D5T**if*!k2c^Mv8078=^6xQv0KpxXBohk<$avzq4~8actWgC}v5gHUM=?gNWL zd50*Mm)tPH-;*tKm$eS%naD&-5eN8-IZ}9NT1ZO%<{ zLZ~iO=xJh)K8sZ9_U^k@70v&FQ^(wCagH&WMr<2=5t>!xC3Ik$yPAp=mhQGto?UzqW^V$Br}9 zJ#W6A$1AuOE#?QR20t(Ri=}dkfbO_nkLJ2-wHXm@JoR@Wan0+4+Ah9UILiz^>QoM2 zcb2d59?0@x!NI|N$}0kka2Ln(5D#`6o(=p)QQfO2J3R!F^ z%)k@_R(!?xj=LxpSOa5FA#5QF8?-;9v;iV^y@kPSwLxa_8?xSP3rV?jUbJCd*TzVC zQIW*9fkBZ*G1P2e01EKp&2ymb3|BN z*0hj}|70pECcPgZ4ZjpDmGxudi5vZ-WNV*w@xJU z*&yI$Y?Qyy&0KP+{yxRMV}>jU9Z!88BHjHPg8s#crMf!G)5bk;X-UoVZ3Ds=_!#&$ z#p)T`O@ZBX%9l}FfeG8?d~JBHSdqkC7r1XmfI|iX7?waWnh#IWidUr3ZhmY}ec-*j zlhs>eo!XZ5?iKfxGumL}p|Q>*q+9`oHB*88_O?$Tw`SuC?m~6$gTmbJ&z#(P%G0rn zFOzeEhrl=~^!yFdR`F_UyY!)tgrF9Bb_RQ}g~J9AbdJjB66C*WYORiLWYecoQd~)d z$?;%ke_Kh~@*8ZjuT&49Q*Da9-|I}usr&o=4zU;`WPr^Q&g4Y4m1ER z!*!*{ZYZQT$NRo3}rPw}6AkQ!Rv3CP^D{NzitXA$44B4h$gFF{9-;Tvr+6V#!D7J0ht z{k!So(;<(jzcrZ_Jm`n>eX%?3BBa*z73@%fc>qH;fyJR zOOAZ=D8*&)XjXU>P4Sgm`>6r(8)*0Na0qyqmfAfy0rPgD)5!wfd=?}hAD`)Z3;REI zZZJT92Jo~vpFJ4{WbdFl92y=c?dMC`-(+NFnb()-A?WdqtbqJotA3S8KtVC)d8@yv zr?zUYw?C56NmbYVhPLbQsi7FW=)(bigIf(U(btc>nwk)lvX3t@-={PiFZ5>OCn2k9 z>-T!=__TFqTSSrQ$4I=(cbgtuy>;rScb=`adA5{3*Q#$S0o}!1qZ?y1ueP=5pXSHZm_w8vK> zrq5agzTrlYa419*W0eJ4$_+A%V0s&Ojo!(lmd=%kBi%{z`vDZd6)zjLs574U`t;K7 zcS%=_ULAR+Gm+>Ox(kazCD#Yis+lMHFAD%7@$vB~kjn@S3L1oFdT%rwjt#$b%$G_? z=K4AM-H=XNn%>Wq5d+)!k5prjsB{O5pU&(GGw0X9`U~)O>4-G0#z`a^#K5Q}F`Mr@ z5vNdxMtIgj*eW)5z13j$hN$}UENm>;YarWKLJa1GQ@R4!7Wijpd!^N`#_;~`ov&Z zjK!#Oqvj|Cn!8I`}? zQc9E>1p0Egni?!vq_sR|o2`G9u_E;1SDx=gofTuSz#Wt*(YTu+u}QI;2^GHrqk2Mj z4v#5L9o;hEM6D|y<3Ckr8CjrWz;7xgsnip(xuN$Upvs~AXicC`FcP()mh3gASb87C zi>-XFc9%tH=~RL};N6%iSYkUx3m)>s@zstvFs*t1N+2BZ$4aM%XY#G>$HF-aD8f{- zf5D0xt12|R&*JpWilQIh@vpfb;gdwPpx~n)mp&H(VPtjFN#_LTqXzTAg@V=wpVvon z($vGYmWFCz%L@)3n(f*Yo#aeqIcS$XecELY+rd^1=+vYJ(8m3e;_8tkprN8T9Q&P{Y(EcQ!ElTF+aDj+aWh|Dx!M<1?KLEC($Hu_zXMo!(#5EaAoF_CzxnD`BxXr{yr`aJwz@`J$~Kr zpKiS_LsoRNufJfB@mg8(&PN9D{R3bryH=9J)hjuNKuuRxgCj_v;^=_``9c{^r0w0G zbzPG>`>@~Z$Chf+VdC)ijrVuSLi+N^e^=lkbm9-^M=Q30-{65wIxGvAXutS-!qqi# zXsNI{1y4Z0r{<88B%QI$Wc<*?bT)@cHQn4`2n4ZujjGnV6cdhs;dW5}dk%yFk4iie z<5j5I=o8B~4Qcx015Yd88IYN;uv`4dX~Ti^8@zh-fbQq#Cvh7NDqnS}@|-t^pp6HQ z#7Rp8^U(u$7qIuq0aML_Lqpv+g+PFYVraPoBzVBY6g$T;`ajfY<(BFrP|wXHsVQtn zn$1i`vYb`4+o?Ri3_+=r1jeA<1w>Z6-SH``Y-Hv8TX|$rUe+ZXH10QvnEgW#g z=!@zV9h%l6ITQ<~y+u`3bi#HoTuy%Uy3p@DaTJQpUt_Ygib+hdIG5{o$8N&W zt4#^k_sGv<*HM2>Az@5gVyoaDXNuI92!e}eFx}n`$tB#VPqpb(?>%ZNldqInuIZAL zSnC4oTIauA;_rs}e+f8$hu&((qvmzRCOeqh#&{Qr^jwTQes*X1d*Lv5#TaC;i*SFT z)xW6wGE%LUt>;nTbG2=Sfktklr*{=;NxK?~qifnD^g=EIQ)_e9==s$Q z4FY!6Is1wTVJmi$qYoNLMxB`}W+m24PzsIIr5TBk27Xj?E&i(bqrpbSW@BY3O#6XhQo({I>WYA@ttybvglHke zf@}L)0^IwApnrFQ32(#h1Na0%GJN~6#^!^dPSlgK^tL`UCnLPwCW$Uo1v!*Tyya|) zi56?~<)msRj;>W#Kr?T%emLnRxbo3c)y-UqNf_OPiqL92II3eJ!Qt(6-6zyS@>_>~ zoqIW~DBCmP){i)6tEj8>k)5?*-tm@YHg4@kfeoWjJsV>AzGbr0HM{7rD_>D`d>1pJ z>M?=E4X)mlQ3#HBdS&W3I8N?)t5G)Z+9mg8iHDBxUa{J8Ph7NJT|^gxO3)9Mkrv~IdxCd1|%0*h@=aSSCb`C&or>r zpVNncF_u!H6-at4wA!B|H6l%&XtHc2jv z<>t}?S<4kF3vIR9I~FOQj{$*~XaI#wt)+2mc3HT{HOu8WkTJ7ovKwv$4No|TS1?s` zL9eR_N1b=qW&hSQv-hrj^MUbRXGr`=AlJ3LPq>Xw-dB^31Edbg!^#>9xV5i((F6_o z8h|kU1itv^gisp-&2}eEpr-+wT`M#J82z}LoQXTF!nYf_6S=S}Md}gzX||6;!=#1M zEoDT+wYILC$t+iEl8`uMd@1sIqPE9ZT*%|c`Hn7~;osbBXbuz@tIl_XV7?xS6dEg2 ztIuWpUKkb;)1th+C3U{|kYflUt#gzHEro{8w^gk%MZ%j0qfhkjCG z1d<9jp?ktT-wE_3rx{2|cK^tRz(r2RL&l95mn8} z&>pEaC{%gu{`5zXD^FbeUZiS9n~pXa%n?G@epY*JdxFr(sCv zJxJN2Z}&c4>%SB(B$}wlH0qPvkklbY_0~l@o5($<8a!1iM~~c> z+JXUm#lb$V%BI1Vzawkd#>&b^Kp&Ofx}iNKJ_3xeH^ZAHj~7vo3#z&O_?Ic?rlTXw zXgW=2KgLZiZr!r<;SG5kJ&0G6q92_3!%-p7a$(PO*l<&iN@Id$ZH4zNtPz&%!Bv*K z$D_yejz(X8WsKZES`-w5CpInh$XcTUlMELHa*(O0VN|K@pl3Wof)l8Yn0T`(4F>RY zk8Z5C$C_owDg8+9yQ`A}#J8qDAO>SDneFysClgL}`r$h(WeFoowXvF-#C>D#l=R>> zH}^nGpZhI{Z3l{8GRa77hW^wf|0CHz*iCqs=J~gKr1jSk^`+c8wzj2tCP4d^lJ?eOfDY314Y<83p(L4nmxvYZ<_ zhqwR?-}hTM+Ln>lZ;StF142?PNoWy1{Zocp%gvNDUXB+>_N9*mt@PSq|6Iw-(TA(P zqx$s)JHFI_iVqg|k5T;8m(9v;;tal^DkhM{$y&M>{L!8LtQ&@o?8a|cGCWYFm~1GU z%{_dxJG#>7~PVG7G>Tjz|Vpifd7F*WD%gLJB_$a#H0-Ci=$5)Z6r)pex2**Yq zg6U}|(-&(K*_*AbW6Kp)5BRc-oHwpzoeflfk<{f`f)!EBoV>Z@1l{cpd|$# zCH~P@0fm*`Z|fP_E$l4t2Dc^H+i$u}>Z`;Be7&6d_X4s~L)~s(kLC?bCt^XF0bZGO z>A4(}w6d0SNq4%H(~pY_qp}&BGJfVD$y?^EuP>Ixm}Nl;y0k)fW5I&%H-?%kX=9#2 z>~;0Gx@vZARmUrhIG;#9v^ciR0C`ac-9cD{DSTxc8{gd?JOQR3Vdxn=5McHPm8$s~ z$EUB>Xtxu=I(GHFMxZGjS#+ITzmiYocA+=sl2trYaG;p1>pH^N0+F zHcRlBisf>=OV((5!!Y{cZTYM5R{lhy^^xuR)}&DW>d;1gtD(FM&fVY##d7(~$ygTJgx_?A23&hFf|uO zBgcjAAin^%3K4^W3ZJao5LJmUbi+8r~I8 zFkDQ9#!;f4tTgUKJy?t>HMV0*`C>{Lv(FgK6wj5J;fX9BBqU{u=lA4(oT_u8x>nIW zpR~OVAR+*-%WFHw9sk>ptfo)|L+h0$6mnj#O6xk+HU3@et;ys4 zH4sHKQK&#hr&@IN>EZJB76g0;6Wvmdv?ZUV&lPjF_OeZ{IeLSJKFnn?&SmXo+5WU- zoyx%zdULANm%eywjWz@B{tsQDtP5KQn-Z&d$NT(-EaOQn?wqP%K7YuLh_+q^YsUH$ zZ`WsU@2ITk?y`{gnZ43VwVLnUZVqI-wds)gxl;O`I=DulSM-Swq+2RBJPir?O8G-G z7PHYY)|@#5Ig=%io+uzT{-0Z@9`&wRW8DQ6hVxPsBRVu&i1^kbjTV_ZY|JYGmEOdg zfq_}9$KYBEeuAFf!_aMKI@!T*kuGJ+gBe_2dcT)3>EfveT$1J zV!g$R^;R!*N>4m1t(2>1TZ7}lA?sEc#Db%}#h+I`RrZWklAo9qZ|D}{;i9e<3xvah zrN^i1JwA+)o|&wNDpHi`?HtU0`aNP{YvvYQf3)YOinxSh0(IRX%f2$+R<>C0tY2|c)PLrgp z4t5aqhKG-DV_Pj2MYUMDQC2=6Yg%)2cQui_rKsAw-zqAT@590@!EbCijYZy#jScaf z-+C&))DrA4iM-_FfSm!2SS?Xk+v&IW2^-eDVP_e+Rp5#Zvvzopql^JrUFMeW@&E^1 z#(H?tgJY-Zu^Mz-!lwJn2T}eIDL8z_6uQ6Ae!F0<5fqdm-@5uCh0sLnOU>bWGQ(x< z1ul~!{a4DTO9K&Ik-=wGiH~p1km%Y4z7H{DlCwcz zo{v_>&%cNN;Z-K}3jAqIgo5YPJvthhYjQAg-+2GJc{mx5pn`cms8fF~e_y|NvEqTH ze>~7$kQ$a<`@IedUfy>Q)d7cvxPOr|1GDL_NzJs0V3Su+Bx^E=w@O{vP? zdK@n2CYiLRhL^o0=?f3v3*C{}3+Wt4AA}E1G`h3ghTXfHLx3pnNZ)rDpPg8OrYz_C-h4^`XGZoH0(7`{KHoFP3)@JmMF4WL4k#!Z@BvcPfI)+`I+Uc-mr#|K99#) z?n-oy{Z-P=S_M5u23s%tj9ZMDCm9#;%Mgu*K;eOL`~`;BQWX{N@$5&~9$uOjjMKOV zZc19nW`{J!USi4&ppPrh~V)^WL*T)0Les(|O&aq?CoW0M!tt zj*H8|yOwCDb?EcJwSFx(Y)E9J$-1vUgMMx58NIsC%+NUe{dK<6=o{XtaV095?E$Zy z_V*iSUNGNg%#)4!dEBndB&p0kp-U>KBP%6E029~Qu@nC~g8CgI zJ3AVH5XHu_ATj8<5%xz5^|gFA?`sp!YpWuF9u1l8Oua%EX@E_8lsU2 z&w5x_u3&QZt`0!VH_0#4>Ie7b*NWMCV_Awd9sGFWM_{qvKGo~s5a zDJih1L!*W6_d(=~#N9c2ii zvSj{I8rt*u{Pup#d-#cqtARlnhbF)xs@_P_Z^Xv3)j!q+rA&Ra%#R!qS@PD5}z0_HB)t1pu7y=G-nxwzM&^4ZAg#x%kLET!#Y|glzqB69#GHe zYOlj}eM1Yw*mz)zkUgCN2m?yZP?+Xh=>8MhrkRzoE^0G1}6$bsx)U`FhOL(_% zp9fQ8oU}slXC@gO%={u${n+HN-v#)fRA4p5GhL+A$FX0NV&0)BRpp`Hoq*o`!1|Fw zn8K879JUD=T4Y#o#zhetPWzDAy+!*R@sA0^`;LPnJ|G}q<)lw}d}s&-VCDmwI8v}? zz<>>+zV^X$bqmyO84zx3=5g4jDfIVic=aZz^Go(mcY~pLrby@7ZyZ0hgT(?-cAV77 zn3c6~jjCq|&9^%JE0!^*FLrdesf=cG*zXSJ-|1f;{5l4V_l@eV@r!t67%jWmEi@6~ zP#|UQD7VuHD<&Pc{uESTCErmjO*Mq;+}-H&j;jaTmNy=!nXCH2f`R(z=cGLk$&lub zle5HMf{8LZEFxn2V79n%HL@_Xx|$t88-V8fShxv5ro`gY!2Eekn8nJC`oAn5<&l9# zc2HCL}Ah`|0-==0Y# zDJYyxg4CO_&`Mms!w19hI^WUFmq}nn<2uP(y=K-@{hj#eKLgt0RRubaQgd^fSy!+Nn9p* z_2*vyx1&-2uc`fam;UqfQ)UiK&3}IJul6?KpHl=RWT};{oJIeB&_ACS+5N0-C9D>} zJzQ0qGJ*TWIG~8glvUN#3>-6Ur#@da%G?Zt63BcYM3s#zK=k)NkHPS%{-o*twCcFN z3ZT=J&D-0Ye}-wB@vrmi>c;+cesyIn{KhwN7pB{P-xvV>uFiL^&UcUU{<`}h6R+#& zpP_IK2P8=G(a}>ge%|wrebSM7#~`7POb$3SJaTYn?O#t6@<|UHpQt;3;<97(-&eul z_56>kTxe@J(jB6}wkJL|iMtSp+Xtu3%GfSsJH0s&7AusJL& zF2<}6(bYGqD0eD18YAAP6@jvH006IMWnE^!F{3txU!Z%cao9xdDy`|?a z*95w-Jy`I+PCC=ZRoH(H?|=P`dC31?Px?Qc-2a{#|7!iet%ra8${>P#4e=^Jdd2N%E=xY0B)W#8Uhck>Pjp~@y2TU7=J?#0ik8#QCFEGaqUtcmv zR!-HrDgN?3g&2?Rm-pV~T0JX?{wZwvn5xHi z&DQ^8XecfkAa}=o$U;te;7y!0*wboB0|4gZv=VZthD#469Ha~?d zVYDVsVqU5%Bmqt+EN)S()e z3{aTqO?qg206|%h{`N{`D>1qZjMb~F+t)#SiKf@@BP+c}T3hvL-#`5>WbE`IQPuZ~ zbhBz)efQrZ0_+5!oiJRWWSB3&#fd}Wc{|&8e0=;%yPjz@tv=f`7$f>u@aw~`exUeLmmy>u_WQ~yc%sG z6c<8!g(-Q%=+m8RJHvdo(9DEMNj9rb*u58YYnJE|y1I@IKGt*(c_Ee*xp|1OOsG74 zWb1I@n~zXtxHN+dAhd~ed|r#AO(u^9ayW$*%q z+FK~H_2EWvN(hQ6xO9Wbc+T=nluqhZm)9c`m-09$^Lji<;=!ZLrJii4KJt*Q@ic8j z81LinUx5)%P3UPtB|wp!m;|KQ@yI6sLN+^Zb81K*?B>xrJ{)H_*lGNKbYDD^2DU$n z)oelp0BF9O5dEPOheDHuy-9CwZU8em#@8Qlb7SL-z&*8b^2hq0SCk?IJ~@$}%q6WC zblU8475nhP+MR)g+$U48X}awL6175RtdVCc*$@-JSibU>g$3Bkzr#nQr}D?V04cSz zU%JH+DxY9`wI$elN{+iFz4?lp%luU7=Z$S&lyw2YE%;h3O}L&RU<+r&bR+p6-K9JY z-K*dE@4gJ9!{S6i5+`-W^Q^Qb=3U4;KW*B_t-xR)?Qa zgqxe2+1*A6{-j?PjB0CC*lb#Qb6Ku+1x2P22%4e+BpmgY&33&gOSSGPKY#x0ccrWU z{Nm5T)h^Mh4N5S7`tp>1YTr!1NdJ{z7-|z>3=?*7-|!UA?(K8D%0E@u?J;KA276?3 zS%&b2t7<5w%#@mzDOH@^+@gemMWF77DOC!+!MqK_?=TZ9XR5RYT*fdecPTjR+#jFd zNY~rT{<)h2_T>q?M3%w)`XR)8mL_#d-47LeM6x!W=-7oeE_~O`e`1&r15#I2L*=1Z^~+cgH}oxrgHBVs|Wc2IiU+DHbjcI~>pW zvE4+U{q7+KtwQe@9?akOe<*upSyA{MB=M$ZU%H&Ml*T0ExFzP97usma+&`vA z&9>&FE9xxm&b{4O*1D&){@4lDt~ySSYi#l6lu<7Q9iD8ZV@8uxQwhI7Y1-J;%si+) zJhMnkGE-VQ@oh@-aK#snBpWV~d%NN-m-EhJuI8Qnhtt)no8ae)4aa$;100ypw-yjL z1|07t2?%`QhOCY-ls+rs&Tm~o$X06n;o_4pXkT`or^S#-xO`<`Ee_o1)(zKBLG^2H z??uzKS0o@&)ja89&VFb^F2^IG)u$}Jq6RwQQavMmF=D;LpKGn?`5X>E`5WG`3mex# zt(uwxsru9IdhuKA@Wdd*i@ zG`Cb5C}$7q^FEZHaM0T(>A%t(sI%vi_}y;BO7Bruakxjwq#H^(IfRwu7f7R3_6Cq; zg(l13pBq;qt?mt3ftSKlm3LssdbNQRVyWghc{$R)GPM{!yY1ZD*b{eu4f&GHp=1R- zJB|yiXvXN2(3k5eE&Og=Z3fQJ@;IAJ9QASg-`|;u)EKu5)%+n>(_Sjx@#cQbta^RU zPqMouYkiX&^gTPyIpPT*mtUt}+-9BK1EgC+(^nzz>rX8h>qg*)M2leUU*9~?e18@4l?B5aiFFjY@<6-$!lU?Tsa}d+m5A!Vr z-T?FrFM)x)Ae-w6W)IF|5Eltwcf!HeB76C=B|LekfmeyvZub+8C{P~_$K!;~R6fww z-u{R9ES`f$>AOhjMPXRj6YuZ_2iC3ba%F>ChVF4>e5b0Tqy&qF%IG-!u29*Eo}6r= z-r|`bgZ8>EJB=tjD~AY1{tFextlkzYgPR*HW#Uv{cJ{<#l-6dfpD0$FIDYhyoJL37 zN(6?=j_!UVck8XpoA366yR+wKa1!S^DQcm|3ehQXrFYt@|IaIMO?Zded@-+k}~ z5=?q!*Bg>uQSaHW(_%Pik{9(T&NG&hI@igl>l7KAuAh9|s4PNwYyI`XPXiFgVGVW& zktt?L;40&?#a#{lDf1b)X;Q>j+H!xc9o0kt#ejsY4+!wptocn;$CNeRQkHeG3IW@+ z*8&E%{@(M=>$(C(_kv>3(Ac3$6!5X zrMeJ7)5#>>Mi6>PfZzBm&d3A8Q=GeT1}wYYv^vJZamwrG0En z$RlbphtnJH%*q_K!bBd1$F%9&W>+Yo$I~q=+4$v_WGLA6*O=2BL;`FaT)p`2DLp@& zzYEhE{Y%p+E-{^R_PqNC8gQ~m$U56DQ@SkR%Gl%eB>h3Oet}A1D_re{vD^HZB-NET zRoXOJ?dclU$on1Ji+FuDT;pk4{7Hi)7%T*h8gGiS4;ed$eP@sJtF*V}7Pp*E-j5jFrgyfS`mCuAhd{)s^hO7# zpNIPSWi0bzg$FCM2S@PuQc}aIkCszLHLJtBxZntboOa$>{i7cG4{n>EJ7OC`zF^!l znC|O`FJ8`nW5V^0Qh2ip0$+@biy;&XX9np*A8X}N9)cIQ`q|vgdCKJYrp_RCb-_Cp zn7wJvc;y8kY8iI2tof|Z#Z3N|GSm!j#II%RoX#5V9D8;#_qohy!Lh!;G?}QRx6CRP zvrU5b=woair5IUg*99`|Uh0DZ$GXG|_UcTE69b zkM_ST>rGS0-Pc<0X=)|>^one+;Q~yr&#vudrv|&xRJRB@ zY`u>Keb>>UnMBY zLrP6Hqpt~}*Jy|?!Y2!`dXq>wE^B96oKxs7F@6!5k3JWK zz+0m`m|mBA(%Nd<42Vzo`J&xFu~f>BLOcc;^rRIOxy&V&)pqvQH{Uspy2N1xsY ZJ$H?eizfeG8jre6YL`!{_557M;gBW(I@B?4euyFN0C`T$x69{N?OU;h_aM>KC*0Cf4*A)4zMi9DcHX z$Ny;Q;)9R6@F092rRghIoimblhIoz9^3Vah7u$+|X1}iFY-#lMHk)=H-2-~?-h%#; zf{f|TM_uqKuOA*ETjB8v;{#YivHX-lh;$`(V(?n?(x#W$eh`p4u&>6*PFp)w)?b#G zxAx~YKGI~YWlLx;sSrsAZ+f$NpTX!PG38{bFO7REu^`>(QzO6ElGhwmKVEH2DYX(y zlh-w4-NZ#?z}OwfeE6d6LK638HGRP48vA)nQ5DS3#W z_W9_V$)kD4Xbm!vp_;{6DAJcGDL+awpv?}{=W1c_1T&c$kET6ZO;rgpbCb%E z;y`Bo#jOup{6^%~Uf$nBBDY+eMbYuDX)+_hI77_-df-c4+b^17;B{C3n{l;X-rp?R zN#5h!LZTKaD;YU+LeQDo0r(^Czz$8%y<1Bd+6BeN%P(&AmOe>xG!Ix z$b_d9#uwA_wGmlV;k0Iz0+W6{v^RI8{%%frt~)a(tFrSqY}L-{LQh~O=Wa@W46c&n0T`fbwfO~!8~W}l7QD;O9b&u_0t=R_3x@Sgrl`n8JEjRr=CF?* zJ6qTI4b{O!fsA)hB(dcBSn1E56h5~Y?33ZEOzTRT?cKUrBxcZ7!6=k>H>KvI&Yplh zI}x{~>DF&wm4Q0^|1i6sno*`qjw+VBoxWZqoQy53>A_GELlU!|8jZ*W4AHZ^cRn(h z&X96EnL>CYO+x1(+19S)VWnB#o$Jo>F&edo+er1nY|1HHYd0U(%0L(dyqu@nPeaCT z`LbE(^+>+ci>f9Qdu0u<;+A*9?zbeky|aC`P}G-(u)RTWbPF)Oc&XHCB9SCBJ=nOM ztf4QAN?PD3j7}>NOIW#%&|f#haVf&mLI_xsVx2duiKs+wXU8q5ExN z{I((qYYm=hwLB3)1|Vv*D#;8UxI{S<`%g?wl212xn+3K|rz2hyK6wMRf-`$I&MW>1 zr#`{)iC7H5E{= zexBTx@IHmxKrmj4uxtER@xi3S(I>4W1eY%T@cOvgGb^DnIXb~F-PXemxTsWG(c4M8 zDPiik#ob@#GgS~sPoa@iUi(%J6G(5!WdW5pIZObB76 zH1R&a6@Gxmwih;39_CQ%N2DZCLDOZEc0a7#&03-z7lT{W?}YJYCYbW?Ax!#hXjB)< zV;p_KuIuSYy#2>R2ZS- zQKqhMq9GZdxwJa?p>?ObdayU!qnoY_JoxbO`Li>sBab<#a)&e7^3_aeneAb;PA0wt z^5f4Lai>-0j2R%k5uIHHxNj=^bw0StNE2-_gq#P9lb$`;iIH^$2(Q{e)bwTeIvDRL zDB#rR^=<6zdgi(BXoCr-S3^TSG4cN_E*@6I#|O}tKm5@9Q?=$=00YdxK!{qY7OPR8 zY)dq8@8qD=rV|F4P(**O#EUs=2_S8GVRI+prutWkX5A*}IgJ|#HtC$7M*><2Oohe8 z;c3e}vZHJLSI#c307HQ?OUC%yUoeaTuYL{5F_p?Tmy2D2AIvcU}+l7Ly96^oV4B5U?{j?)l5M0y!=Sj^3b0z;X_nnRxByCUcJ0&ZS`{nr{U9`RdG zah0W6R;%?w45@eA%rMfLtS)QBh@7kVeaY|)z z==E2#58V`pi)5Ic_{`Wq(XS#hV&|Yp?YOPa2}_P$YM&R%14yDu9T!gteV+^DAdfuA z^o|>o1~>Xr7*vVoOX+;l7flIA$G-%x1?G*It7hrH3w6hzeoTd)*dNh8X>0>wpi|ZL z{uke3B#Mkq)5DD^MUKnRN;4h}$uc?Dq3O4^#Wl|{bT8&pqRF%R31f&C8q&ZpyCnN& zMSJkNQ5aEj$!;e!*5g*&<@~J#O7Igj{*{3*sQ5Wly;gARd*|S8vf#^fwxzpZ=<+G^ zFWs$__V2m4bVK384CXa$cU0u)KQ0lKf>b{dcw3&C2AF8q`2NIswxN3?64y5>tj>0C zDW$cvnCo;#BXwI>JqsehAu=IT9zGR%ud6@m=g*vD{LzhHO{MKEkO{j3b*+1pI$if> z=&^M?r>qM~=(XK{SrS$@(#h9UbM*Wn=T`Q$-^OUkCs(Z>(&Jfs-@fl)g-6WLO%9&h zn?gw6ZO*}BtT-AdGI@{E;*CybAjN*9|sfO{?y3jc1zBY6ifdj4N_#F9|mx7Q#?dTU+_l zGRoH%_WmB1rl54RdBM5Giv-YlD9iO)e=kL*p~zgJp3@G>?0|hbv~|ud>GMc&6F=on zVW`X1aHuk(iuS8j-=8APqH4zPrd`4Ag?t#%-2T9jt?ABkS?C?8xqJL`-*D0hkKG@7 zXKKk3U}?H|l2x%^=7N@$JJ+4EZc+R+ z1v50^G(-`^Iq1eKI$Xi~1HS=lqBCLov-P=VQ=&|3Uz}r24gv+m(1f*L?iqWBt{*Ah z4;R~PFJZ4CY%fH`Lc?V%jz>o_y%?L@H93Z1Y_0D0@f5-x{kS^6>SHjG(MFy1` zGp+m$2;>U)W0nnMBVjvmAz$NJ| zXOX{?OrLw*h}CeOWva{Xt+%x*4o_opMXNkE-xXmF4DV8^?D}deYv{&`zMBE%yElw2 zw6(s9eo}ljs7)80lXIaoKvkrY&v2G*6GhgH7+JLzIm+PukUrpAo*{r>w(v6TN>@gj z63cPsSuR*vjM(;Dsu|vd>-)_cI_lEK@Tf-%$@&@Coj~p0XW!sp%->lEW?ZHl_2|`V z$?DlE%leBtciL-k`7%^uZChO;vCQw~sgfMatU%F~0L;H^1cLHA`x)`rLIrB960LRx z1^B!|`x&!c@YQruMU`RJZ0?Pvxe9Yg7>A6%;?BG=1QqrdSTei1{YLOz*ai}lfHKVK z4>xZ)I8^r84(Cco?KK)@=F4D!isPm!;LXYqLUz;LrqLr8S2yg9)2y57;$rQ(O>oP^ z>?~0Jl+kh>4*Gb0aV5J9?xGIGev1H8J34B%T);V+>J`D|k{rwOkF8wzPF{)T+T2|Ia7b)(A^LaUJ*rHqGfh4+b1S$tZJ~!+< zt=F6ay#0;t6BVg`I}Ar0OFI^Q^5e0Sv2XUo(7)2}KL!YOmsel^7I@alBb06f}xmXi#@ zVP%-BeW9bnl1(s2!kwuZNX_eXP)3Z$oeAN92<(g$_+Kl@BODWb2N^~yIi-+3CE3NF z2zHQpHX>401&On>f$kNe6t@*N49D1JUAIy<&$r#=`5}{j{Q&G6t~(P=c6+*Qn_47p zE6Qk$(6`imL-0|2ebn(=JmbjVzR34qeqq|$=1F36a;O9rb4<#j)2*YBlaofEQ1s>+ zW|e0Oe&i5QHHyH=+NyVaap>f-MMN-ET%TPE3fTvZnyo)*H+fqn<5fWwf0I2_n)IDQ z9k=Oj$pqiAsi8BjN&}$(Oa8((kIQ`C{3p4$$ItJiqpLd(S3cc|q$MZQSa%E{7|Asd zA~{>FQ6?Tp)4c=XNo!~f4Ik994(|lZ?>9>2(^##%l=spcLNJo(1hgYot}+xz_i`F! z-Cxag|3m+qBKTgd)UT(a8X_=Q9C_rq&O^6%q(VZ2_H+b9%1767PGwrX`bgt8TLwI= zp;~)tB~t#(OJ~F|xx0ifv%*kS)+i-%vv*HbQ6hZAa(&>6{#ro6{`~->-s(MwKBBaQrrd@Q zy@Y|NtM1*|8J_c+U!zkUI{o&sr-liHkQ_)Y1r+OZjTTCU<-Ms|dH|m+(Qjw!zRKWt zVsZE>SZG>e&+dk3KiYWu5_3|@hW@g6BWIgt@ok*m$ z39nIa{h}IC!)wlGlgXdg_JV_czGNqT{7!7|*Q>zp$x0lNvLcbRl---6sdisdx3Br0 zWUsLOhF~1hT%Qm|lsVp~qpx$AE(Mm@m+3Sm6Y~||aTc}wJ~zT$l>%fNlTMgVJuIw1 zHace&gG%HZP8AQi$q7X$!q#-Sj_jR$k&%_yd4Kfh*@UTk>JF0)JYU#zMaDxe&f+&e z%qsX)vDaI72Y;}T1`AQ{jd6}0Ee&-p=y1W=Y=B0B9!rf2(BsA|^MbqJmU{Q=F4&aQ z=rlQL6GQqj8@B*9j_O4F8z~NF=SH@kY`*5;dAZwk(5uc)T4r;uyqqC(n1h+S5+N-F zoMGh?q;}sRDjx(te=mG`%|3JelO*bmlKP#%P9hK{JnaAi*SgqyrUt01E$Hr>)2=C9AvCHkV5K3 zCeNpk&)TJfncZKU=ITFufmAqWGgs(W)Wvs?JAsr5FM4|dd!Mb3Zuda)C1NHALpc&I2?#jk{Y}Z{0vQi+S-#KZ@%Sk{CG0Zq^Kxupx4agy!tt;>h#BUfmevgwcT=V_QKP10{|kc z!BW|I0ZK{`RK0?yvu$LBY#6u+x|_RWIjg*xlWC8dV6r9a-jA24ZFg31!ZQpXW7In# zN1CHc$Co^n!*LiV^vR2pJRcM6Q6&)`K#|=Qw%l%LhtVm%7*bf6jZR0*Tqinxi-Ktj z7sqgT(O*;dRgxcBpJwb)P?>v_owq65)YMF5p=7QDj}L>$=xC9OtaD;(trteiWHJq_ zGv9rS;3jf%25TWS9|&>V-G}z}VL!jR`2Ok>z7LbM#5CdzGoqO~_3^y6*vp(~r8&mp zD_ZoL>#GUGHR`}K@&MvYmufq^1Hblm;WiFbacX5!AyMO9(&q1+sjV{o{)%~teuFWm9i+Zxq@Fp5+a($&a^x|d63O`D7 zE3f|b?BRU&hUS7aBH|(jH1?HOr025J#<6{%kGp7qKqv(mUe*)g8z4f9#y})N)kTTa zQ5jxfH6=J_r=!iKv!)pZT_%cKCDkHhcVC6opC~~huU-**ko`m#*w#}VYX1BS zMYYO(Mz@eHK?c5jLr#%pe8XQ)(A1{MY2hv`0G&PKDCH$HdPNU z25a(HYk`X`Za5yj{DI!bydc+aOH)5fkUBTdCi;U}4rn7p`gN~lkm=yXv2i6}5wcZ} zFO@TeWLyawqDSA<5tWmKJ>zmS^%%ZVaR382q&Rc|{>HQ{PDu?T<)t z7+acLtF+yZ@qLU-)1@*-L08EggVmRi&B7^{vXjmSz8L&F`%YNDcfQ5unoYO-saJzv zOHUOv3@Jhn4i?w?hmlqLHN}lphUhKBaFuIo9r~rk5wPeIJ(zvdwCn76kvYCy8O^9S z%9a)JasPUq;DG@*;pL4+fAQ3k7=NJ`U*Ko?uE;jiUEIF&_6dex}B=A}d&`uA~rNgPKQEKhJ|ggW+8QZ^u`+y!|f9 zTS(N`<)6v>10eM>gm*+gzZhS2exx7R7){%b!KFzMU~9zAmrg2Vc?+j%K!-G8@4_7o8udX ziZgpR^n-b$B(R#+;xewwve;|9kNxtpX#(d)&Oa1WxU1ycQ|F#HU{ApLG2BlOVMUI2 zsmW#%)Ard6?bv|APlPkZ-BVqT*jrXTC zpBuU6MuEcA_T<8H$-}AbNoS6FeS}q{UH9lj@lc{*-IFpqx;(vN=GHg)&@%{j5?Ov) zO1!wzkW6hlG}!E0-s9WMr(p-BZ|{EYY{2CpN+=P9x_M7({0n0Fvr-^DdiDFDzx3T9M`CX7@iZmd@OqFX<)+S%k2j@$-g5}vzFgNRbv;;&JK=0^yq zEUwFtO{;G;g>M88edBt+k>N_7jI7u%WlE?u)IO& zar0}irA<(dQ_umJt~YrU*og?%3Y-*`aa5c03-S}3o|%tZf6j*cGR8({AT0|lJW|pg zhK)a)?$>SH3Y-7L7s=nTxZ!2<9MFo}{opNy?jM z;4#pMVf2-ke7=0|TpYopiPQ$keBy}l{N>f9D#_L)5Q`W2$c(mkefZ_zCuxK_bo~=K z>)X48Cbv!D+#k|9Bg@-iNAY=iVOaT&4oaqNk<^l%wC}=`XD)y8QAroM$o?>viq(#y z<|}VZkbNDP^>E|Zx3q*vLXzPT90~+&+SiOid9upjssZ!@VCAMuWFi3m5D>I@e}7Nj z(LPa+tOTnS@*MxWH=Gqzz>nd?i0a7yhal*@%auXDF^Koln|a0(`Lm*(HuAkPKwX^4 zJZx>U%)ZelId_M)F`q6Bht~0pxoTh^p|GU|FweWy70rG6qlPkK^2|IkcFA9b`*&2}<|YEgVp zb-B;{pn`j$9zBgoL?vmuJ2ETg$tCe@Abu>V06FKo@+7XdNG$3;333)vQo#*T292Nh zd#$b={xiaRGVg^;Y5x;q6!RcHN3eWK(RUIMn>R%pF031JTh(gT&`M@xlGoH>UwklR z=$%m0Wq1>tc`@FwS<+Z6b?ocSa%%oVNA+z~V_UL;aun9*PG7>|e#2iyVfJ{vG>LQV z8oz(Tn9Scy<2KVOm1t&h@Ihq=QZe+g(D>`>!};i$Cyg9$S8P3Qj%el zy@dKti)1lEf|YO{@jkkgL}lvxk}hFQCy zIU{z5AFp&Cm34@)taz^C9DOsFXV;i3F5EcWyX6is2i)8^OI0lGjqSw5prx-!<~kRZ z*(wZQ3t^58%|P|X^B`O2l>P(=Ky|M%Br?6?(Pw94rSywnX-S0m6@GBhs&rVS9MRp) zqx)yP!hO_+CD6;XQ(_~JtQ-N->vzW`k+gCMon8+btPjpDwM1WXyi*PpU`R+*jNjRl zBR!-ie_DDg53g0Dcu{7?-J27N=YHFZ?S4Z3p2BUC6RZ&OIyQEp-r9HWskNt#e^}@= z$>BJ2$$O{Yd|C(6`4 z?(Czl#Sln5d#l>#u-)yI($Wk&QaMWMdlH)E?uLMPdzH^&cpYf~e|4@+({h9DowY!q z>+#ZyIW>8LH@i&GBOyyIEtkcS5drlFD>E~34GjXA7%Z}al?;UJKu9!=vSc(TeWIb2 z>3wJ2-J|m(t!~q69O_3&B_(;%b*$f8(&(b63$5z=Y-8|kV81N=wmSPQ>8H7IoKnfF zQ}fQ7#(AmVwA$2TIju?BW6Vy0mLTZSbc-^NvVSUPQFmwa_g6iwNFto7k-ga07wEP= zT9dKyOnN@}5k+&^1$YSkUES`YJxVhB*E~J}$@AhZatL9HEj99EZDxh`gXa9su>e}Y zYK^`{ME~%EO(+eOf=vCFQogUc2qj^=zgl-sz_$2ZDZNeVGHv0fpQl|1 z`RfKnG?PDzKYX*|{=Zmz%ec6nWnCCT2nj(Ff@>1o-8}>du7kU~4elXGAV>ni-Cc*l zCAeFV!QI_m?qZ*F&)vy?pHJ_*KFlwyne?izu6pXJUe(<^8cymPag~x4Jevh5GXY?O zUbxPCBH1w8d%bZg0*5G6Zx_oq3=0o~XUn=w_5)7{3yqo>d~hPrtdDAn^^Bv0cjq5i zdFnboo4uu|&B&-AU|>EF-x!lg)l=b2Tj66YVABZgs?*+0;dxb>PkqAX$Yj3TK1R== zJUbl%&-ppydNcM(YEPK`JZ6cWNKB|8G=6^Vq;3eVgIeK>3X(Bj#KD4j;IB(h^cm8(NI?36)!AA(%MMB5nqXztexd$f--&^#7uWsA$HuG zObTSDk>9oELbuUfUR^>UJZlvHc*yO{z~40}y<>q_Y1Ds9T{);Kv6Mt)T9*%!a+8vl z#-Q@Te7Ej(rl74ojs?qPfS4`5;y_eQYfEyHE~icXhD7M#VAH#g>+(qeNiPuuH18@ zt9Ixt%)Di{9If`6|DAx-GhAG_cfY5lK!B z|M55hR_dl}MIV)y_w#y}JTi7j?*Td0kHVlv%$KOB11tk$ z{d5ngrUrU?-?1@e*H%`(6c-mqvO56lC|WiNxK|GjQdNHN2--K_p1$KGgQIWv5h^#B zwwkKJUMTn#%!M^TJ*pE-mk9vEK`m=Y&VJ5zJ?iFozwh&BXocc>8s>YuV{bIf=5; z5n>#U$CcMz@+z#3-oDI@#H!T9Qfew&*#G1!{kN)V1#9mSW%jz!h-M(?)B`PD7Ajr9$ zKcy-*Y5{6IB9DJ(Y2+YtTey!M2&7=mb-jLb|9zOUGE zm3v?PDm0z(Lu>S*INxFq-<|1NGR!@y_l__vpH|MB_s!XJXHJVC0zv4@i#{-fLzc%2 z(ubH+zITu24#AUC-)2$rNz^*!DHP*_ep=t0A+Gz}@b}&JEppisjZWjIt=i-~spaC# zH;UAHN{#7y9GdGJ97&$aC&S-bs93C#1JUf!aKzmb4*F5vfa2C2rOKQaOC?2~DY_!$aA1 z6mPzc{AZ`qQeOjtZjV{#1@0}FM z$kf`+M2Whgqioco%A%q~8z<-Y9j@}#!;4W0x%L_h5V4^52eZjcM`6x9g+_dL_W(^@ zNMRJ|P-?T@zF$B<9nm}F5cPL_jc29uvmpv6flDwjJm-81tMm*+@H7&xox>)&Smez~x5sNE(#6YSC5q#IQJp2_W1 zRvmwx5+!VbaXe2algI95P0Nwt5U^9IR*vzq>nym5lAmyEloH*o8;y7dTxHq4IcD*wb_>;Y z?Mu>xJ^k5W&IVrIp!Cr*b*5>wFQ*G14G&HtZtSpiq=^ZNly{g>s#MMyI$ozjn#up1 zhSv&NJ2%0(mgAB9y-{MN_M1d$ja^=-kpp|Z>;i-A8B)4uTK`)=rQeMfPTOgRHp(Ax zZ?ux}8h5y{1&fS2o)e6Nj97ykj*$etIa8&`XPCfyd^{kKb8;h($i=)tUPu+j&X{n} z&PR;a#zi$IP!e`H3Yz2#;^?-F@W}1ZJcS}FEDNI-UJEPd$*UghoniP2uk*N$=-}Ug z9O+KZ!3{%j(M+6-sH(c~3Lo>4`NhPecrht|B>q3d!Z>h1ks|UU zy4@;`Tio(Ajmf9{8Y_^GWxN?*>+?ozSehj^g>K7>ze#CtY4o^|$M;2h@t({0<79%< zHdRqU0;cz~3wXhbrIdI4{1eBPQg>SZ@4#T2-h14LQbUwnzol`0KOJ9dJW(lf48q&D zSJ}=)7PLf&YA^XSw>OPt*#&All0U)mTM>|_o>_sNLN|^*wPsOR4Y{zNHUz(y6m8j$ z+Z_9nj~U?o^^Me69~H*8=ntxzqT81;nC7DGP8EW!rMict*fmXgyfQfXI_Jj?;cPPW zBVmY{jg6Na;?&ANK-9oALrUA1#TzpDm*eWkX8ZF}`KOvWD9GqgzC3+X@o~}5vk@oy zQzQahcNU4(>>Dno;r0yJLP!LfCzV?ry|@N(+bI2canGZ$pE`Ii z0T;-g|05Ua1B;^aXLSY8=B?sbPHkX|$#Nqf-tmq#9oerb3mDHduE;#p{~ zp+O}W>zdwXP2jllLtX|s6T^m0^da6~^y@%7IuMBk1LcOQutj`1c)eL4kxc35RtWlxoSkxf%5LZU^5g=P zWz7%X#>cwW9p%nYt)hE{A|!A3>U#-to$O1oaI5g@8T_M_DeHb`+qy#0&!ez&#m z=Uf|pO5w!@94I6+@A{(7w-XZb5Q6=|R&5$Z8KDYtbX}(O^vJvDqJO*&cra$3Rl6fy z7~~UIsHZNvZ$uhr%?>6`nhPAFWz~Qd$0}l~lLV=p#oKQo&o=$)fXT~@tUZ*y)OwB} z-&PeSGGPHJZ*dLV?W*~rpcbLlMDHNuv^|eHnI0k*q;oa6R!= zqGitL^jky1oxMH7-P)f*U&7DFl(F7Qr`wDod(0K@hJIa%TmYFFX6xm<-rh2hH`6U7 zsGve@Illx^a$}+BNyakN66C%)t{RFNNK|szVaRapO`dr@EN{=XaWz*@2J@f`6HWd= zLmf|n!5Awhjdqtp6eO0;c8}@(O>?P-Q7Uen;$$vPE-WzQ5S-rAXRp$Lh^A|1zp6&J zgmYIDK_9YDC#F*)bgVswUuhA|PMvf)nW%g7(5@R%z^Zp;(aAP=`tjG!&aR-{8||4A zPaXCXkEmF1lm%3?y3=mT;X`fsLWi0=7gdPteEY!%2l*P;z6rJ-W8o{+Tv>b6G)2lf zwn1yRyoOyceF8hHp1C}3`~3Q#R~f6ym@}Qrfq=AkJ}8KX-C+u<#XIOf6l7P|C+V5P zu7vOIZaCe;^w zvz?IN8+f)K80^_rF~nKvb4pBwFC@Oj4S$%Lw7JR-?l_dO%D*IN?w{?Li?g#yo*OEL z+_2KC{_LgKies%7WIy4!CfJ4|LG)(HuWy}0aLV21J{K`H+R`H)YZ;M@r2*PbNge+Kta&6L)kR zF5U|AaXrZ8sDvo>ZQ+=N-6rKc{NGxDozMAxJD}}xqzX@Cls0dj)_}v0@Ap&m59U~R z2P(C;+`Z!1JlJ$;T$gLUii&JwUj@+~9WIi%1l}Pi?{=xY5PJFVSkhhniNXE$7y35_ z#ezG!iT-VAW5nPP%fwFBcZ(i$zXMRh2yccBda%MpugWZkNbAl9@#UK!hLkXP$?8m( zAZf(>`MG7eSnpeBzTlh{&WgMUJave&@UAn-kU&s0u%nU$pCES_yvPK&S(1@&LORRs zs%RwRQ);yIe#2>;&w7k=Hr6cHvrS%PEjqR5su#xHjxZ&m=2kt+h^O_UUPTcvh@E+VxM0gBz_%b#y{h=W=EgPjsTyao8ND zzi29WbGTzQ_8HNZUUuF?z+#K~AfI$=L&dV0TK9Y-S=Yds*=UJ<=Px;Frd)I#vH}ey z!tI!$9Q-XG<&+3kqNd%sc(<;qlUW~hk0(AxdFJaw?cno$KG7#>b2aF5EQl5>BKIa% z%?h`76$~OO#qAx6!rgmE3f`$@Uo9X!rpQ7Yce7cM9Wc*PJyZyTIM@g#{GN!%D4&Md zMW50q2oCoUL~?_2pt`tyA36(){-H@RqBz1$zQy2U!zJ;Ar;W z7YpmSq2hKpG~&9U3k0n8@dd(_;rLaDxoi!W9CO*hV8pkr7bu*|iTdeG#TN+(9&)dc zUIW5L#-Ul(*uz* zth@$kR35kM*gIl!tMX6NbfWhg6P4`PmwT7F+@EU>4Xs?)OZ9J$1i`>Q8@UwMM%=R) zsrDulOzW3Th#>~t4rJCpDH6biPo0yAhPf*ux@i;Dy{ibCIb?mpzb*D3(^D^eo*u4s z8|1V)G^Q-t&{_4|HJ>P|6{C%<+nR4CBYtR1(x%?XlT9v$?7I-FYtGo+H`oT6&(F_a zAq#nUkWjF&U;w*id3t$4`m3v|dcZRwYBLSCjKIcHKygrLD020#$EQ!90(&P+Ob}H^ zG@|7M;P&)jkpPI;mGvDH`bplyf4)R|x-0fY2n>-gp*xcOIv$Q6MH#fbxzrZO)am5;xsuYqyFLS?!Q|Mp;n?gQdUu2F zu)2#%%;((;fgMn36!2lusATWs6q}FUOC`j|NYsDIm`Fp4@+lr;1Tt(!k+@!X2K_Xw{3bklkf9K06--!2z3reBDv>J;f(4 zH?nUHT|)K}ofo~PBWCo6-0oD~B(q${joF53!|qS56-3v{c#N%r%8>(QKw#7sLnc z$Tyi8-y2YC6c9!iaIT(k@Z%1T#vu2W^n&)=b`h7PbsK`dU8EdUnVI*hQglpdB2%j%g_@QOLOBS`V#A?}ms|(d?XFC;J5(^RhT!atz9ytu=IEI?uQbQ){y? z*DMOgSJA)?XJ5rBaAnEiHPsFt3nRpBMEPd-y{F*%xt!hVr^xtKwx5swN7wv zVwK&mH^9d1SP!_Oh0UQ~2li#Uvz}uwJoLhR(DgJoopTr+mxmNkM?rmIi~YOYyAl5H zDF#CibZHGmyYjkbYrW#j)_6CoDruux`a>zL9$Y)`@j_IfI?^PgesrQl(Y+f2mKw_} zEx`z1oDX-~KjaWu4DPCIiF}seyT9SY1}5ide!Jc3(SbEJI%-wR^EH)0#&PQUl^-KCxh;$dDIBlrckA&9uL`i-9E2*qdx4K9XX3k5)YZGDwETS&fZk82OnsxJ6 zE>9TBmWOQ3j+IDB>j)A@!q2u3 z12qgBLLX*+T5x|>Z)9N68}%Z7;E!Zvw9d2~3ZEP+2+`-3$si7}dzesnzODUIxD@P* zjm^-^+FGG^U}c$b_p5cGW7{?89Pdr}_|yrv#t9B8wi z5*YIR7G(Qgg;>CRyio1{Q&Z{Az^vuP)amG}UTFdu7-K&*p7)p0nRO<8FjHFC+AACl zlb#U&C3X|cly^cSIic#TU$65aG|np<0y~?HG+zmbzuo{hLiKv@-$+x6@T{DyhMko? zaf!yd$GXu1cSt5L*k`8IAo>F#bbao0>Yc3}KC6}9FBMBpM_>`naEaaDIj zF2Hhv{qy%$RpiNN0{r26zY&ku1!cOrQk3La*V&|V5(GO>g=T+VH}ux|JyIK6{)jW2 zS94{W)^9_G1NAXzDnx@U`>xC6pxhxGo8-va320?dCk;{49Xl9UIM^zx2n+ z|8NuR!_=rL6WyW9BDwl`h?qf8_UdRlHFU}jkIK*ZVarkO?ia4cNKY@*ocjo&Bh=Ji zmFvhire(u(t_}X>8&b)WNWEqRt0{N?#klvlm4t1r+;?T&-&0PF9aiS8ynozAjbAER z?M5-%;d^b61=(iNz48jrj^e9D+GKZWfqF%+Zi$9n-S%Do?5og~t3AC?C+?(taCF(g zj7biDDt+a??UJ?5)HiVx$taCnQf1!KvtS=Xv5rss%xkCtvh=fJQ}RKFJ|A>VgzkB7 zu$OA;rabnu=>)bs!KdwTb|nz#gg@AFm~=7WJ;9Fc!XVj_oTx0ZJkp|jWpk>sPk}$u zVC%!JI- zy#dVmTsxwz=*9^9ep*=n%*6PP`n#9xbHjzp`x zGI{z3B{4>Ip@+Ko9i75RnBfv1>)x|6ic_LA+1jDH;ml-m_RwBMG8%tN@hXUUs^AX` z<8mUt)T=gpWBfag8$-SUQo-}-FDJl>!wgKwir1m)&o2IGmpd*9Dt>&&eV zAI-F9?XIM|LAXzGnJj{K%3?O*)4t><6Xn#p)!#34%N5wU65k{;i)2U|QTQ?C! z?4(gB_X}@d_V&j(>JlOI=$5ayvf-Q7i}C3PnKJiV6Tx%~Lq2aCjO>|u6ODg!cjvxr z^ss$FG{dmyJZi3`b%ixGZY}G6ik^8BYI4x_GgDQY=&Zecb?CiDoF{w4nUW@Lo`yGi zM7Zv<{Y$F(0IXP(VYa*7ntChZ-?0nff>P#EL{89;7)s92td0vhbzbDeC%0CC{{D-= z!6U}*3r&MHVFa-o&Os=|Su9VJd&dWgg<5;Js!h$zzFz$3U#X^@$aOiWgsXuUdqVnv4x0Dx$h~ zrybzkNzMDbd#)gV?Q_v4K2?&zRQ2y=!H<<7xERlmheRi3^A*{Mp#l6d)fFZyHsPfS ztyQY!W;=NDqwAU!7kIx^W7b$fS}1LTZ#UBW``_Cjt-gPr#F;Q&S8gY#eO|2Hr;F4W zcTktaX+ch{3*|`mCKmPCm{3+y9pQJ0<$qZmv+EB^pYPf3k7_ z-kR@3N!G%qrHS*J$S@fqzqR)Ij=J-(e7H3(edgJr8HVwbxhNQ9%UO4}Cp+3(TReu> z6)>J@&l}_B&gX3@7UdjNcGe&bzqwqQ;_bKMCFgKB%siOX+#7s$GIhU=`U0Pv3N-&! z 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%0D`jM4Egh_Fobv+=X&Ywc%r75ZL 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 KLe_cHJO2mJJ{e{J diff --git a/examples/skywalking/docker-compose.yaml b/examples/skywalking/docker-compose.yaml deleted file mode 100644 index 0a32f7fe6c04..000000000000 --- a/examples/skywalking/docker-compose.yaml +++ /dev/null @@ -1,130 +0,0 @@ -services: - - envoy-front-proxy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: envoy-front-proxy.yaml - depends_on: - skywalking-ui: - condition: service_healthy - envoy-1: - condition: service_started - envoy-2: - condition: service_started - ports: - - "${PORT_PROXY:-10000}:10000" - - envoy-1: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: envoy-1.yaml - depends_on: - skywalking-ui: - condition: service_healthy - service-1: - condition: service_healthy - envoy-2: - condition: service_started - - envoy-2: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: envoy-2.yaml - depends_on: - skywalking-ui: - condition: service_healthy - service-2: - condition: service_healthy - - service-1: - build: - context: ../shared/python - target: aiohttp-tracing-service - environment: - - SERVICE_NAME=1 - - service-2: - build: - context: ../shared/python - target: aiohttp-tracing-service - environment: - - SERVICE_NAME=2 - - # Skywalking components. - elasticsearch: - build: - context: . - dockerfile: Dockerfile-elasticsearch - healthcheck: - test: ["CMD-SHELL", "curl -sf http://localhost:9200/_cluster/health || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s - environment: - discovery.type: single-node - ingest.geoip.downloader.enabled: "false" - - cluster.routing.allocation.disk.watermark.low: "${ES_WATERMARK_LOW:-85%}" - cluster.routing.allocation.disk.watermark.high: "${ES_WATERMARK_HIGH:-90%}" - cluster.routing.allocation.disk.watermark.flood_stage: "${ES_WATERMARK_FLOOD:-95%}" - cluster.routing.allocation.disk.watermark.flood_stage.frozen: "${ES_WATERMARK_FLOOD_FROZEN:-95%}" - cluster.routing.allocation.disk.watermark.flood_stage.frozen.max_headroom: "${ES_MAX_HEADROOM:-20GB}" - - # NB: This setting is for demo purposes only, you are strongly advised to configure - # Elasticsearch with security enabled - xpack.security.enabled: "false" - ulimits: - memlock: - soft: -1 - hard: -1 - - skywalking-oap: - build: - context: . - dockerfile: Dockerfile-skywalking-oap - depends_on: - elasticsearch: - condition: service_healthy - environment: - SW_HEALTH_CHECKER: default - SW_STORAGE: elasticsearch - SW_STORAGE_ES_CLUSTER_NODES: elasticsearch:9200 - healthcheck: - test: - - CMD-SHELL - - /skywalking/bin/swctl health - interval: 10s - timeout: 120s - retries: 10 - start_period: 10s - restart: on-failure - - skywalking-ui: - build: - context: . - dockerfile: Dockerfile-skywalking-ui - healthcheck: - test: - - CMD-SHELL - - >- - curl --silent --fail http://localhost:8080/graphql -X POST -H "Content-Type:application/json" - -d "{ \"query\": \"query version { version }\"}" || exit 1 - interval: 5s - timeout: 10s - retries: 10 - start_period: 10s - depends_on: - skywalking-oap: - condition: service_healthy - ports: - - "${PORT_UI:-8080}:8080" - environment: - SW_OAP_ADDRESS: http://skywalking-oap:12800 - SW_HEALTH_CHECKER: default diff --git a/examples/skywalking/envoy-1.yaml b/examples/skywalking/envoy-1.yaml deleted file mode 100644 index 85a51019cba6..000000000000 --- a/examples/skywalking/envoy-1.yaml +++ /dev/null @@ -1,136 +0,0 @@ -# This proxy listens on 2 ports: -# -# 10000 -> routes to `service-1` -# 10001 -> routes to `envoy-2` - -# The `client_config` settings below are used by Skywalking to identify the proxy. - -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - traffic_direction: INBOUND - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - tracing: - provider: - name: envoy.tracers.skywalking - typed_config: - "@type": type.googleapis.com/envoy.config.trace.v3.SkyWalkingConfig - grpc_service: - envoy_grpc: - cluster_name: skywalking - timeout: 0.250s - client_config: - service_name: envoy-proxy-1 - instance_name: envoy-proxy-1-1 - codec_type: AUTO - stat_prefix: ingress_http - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - start_child_span: true - route_config: - name: service1_route - virtual_hosts: - - name: service1 - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service_cluster1 - decorator: - operation: routeToService1 - - address: - socket_address: - address: 0.0.0.0 - port_value: 10001 - traffic_direction: OUTBOUND - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - tracing: - provider: - name: envoy.tracers.skywalking - typed_config: - "@type": type.googleapis.com/envoy.config.trace.v3.SkyWalkingConfig - grpc_service: - envoy_grpc: - cluster_name: skywalking - timeout: 0.250s - client_config: - service_name: envoy-proxy-1 - instance_name: envoy-proxy-1-2 - codec_type: AUTO - stat_prefix: egress_http - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - start_child_span: true - route_config: - name: envoy2_route - virtual_hosts: - - name: envoy2 - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: envoy_cluster2 - decorator: - operation: routeToEnvoy2 - - clusters: - - name: service_cluster1 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service_cluster1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-1 - port_value: 8080 - - name: envoy_cluster2 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: envoy_cluster2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: envoy-2 - port_value: 10000 - - name: skywalking - type: STRICT_DNS - lb_policy: ROUND_ROBIN - typed_extension_protocol_options: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicit_http_config: - http2_protocol_options: {} - load_assignment: - cluster_name: skywalking - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: skywalking-oap - port_value: 11800 diff --git a/examples/skywalking/envoy-2.yaml b/examples/skywalking/envoy-2.yaml deleted file mode 100644 index d37bc8cabef3..000000000000 --- a/examples/skywalking/envoy-2.yaml +++ /dev/null @@ -1,79 +0,0 @@ -# This proxy listens on port 10000 and routes all queries to `service-2`. - -# The `client_config` setting below is used by Skywalking to identify the proxy. - -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - traffic_direction: INBOUND - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - tracing: - provider: - name: envoy.tracers.skywalking - typed_config: - "@type": type.googleapis.com/envoy.config.trace.v3.SkyWalkingConfig - grpc_service: - envoy_grpc: - cluster_name: skywalking - timeout: 0.250s - client_config: - service_name: envoy-proxy-2 - instance_name: envoy-proxy-2-0 - codec_type: AUTO - stat_prefix: ingress_http - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - start_child_span: true - route_config: - name: service2_route - virtual_hosts: - - name: service2 - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service_cluster2 - decorator: - operation: routeToService2 - - clusters: - - name: service_cluster2 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service_cluster2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-2 - port_value: 8080 - - name: skywalking - type: STRICT_DNS - lb_policy: ROUND_ROBIN - typed_extension_protocol_options: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicit_http_config: - http2_protocol_options: {} - load_assignment: - cluster_name: skywalking - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: skywalking-oap - port_value: 11800 diff --git a/examples/skywalking/envoy-front-proxy.yaml b/examples/skywalking/envoy-front-proxy.yaml deleted file mode 100644 index 0b318ba00bc8..000000000000 --- a/examples/skywalking/envoy-front-proxy.yaml +++ /dev/null @@ -1,101 +0,0 @@ -# This proxy listens on port 10000, and routes the following paths: -# -# /trace/1 -> routes to `envoy-1` on port 10000 -# /trace/2 -> routes to `envoy-1` on port 10001 (for onward routing to `envoy-2`) - -# The `client_config` setting below is used by Skywalking to identify the proxy. - -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - traffic_direction: OUTBOUND - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - generate_request_id: true - tracing: - provider: - name: envoy.tracers.skywalking - typed_config: - "@type": type.googleapis.com/envoy.config.trace.v3.SkyWalkingConfig - grpc_service: - envoy_grpc: - cluster_name: skywalking - timeout: 0.250s - client_config: - service_name: envoy-proxy-front - instance_name: envoy-proxy-front - codec_type: AUTO - stat_prefix: ingress_http - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - start_child_span: true - route_config: - name: proxy_routes - virtual_hosts: - - name: proxy - domains: - - "*" - routes: - - match: - prefix: "/trace/1" - route: - cluster: envoy_cluster1 - decorator: - operation: routeToEnvoy1 - - match: - prefix: "/trace/2" - route: - cluster: envoy_cluster2 - decorator: - operation: routeToEnvoy2ViaEnvoy1 - - clusters: - - name: envoy_cluster1 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: envoy_cluster1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: envoy-1 - port_value: 10000 - - name: envoy_cluster2 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: envoy_cluster2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: envoy-1 - port_value: 10001 - - name: skywalking - type: STRICT_DNS - lb_policy: ROUND_ROBIN - typed_extension_protocol_options: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - explicit_http_config: - http2_protocol_options: {} - load_assignment: - cluster_name: skywalking - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: skywalking-oap - port_value: 11800 diff --git a/examples/skywalking/example.rst b/examples/skywalking/example.rst deleted file mode 100644 index d3c8baddc3d7..000000000000 --- a/examples/skywalking/example.rst +++ /dev/null @@ -1,131 +0,0 @@ -.. _install_sandboxes_skywalking: - -Skywalking tracing -================== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -The Skywalking tracing sandbox demonstrates Envoy's :ref:`request tracing ` -capabilities using `Skywalking `_ as the tracing provider. - -In this example, 2 backend services are provided: - -- ``service-1`` -- ``service-2`` - -3 Envoy proxies are also provided to route requests to them: - -- ``envoy-front-proxy`` (:download:`envoy-front-proxy.yaml <_include/skywalking/envoy-front-proxy.yaml>`) -- ``envoy-1`` (:download:`envoy-1.yaml <_include/skywalking/envoy-1.yaml>`) -- ``envoy-2`` (:download:`envoy-2.yaml <_include/skywalking/envoy-2.yaml>`) - -Of these services, only the Envoy ``front-proxy`` service is exposed outside of the -:download:`composition <_include/skywalking/docker-compose.yaml>`, on port ``10000``. - -For ``service-1``, requests are routed based on the request path ``trace/1``, as follows: - - User -> Envoy(``envoy-front-proxy``) -> Envoy(``envoy-1``) -> ``service-1`` - -For ``service-2``, requests are routed based on the request path ``trace/2`` as follows: - - User -> Envoy(``envoy-front-proxy``) -> Envoy(``envoy-1``) -> Envoy(``envoy-2``) -> ``service-2`` - -All Envoy proxies are configured to collect request traces, as can be seen in their configurations, -propagating the spans generated by the Skywalking tracer to a Skywalking OAP cluster. - -Each span records the latency of upstream API calls as well as information -needed to correlate the span with other related spans (e.g., the trace ID). - -The Skywalking web UI for viewing the collected traces is available on port ``8080``. - -Step 1: Build the sandbox -************************* - -Change directory to ``examples/skywalking`` in the Envoy repository. - -To build this sandbox example, and start the example services run the following commands: - -.. code-block:: console - - $ pwd - envoy/examples/skywalking - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - Name Command State Ports - --------------------------------------------------------------------------------------------------------------------------- - skywalking_elasticsearch_1 /bin/tini -- /usr/local/bi ... Up (healthy) 9200/tcp, 9300/tcp - skywalking_envoy-1_1 /docker-entrypoint.sh /usr ... Up 10000/tcp - skywalking_envoy-2_1 /docker-entrypoint.sh /usr ... Up 10000/tcp - skywalking_envoy-front-proxy_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp,:::10000->10000/tcp - skywalking_service-1_1 python3 /code/service.py Up (healthy) - skywalking_service-2_1 python3 /code/service.py Up (healthy) - skywalking_skywalking-oap_1 bash docker-entrypoint.sh Up (healthy) 11800/tcp, 1234/tcp, 12800/tcp - skywalking_skywalking-ui_1 bash docker-entrypoint.sh Up (healthy) 0.0.0.0:8080->8080/tcp,:::8080->8080/tcp - -Step 2: Make a request to ``service-1`` -*************************************** - -Now send a request to ``service-1``, by calling http://localhost:10000/trace/1. - -This will be routed via 2 of the Envoy proxies: - -- ``front-proxy`` -- ``envoy-1`` - -.. code-block:: console - - $ curl localhost:10000/trace/1 - Hello from behind Envoy (service 1)! - -Step 3: Make a request to ``service-2`` -*************************************** - -Now send a request to ``service-2``, by calling http://localhost:10000/trace/2. - -This will be routed via all 3 of the Envoy proxies: - -- ``front-proxy`` -- ``envoy-1`` -- ``envoy-2`` - -.. code-block:: console - - $ curl localhost:10000/trace/2 - Hello from behind Envoy (service 2)! - -Step 4: View the traces in Skywalking UI -**************************************** - -Point your browser to http://localhost:8080. - -You should see the Skywalking dashboard. - -You may need to wait a moment for the traces to be added, but clicking on ``General Service > Services``, you -should see the Envoy services listed. - -.. image:: /start/sandboxes/_include/skywalking/_static/skywalking-services.png - -From here you can explore the metrics and views that skywalking offers, such as the ``Topology``: - -.. image:: /start/sandboxes/_include/skywalking/_static/skywalking-topology.png - -You can also view tracing information for the requests that you made: - -.. image:: /start/sandboxes/_include/skywalking/_static/skywalking-trace.png - -.. seealso:: - - :ref:`Request tracing ` - Learn more about using Envoy's request tracing. - - :ref:`Envoy admin quick start guide ` - Quick start guide to the Envoy admin interface. - - `Apache SkyWalking `_ - SkyWalking observability analysis platform and application performance management system. diff --git a/examples/skywalking/verify.sh b/examples/skywalking/verify.sh deleted file mode 100755 index b115b7ca2d82..000000000000 --- a/examples/skywalking/verify.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash -e - -export NAME=skywalking -export PORT_PROXY="${SKYWALKING_PORT_PROXY:-11910}" -export PORT_UI="${SKYWALKING_PORT_UI:-11911}" - -# NB: This allows ES to run in a low-resource environment, -# dont do this in a production environment. -export ES_MAX_HEADROOM=1GB -export ES_WATERMARK_FLOOD_FROZEN=99% -export ES_WATERMARK_FLOOD=99% -export ES_WATERMARK_HIGH=99% -export ES_WATERMARK_LOW=99% - - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Make a request to service-1" -responds_with \ - "Hello from behind Envoy (service 1)!" \ - "http://localhost:${PORT_PROXY}/trace/1" - -run_log "Make a request to service-2" -responds_with \ - "Hello from behind Envoy (service 2)!" \ - "http://localhost:${PORT_PROXY}/trace/2" - -run_log "View the traces in Skywalking UI" -responds_with \ - "" \ - "http://localhost:${PORT_UI}" - -run_log "Test OAP Server" -responds_with \ - "getEndpoints" \ - "http://localhost:${PORT_UI}/graphql" \ - -X POST \ - -H "Content-Type:application/json" \ - -d "{ \"query\": \"query queryEndpoints(\$serviceId: ID!, \$keyword: String!) { - getEndpoints: searchEndpoint(serviceId: \$serviceId, keyword: \$keyword, limit: 100) { - key: id - label: name - } - }\", - \"variables\": { \"serviceId\": \"\", \"keyword\": \"\" } - }" - -responds_with \ - "currentTimestamp" \ - "http://localhost:${PORT_UI}/graphql" \ - -X POST \ - -H "Content-Type:application/json" \ - -d "{ \"query\": \"query queryOAPTimeInfo { - getTimeInfo { - timezone - currentTimestamp - } - }\", - \"variables\": {} - }" diff --git a/examples/tls-inspector/README.md b/examples/tls-inspector/README.md deleted file mode 100644 index cc93e98e5adf..000000000000 --- a/examples/tls-inspector/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/tls-inspector.html). diff --git a/examples/tls-inspector/docker-compose.yaml b/examples/tls-inspector/docker-compose.yaml deleted file mode 100644 index 162f846321bb..000000000000 --- a/examples/tls-inspector/docker-compose.yaml +++ /dev/null @@ -1,40 +0,0 @@ -services: - - tls-inspector: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - depends_on: - service-https-http2: - condition: service_started - service-https-http1.1: - condition: service_started - service-http: - condition: service_started - ports: - - "${PORT_PROXY:-10000}:10000" - - "${PORT_ADMIN:-12345}:12345" - - service-https-http2: - build: - context: . - dockerfile: ../shared/echo2/Dockerfile - hostname: service-https-http2 - environment: - - HTTP_PORT=0 - - service-https-http1.1: - build: - context: . - dockerfile: ../shared/echo2/Dockerfile - hostname: service-https-http1.1 - environment: - - HTTP_PORT=0 - - service-http: - build: - context: . - dockerfile: ../shared/echo2/Dockerfile - hostname: service-http - environment: - - HTTPS_PORT=0 diff --git a/examples/tls-inspector/envoy.yaml b/examples/tls-inspector/envoy.yaml deleted file mode 100644 index 75c5c52572ab..000000000000 --- a/examples/tls-inspector/envoy.yaml +++ /dev/null @@ -1,79 +0,0 @@ -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 12345 -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - listener_filters: - - name: "envoy.filters.listener.tls_inspector" - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector - filter_chains: - - filter_chain_match: - transport_protocol: tls - application_protocols: [h2] - filters: - - name: envoy.filters.network.tcp_proxy - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: service-https-http2 - stat_prefix: https_passthrough - - filter_chain_match: - transport_protocol: tls - application_protocols: [http/1.1] - filters: - - name: envoy.filters.network.tcp_proxy - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: service-https-http1.1 - stat_prefix: https_passthrough - - filter_chain_match: - filters: - - name: envoy.filters.network.tcp_proxy - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: service-http - stat_prefix: ingress_http - - clusters: - - name: service-https-http2 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service-https-http2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-https-http2 - port_value: 443 - - name: service-https-http1.1 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service-https-http1.1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-https-http1.1 - port_value: 443 - - name: service-http - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service-http - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-http - port_value: 80 diff --git a/examples/tls-inspector/example.rst b/examples/tls-inspector/example.rst deleted file mode 100644 index 9944a426379b..000000000000 --- a/examples/tls-inspector/example.rst +++ /dev/null @@ -1,108 +0,0 @@ -.. _install_sandboxes_tls_inspector: - -TLS Inspector Listener Filter -============================= - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - - :ref:`jq ` - Parse ``json`` output from the upstream echo servers. - -This example demonstrates how the ``TLS`` inspector can be used to select ``FilterChains`` to -distribute the traffic between upstream clusters according to the matched ``transport_protocol`` and/or -``application_protocols``. - -It also demonstrates the admin statistics generated by the ``TLS`` inspector listener filter. - -Step 1: Build the sandbox -************************* - -Change directory to ``examples/tls-inspector`` in the Envoy repository, and bring up the services. - -This starts one proxy listening on ``localhost:10000``, and with an admin interface listening on port 12345. - -It also starts three upstream ``HTTP`` services that echo back received headers in ``json`` format. - -The first 2 services are ``HTTPS`` services listening on port ``443`` and the other has no ``TLS`` and listens on -port ``80``. - -.. code-block:: console - - $ pwd - envoy/examples/tls-inspector - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - --------------------------------------------------------------------------------------------------------------------------------- - tls-inspector_service-http_1 docker-entrypoint.sh node ... Up - tls-inspector_service-https-http1.1_1 docker-entrypoint.sh node ... Up - tls-inspector_service-https-http2_1 docker-entrypoint.sh node ... Up - tls-inspector_tls-inspector_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp, 0.0.0.0:12345->12345/tcp - - -Step 2: Access services -*********************** - -Querying the service at port 10000 with a different HTTP version specified over TLS, or -with HTTP protocol without TLS, the requests will be handled by different upstream services. - -Query the proxy with ``HTTP1.1`` and ``TLS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block:: console - - $ curl -sk --http1.1 https://localhost:10000 | jq '.os.hostname' - "service-https-http1.1" - -The upstream ``service-https-http1.1`` handles the request. - -Query the proxy with ``HTTP2`` and ``TLS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block:: console - - $ curl -sk --http2 https://localhost:10000 | jq '.os.hostname' - "service-https-http2" - -The upstream ``service-https-http2`` handles the request. - -Query the proxy with no ``TLS`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block:: console - - $ curl -sk http://localhost:10000 | jq '.os.hostname' - "service-http" - -The upstream ``service-http`` handles the request. Since TLS Inspector listener filter detects the -transport is plaintext, it will not set transport_protocol to ``TLS``. - -Step 3: View the admin statistics -********************************* - -TLS inspector has a statistics tree rooted at ``tls_inspector``, which can be extracted with the -admin access entrypoint configured. - -.. code-block:: console - - $ curl -sk http://localhost:12345/stats |grep tls_inspector - tls_inspector.alpn_found: 2 - tls_inspector.alpn_not_found: 0 - tls_inspector.client_hello_too_large: 0 - tls_inspector.connection_closed: 0 - tls_inspector.read_error: 0 - tls_inspector.sni_found: 2 - tls_inspector.sni_not_found: 0 - tls_inspector.tls_found: 2 - tls_inspector.tls_not_found: 1 - -Viewing the admin statistics we can see that ``TLS``, ``SNI`` and ``ALPN`` are all detected since -we access services twice via ``HTTP`` over ``TLS``. It also shows one ``tls_not_found`` from the -plaintext query. diff --git a/examples/tls-inspector/verify.sh b/examples/tls-inspector/verify.sh deleted file mode 100755 index 2fe113c82906..000000000000 --- a/examples/tls-inspector/verify.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -e - -export NAME=tls-inspector -export PORT_PROXY="${TLS_INSPECTOR_PORT_PROXY:-12010}" -export PORT_ADMIN="${TLS_INSPECTOR_PORT_ADMIN:-12011}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -wait_for 30 sh -c "curl -s http://localhost:${PORT_ADMIN}/stats | grep 'tls_inspector.alpn_found: 0'" - -run_log "Curl tls inspector: HTTPS -> HTTP/1.1" -curl -sk --http1.1 "https://localhost:${PORT_PROXY}" | jq '.os.hostname' | grep service-https-http1.1 - -run_log "Curl tls inspector: HTTPS -> HTTP/2" -curl -sk --http2 "https://localhost:${PORT_PROXY}" | jq '.os.hostname' | grep service-https-http2 - -run_log "Curl tls inspector: HTTP" -curl -s "http://localhost:${PORT_PROXY}" | jq '.os.hostname' | grep service-http - -run_log "Check stats of tls inspector" -curl -s "http://localhost:${PORT_ADMIN}/stats" | grep "tls_inspector.alpn_found: 2" -curl -s "http://localhost:${PORT_ADMIN}/stats" | grep "tls_inspector.sni_found: 2" -curl -s "http://localhost:${PORT_ADMIN}/stats" | grep "tls_inspector.tls_found: 2" -curl -s "http://localhost:${PORT_ADMIN}/stats" | grep "tls_inspector.tls_not_found: 1" diff --git a/examples/tls-sni/README.md b/examples/tls-sni/README.md deleted file mode 100644 index 8f43b19a059f..000000000000 --- a/examples/tls-sni/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/tls-sni.html). diff --git a/examples/tls-sni/docker-compose.yaml b/examples/tls-sni/docker-compose.yaml deleted file mode 100644 index 25fdfc74e9a9..000000000000 --- a/examples/tls-sni/docker-compose.yaml +++ /dev/null @@ -1,43 +0,0 @@ -services: - - proxy: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - target: envoy-certs - ports: - - "${PORT_PROXY:-10000}:10000" - - proxy-client: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - target: envoy-certs - args: - ENVOY_CONFIG: envoy-client.yaml - ports: - - "${PORT_PROXY_CLIENT:-20000}:10000" - - http-upstream1: - build: - context: . - dockerfile: ../shared/echo2/Dockerfile - hostname: http-upstream1 - environment: - - HTTPS_PORT=0 - - http-upstream2: - build: - context: . - dockerfile: ../shared/echo2/Dockerfile - hostname: http-upstream2 - environment: - - HTTPS_PORT=0 - - https-upstream3: - build: - context: . - dockerfile: ../shared/echo2/Dockerfile - hostname: https-upstream3 - environment: - - HTTP_PORT=0 diff --git a/examples/tls-sni/envoy-client.yaml b/examples/tls-sni/envoy-client.yaml deleted file mode 100644 index 6ad6fae1caa3..000000000000 --- a/examples/tls-sni/envoy-client.yaml +++ /dev/null @@ -1,91 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: app - domains: - - "*" - routes: - - match: - prefix: "/domain1" - route: - cluster: proxy-client-domain1 - - match: - prefix: "/domain2" - route: - cluster: proxy-client-domain2 - - match: - prefix: "/domain3" - route: - cluster: proxy-client-domain3 - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - clusters: - - name: proxy-client-domain1 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: proxy-client-domain1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: proxy - port_value: 10000 - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - sni: domain1.example.com - - - name: proxy-client-domain2 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: proxy-client-domain2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: proxy - port_value: 10000 - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - sni: domain2.example.com - - - name: proxy-client-domain3 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: proxy-client-domain3 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: proxy - port_value: 10000 - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext - sni: domain3.example.com diff --git a/examples/tls-sni/envoy.yaml b/examples/tls-sni/envoy.yaml deleted file mode 100644 index cbd488c99f22..000000000000 --- a/examples/tls-sni/envoy.yaml +++ /dev/null @@ -1,130 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - listener_filters: - - name: "envoy.filters.listener.tls_inspector" - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector - filter_chains: - - filter_chain_match: - server_names: - - domain1.example.com - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: app - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: proxy-domain1 - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext - common_tls_context: - tls_certificates: - - certificate_chain: - filename: certs/domain1.crt.pem - private_key: - filename: certs/domain1.key.pem - - - filter_chain_match: - server_names: - - domain2.example.com - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: app - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: proxy-domain2 - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext - common_tls_context: - tls_certificates: - - certificate_chain: - filename: certs/domain2.crt.pem - private_key: - filename: certs/domain2.key.pem - - - filter_chain_match: - server_names: - - domain3.example.com - filters: - - name: envoy.filters.network.tcp_proxy - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: proxy-domain3 - stat_prefix: ingress_domain3 - - clusters: - - name: proxy-domain1 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: proxy-domain1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: http-upstream1 - port_value: 80 - - - name: proxy-domain2 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: proxy-domain2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: http-upstream2 - port_value: 80 - - - name: proxy-domain3 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: proxy-domain3 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: https-upstream3 - port_value: 443 diff --git a/examples/tls-sni/example.rst b/examples/tls-sni/example.rst deleted file mode 100644 index 726f833af2ec..000000000000 --- a/examples/tls-sni/example.rst +++ /dev/null @@ -1,175 +0,0 @@ -.. _install_sandboxes_tls_sni: - -TLS Server name indication (``SNI``) -==================================== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - - :ref:`jq ` - Parse ``json`` output from the upstream echo servers. - -This example demonstrates an Envoy proxy that listens on three ``TLS`` domains -on the same ``IP`` address. - -The first two domains (``domain1`` and ``domain2``) terminate the ``TLS`` and proxy -to upstream ``HTTP`` hosts. - -The other domain (``domain3``) is proxied unterminated, based on the ``SNI`` headers. - -It also demonstrates Envoy acting as a client proxy connecting to upstream ``SNI`` services. - -.. _install_sandboxes_tls_sni_step1: - -Step 1: Create keypairs for each of the domain endpoints -******************************************************** - -Change directory to ``examples/tls-sni`` in the Envoy repository. - -The example creates two Envoy ``TLS`` endpoints and they will require their own -keypairs. - -Create self-signed certificates for these endpoints as follows: - -.. code-block:: console - - $ pwd - envoy/examples/tls-sni - - $ mkdir -p certs - - $ openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ - -subj "/C=US/ST=CA/O=MyExample, Inc./CN=domain1.example.com" \ - -keyout certs/domain1.key.pem \ - -out certs/domain1.crt.pem - Generating a RSA private key - .............+++++ - ...................+++++ - writing new private key to 'certs/domain1.key.pem' - ----- - - $ openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ - -subj "/C=US/ST=CA/O=MyExample, Inc./CN=domain2.example.com" \ - -keyout certs/domain2.key.pem \ - -out certs/domain2.crt.pem - Generating a RSA private key - .............+++++ - ...................+++++ - writing new private key to 'certs/domain2.key.pem' - ----- - -.. warning:: - - ``SNI`` does *not* validate that the certificates presented are correct for the domain, or that they - were issued by a recognised certificate authority. - - See the :ref:`Securing Envoy quick start guide ` for more information about - :ref:`validating cerfificates `. - -.. _install_sandboxes_tls_sni_step2: - -Step 2: Start the containers -**************************** - -Build and start the containers. - -This starts two upstream ``HTTP`` containers listening on the internal Docker network on port ``80``, and -an upstream ``HTTPS`` service listening on internal port ``443`` - -In front of these is an Envoy proxy that listens on https://localhost:10000 and serves three ``SNI`` routed -``TLS`` domains: - -- ``domain1.example.com`` -- ``domain2.example.com`` -- ``domain3.example.com`` - -The first two domains use the keys and certificates :ref:`you created in step 1 ` to terminate ``TLS`` and -proxy to the two upstream ``HTTP`` servers. - -The third domain proxies to the upstream ``TLS`` server based on the requested ``SNI`` address, but does no ``TLS`` termination itself. - -The composition also starts an Envoy proxy client which listens on http://localhost:20000. - -The client proxy has no ``TLS`` termination but instead proxies three routed paths - -``/domain1``, ``/domain2`` and ``/domain3`` - to the ``SNI``-enabled proxy. - -.. code-block:: console - - $ pwd - envoy/examples/tls-sni - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ------------------------------------------------------------------------------------------- - tls-sni_http-upstream1_1 node ./index.js Up - tls-sni_http-upstream2_1 node ./index.js Up - tls-sni_http-upstream3_1 node ./index.js Up - tls-sni_proxy_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp - tls-sni_proxy-client_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:20000->10000/tcp - -Step 2: Query the ``SNI`` endpoints directly with curl -****************************************************** - -You can use curl to query the ``SNI``-routed ``HTTPS`` endpoints of the Envoy proxy directly. - -To do this you must explicitly tell curl to resolve the ``DNS`` for the endpoints correctly. - -Each endpoint should proxy to the respective ``http-upstream`` or ``https-upstream`` service. - -.. code-block:: console - - $ curl -sk --resolve domain1.example.com:10000:127.0.0.1 \ - https://domain1.example.com:10000 \ - | jq -r '.os.hostname' - http-upstream1 - - $ curl -sk --resolve domain2.example.com:10000:127.0.0.1 \ - https://domain2.example.com:10000 \ - | jq -r '.os.hostname' - http-upstream2 - - $ curl -sk --resolve domain3.example.com:10000:127.0.0.1 \ - https://domain3.example.com:10000 \ - | jq -r '.os.hostname' - https-upstream3 - -Step 3: Query the ``SNI`` endpoints via an Envoy proxy client -************************************************************* - -Next, query the Envoy proxy client using the routed paths. - -These route via the ``SNI`` proxy endpoints to the respective ``http-upstream`` or -``https-upstream`` services. - -.. code-block:: console - - $ curl -s http://localhost:20000/domain1 \ - | jq '.os.hostname' - http-upstream1 - - $ curl -s http://localhost:20000/domain2 \ - | jq '.os.hostname' - http-upstream2 - - $ curl -s http://localhost:20000/domain3 \ - | jq '.os.hostname' - https-upstream3 - -.. seealso:: - - :ref:`Securing Envoy quick start guide ` - Outline of key concepts for securing Envoy. - - :ref:`TLS sandbox ` - Sandbox featuring examples of how Envoy can be configured to make - use of encrypted connections using ``HTTP`` over ``TLS``. - - :ref:`Double proxy sandbox ` - An example of securing traffic between proxies with validation and - mutual authentication using ``mTLS`` with non-``HTTP`` traffic. diff --git a/examples/tls-sni/verify.sh b/examples/tls-sni/verify.sh deleted file mode 100755 index f615835b5ea1..000000000000 --- a/examples/tls-sni/verify.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash -e - -export NAME=tls-sni -export MANUAL=true -export PORT_PROXY="${TLS_SNI_PORT_PROXY:-12020}" -export PORT_PROXY_CLIENT="${TLS_SNI_PORT_PROXY_CLIENT:-12021}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -# TODO(phlax): remove openssl bug workaround when openssl/ubuntu are updated -# see #15555 for more info -touch ~/.rnd - -create_self_signed_certs () { - local domain="$1" - openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ - -subj "/C=US/ST=CA/O=MyExample, Inc./CN=${domain}.example.com" \ - -keyout "certs/${domain}.key.pem" \ - -out "certs/${domain}.crt.pem" -} - -mkdir -p certs - -run_log "Create certificates for each of the services" -create_self_signed_certs domain1 -create_self_signed_certs domain2 - -bring_up_example - -run_log "Query domain1 with curl and tls/sni" -curl -sk --resolve "domain1.example.com:${PORT_PROXY}:127.0.0.1" \ - "https://domain1.example.com:${PORT_PROXY}" \ - | jq '.os.hostname' | grep http-upstream1 - -run_log "Query domain2 with curl and tls/sni" -curl -sk --resolve "domain2.example.com:${PORT_PROXY}:127.0.0.1" \ - "https://domain2.example.com:${PORT_PROXY}" \ - | jq '.os.hostname' | grep http-upstream2 - -run_log "Query domain3 with curl and tls/sni" -curl -sk --resolve "domain3.example.com:${PORT_PROXY}:127.0.0.1" \ - "https://domain3.example.com:${PORT_PROXY}" \ - | jq '.os.hostname' | grep https-upstream3 - -run_log "Query domain1 via Envoy sni client" -curl -s "http://localhost:${PORT_PROXY_CLIENT}/domain1" \ - | jq '.os.hostname' | grep http-upstream1 - -run_log "Query domain2 via Envoy sni client" -curl -s "http://localhost:${PORT_PROXY_CLIENT}/domain2" \ - | jq '.os.hostname' | grep http-upstream2 - -run_log "Query domain3 via Envoy sni client" -curl -s "http://localhost:${PORT_PROXY_CLIENT}/domain3" \ - | jq '.os.hostname' | grep https-upstream3 diff --git a/examples/tls/README.md b/examples/tls/README.md deleted file mode 100644 index 61d68e1757a7..000000000000 --- a/examples/tls/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/tls.html). diff --git a/examples/tls/docker-compose.yaml b/examples/tls/docker-compose.yaml deleted file mode 100644 index 3b58da76c8b4..000000000000 --- a/examples/tls/docker-compose.yaml +++ /dev/null @@ -1,53 +0,0 @@ -services: - - proxy-https-to-http: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: ./envoy-https-http.yaml - ports: - - "${PORT_PROXY0:-10000}:10000" - - proxy-https-to-https: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: ./envoy-https-https.yaml - ports: - - "${PORT_PROXY1:-10001}:10000" - - proxy-http-to-https: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: ./envoy-http-https.yaml - ports: - - "${PORT_PROXY2:-10002}:10000" - - proxy-https-passthrough: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: ./envoy-https-passthrough.yaml - ports: - - "${PORT_PROXY3:-10003}:10000" - - service-http: - build: - context: . - dockerfile: ../shared/echo2/Dockerfile - hostname: service-http - environment: - - HTTPS_PORT=0 - - service-https: - build: - context: . - dockerfile: ../shared/echo2/Dockerfile - hostname: service-https - environment: - - HTTP_PORT=0 diff --git a/examples/tls/envoy-http-https.yaml b/examples/tls/envoy-http-https.yaml deleted file mode 100644 index 66952f6075dc..000000000000 --- a/examples/tls/envoy-http-https.yaml +++ /dev/null @@ -1,46 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: app - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service-https - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - clusters: - - name: service-https - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service-https - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-https - port_value: 443 - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext diff --git a/examples/tls/envoy-https-http.yaml b/examples/tls/envoy-https-http.yaml deleted file mode 100644 index c6d1ba0bac76..000000000000 --- a/examples/tls/envoy-https-http.yaml +++ /dev/null @@ -1,106 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: app - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service-http - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext - common_tls_context: - tls_certificates: - # The following self-signed certificate pair is generated using: - # $ openssl req -x509 -newkey rsa:2048 -keyout a/front-proxy-key.pem -out a/front-proxy-crt.pem \ - # -days 3650 -nodes -subj '/CN=front-envoy' - # - # Instead of feeding it as an inline_string, certificate pair can also be fed to Envoy - # via filename. Reference: https://envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#config-core-v3-datasource. - # - # Or in a dynamic configuration scenario, certificate pair can be fetched remotely via - # Secret Discovery Service (SDS). Reference: https://envoyproxy.io/docs/envoy/latest/configuration/security/secret. - - certificate_chain: - inline_string: | - -----BEGIN CERTIFICATE----- - MIICqDCCAZACCQCquzpHNpqBcDANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtm - cm9udC1lbnZveTAeFw0yMDA3MDgwMTMxNDZaFw0zMDA3MDYwMTMxNDZaMBYxFDAS - BgNVBAMMC2Zyb250LWVudm95MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC - AQEAthnYkqVQBX+Wg7aQWyCCb87hBce1hAFhbRM8Y9dQTqxoMXZiA2n8G089hUou - oQpEdJgitXVS6YMFPFUUWfwcqxYAynLK4X5im26Yfa1eO8La8sZUS+4Bjao1gF5/ - VJxSEo2yZ7fFBo8M4E44ZehIIocipCRS+YZehFs6dmHoq/MGvh2eAHIa+O9xssPt - ofFcQMR8rwBHVbKy484O10tNCouX4yUkyQXqCRy6HRu7kSjOjNKSGtjfG+h5M8bh - 10W7ZrsJ1hWhzBulSaMZaUY3vh5ngpws1JATQVSK1Jm/dmMRciwlTK7KfzgxHlSX - 58ENpS7yPTISkEICcLbXkkKGEQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCmj6Hg - vwOxWz0xu+6fSfRL6PGJUGq6wghCfUvjfwZ7zppDUqU47fk+yqPIOzuGZMdAqi7N - v1DXkeO4A3hnMD22Rlqt25vfogAaZVToBeQxCPd/ALBLFrvLUFYuSlS3zXSBpQqQ - Ny2IKFYsMllz5RSROONHBjaJOn5OwqenJ91MPmTAG7ujXKN6INSBM0PjX9Jy4Xb9 - zT+I85jRDQHnTFce1WICBDCYidTIvJtdSSokGSuy4/xyxAAc/BpZAfOjBQ4G1QRe - 9XwOi790LyNUYFJVyeOvNJwveloWuPLHb9idmY5YABwikUY6QNcXwyHTbRCkPB2I - m+/R4XnmL4cKQ+5Z - -----END CERTIFICATE----- - private_key: - inline_string: | - -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2GdiSpVAFf5aD - tpBbIIJvzuEFx7WEAWFtEzxj11BOrGgxdmIDafwbTz2FSi6hCkR0mCK1dVLpgwU8 - VRRZ/ByrFgDKcsrhfmKbbph9rV47wtryxlRL7gGNqjWAXn9UnFISjbJnt8UGjwzg - Tjhl6EgihyKkJFL5hl6EWzp2Yeir8wa+HZ4Achr473Gyw+2h8VxAxHyvAEdVsrLj - zg7XS00Ki5fjJSTJBeoJHLodG7uRKM6M0pIa2N8b6HkzxuHXRbtmuwnWFaHMG6VJ - oxlpRje+HmeCnCzUkBNBVIrUmb92YxFyLCVMrsp/ODEeVJfnwQ2lLvI9MhKQQgJw - tteSQoYRAgMBAAECggEAeDGdEkYNCGQLe8pvg8Z0ccoSGpeTxpqGrNEKhjfi6NrB - NwyVav10iq4FxEmPd3nobzDPkAftfvWc6hKaCT7vyTkPspCMOsQJ39/ixOk+jqFx - lNa1YxyoZ9IV2DIHR1iaj2Z5gB367PZUoGTgstrbafbaNY9IOSyojCIO935ubbcx - DWwL24XAf51ez6sXnI8V5tXmrFlNXhbhJdH8iIxNyM45HrnlUlOk0lCK4gmLJjy9 - 10IS2H2Wh3M5zsTpihH1JvM56oAH1ahrhMXs/rVFXXkg50yD1KV+HQiEbglYKUxO - eMYtfaY9i2CuLwhDnWp3oxP3HfgQQhD09OEN3e0IlQKBgQDZ/3poG9TiMZSjfKqL - xnCABMXGVQsfFWNC8THoW6RRx5Rqi8q08yJrmhCu32YKvccsOljDQJQQJdQO1g09 - e/adJmCnTrqxNtjPkX9txV23Lp6Ak7emjiQ5ICu7iWxrcO3zf7hmKtj7z+av8sjO - mDI7NkX5vnlE74nztBEjp3eC0wKBgQDV2GeJV028RW3b/QyP3Gwmax2+cKLR9PKR - nJnmO5bxAT0nQ3xuJEAqMIss/Rfb/macWc2N/6CWJCRT6a2vgy6xBW+bqG6RdQMB - xEZXFZl+sSKhXPkc5Wjb4lQ14YWyRPrTjMlwez3k4UolIJhJmwl+D7OkMRrOUERO - EtUvc7odCwKBgBi+nhdZKWXveM7B5N3uzXBKmmRz3MpPdC/yDtcwJ8u8msUpTv4R - JxQNrd0bsIqBli0YBmFLYEMg+BwjAee7vXeDFq+HCTv6XMva2RsNryCO4yD3I359 - XfE6DJzB8ZOUgv4Dvluie3TB2Y6ZQV/p+LGt7G13yG4hvofyJYvlg3RPAoGAcjDg - +OH5zLN2eqah8qBN0CYa9/rFt0AJ19+7/smLTJ7QvQq4g0gwS1couplcCEnNGWiK - 72y1n/ckvvplmPeAE19HveMvR9UoCeV5ej86fACy8V/oVpnaaLBvL2aCMjPLjPP9 - DWeCIZp8MV86cvOrGfngf6kJG2qZTueXl4NAuwkCgYEArKkhlZVXjwBoVvtHYmN2 - o+F6cGMlRJTLhNc391WApsgDZfTZSdeJsBsvvzS/Nc0burrufJg0wYioTlpReSy4 - ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU - q5sGxGrC1RECGB5Zwx2S2ZY= - -----END PRIVATE KEY----- - - clusters: - - name: service-http - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service-http - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-http - port_value: 80 diff --git a/examples/tls/envoy-https-https.yaml b/examples/tls/envoy-https-https.yaml deleted file mode 100644 index 8501c1ead0f3..000000000000 --- a/examples/tls/envoy-https-https.yaml +++ /dev/null @@ -1,110 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: app - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service-https - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext - common_tls_context: - tls_certificates: - # The following self-signed certificate pair is generated using: - # $ openssl req -x509 -newkey rsa:2048 -keyout a/front-proxy-key.pem -out a/front-proxy-crt.pem \ - # -days 3650 -nodes -subj '/CN=front-envoy' - # - # Instead of feeding it as an inline_string, certificate pair can also be fed to Envoy - # via filename. Reference: https://envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#config-core-v3-datasource. - # - # Or in a dynamic configuration scenario, certificate pair can be fetched remotely via - # Secret Discovery Service (SDS). Reference: https://envoyproxy.io/docs/envoy/latest/configuration/security/secret. - - certificate_chain: - inline_string: | - -----BEGIN CERTIFICATE----- - MIICqDCCAZACCQCquzpHNpqBcDANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtm - cm9udC1lbnZveTAeFw0yMDA3MDgwMTMxNDZaFw0zMDA3MDYwMTMxNDZaMBYxFDAS - BgNVBAMMC2Zyb250LWVudm95MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC - AQEAthnYkqVQBX+Wg7aQWyCCb87hBce1hAFhbRM8Y9dQTqxoMXZiA2n8G089hUou - oQpEdJgitXVS6YMFPFUUWfwcqxYAynLK4X5im26Yfa1eO8La8sZUS+4Bjao1gF5/ - VJxSEo2yZ7fFBo8M4E44ZehIIocipCRS+YZehFs6dmHoq/MGvh2eAHIa+O9xssPt - ofFcQMR8rwBHVbKy484O10tNCouX4yUkyQXqCRy6HRu7kSjOjNKSGtjfG+h5M8bh - 10W7ZrsJ1hWhzBulSaMZaUY3vh5ngpws1JATQVSK1Jm/dmMRciwlTK7KfzgxHlSX - 58ENpS7yPTISkEICcLbXkkKGEQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCmj6Hg - vwOxWz0xu+6fSfRL6PGJUGq6wghCfUvjfwZ7zppDUqU47fk+yqPIOzuGZMdAqi7N - v1DXkeO4A3hnMD22Rlqt25vfogAaZVToBeQxCPd/ALBLFrvLUFYuSlS3zXSBpQqQ - Ny2IKFYsMllz5RSROONHBjaJOn5OwqenJ91MPmTAG7ujXKN6INSBM0PjX9Jy4Xb9 - zT+I85jRDQHnTFce1WICBDCYidTIvJtdSSokGSuy4/xyxAAc/BpZAfOjBQ4G1QRe - 9XwOi790LyNUYFJVyeOvNJwveloWuPLHb9idmY5YABwikUY6QNcXwyHTbRCkPB2I - m+/R4XnmL4cKQ+5Z - -----END CERTIFICATE----- - private_key: - inline_string: | - -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2GdiSpVAFf5aD - tpBbIIJvzuEFx7WEAWFtEzxj11BOrGgxdmIDafwbTz2FSi6hCkR0mCK1dVLpgwU8 - VRRZ/ByrFgDKcsrhfmKbbph9rV47wtryxlRL7gGNqjWAXn9UnFISjbJnt8UGjwzg - Tjhl6EgihyKkJFL5hl6EWzp2Yeir8wa+HZ4Achr473Gyw+2h8VxAxHyvAEdVsrLj - zg7XS00Ki5fjJSTJBeoJHLodG7uRKM6M0pIa2N8b6HkzxuHXRbtmuwnWFaHMG6VJ - oxlpRje+HmeCnCzUkBNBVIrUmb92YxFyLCVMrsp/ODEeVJfnwQ2lLvI9MhKQQgJw - tteSQoYRAgMBAAECggEAeDGdEkYNCGQLe8pvg8Z0ccoSGpeTxpqGrNEKhjfi6NrB - NwyVav10iq4FxEmPd3nobzDPkAftfvWc6hKaCT7vyTkPspCMOsQJ39/ixOk+jqFx - lNa1YxyoZ9IV2DIHR1iaj2Z5gB367PZUoGTgstrbafbaNY9IOSyojCIO935ubbcx - DWwL24XAf51ez6sXnI8V5tXmrFlNXhbhJdH8iIxNyM45HrnlUlOk0lCK4gmLJjy9 - 10IS2H2Wh3M5zsTpihH1JvM56oAH1ahrhMXs/rVFXXkg50yD1KV+HQiEbglYKUxO - eMYtfaY9i2CuLwhDnWp3oxP3HfgQQhD09OEN3e0IlQKBgQDZ/3poG9TiMZSjfKqL - xnCABMXGVQsfFWNC8THoW6RRx5Rqi8q08yJrmhCu32YKvccsOljDQJQQJdQO1g09 - e/adJmCnTrqxNtjPkX9txV23Lp6Ak7emjiQ5ICu7iWxrcO3zf7hmKtj7z+av8sjO - mDI7NkX5vnlE74nztBEjp3eC0wKBgQDV2GeJV028RW3b/QyP3Gwmax2+cKLR9PKR - nJnmO5bxAT0nQ3xuJEAqMIss/Rfb/macWc2N/6CWJCRT6a2vgy6xBW+bqG6RdQMB - xEZXFZl+sSKhXPkc5Wjb4lQ14YWyRPrTjMlwez3k4UolIJhJmwl+D7OkMRrOUERO - EtUvc7odCwKBgBi+nhdZKWXveM7B5N3uzXBKmmRz3MpPdC/yDtcwJ8u8msUpTv4R - JxQNrd0bsIqBli0YBmFLYEMg+BwjAee7vXeDFq+HCTv6XMva2RsNryCO4yD3I359 - XfE6DJzB8ZOUgv4Dvluie3TB2Y6ZQV/p+LGt7G13yG4hvofyJYvlg3RPAoGAcjDg - +OH5zLN2eqah8qBN0CYa9/rFt0AJ19+7/smLTJ7QvQq4g0gwS1couplcCEnNGWiK - 72y1n/ckvvplmPeAE19HveMvR9UoCeV5ej86fACy8V/oVpnaaLBvL2aCMjPLjPP9 - DWeCIZp8MV86cvOrGfngf6kJG2qZTueXl4NAuwkCgYEArKkhlZVXjwBoVvtHYmN2 - o+F6cGMlRJTLhNc391WApsgDZfTZSdeJsBsvvzS/Nc0burrufJg0wYioTlpReSy4 - ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU - q5sGxGrC1RECGB5Zwx2S2ZY= - -----END PRIVATE KEY----- - - clusters: - - name: service-https - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service-https - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-https - port_value: 443 - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext diff --git a/examples/tls/envoy-https-passthrough.yaml b/examples/tls/envoy-https-passthrough.yaml deleted file mode 100644 index 8707204a5c41..000000000000 --- a/examples/tls/envoy-https-passthrough.yaml +++ /dev/null @@ -1,27 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.tcp_proxy - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: service-https - stat_prefix: https_passthrough - - clusters: - - name: service-https - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service-https - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-https - port_value: 443 diff --git a/examples/tls/example.rst b/examples/tls/example.rst deleted file mode 100644 index 39baf0f86735..000000000000 --- a/examples/tls/example.rst +++ /dev/null @@ -1,179 +0,0 @@ -.. _install_sandboxes_tls: - -Transport layer security (``TLS``) -================================== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - - :ref:`jq ` - Parse ``json`` output from the upstream echo servers. - -This example walks through some of the ways that Envoy can be configured to make -use of encrypted connections using ``HTTP`` over ``TLS``. - -It demonstrates a number of commonly used proxying and ``TLS`` termination patterns: - -- ``https`` -> ``http`` -- ``https`` -> ``https`` -- ``http`` -> ``https`` -- ``https`` passthrough - -To better understand the provided examples, and for a description of how ``TLS`` is -configured with Envoy, please see the :ref:`securing Envoy quick start guide `. - -.. warning:: - - For the sake of simplicity, the examples provided here do not authenticate any client certificates, - or validate any of the provided certificates. - - When using ``TLS``, you are strongly encouraged to :ref:`validate ` - all certificates wherever possible. - - You should also :ref:`authenticate clients ` - where you control both sides of the connection, or relevant protocols are available. - -Step 1: Build the sandbox -************************* - -Change directory to ``examples/tls`` in the Envoy repository. - -This starts four proxies listening on ``localhost`` ports ``10000-10003``. - -It also starts two upstream services, one ``HTTP`` and one ``HTTPS``, which echo back received headers -in ``json`` format. - -The upstream services listen on the internal Docker network on ports ``80`` and ``443`` respectively. - -.. code-block:: console - - $ pwd - envoy/examples/tls - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ----------------------------------------------------------------------------------------------- - tls_proxy-https-to-http_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp - tls_proxy-https-to-https_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10001->10000/tcp - tls_proxy-http-to-https_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10002->10000/tcp - tls_proxy-https-passthrough_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10003->10000/tcp - tls_service-http_1 node ./index.js Up - tls_service-https_1 node ./index.js Up - -Step 2: Test proxying ``https`` -> ``http`` -******************************************* - -The Envoy proxy listening on https://localhost:10000 terminates ``HTTPS`` and proxies to the upstream ``HTTP`` service. - -The :download:`https -> http configuration <_include/tls/envoy-https-http.yaml>` adds a ``TLS`` -:ref:`transport_socket ` to the -:ref:`listener `. - -Querying the service at port ``10000`` you should see an ``x-forwarded-proto`` header of ``https`` has -been added: - -.. code-block:: console - - $ curl -sk https://localhost:10000 | jq -r '.headers["x-forwarded-proto"]' - https - -The upstream ``service-http`` handles the request. - -.. code-block:: console - - $ curl -sk https://localhost:10000 | jq -r '.os.hostname' - service-http - -Step 3: Test proxying ``https`` -> ``https`` -******************************************** - -The Envoy proxy listening on https://localhost:10001 terminates ``HTTPS`` and proxies to the upstream ``HTTPS`` service. - -The :download:`https -> https configuration <_include/tls/envoy-https-https.yaml>` adds a ``TLS`` -:ref:`transport_socket ` to both the -:ref:`listener ` and the -:ref:`cluster `. - -Querying the service at port ``10001`` you should see an ``x-forwarded-proto`` header of ``https`` has -been added: - -.. code-block:: console - - $ curl -sk https://localhost:10001 | jq -r '.headers["x-forwarded-proto"]' - https - -The upstream ``service-https`` handles the request. - -.. code-block:: console - - $ curl -sk https://localhost:10001 | jq -r '.os.hostname' - service-https - -Step 4: Test proxying ``http`` -> ``https`` -******************************************* - -The Envoy proxy listening on http://localhost:10002 terminates ``HTTP`` and proxies to the upstream ``HTTPS`` service. - -The :download:`http -> https configuration <_include/tls/envoy-http-https.yaml>` adds a ``TLS`` -:ref:`transport_socket ` to the -:ref:`cluster `. - -Querying the service at port ``10002`` you should see an ``x-forwarded-proto`` header of ``http`` has -been added: - -.. code-block:: console - - $ curl -s http://localhost:10002 | jq -r '.headers["x-forwarded-proto"]' - http - -The upstream ``service-https`` handles the request. - -.. code-block:: console - - $ curl -s http://localhost:10002 | jq -r '.os.hostname' - service-https - - -Step 5: Test proxying ``https`` passthrough -******************************************* - -The Envoy proxy listening on https://localhost:10003 proxies directly to the upstream ``HTTPS`` service which -does the ``TLS`` termination. - -The :download:`https passthrough configuration <_include/tls/envoy-https-passthrough.yaml>` requires no ``TLS`` -or ``HTTP`` setup, and instead uses a simple -:ref:`tcp_proxy `. - -Querying the service at port ``10003`` you should see that no ``x-forwarded-proto`` header has been -added: - -.. code-block:: console - - $ curl -sk https://localhost:10003 | jq -r '.headers["x-forwarded-proto"]' - null - -The upstream ``service-https`` handles the request. - -.. code-block:: console - - $ curl -sk https://localhost:10003 | jq -r '.os.hostname' - service-https - -.. seealso:: - - :ref:`Securing Envoy quick start guide ` - Outline of key concepts for securing Envoy. - - :ref:`TLS SNI sandbox ` - Example of using Envoy to serve multiple domains protected by TLS and - served from the same ``IP`` address. - - :ref:`Double proxy sandbox ` - An example of securing traffic between proxies with validation and - mutual authentication using ``mTLS`` with non-``HTTP`` traffic. diff --git a/examples/tls/verify.sh b/examples/tls/verify.sh deleted file mode 100755 index 46c4af01e2d4..000000000000 --- a/examples/tls/verify.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash -e - -export NAME=tls -export PORT_PROXY0="${TLS_PORT_PROXY0:-12000}" -export PORT_PROXY1="${TLS_PORT_PROXY1:-12001}" -export PORT_PROXY2="${TLS_PORT_PROXY2:-12002}" -export PORT_PROXY3="${TLS_PORT_PROXY3:-12003}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Test https -> http" -responds_with \ - '"x-forwarded-proto": "https",' \ - -k \ - "https://localhost:${PORT_PROXY0}" -curl -sk "https://localhost:${PORT_PROXY0}" | jq '.os.hostname' | grep '"service-http"' - -run_log "Test https -> https" -responds_with \ - '"x-forwarded-proto": "https",' \ - -k \ - "https://localhost:${PORT_PROXY1}" -curl -sk "https://localhost:${PORT_PROXY1}" | jq '.os.hostname' | grep '"service-https"' - -run_log "Test http -> https" -responds_with \ - '"x-forwarded-proto": "http",' \ - "http://localhost:${PORT_PROXY2}" -curl -s "http://localhost:${PORT_PROXY2}" | jq '.os.hostname' | grep '"service-https"' - -run_log "Test https passthrough" -responds_without \ - '"x-forwarded-proto"' \ - -k \ - "https://localhost:${PORT_PROXY3}" -curl -sk "https://localhost:${PORT_PROXY3}" | jq '.os.hostname' | grep '"service-https"' diff --git a/examples/udp/Dockerfile-udp b/examples/udp/Dockerfile-udp deleted file mode 100644 index 76a45d7ac5ad..000000000000 --- a/examples/udp/Dockerfile-udp +++ /dev/null @@ -1 +0,0 @@ -FROM mendhak/udp-listener@sha256:ecc2961447560372fd6660c6db4bcf7e70d61f37f1421b1f8c4c7647da7c0aca diff --git a/examples/udp/README.md b/examples/udp/README.md deleted file mode 100644 index 15afa0a3ff05..000000000000 --- a/examples/udp/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/udp.html). diff --git a/examples/udp/docker-compose.yaml b/examples/udp/docker-compose.yaml deleted file mode 100644 index 5189bd79f51f..000000000000 --- a/examples/udp/docker-compose.yaml +++ /dev/null @@ -1,14 +0,0 @@ -services: - - testing: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - ports: - - "${PORT_PROXY:-10000}:10000/udp" - - "${PORT_ADMIN:-10001}:10001" - - service-udp: - build: - context: . - dockerfile: Dockerfile-udp diff --git a/examples/udp/envoy.yaml b/examples/udp/envoy.yaml deleted file mode 100644 index d6e402808953..000000000000 --- a/examples/udp/envoy.yaml +++ /dev/null @@ -1,40 +0,0 @@ -static_resources: - listeners: - - name: listener_0 - address: - socket_address: - protocol: UDP - address: 0.0.0.0 - port_value: 10000 - listener_filters: - - name: envoy.filters.udp_listener.udp_proxy - typed_config: - '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.UdpProxyConfig - stat_prefix: service - matcher: - on_no_match: - action: - name: route - typed_config: - '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.Route - cluster: service_udp - - clusters: - - name: service_udp - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service_udp - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-udp - port_value: 5005 - -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 10001 diff --git a/examples/udp/example.rst b/examples/udp/example.rst deleted file mode 100644 index 6248af717583..000000000000 --- a/examples/udp/example.rst +++ /dev/null @@ -1,91 +0,0 @@ -.. _install_sandboxes_udp: - -User Datagram Protocol (``UDP``) -================================ - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - - :ref:`netcat ` - Used to send ``UDP`` packets. - -This sandbox provides a very simple example of Envoy proxying ``UDP``. - -It also demonstrates ``UDP`` traffic stats provided by the Envoy admin endpoint. - -Step 1: Build the sandbox -************************* - -Change directory to ``examples/udp`` in the Envoy repository. - -Start the Docker composition: - -.. code-block:: console - - $ pwd - envoy/examples/udp - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ----------------------------------------------------------------------------------------------------------------------- - udp_envoy-udp_1 /docker-entrypoint.sh /usr ... Up 10000/tcp, 0.0.0.0:10000->10000/udp, 0.0.0.0:10001->10001/tcp - udp_service-udp_1 python -u /udplistener.py Up 5005/tcp, 5005/udp - -Envoy should proxy ``UDP`` on port ``10000`` to an upstream server listening on port ``5005``. - -Envoy also provides an admin endpoint listening on port ``10001``. - -Step 2: Send some ``UDP`` messages -********************************** - -You can use ``netcat`` to send packets to the upstream server, proxied by Envoy: - -.. code-block:: console - - echo -n HELO | nc -4u -w1 127.0.0.1 10000 - echo -n OLEH | nc -4u -w1 127.0.0.1 10000 - -Step 3: Check the logs of the upstream ``UDP`` listener server -************************************************************** - -Checking the logs of the upstream server you should see the packets that you sent: - -.. code-block:: console - - $ docker compose logs service-udp - Attaching to udp_service-udp_1 - service-udp_1 | Listening on UDP port 5005 - service-udp_1 | HELO - service-udp_1 | OLEH - -Step 4: View the Envoy admin ``UDP`` stats -****************************************** - -You can view the ``UDP``-related stats provided by the Envoy admin endpoint. - -For example, to view the non-zero stats: - -.. code-block:: console - - $ curl -s http://127.0.0.1:10001/stats | grep udp | grep -v "\: 0" - cluster.service_udp.default.total_match_count: 1 - cluster.service_udp.max_host_weight: 1 - cluster.service_udp.membership_change: 1 - cluster.service_udp.membership_healthy: 1 - cluster.service_udp.membership_total: 1 - cluster.service_udp.udp.sess_tx_datagrams: 2 - cluster.service_udp.update_attempt: 1 - cluster.service_udp.update_success: 1 - cluster.service_udp.upstream_cx_tx_bytes_total: 8 - udp.service.downstream_sess_active: 2 - udp.service.downstream_sess_rx_bytes: 8 - udp.service.downstream_sess_rx_datagrams: 2 - udp.service.downstream_sess_total: 2 - cluster.service_udp.upstream_cx_connect_ms: No recorded values - cluster.service_udp.upstream_cx_length_ms: No recorded values diff --git a/examples/udp/verify.sh b/examples/udp/verify.sh deleted file mode 100755 index 7ebdad6dd6c7..000000000000 --- a/examples/udp/verify.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -e - -export NAME=udp -export PORT_PROXY="${UDP_PORT_PROXY:-12100}" -export PORT_ADMIN="${UDP_PORT_ADMIN:-12101}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Send some UDP packets" -echo -n HELO | nc -4u -w1 127.0.0.1 "${PORT_PROXY}" -echo -n OLEH | nc -4u -w1 127.0.0.1 "${PORT_PROXY}" - -run_log "Check backend log" -"${DOCKER_COMPOSE[@]}" logs service-udp | grep HELO -"${DOCKER_COMPOSE[@]}" logs service-udp | grep OLEH - -run_log "Check admin stats" -curl -s "http://127.0.0.1:${PORT_ADMIN}/stats" | grep udp | grep -v "\: 0" diff --git a/examples/verify-common.sh b/examples/verify-common.sh deleted file mode 100644 index c6c0692b944c..000000000000 --- a/examples/verify-common.sh +++ /dev/null @@ -1,252 +0,0 @@ -#!/bin/bash -e - -DELAY="${DELAY:-0}" -DOCKER_NO_PULL="${DOCKER_NO_PULL:-}" -MANUAL="${MANUAL:-}" -NAME="${NAME:-}" -PATHS="${PATHS:-.}" -UPARGS="${UPARGS:-}" -ENVOY_EXAMPLES_DEBUG="${ENVOY_EXAMPLES_DEBUG:-}" - - -if [[ -n "$DOCKER_COMPOSE" ]]; then - read -ra DOCKER_COMPOSE <<< "$DOCKER_COMPOSE" -else - DOCKER_COMPOSE=(docker compose) -fi - -run_log () { - echo -e "\n> [${NAME}] ${*}" -} - -bring_up_example_stack () { - local args path up_args - args=("${UPARGS[@]}") - path="$1" - read -ra up_args <<< "up --quiet-pull --pull missing --build --wait -d ${args[*]}" - - if [[ -z "$DOCKER_NO_PULL" ]]; then - run_log "Pull the images ($path)" - "${DOCKER_COMPOSE[@]}" pull -q - echo - fi - run_log "Bring up services ($path)" - "${DOCKER_COMPOSE[@]}" "${up_args[@]}" || return 1 - - if [[ -n "$ENVOY_EXAMPLES_DEBUG" ]]; then - echo "----------------------------------------------" - docker system df -v - echo - sudo du -ch / | grep "[0-9]G" - echo - df -h - echo - echo "----------------------------------------------" - fi - echo -} - -bring_up_example () { - local path paths - read -ra paths <<< "$(echo "$PATHS" | tr ',' ' ')" - - for path in "${paths[@]}"; do - pushd "$path" > /dev/null || return 1 - bring_up_example_stack "$path" || { - echo "ERROR: starting ${NAME} ${path}" >&2 - return 1 - } - popd > /dev/null || return 1 - done - if [[ "$DELAY" -ne "0" ]]; then - run_log "Snooze for ${DELAY} while ${NAME} gets started" - sleep "$DELAY" - fi - for path in "${paths[@]}"; do - pushd "$path" > /dev/null || return 1 - "${DOCKER_COMPOSE[@]}" ps - "${DOCKER_COMPOSE[@]}" logs - popd > /dev/null || return 1 - done -} - -bring_down_example () { - local path paths - read -ra paths <<< "$(echo "$PATHS" | tr ',' ' ')" - for path in "${paths[@]}"; do - pushd "$path" > /dev/null || return 1 - cleanup_stack "$path" || { - echo "ERROR: cleanup ${NAME} ${path}" >&2 - } - popd > /dev/null - done -} - -cleanup_stack () { - local path down_args - path="$1" - down_args=(--remove-orphans) - - if [[ -n "$DOCKER_RMI_CLEANUP" ]]; then - down_args+=(--rmi all) - fi - - # Remove sandbox volumes by default - if [[ -z "$DOCKER_SAVE_VOLUMES" ]]; then - down_args+=(--volumes) - fi - - run_log "Cleanup ($path)" - "${DOCKER_COMPOSE[@]}" down "${down_args[@]}" -} - -debug_failure () { - >&2 echo "FAILURE DEBUG" - >&2 echo "DISK SPACE" - df -h - >&2 echo "DOCKER COMPOSE LOGS" - "${DOCKER_COMPOSE[@]}" logs - >&2 echo "DOCKER COMPOSE PS" - "${DOCKER_COMPOSE[@]}" ps -} - -cleanup () { - local code="$?" - - if [[ "$code" -ne 0 ]]; then - debug_failure - fi - - bring_down_example - - if type -t finally &> /dev/null; then - finally - fi - - if [[ "$code" -ne 0 ]]; then - run_log Failed - else - run_log Success - fi - echo -} - -_curl () { - local arg curl_command - curl_command=(curl -s) - if [[ ! "$*" =~ "-X" ]]; then - curl_command+=(-X GET) - fi - for arg in "${@}"; do - curl_command+=("$arg") - done - "${curl_command[@]}" || { - echo "ERROR: curl (${curl_command[*]})" >&2 - return 1 - } -} - -move_if_exists () { - if [ -e "$1" ]; then - mv "$1" "$2" - else - echo "Warning: $1 does not exist. Skipping move operation." - fi -} - -responds_with () { - local expected response - expected="$1" - shift - response=$(_curl "${@}") - grep -Fs "$expected" <<< "$response" || { - echo "ERROR: curl (${*})" >&2 - echo "EXPECTED:" >&2 - echo "$expected" >&2 - echo "RECEIVED:" >&2 - echo "$response" >&2 - return 1 - } -} - -responds_without () { - local expected response - expected="$1" - shift - response=$(_curl "${@}") - # shellcheck disable=2266 - grep -s "$expected" <<< "$response" | [[ "$(wc -l)" -eq 0 ]] || { - echo "ERROR: curl (${*})" >&2 - echo "DID NOT EXPECT: $expected" >&2 - echo "RECEIVED:" >&2 - echo "$response" >&2 - return 1 - } -} - -responds_with_header () { - local expected response - expected="$1" - shift - response=$(_curl --head "${@}") - grep -s "$expected" <<< "$response" || { - echo "ERROR: curl (${*})" >&2 - echo "EXPECTED HEADER:" >&2 - echo "$expected" >&2 - echo "RECEIVED:" >&2 - echo "$response" >&2 - return 1 - } -} - -responds_without_header () { - local expected response - expected="$1" - shift - response=$(_curl --head "${@}") - # shellcheck disable=2266 - grep -s "$expected" <<< "$response" | [[ "$(wc -l)" -eq 0 ]] || { - echo "ERROR: curl (${*})" >&2 - echo "DID NOT EXPECT HEADER: $expected" >&2 - echo "RECEIVED:" >&2 - echo "$response" >&2 - return 1 - } -} - -wait_for () { - local i=1 returns=1 seconds="$1" - shift - while ((i<=seconds)); do - if "${@}" &> /dev/null; then - returns=0 - break - else - sleep 1 - ((i++)) - fi - done - if [[ "$returns" != 0 ]]; then - echo "Wait (${seconds}) failed: ${*}" >&2 - fi - return "$returns" -} - -trap 'cleanup' EXIT - -if [[ -z "$NAME" ]]; then - echo "ERROR: You must set the '$NAME' variable before sourcing this script" >&2 - exit 1 -fi - -if [[ -z "$MANUAL" ]]; then - bring_up_example -fi - - -# These allow the functions to be used in subshells, e.g. in `wait_for` -export -f responds_with -export -f responds_without -export -f responds_with_header -export -f responds_without_header -export -f _curl diff --git a/examples/vrp-litmus/Dockerfile-vrp b/examples/vrp-litmus/Dockerfile-vrp deleted file mode 100644 index f0e6704d6d21..000000000000 --- a/examples/vrp-litmus/Dockerfile-vrp +++ /dev/null @@ -1 +0,0 @@ -FROM envoyproxy/envoy-google-vrp-dev:latest diff --git a/examples/vrp-litmus/README.md b/examples/vrp-litmus/README.md deleted file mode 100644 index 58c14b3fd8cb..000000000000 --- a/examples/vrp-litmus/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Simple litmus test to verify the VRP image in CI. For more details on VRP, -please see -https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/google_vrp. diff --git a/examples/vrp-litmus/docker-compose.yaml b/examples/vrp-litmus/docker-compose.yaml deleted file mode 100644 index e74efae098ee..000000000000 --- a/examples/vrp-litmus/docker-compose.yaml +++ /dev/null @@ -1,11 +0,0 @@ -services: - - vrp: - build: - context: . - dockerfile: Dockerfile-vrp - environment: - ENVOY_EDGE_EXTRA_ARGS: "" - ENVOY_ORIGIN_EXTRA_ARGS: "" - ports: - - "10000:10000" diff --git a/examples/vrp-litmus/verify.sh b/examples/vrp-litmus/verify.sh deleted file mode 100755 index 02791785c628..000000000000 --- a/examples/vrp-litmus/verify.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -e - -export NAME=vrp-litmus -export DELAY=10 - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - - -run_log "Test proxy" -responds_with \ - normal \ - https://localhost:10000/content \ - -k diff --git a/examples/vrp-local/Dockerfile-vrp b/examples/vrp-local/Dockerfile-vrp deleted file mode 100644 index 71f9b502e903..000000000000 --- a/examples/vrp-local/Dockerfile-vrp +++ /dev/null @@ -1 +0,0 @@ -FROM envoy-google-vrp:local diff --git a/examples/vrp-local/README.md b/examples/vrp-local/README.md deleted file mode 100644 index 55d3dca8a801..000000000000 --- a/examples/vrp-local/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Simple test to verify local Envoy binary injection into the VRP image. For more -details on VRP, please see -https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/google_vrp. diff --git a/examples/vrp-local/docker-compose.yaml b/examples/vrp-local/docker-compose.yaml deleted file mode 100644 index e74efae098ee..000000000000 --- a/examples/vrp-local/docker-compose.yaml +++ /dev/null @@ -1,11 +0,0 @@ -services: - - vrp: - build: - context: . - dockerfile: Dockerfile-vrp - environment: - ENVOY_EDGE_EXTRA_ARGS: "" - ENVOY_ORIGIN_EXTRA_ARGS: "" - ports: - - "10000:10000" diff --git a/examples/vrp-local/verify.sh b/examples/vrp-local/verify.sh deleted file mode 100755 index 201bb2837e7c..000000000000 --- a/examples/vrp-local/verify.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash -e - -export NAME=vrp-local -export MANUAL=true - -# This sandbox is not really a sandbox (atm) and is only here for testing purposes. -# It is also not really structured in the same way and tends to be the hardest to fix -# when things change. - -# It tests 3 things -# - run with default (prebuilt) container -# - build container with custom Envoy (mocked) -# - rebuild and run container with default (mocked) binary - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - - -tag_default_vrp () { - # Image should already be present in CI - if [[ -z "$DOCKER_NO_PULL" ]]; then - docker pull -q envoyproxy/envoy:google-vrp-dev - fi - docker tag envoyproxy/envoy:google-vrp-dev envoy-google-vrp:local -} - -rebuild_vrp () { - pushd "$(dirname "${BASH_SOURCE[0]}")/../.." > /dev/null - # Follow instructions from - # https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/google_vrp#rebuilding-the-docker-image, - # but here we just use a mock Envoy and mock tools. - mkdir -p linux/arm64 linux/amd64 mockbin/utils - - echo "echo 'VRP PROXY MOCK 2'" > mockbin/envoy - chmod +x mockbin/envoy - touch mockbin/utils/su-exec - - tar czf release.tar.zst -C mockbin . - cp release.tar.zst linux/amd64 - mv release.tar.zst linux/arm64 - touch linux/amd64/schema_validator_tool - touch linux/arm64/schema_validator_tool - - ./ci/docker_rebuild_google-vrp.sh "$@" - popd > /dev/null -} - -test_vrp () { - wait_for 10 bash -c "responds_with 'normal' https://localhost:10000/content -k" - run_log "Test proxy" - responds_with \ - normal \ - https://localhost:10000/content \ - -k -} - -# Running this does not currently work with buildkit (even if that is how it is built). -export DOCKER_BUILDKIT=0 - -# Test running the default build -tag_default_vrp -bring_up_example -test_vrp -bring_down_example - -# Test a build with custom binary -echo "echo 'VRP PROXY MOCK'" > /tmp/envoy -chmod +x /tmp/envoy -rebuild_vrp /tmp/envoy -docker run --rm --entrypoint=/bin/sh envoy-google-vrp:local -c "/usr/local/bin/envoy | grep MOCK" - -# Test rebuilding and running the default build (with some mock tools) -rebuild_vrp -bring_up_example -docker run --rm --entrypoint=/bin/sh envoy-google-vrp:local -c "/usr/local/bin/envoy | grep 'MOCK 2'" diff --git a/examples/wasm-cc/BUILD b/examples/wasm-cc/BUILD deleted file mode 100644 index 2ea0b7d13906..000000000000 --- a/examples/wasm-cc/BUILD +++ /dev/null @@ -1,52 +0,0 @@ -load("@bazel_skylib//lib:selects.bzl", "selects") -load( - "//bazel:envoy_build_system.bzl", - "envoy_package", -) -load("//bazel/wasm:wasm.bzl", "envoy_wasm_cc_binary") - -licenses(["notice"]) # Apache 2 - -envoy_package() - -exports_files(["example.rst"]) - -selects.config_setting_group( - name = "include_wasm_config", - match_all = [ - "//bazel:x86", - "//bazel:wasm_v8", - ], -) - -filegroup( - name = "configs", - srcs = glob([ - "**/*.wasm", - ]) + select({ - ":include_wasm_config": glob( - [ - "**/*.yaml", - ], - exclude = [ - "**/*docker-compose*.yaml", - ], - ), - "//conditions:default": [], - }), -) - -envoy_wasm_cc_binary( - name = "envoy_filter_http_wasm_example.wasm", - srcs = ["envoy_filter_http_wasm_example.cc"], -) - -envoy_wasm_cc_binary( - name = "envoy_filter_http_wasm_updated_example.wasm", - srcs = ["envoy_filter_http_wasm_updated_example.cc"], -) - -filegroup( - name = "files", - srcs = glob(["**/*"], exclude = ["example.rst"]), -) diff --git a/examples/wasm-cc/Dockerfile-proxy b/examples/wasm-cc/Dockerfile-proxy deleted file mode 100644 index 8c578ff715dc..000000000000 --- a/examples/wasm-cc/Dockerfile-proxy +++ /dev/null @@ -1,5 +0,0 @@ -FROM envoyproxy/envoy:dev -COPY ./envoy.yaml /etc/envoy.yaml -COPY ./lib/envoy_filter_http_wasm_example.wasm /lib/envoy_filter_http_wasm_example.wasm -RUN chmod go+r /etc/envoy.yaml /lib/envoy_filter_http_wasm_example.wasm -CMD ["/usr/local/bin/envoy", "-c", "/etc/envoy.yaml", "--service-cluster", "proxy"] diff --git a/examples/wasm-cc/README.md b/examples/wasm-cc/README.md deleted file mode 100644 index f5e04fd7503a..000000000000 --- a/examples/wasm-cc/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/wasm-cc). diff --git a/examples/wasm-cc/docker-compose-wasm.yaml b/examples/wasm-cc/docker-compose-wasm.yaml deleted file mode 100644 index e9527a2f4b36..000000000000 --- a/examples/wasm-cc/docker-compose-wasm.yaml +++ /dev/null @@ -1,38 +0,0 @@ -services: - wasm_compile_update: - build: - context: ../shared/build - command: > - bash -c " - bazel build --experimental_repository_downloader_retries=2 --disk_cache=/tmp/disk_cache - --repository_cache=/tmp/repository_cache --experimental_repository_cache_hardlinks - //examples/wasm-cc:envoy_filter_http_wasm_updated_example.wasm - && cp -a bazel-bin/examples/wasm-cc/*updated*.wasm /output" - entrypoint: /source/examples/shared/build/build-entrypoint.sh - environment: - - BUILD_UID=${UID:-1000} - - TEST_TMPDIR=/tmp - working_dir: /source - volumes: - - ${ENVOY_DOCKER_BUILD_DIR:-/tmp/envoy-docker-build}:/tmp - - ../..:/source - - ./lib:/output - - wasm_compile: - build: - context: ../shared/build - command: > - bash -c " - bazel build --experimental_repository_downloader_retries=2 --disk_cache=/tmp/disk_cache - --repository_cache=/tmp/repository_cache --experimental_repository_cache_hardlinks - //examples/wasm-cc:envoy_filter_http_wasm_example.wasm - && cp -a bazel-bin/examples/wasm-cc/* /output" - entrypoint: /source/examples/shared/build/build-entrypoint.sh - environment: - - BUILD_UID=${UID:-1000} - - TEST_TMPDIR=/tmp - working_dir: /source - volumes: - - ${ENVOY_DOCKER_BUILD_DIR:-/tmp/envoy-docker-build}:/tmp - - ../..:/source - - ./lib:/output diff --git a/examples/wasm-cc/docker-compose.yaml b/examples/wasm-cc/docker-compose.yaml deleted file mode 100644 index f9edb0aec44d..000000000000 --- a/examples/wasm-cc/docker-compose.yaml +++ /dev/null @@ -1,17 +0,0 @@ -services: - - proxy: - build: - context: . - dockerfile: Dockerfile-proxy - depends_on: - - web_service - ports: - - "8000:8000" - - "8001:8001" - - web_service: - environment: - - PORT=9000 - build: - context: ../shared/echo diff --git a/examples/wasm-cc/envoy.yaml b/examples/wasm-cc/envoy.yaml deleted file mode 100644 index f580d2f88f55..000000000000 --- a/examples/wasm-cc/envoy.yaml +++ /dev/null @@ -1,128 +0,0 @@ -static_resources: - listeners: - - name: listener_0 - address: - socket_address: - address: 0.0.0.0 - port_value: 8000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: auto - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: local_service - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: web_service - - http_filters: - - name: envoy.filters.http.wasm - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - config: - name: "my_plugin" - root_id: "my_root_id" - # if your wasm filter requires custom configuration you can add - # as follows - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {} - vm_config: - vm_id: "my_vm_id" - code: - local: - filename: "lib/envoy_filter_http_wasm_example.wasm" - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - name: listener_1 - address: - socket_address: - address: 0.0.0.0 - port_value: 8001 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: auto - stat_prefix: ingress_http2 - route_config: - name: local_route2 - virtual_hosts: - - name: local_service2 - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: web_service_with_wasm_filter - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: web_service - type: strict_dns - lb_policy: round_robin - load_assignment: - cluster_name: service1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: web_service - port_value: 9000 - - name: web_service_with_wasm_filter - type: strict_dns - lb_policy: round_robin - load_assignment: - cluster_name: service1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: web_service - port_value: 9000 - typed_extension_protocol_options: - envoy.extensions.upstreams.http.v3.HttpProtocolOptions: - "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions - upstream_http_protocol_options: - auto_sni: false - auto_san_validation: false - explicit_http_config: - http2_protocol_options: {} - http_filters: - - name: envoy.filters.http.wasm - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm - config: - name: "my_plugin" - root_id: "my_root_id" - # if your wasm filter requires custom configuration you can add - # as follows - configuration: - "@type": "type.googleapis.com/google.protobuf.StringValue" - value: | - {} - vm_config: - vm_id: "my_vm_id" - code: - local: - filename: "lib/envoy_filter_http_wasm_example.wasm" - - name: envoy.filters.http.upstream_codec - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.upstream_codec.v3.UpstreamCodec diff --git a/examples/wasm-cc/envoy_filter_http_wasm_example.cc b/examples/wasm-cc/envoy_filter_http_wasm_example.cc deleted file mode 100644 index 06478271add8..000000000000 --- a/examples/wasm-cc/envoy_filter_http_wasm_example.cc +++ /dev/null @@ -1,91 +0,0 @@ -// NOLINT(namespace-envoy) -#include -#include -#include - -#include "proxy_wasm_intrinsics.h" - -class ExampleRootContext : public RootContext { -public: - explicit ExampleRootContext(uint32_t id, std::string_view root_id) : RootContext(id, root_id) {} - - bool onStart(size_t) override; - bool onConfigure(size_t) override; - void onTick() override; -}; - -class ExampleContext : public Context { -public: - explicit ExampleContext(uint32_t id, RootContext* root) : Context(id, root) {} - - void onCreate() override; - FilterHeadersStatus onRequestHeaders(uint32_t headers, bool end_of_stream) override; - FilterDataStatus onRequestBody(size_t body_buffer_length, bool end_of_stream) override; - FilterHeadersStatus onResponseHeaders(uint32_t headers, bool end_of_stream) override; - FilterDataStatus onResponseBody(size_t body_buffer_length, bool end_of_stream) override; - void onDone() override; - void onLog() override; - void onDelete() override; -}; -static RegisterContextFactory register_ExampleContext(CONTEXT_FACTORY(ExampleContext), - ROOT_FACTORY(ExampleRootContext), - "my_root_id"); - -bool ExampleRootContext::onStart(size_t) { - LOG_TRACE("onStart"); - return true; -} - -bool ExampleRootContext::onConfigure(size_t) { - LOG_TRACE("onConfigure"); - proxy_set_tick_period_milliseconds(1000); // 1 sec - return true; -} - -void ExampleRootContext::onTick() { LOG_TRACE("onTick"); } - -void ExampleContext::onCreate() { LOG_WARN(std::string("onCreate " + std::to_string(id()))); } - -FilterHeadersStatus ExampleContext::onRequestHeaders(uint32_t, bool) { - LOG_DEBUG(std::string("onRequestHeaders ") + std::to_string(id())); - auto result = getRequestHeaderPairs(); - auto pairs = result->pairs(); - LOG_INFO(std::string("headers: ") + std::to_string(pairs.size())); - for (auto& p : pairs) { - LOG_INFO(std::string(p.first) + std::string(" -> ") + std::string(p.second)); - } - return FilterHeadersStatus::Continue; -} - -FilterHeadersStatus ExampleContext::onResponseHeaders(uint32_t, bool) { - LOG_DEBUG(std::string("onResponseHeaders ") + std::to_string(id())); - auto result = getResponseHeaderPairs(); - auto pairs = result->pairs(); - LOG_INFO(std::string("headers: ") + std::to_string(pairs.size())); - for (auto& p : pairs) { - LOG_INFO(std::string(p.first) + std::string(" -> ") + std::string(p.second)); - } - addResponseHeader("X-Wasm-custom", "FOO"); - replaceResponseHeader("content-type", "text/plain; charset=utf-8"); - removeResponseHeader("content-length"); - return FilterHeadersStatus::Continue; -} - -FilterDataStatus ExampleContext::onRequestBody(size_t body_buffer_length, - bool /* end_of_stream */) { - auto body = getBufferBytes(WasmBufferType::HttpRequestBody, 0, body_buffer_length); - LOG_ERROR(std::string("onRequestBody ") + std::string(body->view())); - return FilterDataStatus::Continue; -} - -FilterDataStatus ExampleContext::onResponseBody(size_t body_buffer_length, - bool /* end_of_stream */) { - setBuffer(WasmBufferType::HttpResponseBody, 0, body_buffer_length, "Hello, world\n"); - return FilterDataStatus::Continue; -} - -void ExampleContext::onDone() { LOG_WARN(std::string("onDone " + std::to_string(id()))); } - -void ExampleContext::onLog() { LOG_WARN(std::string("onLog " + std::to_string(id()))); } - -void ExampleContext::onDelete() { LOG_WARN(std::string("onDelete " + std::to_string(id()))); } diff --git a/examples/wasm-cc/envoy_filter_http_wasm_updated_example.cc b/examples/wasm-cc/envoy_filter_http_wasm_updated_example.cc deleted file mode 100644 index 67e6d24d96ab..000000000000 --- a/examples/wasm-cc/envoy_filter_http_wasm_updated_example.cc +++ /dev/null @@ -1,91 +0,0 @@ -// NOLINT(namespace-envoy) -#include -#include -#include - -#include "proxy_wasm_intrinsics.h" - -class ExampleRootContext : public RootContext { -public: - explicit ExampleRootContext(uint32_t id, std::string_view root_id) : RootContext(id, root_id) {} - - bool onStart(size_t) override; - bool onConfigure(size_t) override; - void onTick() override; -}; - -class ExampleContext : public Context { -public: - explicit ExampleContext(uint32_t id, RootContext* root) : Context(id, root) {} - - void onCreate() override; - FilterHeadersStatus onRequestHeaders(uint32_t headers, bool end_of_stream) override; - FilterDataStatus onRequestBody(size_t body_buffer_length, bool end_of_stream) override; - FilterHeadersStatus onResponseHeaders(uint32_t headers, bool end_of_stream) override; - FilterDataStatus onResponseBody(size_t body_buffer_length, bool end_of_stream) override; - void onDone() override; - void onLog() override; - void onDelete() override; -}; -static RegisterContextFactory register_ExampleContext(CONTEXT_FACTORY(ExampleContext), - ROOT_FACTORY(ExampleRootContext), - "my_root_id"); - -bool ExampleRootContext::onStart(size_t) { - LOG_TRACE("onStart"); - return true; -} - -bool ExampleRootContext::onConfigure(size_t) { - LOG_TRACE("onConfigure"); - proxy_set_tick_period_milliseconds(1000); // 1 sec - return true; -} - -void ExampleRootContext::onTick() { LOG_TRACE("onTick"); } - -void ExampleContext::onCreate() { LOG_WARN(std::string("onCreate " + std::to_string(id()))); } - -FilterHeadersStatus ExampleContext::onRequestHeaders(uint32_t, bool) { - LOG_DEBUG(std::string("onRequestHeaders ") + std::to_string(id())); - auto result = getRequestHeaderPairs(); - auto pairs = result->pairs(); - LOG_INFO(std::string("headers: ") + std::to_string(pairs.size())); - for (auto& p : pairs) { - LOG_INFO(std::string(p.first) + std::string(" -> ") + std::string(p.second)); - } - return FilterHeadersStatus::Continue; -} - -FilterHeadersStatus ExampleContext::onResponseHeaders(uint32_t, bool) { - LOG_DEBUG(std::string("onResponseHeaders ") + std::to_string(id())); - auto result = getResponseHeaderPairs(); - auto pairs = result->pairs(); - LOG_INFO(std::string("headers: ") + std::to_string(pairs.size())); - for (auto& p : pairs) { - LOG_INFO(std::string(p.first) + std::string(" -> ") + std::string(p.second)); - } - addResponseHeader("X-Wasm-custom", "BAR"); - replaceResponseHeader("content-type", "text/html; charset=utf-8"); - removeResponseHeader("content-length"); - return FilterHeadersStatus::Continue; -} - -FilterDataStatus ExampleContext::onRequestBody(size_t body_buffer_length, - bool /* end_of_stream */) { - auto body = getBufferBytes(WasmBufferType::HttpRequestBody, 0, body_buffer_length); - LOG_ERROR(std::string("onRequestBody ") + std::string(body->view())); - return FilterDataStatus::Continue; -} - -FilterDataStatus ExampleContext::onResponseBody(size_t /* body_buffer_length */, - bool /* end_of_stream */) { - setBuffer(WasmBufferType::HttpResponseBody, 0, 17, "Hello, Wasm world"); - return FilterDataStatus::Continue; -} - -void ExampleContext::onDone() { LOG_WARN(std::string("onDone " + std::to_string(id()))); } - -void ExampleContext::onLog() { LOG_WARN(std::string("onLog " + std::to_string(id()))); } - -void ExampleContext::onDelete() { LOG_WARN(std::string("onDelete " + std::to_string(id()))); } diff --git a/examples/wasm-cc/example.rst b/examples/wasm-cc/example.rst deleted file mode 100644 index 18be02fa13e3..000000000000 --- a/examples/wasm-cc/example.rst +++ /dev/null @@ -1,184 +0,0 @@ -.. _install_sandboxes_wasm_filter: - -Wasm C++ filter -=============== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -.. sidebar:: Compatibility - - The provided Wasm binary was compiled for the ``x86_64`` architecture. If you would like to use this sandbox - with the ``arm64`` architecture, change directory to ``examples/wasm-cc`` and skip to Step 3. - -This sandbox demonstrates a basic :ref:`Envoy Wasm filter ` written in C++ which injects -content into the body of an ``HTTP`` response, and adds and updates some headers. - -It also takes you through the steps required to build your own C++ :ref:`Wasm filter `, -and run it with Envoy. - -Step 1: Start all of our containers -*********************************** - -First lets start the containers - an Envoy proxy which uses a Wasm Filter, and a backend which echos back our request. -The Envoy configuration exposes two listeners, the first one listens in port 8000 which contains the wasm filter in -the listener filter chain. The second one listens in port 8001 routing to a cluster containing the wasm filter in the -cluster filter chain. - -Change to the ``examples/wasm-cc`` folder in the Envoy repo, and start the composition: - -.. code-block:: console - - $ pwd - envoy/examples/wasm-cc - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ----------------------------------------------------------------------------------------------- - wasm_proxy_1 /docker-entrypoint.sh /usr ... Up 10000/tcp, 0.0.0.0:8000->8000/tcp, 0.0.0.0:8001->8001/tcp - wasm_web_service_1 node ./index.js Up - -Step 2: Check web response -************************** - -The Wasm filter should inject "Hello, world" at the end of the response body when you make a request to the proxy. - -.. code-block:: console - - $ curl -s http://localhost:8000 | grep "Hello, world" - }Hello, world - -The filter also sets the ``content-type`` header to ``text/plain``, and adds a custom ``x-wasm-custom`` header. - -.. code-block:: console - - $ curl -v http://localhost:8000 | grep "content-type: " - content-type: text/plain; charset=utf-8 - - $ curl -v http://localhost:8000 | grep "x-wasm-custom: " - x-wasm-custom: FOO - -Similar outputs could be obtained in the second listener routing to the cluster with upstream wasm filter. - -.. code-block:: console - - $ curl -s http://localhost:8001 | grep "Hello, world" - }Hello, world - - $ curl -v http://localhost:8001 | grep "content-type: " - content-type: text/plain; charset=utf-8 - - $ curl -v http://localhost:8001 | grep "x-wasm-custom: " - x-wasm-custom: FOO - -Step 3: Compile the updated filter -********************************** - -There are two source code files provided for the Wasm filter. - -:download:`envoy_filter_http_wasm_example.cc <_include/wasm-cc/envoy_filter_http_wasm_example.cc>` provides the source code for -the included prebuilt binary. - -:download:`envoy_filter_http_wasm_updated_example.cc <_include/wasm-cc/envoy_filter_http_wasm_updated_example.cc>` makes a few -changes to the original. - -The following diff shows the changes that have been made: - -.. literalinclude:: _include/wasm-cc/envoy_filter_http_wasm_updated_example.cc - :diff: _include/wasm-cc/envoy_filter_http_wasm_example.cc - -.. warning:: - - These instructions for compiling an updated Wasm binary use the - `envoyproxy/envoy-build-ubuntu `_ image. - You will need 4-5GB of disk space to accommodate this image. - -Export ``UID`` from your host system. This will ensure that the binary created inside the build container has the same permissions -as your host user: - -.. code-block:: console - - $ export UID - -.. note:: - - The build composition is designed to work in a similar way to the ``./ci/run_envoy_docker.sh`` command in the Envoy repo. - - Bazel temporary artefacts are created in ``/tmp/envoy-docker-build`` with the uid taken from the ``UID`` env var. - -Stop the proxy server and compile the Wasm binary with the updated code: - -.. code-block:: console - - $ docker compose stop proxy - $ docker compose -f docker-compose-wasm.yaml up --remove-orphans wasm_compile_update - -The compiled binary should now be in the ``lib`` folder. - -.. code-block:: console - - $ ls -l lib - total 120 - -r-xr-xr-x 1 root root 59641 Oct 20 00:00 envoy_filter_http_wasm_example.wasm - -r-xr-xr-x 1 root root 59653 Oct 20 10:16 envoy_filter_http_wasm_updated_example.wasm - -Step 4: Edit the Dockerfile and restart the proxy -************************************************* - -Edit the ``Dockerfile-proxy`` recipe provided in the example to use the updated binary you created in step 3. - -Find the ``COPY`` line that adds the Wasm binary to the image: - -.. literalinclude:: _include/wasm-cc/Dockerfile-proxy - :language: dockerfile - :emphasize-lines: 3 - :linenos: - -Replace this line with the following: - -.. code-block:: dockerfile - - COPY ./lib/envoy_filter_http_wasm_updated_example.wasm /lib/envoy_filter_http_wasm_example.wasm - -Now, rebuild and start the proxy container. - -.. code-block:: console - - $ docker compose up --build -d proxy - -Step 5: Check the proxy has been updated -**************************************** - -The Wasm filter should instead inject "Hello, Wasm world" at the end of the response body. - -.. code-block:: console - - $ curl -s http://localhost:8000 | grep "Hello, Wasm world" - }Hello, Wasm world - -The ``content-type`` and ``x-wasm-custom`` headers should also have changed - -.. code-block:: console - - $ curl -v http://localhost:8000 | grep "content-type: " - content-type: text/html; charset=utf-8 - - $ curl -v http://localhost:8000 | grep "x-wasm-custom: " - x-wasm-custom: BAR - -.. seealso:: - - :ref:`Envoy Wasm filter ` - Further information about the Envoy Wasm filter. - - :ref:`Envoy Wasm API(V3) ` - The Envoy Wasm API - version 3. - - `Proxy Wasm C++ SDK `_ - WebAssembly for proxies (C++ SDK) diff --git a/examples/wasm-cc/lib/envoy_filter_http_wasm_example.wasm b/examples/wasm-cc/lib/envoy_filter_http_wasm_example.wasm deleted file mode 100755 index df2554e971e8538ce58066a2bdacfddf4c1b1bab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59641 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 _B0CI4>4?o^FEujAI897+y_ZpTU(6YNLQC0 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 routes to `service-1` -# 10001 -> routes to `envoy-2` - -node: - # The cluster name is used by Zipkin to identify the proxy. - cluster: envoy-proxy-1 - -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - traffic_direction: INBOUND - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - tracing: - provider: - name: envoy.tracers.zipkin - typed_config: - "@type": type.googleapis.com/envoy.config.trace.v3.ZipkinConfig - collector_cluster: zipkin - collector_endpoint: "/api/v2/spans" - collector_endpoint_version: HTTP_JSON - codec_type: AUTO - stat_prefix: ingress_http - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - route_config: - name: service1_route - virtual_hosts: - - name: service1 - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service_cluster1 - decorator: - operation: routeToService1 - - address: - socket_address: - address: 0.0.0.0 - port_value: 10001 - traffic_direction: OUTBOUND - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - tracing: - provider: - name: envoy.tracers.zipkin - typed_config: - "@type": type.googleapis.com/envoy.config.trace.v3.ZipkinConfig - collector_cluster: zipkin - collector_endpoint: "/api/v2/spans" - collector_endpoint_version: HTTP_JSON - codec_type: AUTO - stat_prefix: egress_http - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - route_config: - name: envoy2_route - virtual_hosts: - - name: envoy2 - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: envoy_cluster2 - decorator: - operation: routeToEnvoy2 - - clusters: - - name: service_cluster1 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service_cluster1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-1 - port_value: 8080 - - name: envoy_cluster2 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: envoy_cluster2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: envoy-2 - port_value: 10000 - - name: zipkin - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: zipkin - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: zipkin - port_value: 9411 diff --git a/examples/zipkin/envoy-2.yaml b/examples/zipkin/envoy-2.yaml deleted file mode 100644 index ac502803ec27..000000000000 --- a/examples/zipkin/envoy-2.yaml +++ /dev/null @@ -1,71 +0,0 @@ -# This proxy listens on port 10000 and routes all queries to `service-2`. - -node: - # The cluster name is used by Zipkin to identify the proxy. - cluster: envoy-proxy-2 - -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - traffic_direction: INBOUND - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - tracing: - provider: - name: envoy.tracers.zipkin - typed_config: - "@type": type.googleapis.com/envoy.config.trace.v3.ZipkinConfig - collector_cluster: zipkin - collector_endpoint: "/api/v2/spans" - collector_endpoint_version: HTTP_JSON - codec_type: AUTO - stat_prefix: ingress_http - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - route_config: - name: service2_route - virtual_hosts: - - name: service2 - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service_cluster2 - decorator: - operation: routeToService2 - - clusters: - - name: service_cluster2 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service_cluster2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-2 - port_value: 8080 - - name: zipkin - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: zipkin - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: zipkin - port_value: 9411 diff --git a/examples/zipkin/envoy-front-proxy.yaml b/examples/zipkin/envoy-front-proxy.yaml deleted file mode 100644 index 6f7a9520444b..000000000000 --- a/examples/zipkin/envoy-front-proxy.yaml +++ /dev/null @@ -1,100 +0,0 @@ -# This proxy listens on port 10000, and routes the following paths: -# -# /trace/1 -> routes to `envoy-1` on port 10000 -# /trace/2 -> routes to `envoy-1` on port 10001 (for onward routing to `envoy-2`) - -node: - # The cluster name is used by Zipkin to identify the proxy. - cluster: envoy-proxy-front - -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - traffic_direction: OUTBOUND - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - generate_request_id: true - tracing: - provider: - name: envoy.tracers.zipkin - typed_config: - "@type": type.googleapis.com/envoy.config.trace.v3.ZipkinConfig - collector_cluster: zipkin - collector_endpoint: "/api/v2/spans" - collector_endpoint_version: HTTP_JSON - codec_type: AUTO - stat_prefix: ingress_http - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - route_config: - name: proxy_routes - virtual_hosts: - - name: proxy - domains: - - "*" - routes: - - match: - prefix: "/trace/1" - route: - cluster: envoy_cluster1 - decorator: - operation: routeToEnvoy1 - - match: - prefix: "/trace/2" - route: - cluster: envoy_cluster2 - decorator: - operation: routeToEnvoy2ViaEnvoy1 - response_headers_to_add: - - header: - key: "x-b3-traceid" - value: "%REQ(x-b3-traceid)%" - - header: - key: "x-request-id" - value: "%REQ(x-request-id)%" - - clusters: - - name: envoy_cluster1 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: envoy_cluster1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: envoy-1 - port_value: 10000 - - name: envoy_cluster2 - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: envoy_cluster2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: envoy-1 - port_value: 10001 - - name: zipkin - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: zipkin - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: zipkin - port_value: 9411 diff --git a/examples/zipkin/example.rst b/examples/zipkin/example.rst deleted file mode 100644 index 34c0f9c82e9e..000000000000 --- a/examples/zipkin/example.rst +++ /dev/null @@ -1,127 +0,0 @@ -.. _install_sandboxes_zipkin: - -Zipkin tracing -============== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -The Zipkin tracing sandbox demonstrates Envoy's :ref:`request tracing ` -capabilities using `Zipkin `_ as the tracing provider. - -In this example, 2 backend services are provided: - -- ``service-1`` -- ``service-2`` - -3 Envoy proxies are also provided to route requests to them: - -- ``envoy-front-proxy`` (:download:`envoy-front-proxy.yaml <_include/zipkin/envoy-front-proxy.yaml>`) -- ``envoy-1`` (:download:`envoy-1.yaml <_include/zipkin/envoy-1.yaml>`) -- ``envoy-2`` (:download:`envoy-2.yaml <_include/zipkin/envoy-2.yaml>`) - -Of these services, only the Envoy ``front-proxy`` service is exposed outside of the -:download:`composition <_include/zipkin/docker-compose.yaml>`, on port ``10000``. - -For ``service-1``, requests are routed based on the request path ``trace/1``, as follows: - - User -> Envoy(``envoy-front-proxy``) -> Envoy(``envoy-1``) -> ``service-1`` - -For ``service-2``, requests are routed based on the request path ``trace/2`` as follows: - - User -> Envoy(``envoy-front-proxy``) -> Envoy(``envoy-1``) -> Envoy(``envoy-2``) -> ``service-2`` - -All Envoy proxies are configured to collect request traces, as can be seen in their configurations, -propagating the spans (parent/child/shared context) generated by the Zipkin tracer to a Zipkin cluster. - -Each span records the latency of upstream API calls as well as information -needed to correlate the span with other related spans (e.g., the trace ID). - -The Zipkin collector provides a web UI for viewing the collected traces on port ``9411``. - -Step 1: Build the sandbox -************************* - -Change directory to ``examples/zipkin`` in the Envoy repository. - -To build this sandbox example, and start the example services run the following commands: - -.. code-block:: console - - $ pwd - envoy/examples/zipkin - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - ----------------------------------------------------------------------------------------------------------- - zipkin_envoy-1_1 /docker-entrypoint.sh /usr ... Up 10000/tcp - zipkin_envoy-2_1 /docker-entrypoint.sh /usr ... Up 10000/tcp - zipkin_envoy-front-proxy_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp - zipkin_service-1_1 python3 /code/service.py Up (healthy) - zipkin_service-2_1 python3 /code/service.py Up (healthy) - zipkin_zipkin_1 start-zipkin Up (healthy) 9410/tcp, 0.0.0.0:9411->9411/tcp - -Step 2: Make a request to ``service-1`` -*************************************** - -Now send a request to ``service-1``, by calling http://localhost:10000/trace/1. - -This will be routed via 2 of the Envoy proxies: - -- ``front-proxy`` -- ``envoy-1`` - -.. code-block:: console - - $ curl localhost:10000/trace/1 - Hello from behind Envoy (service 1)! - -Step 3: Make a request to ``service-2`` -*************************************** - -Now send a request to ``service-2``, by calling http://localhost:10000/trace/2. - -This will be routed via all 3 of the Envoy proxies: - -- ``front-proxy`` -- ``envoy-1`` -- ``envoy-2`` - -.. code-block:: console - - $ curl localhost:10000/trace/2 - Hello from behind Envoy (service 2)! - -Step 4: View the traces in Zipkin UI -************************************ - -Point your browser to http://localhost:9411 . - -You should see the Zipkin dashboard. - -Click the ``RUN QUERY`` button, and expand the traces by clicking ``EXPAND ALL``. - -Here you can explore the paths taken by the requests, as well as the latency incurred at each hop, -and other contextual information. - -Note that Zipkin identifies the Envoy proxies by the name provided in the bootstrap ``node/cluster`` configuration. - -.. image:: /start/sandboxes/_include/zipkin/_static/zipkin-ui.png - -You can also explore the Zipkin dependency UI to view relationships between nodes and the path of traces. - -.. image:: /start/sandboxes/_include/zipkin/_static/zipkin-ui-dependency.png - -.. seealso:: - - :ref:`Request tracing ` - Learn more about using Envoy's request tracing. - - `Zipkin `_ - Zipkin tracing website. diff --git a/examples/zipkin/verify.sh b/examples/zipkin/verify.sh deleted file mode 100755 index 0744d0a41be8..000000000000 --- a/examples/zipkin/verify.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -e - -export NAME=zipkin -export PORT_PROXY="${ZIPKIN_PORT_PROXY:-12600}" -export PORT_UI="${ZIPKIN_PORT_UI:-12601}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Make a request to service-1" -responds_with \ - "Hello from behind Envoy (service 1)!" \ - "http://localhost:${PORT_PROXY}/trace/1" - -run_log "Make a request to service-2" -responds_with \ - "Hello from behind Envoy (service 2)!" \ - "http://localhost:${PORT_PROXY}/trace/2" - -run_log "View the traces in Zipkin UI" -responds_with \ - "" \ - "http://localhost:${PORT_UI}/zipkin/" diff --git a/examples/zstd/README.md b/examples/zstd/README.md deleted file mode 100644 index cd7f3bf4884f..000000000000 --- a/examples/zstd/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/zstd.html) diff --git a/examples/zstd/docker-compose.yaml b/examples/zstd/docker-compose.yaml deleted file mode 100644 index a37faeb6f2cc..000000000000 --- a/examples/zstd/docker-compose.yaml +++ /dev/null @@ -1,15 +0,0 @@ -services: - - envoy-stats: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - ports: - - "${PORT_PROXY:-10000}:10000" - - "${PORT_ADMIN0:-9901}:9901" - - "${PORT_ADMIN1:-9902}:9902" - - service: - build: - context: ../shared/python - target: aiohttp-data-service diff --git a/examples/zstd/envoy.yaml b/examples/zstd/envoy.yaml deleted file mode 100644 index b3862468db75..000000000000 --- a/examples/zstd/envoy.yaml +++ /dev/null @@ -1,238 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service - http_filters: - - name: envoy.filters.http.compressor - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor - response_direction_config: - common_config: - min_content_length: 100 - content_type: - - application/json - disable_on_etag_header: true - compressor_library: - name: text_optimized - typed_config: - "@type": type.googleapis.com/envoy.extensions.compression.zstd.compressor.v3.Zstd - compression_level: 10 - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext - common_tls_context: - tls_certificates: - # The following self-signed certificate pair is generated using: - # $ openssl req -x509 -newkey rsa:2048 -keyout a/zstd-key.pem -out a/zstd-crt.pem -days 3650 -nodes -subj '/CN=zstd' - # - # Instead of feeding it as an inline_string, certificate pair can also be fed to Envoy - # via filename. Reference: https://envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#config-core-v3-datasource. - # - # Or in a dynamic configuration scenario, certificate pair can be fetched remotely via - # Secret Discovery Service (SDS). Reference: https://envoyproxy.io/docs/envoy/latest/configuration/security/secret. - - certificate_chain: - inline_string: | - -----BEGIN CERTIFICATE----- - MIICqDCCAZACCQCquzpHNpqBcDANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtm - cm9udC1lbnZveTAeFw0yMDA3MDgwMTMxNDZaFw0zMDA3MDYwMTMxNDZaMBYxFDAS - BgNVBAMMC2Zyb250LWVudm95MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC - AQEAthnYkqVQBX+Wg7aQWyCCb87hBce1hAFhbRM8Y9dQTqxoMXZiA2n8G089hUou - oQpEdJgitXVS6YMFPFUUWfwcqxYAynLK4X5im26Yfa1eO8La8sZUS+4Bjao1gF5/ - VJxSEo2yZ7fFBo8M4E44ZehIIocipCRS+YZehFs6dmHoq/MGvh2eAHIa+O9xssPt - ofFcQMR8rwBHVbKy484O10tNCouX4yUkyQXqCRy6HRu7kSjOjNKSGtjfG+h5M8bh - 10W7ZrsJ1hWhzBulSaMZaUY3vh5ngpws1JATQVSK1Jm/dmMRciwlTK7KfzgxHlSX - 58ENpS7yPTISkEICcLbXkkKGEQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCmj6Hg - vwOxWz0xu+6fSfRL6PGJUGq6wghCfUvjfwZ7zppDUqU47fk+yqPIOzuGZMdAqi7N - v1DXkeO4A3hnMD22Rlqt25vfogAaZVToBeQxCPd/ALBLFrvLUFYuSlS3zXSBpQqQ - Ny2IKFYsMllz5RSROONHBjaJOn5OwqenJ91MPmTAG7ujXKN6INSBM0PjX9Jy4Xb9 - zT+I85jRDQHnTFce1WICBDCYidTIvJtdSSokGSuy4/xyxAAc/BpZAfOjBQ4G1QRe - 9XwOi790LyNUYFJVyeOvNJwveloWuPLHb9idmY5YABwikUY6QNcXwyHTbRCkPB2I - m+/R4XnmL4cKQ+5Z - -----END CERTIFICATE----- - private_key: - inline_string: | - -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2GdiSpVAFf5aD - tpBbIIJvzuEFx7WEAWFtEzxj11BOrGgxdmIDafwbTz2FSi6hCkR0mCK1dVLpgwU8 - VRRZ/ByrFgDKcsrhfmKbbph9rV47wtryxlRL7gGNqjWAXn9UnFISjbJnt8UGjwzg - Tjhl6EgihyKkJFL5hl6EWzp2Yeir8wa+HZ4Achr473Gyw+2h8VxAxHyvAEdVsrLj - zg7XS00Ki5fjJSTJBeoJHLodG7uRKM6M0pIa2N8b6HkzxuHXRbtmuwnWFaHMG6VJ - oxlpRje+HmeCnCzUkBNBVIrUmb92YxFyLCVMrsp/ODEeVJfnwQ2lLvI9MhKQQgJw - tteSQoYRAgMBAAECggEAeDGdEkYNCGQLe8pvg8Z0ccoSGpeTxpqGrNEKhjfi6NrB - NwyVav10iq4FxEmPd3nobzDPkAftfvWc6hKaCT7vyTkPspCMOsQJ39/ixOk+jqFx - lNa1YxyoZ9IV2DIHR1iaj2Z5gB367PZUoGTgstrbafbaNY9IOSyojCIO935ubbcx - DWwL24XAf51ez6sXnI8V5tXmrFlNXhbhJdH8iIxNyM45HrnlUlOk0lCK4gmLJjy9 - 10IS2H2Wh3M5zsTpihH1JvM56oAH1ahrhMXs/rVFXXkg50yD1KV+HQiEbglYKUxO - eMYtfaY9i2CuLwhDnWp3oxP3HfgQQhD09OEN3e0IlQKBgQDZ/3poG9TiMZSjfKqL - xnCABMXGVQsfFWNC8THoW6RRx5Rqi8q08yJrmhCu32YKvccsOljDQJQQJdQO1g09 - e/adJmCnTrqxNtjPkX9txV23Lp6Ak7emjiQ5ICu7iWxrcO3zf7hmKtj7z+av8sjO - mDI7NkX5vnlE74nztBEjp3eC0wKBgQDV2GeJV028RW3b/QyP3Gwmax2+cKLR9PKR - nJnmO5bxAT0nQ3xuJEAqMIss/Rfb/macWc2N/6CWJCRT6a2vgy6xBW+bqG6RdQMB - xEZXFZl+sSKhXPkc5Wjb4lQ14YWyRPrTjMlwez3k4UolIJhJmwl+D7OkMRrOUERO - EtUvc7odCwKBgBi+nhdZKWXveM7B5N3uzXBKmmRz3MpPdC/yDtcwJ8u8msUpTv4R - JxQNrd0bsIqBli0YBmFLYEMg+BwjAee7vXeDFq+HCTv6XMva2RsNryCO4yD3I359 - XfE6DJzB8ZOUgv4Dvluie3TB2Y6ZQV/p+LGt7G13yG4hvofyJYvlg3RPAoGAcjDg - +OH5zLN2eqah8qBN0CYa9/rFt0AJ19+7/smLTJ7QvQq4g0gwS1couplcCEnNGWiK - 72y1n/ckvvplmPeAE19HveMvR9UoCeV5ej86fACy8V/oVpnaaLBvL2aCMjPLjPP9 - DWeCIZp8MV86cvOrGfngf6kJG2qZTueXl4NAuwkCgYEArKkhlZVXjwBoVvtHYmN2 - o+F6cGMlRJTLhNc391WApsgDZfTZSdeJsBsvvzS/Nc0burrufJg0wYioTlpReSy4 - ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU - q5sGxGrC1RECGB5Zwx2S2ZY= - -----END PRIVATE KEY----- - - address: - socket_address: - address: 0.0.0.0 - port_value: 9902 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/stats/prometheus" - route: - cluster: envoy-stats - http_filters: - - name: envoy.filters.http.compressor - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor - response_direction_config: - common_config: - min_content_length: 100 - content_type: - - text/plain - disable_on_etag_header: true - compressor_library: - name: text_optimized - typed_config: - "@type": type.googleapis.com/envoy.extensions.compression.zstd.compressor.v3.Zstd - compression_level: 10 - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext - common_tls_context: - tls_certificates: - # The following self-signed certificate pair is generated using: - # $ openssl req -x509 -newkey rsa:2048 -keyout a/zstd.pem -out a/zstd-crt.pem -days 3650 -nodes -subj '/CN=zstd' - # - # Instead of feeding it as an inline_string, certificate pair can also be fed to Envoy - # via filename. Reference: https://envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#config-core-v3-datasource. - # - # Or in a dynamic configuration scenario, certificate pair can be fetched remotely via - # Secret Discovery Service (SDS). Reference: https://envoyproxy.io/docs/envoy/latest/configuration/security/secret. - - certificate_chain: - inline_string: | - -----BEGIN CERTIFICATE----- - MIICqDCCAZACCQCquzpHNpqBcDANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtm - cm9udC1lbnZveTAeFw0yMDA3MDgwMTMxNDZaFw0zMDA3MDYwMTMxNDZaMBYxFDAS - BgNVBAMMC2Zyb250LWVudm95MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC - AQEAthnYkqVQBX+Wg7aQWyCCb87hBce1hAFhbRM8Y9dQTqxoMXZiA2n8G089hUou - oQpEdJgitXVS6YMFPFUUWfwcqxYAynLK4X5im26Yfa1eO8La8sZUS+4Bjao1gF5/ - VJxSEo2yZ7fFBo8M4E44ZehIIocipCRS+YZehFs6dmHoq/MGvh2eAHIa+O9xssPt - ofFcQMR8rwBHVbKy484O10tNCouX4yUkyQXqCRy6HRu7kSjOjNKSGtjfG+h5M8bh - 10W7ZrsJ1hWhzBulSaMZaUY3vh5ngpws1JATQVSK1Jm/dmMRciwlTK7KfzgxHlSX - 58ENpS7yPTISkEICcLbXkkKGEQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCmj6Hg - vwOxWz0xu+6fSfRL6PGJUGq6wghCfUvjfwZ7zppDUqU47fk+yqPIOzuGZMdAqi7N - v1DXkeO4A3hnMD22Rlqt25vfogAaZVToBeQxCPd/ALBLFrvLUFYuSlS3zXSBpQqQ - Ny2IKFYsMllz5RSROONHBjaJOn5OwqenJ91MPmTAG7ujXKN6INSBM0PjX9Jy4Xb9 - zT+I85jRDQHnTFce1WICBDCYidTIvJtdSSokGSuy4/xyxAAc/BpZAfOjBQ4G1QRe - 9XwOi790LyNUYFJVyeOvNJwveloWuPLHb9idmY5YABwikUY6QNcXwyHTbRCkPB2I - m+/R4XnmL4cKQ+5Z - -----END CERTIFICATE----- - private_key: - inline_string: | - -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2GdiSpVAFf5aD - tpBbIIJvzuEFx7WEAWFtEzxj11BOrGgxdmIDafwbTz2FSi6hCkR0mCK1dVLpgwU8 - VRRZ/ByrFgDKcsrhfmKbbph9rV47wtryxlRL7gGNqjWAXn9UnFISjbJnt8UGjwzg - Tjhl6EgihyKkJFL5hl6EWzp2Yeir8wa+HZ4Achr473Gyw+2h8VxAxHyvAEdVsrLj - zg7XS00Ki5fjJSTJBeoJHLodG7uRKM6M0pIa2N8b6HkzxuHXRbtmuwnWFaHMG6VJ - oxlpRje+HmeCnCzUkBNBVIrUmb92YxFyLCVMrsp/ODEeVJfnwQ2lLvI9MhKQQgJw - tteSQoYRAgMBAAECggEAeDGdEkYNCGQLe8pvg8Z0ccoSGpeTxpqGrNEKhjfi6NrB - NwyVav10iq4FxEmPd3nobzDPkAftfvWc6hKaCT7vyTkPspCMOsQJ39/ixOk+jqFx - lNa1YxyoZ9IV2DIHR1iaj2Z5gB367PZUoGTgstrbafbaNY9IOSyojCIO935ubbcx - DWwL24XAf51ez6sXnI8V5tXmrFlNXhbhJdH8iIxNyM45HrnlUlOk0lCK4gmLJjy9 - 10IS2H2Wh3M5zsTpihH1JvM56oAH1ahrhMXs/rVFXXkg50yD1KV+HQiEbglYKUxO - eMYtfaY9i2CuLwhDnWp3oxP3HfgQQhD09OEN3e0IlQKBgQDZ/3poG9TiMZSjfKqL - xnCABMXGVQsfFWNC8THoW6RRx5Rqi8q08yJrmhCu32YKvccsOljDQJQQJdQO1g09 - e/adJmCnTrqxNtjPkX9txV23Lp6Ak7emjiQ5ICu7iWxrcO3zf7hmKtj7z+av8sjO - mDI7NkX5vnlE74nztBEjp3eC0wKBgQDV2GeJV028RW3b/QyP3Gwmax2+cKLR9PKR - nJnmO5bxAT0nQ3xuJEAqMIss/Rfb/macWc2N/6CWJCRT6a2vgy6xBW+bqG6RdQMB - xEZXFZl+sSKhXPkc5Wjb4lQ14YWyRPrTjMlwez3k4UolIJhJmwl+D7OkMRrOUERO - EtUvc7odCwKBgBi+nhdZKWXveM7B5N3uzXBKmmRz3MpPdC/yDtcwJ8u8msUpTv4R - JxQNrd0bsIqBli0YBmFLYEMg+BwjAee7vXeDFq+HCTv6XMva2RsNryCO4yD3I359 - XfE6DJzB8ZOUgv4Dvluie3TB2Y6ZQV/p+LGt7G13yG4hvofyJYvlg3RPAoGAcjDg - +OH5zLN2eqah8qBN0CYa9/rFt0AJ19+7/smLTJ7QvQq4g0gwS1couplcCEnNGWiK - 72y1n/ckvvplmPeAE19HveMvR9UoCeV5ej86fACy8V/oVpnaaLBvL2aCMjPLjPP9 - DWeCIZp8MV86cvOrGfngf6kJG2qZTueXl4NAuwkCgYEArKkhlZVXjwBoVvtHYmN2 - o+F6cGMlRJTLhNc391WApsgDZfTZSdeJsBsvvzS/Nc0burrufJg0wYioTlpReSy4 - ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU - q5sGxGrC1RECGB5Zwx2S2ZY= - -----END PRIVATE KEY----- - - clusters: - - name: envoy-stats - connect_timeout: 0.25s - type: STATIC - load_assignment: - cluster_name: envoy-stats - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: 127.0.0.1 - port_value: 9901 - - name: service - connect_timeout: 0.25s - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service - port_value: 8080 -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 9901 diff --git a/examples/zstd/example.rst b/examples/zstd/example.rst deleted file mode 100644 index a44773c3c539..000000000000 --- a/examples/zstd/example.rst +++ /dev/null @@ -1,95 +0,0 @@ -.. _install_sandboxes_zstd: - -Zstd -====== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`curl ` - Used to make ``HTTP`` requests. - -By enabling compression in Envoy you can save some network bandwidth, at the expense of increased processor usage. - -Envoy supports compression and decompression for both requests and responses. - -This sandbox provides an example of response compression served over ``HTTPS``. - -The sandbox covers two scenarios: - -- compression of files from an upstream server -- compression of Envoy's own statistics - -Step 1: Start all of our containers -*********************************** - -Change to the ``examples/zstd`` directory and bring up the docker composition. - -.. code-block:: console - - $ pwd - envoy/examples/zstd - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - Name Command State Ports - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - zstd_envoy-stats_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp,:::10000->10000/tcp, 0.0.0.0:9901->9901/tcp,:::9901->9901/tcp, 0.0.0.0:9902->9902/tcp,:::9902->9902/tcp - zstd_service_1 python3 /code/service.py Up - -Step 2: Test Envoy’s compression of upstream files -************************************************** - -The sandbox is configured with two endpoints on port ``10000`` for serving upstream files: - -- ``/file.txt`` -- ``/file.json`` - -Only ``/file.json`` is configured to be compressed. - -Use ``curl`` to check that the response from requesting ``file.json`` contains the ``content-encoding: zstd`` header. - -You will need to add an ``accept-encoding: zstd`` request header. - -.. code-block:: console - - $ curl -ski -H "Accept-Encoding: zstd" https://localhost:10000/file.json | grep "content-encoding" - content-encoding: zstd - -As only files with a content-type of ``application/json`` are configured to be compressed, the response from requesting ``file.txt`` should not contain the ``content-encoding: zstd`` header, and the file will not be compressed: - -.. code-block:: console - - $ curl -ski -H "Accept-Encoding: zstd" https://localhost:10000/file.txt | grep "content-encoding" - -Step 3: Test compression of Envoy’s statistics -********************************************** - -The sandbox is configured with two ports serving Envoy’s admin and statistics interface: - -- ``9901`` exposes the standard admin interface without tls -- ``9902`` exposes a compressed version of the admin interface with tls - -Use ``curl`` to make a request for uncompressed statistics on port ``9901``, it should not contain the ``content-encoding`` header in the response: - -.. code-block:: console - - $ curl -ski -H "Accept-Encoding: zstd" http://localhost:9901/stats/prometheus | grep "content-encoding" - -Now, use ``curl`` to make a request for the compressed statistics: - -.. code-block:: console - - $ curl -ski -H "Accept-Encoding: zstd" https://localhost:9902/stats/prometheus | grep "content-encoding" - content-encoding: zstd - -.. seealso:: - :ref:`Zstd API ` - API and configuration reference for Envoy's zstd compression. - - :ref:`Compression configuration ` - Reference documentation for Envoy's compressor filter. - - :ref:`Envoy admin quick start guide ` - Quick start guide to the Envoy admin interface. diff --git a/examples/zstd/verify.sh b/examples/zstd/verify.sh deleted file mode 100755 index 0437e2f1d69c..000000000000 --- a/examples/zstd/verify.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -e - -export NAME=zstd -export PORT_PROXY="${ZSTD_PORT_PROXY:-12610}" -export PORT_ADMIN0="${ZSTD_PORT_ADMIN0:-12611}" -export PORT_ADMIN1="${ZSTD_PORT_ADMIN1:-12612}" - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Test service: localhost:${PORT_PROXY}/file.json with compression" -responds_with_header \ - "content-encoding: zstd" \ - "https://localhost:${PORT_PROXY}/file.json" \ - -ki -H "Accept-Encoding: zstd" - -run_log "Test service: localhost:${PORT_PROXY}/file.txt without compression" -responds_without_header \ - "content-encoding: zstd" \ - "https://localhost:${PORT_PROXY}/file.txt" \ - -ki -H "Accept-Encoding: zstd" - -run_log "Test service: localhost:${PORT_ADMIN0}/stats/prometheus without compression" -responds_without_header \ - "content-encoding: zstd" \ - "http://localhost:${PORT_ADMIN0}/stats/prometheus" \ - -ki -H "Accept-Encoding: zstd" - -run_log "Test service: localhost:${PORT_ADMIN1}/stats/prometheus with compression" -responds_with_header \ - "content-encoding: zstd" \ - "https://localhost:${PORT_ADMIN1}/stats/prometheus" \ - -ki -H "Accept-Encoding: zstd" diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index f06cd6fb1034..ee10c1279eac 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -536,8 +536,8 @@ EXTENSIONS = { # These can be changed to ["//visibility:public"], for downstream builds which # need to directly reference Envoy extensions. -EXTENSION_CONFIG_VISIBILITY = ["//:extension_config", "//:contrib_library", "//:examples_library", "//:mobile_library"] -EXTENSION_PACKAGE_VISIBILITY = ["//:extension_library", "//:contrib_library", "//:examples_library", "//:mobile_library"] +EXTENSION_CONFIG_VISIBILITY = ["//:extension_config", "//:contrib_library", "//:mobile_library"] +EXTENSION_PACKAGE_VISIBILITY = ["//:extension_library", "//:contrib_library", "//:mobile_library"] CONTRIB_EXTENSION_PACKAGE_VISIBILITY = ["//:contrib_library"] MOBILE_PACKAGE_VISIBILITY = ["//:mobile_library"] From a8ec87b0bb133c19696dc977573a2af6fe08751c Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 6 Aug 2024 14:39:14 -0400 Subject: [PATCH 043/130] mobile: improved HTTP/3 cronet testing (#35600) This adds testing of doing retries post HTTP/3 handshake, and resetting brokenness on networ kchange. Risk Level: n/a (adds a stat to tracking) Testing: yes Docs Changes: no Release Notes: no --------- Signed-off-by: Alyssa Wilk --- mobile/library/cc/engine_builder.cc | 1 + mobile/test/common/integration/test_server.cc | 18 +- .../org/chromium/net/CronetHttp3Test.java | 220 +++++++++++++++--- 3 files changed, 207 insertions(+), 32 deletions(-) diff --git a/mobile/library/cc/engine_builder.cc b/mobile/library/cc/engine_builder.cc index dde68d120c63..ac4cb04e13f5 100644 --- a/mobile/library/cc/engine_builder.cc +++ b/mobile/library/cc/engine_builder.cc @@ -760,6 +760,7 @@ std::unique_ptr EngineBuilder::generate list->add_patterns()->set_prefix("cluster.base.upstream_cx_"); list->add_patterns()->set_prefix("cluster.stats.upstream_cx_"); list->add_patterns()->set_exact("cluster.base.http2.keepalive_timeout"); + list->add_patterns()->set_exact("cluster.base.upstream_http3_broken"); list->add_patterns()->set_exact("cluster.stats.http2.keepalive_timeout"); list->add_patterns()->set_prefix("http.hcm.downstream_rq_"); list->add_patterns()->set_prefix("http.hcm.decompressor."); diff --git a/mobile/test/common/integration/test_server.cc b/mobile/test/common/integration/test_server.cc index 3b3ad20b88c3..3f69bfe33a1c 100644 --- a/mobile/test/common/integration/test_server.cc +++ b/mobile/test/common/integration/test_server.cc @@ -203,8 +203,22 @@ void TestServer::start(TestServerType type, int port) { } } - upstream_ = std::make_unique(std::move(factory), port_, version_, - upstream_config_, true); +// We have series of Cronvoy tests which don't bind to port 0, and often hit +// port conflicts with other processes using 127.0.0.1. Default non-apple +// builds to 127.0.0.1 (this fails on iOS and probably OSX with Can't assign +// requested address) +#if !defined(__APPLE__) + if (version_ == Network::Address::IpVersion::v4) { +#else + if (false) { +#endif + auto address = Network::Utility::parseInternetAddressNoThrow("127.0.0.3", port_); + upstream_ = + std::make_unique(std::move(factory), address, upstream_config_, true); + } else { + upstream_ = std::make_unique(std::move(factory), port_, version_, + upstream_config_, true); + } // Legacy behavior for cronet tests. if (type == TestServerType::HTTP3) { diff --git a/mobile/test/java/org/chromium/net/CronetHttp3Test.java b/mobile/test/java/org/chromium/net/CronetHttp3Test.java index 9b0800b18e67..41ad3f848fdc 100644 --- a/mobile/test/java/org/chromium/net/CronetHttp3Test.java +++ b/mobile/test/java/org/chromium/net/CronetHttp3Test.java @@ -2,11 +2,14 @@ import static org.chromium.net.testing.CronetTestRule.getContext; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import io.envoyproxy.envoymobile.engine.types.EnvoyNetworkType; import org.chromium.net.impl.CronvoyUrlRequestContext; import io.envoyproxy.envoymobile.engine.EnvoyEngine; import org.chromium.net.impl.CronvoyLogger; import androidx.test.core.app.ApplicationProvider; +import org.chromium.net.testing.TestUploadDataProvider; import androidx.test.filters.SmallTest; import org.chromium.net.impl.CronvoyUrlRequestContext; import org.chromium.net.impl.NativeCronvoyEngineBuilderImpl; @@ -28,7 +31,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Collections; - /** * Test CronetEngine with production HTTP/3 logic */ @@ -51,6 +53,8 @@ public class CronetHttp3Test { private CronvoyLogger logger; // The engine for this test. private CronvoyUrlRequestContext cronvoyEngine; + // A URL which will point to the IP and port of the test servers. + private String testServerUrl; @BeforeClass public static void loadJniLibrary() { @@ -72,6 +76,7 @@ public void setUp(boolean setUpLogging) throws Exception { http2TestServer = HttpTestServerFactory.start( HttpTestServerFactory.Type.HTTP2_WITH_TLS, http3TestServer.getPort(), headers, "This is a simple text file served by QUIC.\n", Collections.emptyMap()); + testServerUrl = "https://" + http2TestServer.getAddress() + "/"; // Optionally, set up logging. This will slow down the tests a bit but make debugging much // easier. @@ -83,6 +88,18 @@ public void log(int logLevel, String message) { } }; } + + // Set up the Envoy engine. + NativeCronvoyEngineBuilderImpl nativeCronetEngineBuilder = + new NativeCronvoyEngineBuilderImpl(ApplicationProvider.getApplicationContext()); + nativeCronetEngineBuilder.addRuntimeGuard("reset_brokenness_on_nework_change", true); + if (setUpLogging) { + nativeCronetEngineBuilder.setLogger(logger); + nativeCronetEngineBuilder.setLogLevel(EnvoyEngine.LogLevel.TRACE); + } + // Make sure the handshake will work despite lack of real certs. + nativeCronetEngineBuilder.setMockCertVerifierForTesting(); + cronvoyEngine = new CronvoyUrlRequestContext(nativeCronetEngineBuilder); } @After @@ -90,50 +107,193 @@ public void tearDown() throws Exception { // Shut down Envoy and the test servers. cronvoyEngine.shutdown(); http2TestServer.shutdown(); - http3TestServer.shutdown(); + if (http3TestServer != null) { + http3TestServer.shutdown(); + } + } + + private TestUrlRequestCallback doBasicGetRequest() { + TestUrlRequestCallback callback = new TestUrlRequestCallback(); + UrlRequest.Builder urlRequestBuilder = + cronvoyEngine.newUrlRequestBuilder(testServerUrl, callback, callback.getExecutor()); + urlRequestBuilder.build().start(); + callback.blockForDone(); + return callback; + } + + // Sets up a basic POST request with 4 byte body, set idempotent. + private TestUrlRequestCallback doBasicPostRequest() { + TestUrlRequestCallback callback = new TestUrlRequestCallback(); + ExperimentalUrlRequest.Builder urlRequestBuilder = + cronvoyEngine.newUrlRequestBuilder(testServerUrl, callback, callback.getExecutor()); + urlRequestBuilder.addHeader("content-type", "text"); + urlRequestBuilder.setHttpMethod("POST"); + urlRequestBuilder.setIdempotency(ExperimentalUrlRequest.Builder.IDEMPOTENT); + TestUploadDataProvider dataProvider = new TestUploadDataProvider( + TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); + dataProvider.addRead("test".getBytes()); + urlRequestBuilder.setUploadDataProvider(dataProvider, callback.getExecutor()); + urlRequestBuilder.build().start(); + callback.blockForDone(); + return callback; + } + + void doInitialHttp2Request() { + // Do a request to https://127.0.0.1:test_server_port/ + TestUrlRequestCallback callback = doBasicGetRequest(); + + // Make sure the request succeeded. It should go out over HTTP/2 as it's the first + // request and HTTP/3 support is not established. + assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); + assertEquals("h2", callback.mResponseInfo.getNegotiatedProtocol()); } @Test @SmallTest @Feature({"Cronet"}) - public void testInitEngineAndStartRequest() throws Exception { + public void basicHttp3Get() throws Exception { // Ideally we could override this from the command line but that's TBD. setUp(printEnvoyLogs); - // Set up the Envoy engine. - NativeCronvoyEngineBuilderImpl nativeCronetEngineBuilder = - new NativeCronvoyEngineBuilderImpl(ApplicationProvider.getApplicationContext()); - if (printEnvoyLogs) { - nativeCronetEngineBuilder.setLogger(logger); - nativeCronetEngineBuilder.setLogLevel(EnvoyEngine.LogLevel.TRACE); - } - // Make sure the handshake will work despite lack of real certs. - nativeCronetEngineBuilder.setMockCertVerifierForTesting(); - cronvoyEngine = new CronvoyUrlRequestContext(nativeCronetEngineBuilder); + // Do the initial HTTP/2 request to get the alt-svc response. + doInitialHttp2Request(); - // Do a request to https://127.0.0.1:test_server_port/ - TestUrlRequestCallback callback1 = new TestUrlRequestCallback(); - String newUrl = "https://" + http2TestServer.getAddress() + "/"; + // Set up a second request, which will hopefully go out over HTTP/3 due to alt-svc + // advertisement. + TestUrlRequestCallback callback = doBasicGetRequest(); + + // Verify the second request used HTTP/3 + assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); + assertEquals("h3", callback.mResponseInfo.getNegotiatedProtocol()); + } + + @Test + @SmallTest + @Feature({"Cronet"}) + public void failToHttp2() throws Exception { + // Ideally we could override this from the command line but that's TBD. + setUp(printEnvoyLogs); + + // Do the initial HTTP/2 request to get the alt-svc response. + doInitialHttp2Request(); + + // Set up a second request, which will hopefully go out over HTTP/3 due to alt-svc + // advertisement. + TestUrlRequestCallback getCallback = doBasicGetRequest(); + + // Verify the second request used HTTP/3 + assertEquals(200, getCallback.mResponseInfo.getHttpStatusCode()); + assertEquals("h3", getCallback.mResponseInfo.getNegotiatedProtocol()); + + // Now stop the HTTP/3 server. + http3TestServer.shutdown(); + http3TestServer = null; + + // The next request will fail on HTTP2 but should succeed on HTTP/2 despite having a body. + TestUrlRequestCallback postCallback = doBasicPostRequest(); + assertEquals(200, postCallback.mResponseInfo.getHttpStatusCode()); + assertEquals("h2", postCallback.mResponseInfo.getNegotiatedProtocol()); + } + + @Test + @SmallTest + @Feature({"Cronet"}) + public void testNoRetryPostAfterHandshake() throws Exception { + setUp(printEnvoyLogs); + + // Do the initial HTTP/2 request to get the alt-svc response. + doInitialHttp2Request(); + + // Set up a second request, which will hopefully go out over HTTP/3 due to alt-svc + // advertisement. + TestUrlRequestCallback callback = new TestUrlRequestCallback(); UrlRequest.Builder urlRequestBuilder = - cronvoyEngine.newUrlRequestBuilder(newUrl, callback1, callback1.getExecutor()); + cronvoyEngine.newUrlRequestBuilder(testServerUrl, callback, callback.getExecutor()); + // Set the upstream to reset after the request. + urlRequestBuilder.addHeader("reset_after_request", "yes"); + urlRequestBuilder.addHeader("content-type", "text"); + urlRequestBuilder.setHttpMethod("POST"); + TestUploadDataProvider dataProvider = new TestUploadDataProvider( + TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); + dataProvider.addRead("test".getBytes()); + urlRequestBuilder.setUploadDataProvider(dataProvider, callback.getExecutor()); + urlRequestBuilder.build().start(); - callback1.blockForDone(); + callback.blockForDone(); - // Make sure the request succeeded. It should go out over HTTP/2 as it's the first - // request and HTTP/3 support is not established. - assertEquals(200, callback1.mResponseInfo.getHttpStatusCode()); - assertEquals("h2", callback1.mResponseInfo.getNegotiatedProtocol()); + // Both HTTP/3 and HTTP/2 servers will reset after the request. + assertTrue(callback.mOnErrorCalled); + // There are 2 requests - the initial HTTP/2 alt-svc request and the HTTP/3 request. + // By default, POST requests will not retry. + String stats = cronvoyEngine.getEnvoyEngine().dumpStats(); + assertTrue(stats.contains("cluster.base.upstream_rq_total: 2")); + } + + // Set up to use HTTP/3, then force HTTP/3 to fail post-handshake. The request should + // be retried on HTTP/2 and HTTP/3 will be marked broken. + public void retryPostHandshake() throws Exception { + // Do the initial HTTP/2 request to get the alt-svc response. + doInitialHttp2Request(); // Set up a second request, which will hopefully go out over HTTP/3 due to alt-svc // advertisement. - TestUrlRequestCallback callback2 = new TestUrlRequestCallback(); - UrlRequest.Builder urlRequestBuilder2 = - cronvoyEngine.newUrlRequestBuilder(newUrl, callback2, callback2.getExecutor()); - urlRequestBuilder2.build().start(); - callback2.blockForDone(); + TestUrlRequestCallback callback = new TestUrlRequestCallback(); + ExperimentalUrlRequest.Builder urlRequestBuilder = + cronvoyEngine.newUrlRequestBuilder(testServerUrl, callback, callback.getExecutor()); + urlRequestBuilder.addHeader("reset_after_request", "yes"); + urlRequestBuilder.addHeader("content-type", "text"); + urlRequestBuilder.setHttpMethod("POST"); + TestUploadDataProvider dataProvider = new TestUploadDataProvider( + TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()); + dataProvider.addRead("test".getBytes()); + urlRequestBuilder.setUploadDataProvider(dataProvider, callback.getExecutor()); + // Set the request to be idempotent so Envoy knows it's safe to retry post-handshake + urlRequestBuilder.setIdempotency(ExperimentalUrlRequest.Builder.IDEMPOTENT); - // Verify the second request used HTTP/3 - assertEquals(200, callback2.mResponseInfo.getHttpStatusCode()); - assertEquals("h3", callback2.mResponseInfo.getNegotiatedProtocol()); + urlRequestBuilder.build().start(); + callback.blockForDone(); + + String stats = cronvoyEngine.getEnvoyEngine().dumpStats(); + + // Both HTTP/3 and HTTP/2 servers will reset after the request. + assertTrue(callback.mOnErrorCalled); + // Unlike testNoRetryPostPostHandshake there will be 3 requests - the initial HTTP/2 alt-svc + // request, the HTTP/3 request, and the HTTP/2 retry. + assertTrue(stats.contains("cluster.base.upstream_rq_total: 3")); + assertTrue(stats.contains("cluster.base.upstream_rq_retry: 1")); + // Because H/3 was disallowed on the final retry and TCP connected, H/3 gets marked as broken. + assertTrue(stats.contains("cluster.base.upstream_http3_broken: 1")); + } + + @Test + @SmallTest + @Feature({"Cronet"}) + public void testRetryPostHandshake() throws Exception { + setUp(printEnvoyLogs); + + retryPostHandshake(); + } + + @Test + @SmallTest + @Feature({"Cronet"}) + public void networkChangeAffectsBrokenness() throws Exception { + setUp(printEnvoyLogs); + + // Set HTTP/3 to be marked as broken. + retryPostHandshake(); + + // From prior calls, there was one HTTP/3 connection established. + String preStats = cronvoyEngine.getEnvoyEngine().dumpStats(); + assertTrue(preStats.contains("cluster.base.upstream_cx_http3_total: 1")); + + // This should change QUIC brokenness to "failed recently". + cronvoyEngine.getEnvoyEngine().setPreferredNetwork(EnvoyNetworkType.WLAN); + + // The next request may go out over HTTP/2 or HTTP/3 (depends on who wins the race) + // but HTTP/3 will be tried. + doBasicGetRequest(); + String postStats = cronvoyEngine.getEnvoyEngine().dumpStats(); + assertTrue(postStats.contains("cluster.base.upstream_cx_http3_total: 2")); } } From 12f83d06ed9f7d3927c9f3d2017ab1e2111133d5 Mon Sep 17 00:00:00 2001 From: phlax Date: Tue, 6 Aug 2024 20:54:44 +0100 Subject: [PATCH 044/130] ci: Fix diskspace issue when caching checks (#35599) using the CI diskspace hack Signed-off-by: Ryan Northey --- .azure-pipelines/ci.yml | 3 +++ .azure-pipelines/stage/checks.yml | 1 + 2 files changed, 4 insertions(+) diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml index e73b1a5a059d..0c407ac00ba0 100644 --- a/.azure-pipelines/ci.yml +++ b/.azure-pipelines/ci.yml @@ -328,6 +328,9 @@ steps: else sudo chown -R azure-pipelines:azure-pipelines ${{ parameters.pathCacheTemp }}/all fi + sudo systemctl stop docker docker.socket + sudo umount /var/lib/docker + sudo rm -rf /mnt/docker displayName: "Cache/save (${{ parameters.cacheName}})" condition: and(succeeded(), ne('${{ parameters.cacheName }}', ''), ne(variables.CACHE_RESTORED, 'true')) diff --git a/.azure-pipelines/stage/checks.yml b/.azure-pipelines/stage/checks.yml index 63882b9fa159..b4e650287a23 100644 --- a/.azure-pipelines/stage/checks.yml +++ b/.azure-pipelines/stage/checks.yml @@ -83,6 +83,7 @@ jobs: cacheName: $(CI_TARGET) envoyBuildFilterExample: $(ENVOY_FILTER_EXAMPLE) cacheTestResults: ${{ parameters.cacheTestResults }} + diskspaceHack: true managedAgent: false repoFetchDepth: $(REPO_FETCH_DEPTH) repoFetchTags: $(REPO_FETCH_TAGS) From de6ae0f2bbf22d1a12f57d27ac6115df6669dadd Mon Sep 17 00:00:00 2001 From: phlax Date: Tue, 6 Aug 2024 20:56:32 +0100 Subject: [PATCH 045/130] ci/verify: Fix failing ubuntu downgrade (#35608) Signed-off-by: Ryan Northey --- .github/workflows/_stage_verify.yml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/.github/workflows/_stage_verify.yml b/.github/workflows/_stage_verify.yml index 1377a5cb6cfb..2110aec94924 100644 --- a/.github/workflows/_stage_verify.yml +++ b/.github/workflows/_stage_verify.yml @@ -49,24 +49,6 @@ jobs: export NO_BUILD_SETUP=1 rbe: false steps-pre: | - - run: | - # TODO(phlax): Remove this once resolved properly - # Downgrade Docker to workaround https://github.com/containers/skopeo/issues/2365 - sudo install -m 0755 -d /etc/apt/keyrings - sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc - sudo chmod a+r /etc/apt/keyrings/docker.asc - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \ - https://download.docker.com/linux/ubuntu \ - $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \ - | sudo tee /etc/apt/sources.list.d/docker.list \ - > /dev/null - sudo apt-get update - sudo apt-get install -y -qq --allow-downgrades \ - docker-ce=5:24.0.9-1~ubuntu.24.04~noble \ - docker-ce-cli=5:24.0.9-1~ubuntu.24.04~noble - sudo systemctl restart docker - sudo docker --version - shell: bash - id: url uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.30 with: From eb2fbdcb536e78f7e984a95ee36d0eabf6e17261 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 6 Aug 2024 16:30:48 -0400 Subject: [PATCH 046/130] mobile: updating files (#35607) --- mobile/test/performance/files_em_does_not_use | 1 + 1 file changed, 1 insertion(+) diff --git a/mobile/test/performance/files_em_does_not_use b/mobile/test/performance/files_em_does_not_use index 5d9be3672ef2..16c1f69644f8 100644 --- a/mobile/test/performance/files_em_does_not_use +++ b/mobile/test/performance/files_em_does_not_use @@ -20,3 +20,4 @@ source/common/tls/ocsp/ocsp.h source/common/formatter/http_specific_formatter.h source/common/formatter/stream_info_formatter.h source/common/tls/default_tls_certificate_selector.h +source/common/json/json_streamer.h From 6bdb8ff640b43e86014a40dde8e2fa05029a842f Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Tue, 6 Aug 2024 15:15:58 -0700 Subject: [PATCH 047/130] dynamic_modules: adds initial object loading logic (#35550) Commit Message: dynamic_modules: adds initial object loading logic Additional Description: This is the very first commit of the dynamic loading feature discussed among community members. This is the effort to upstream the playground repository https://github.com/mathetake/envoy-dynamic-modules as an Envoy core extension. Series of commits will follow this little by little. #2053, #24230, #32502 Risk Level: N/A (not compiled into the final build yet) Testing: unit Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A [Optional Runtime guard:] [Optional Fixes #Issue] [Optional Fixes commit #PR or SHA] [Optional Deprecated:] [Optional [API Considerations](https://github.com/envoyproxy/envoy/blob/main/api/review_checklist.md):] --------- Signed-off-by: Takeshi Yoneda --- CODEOWNERS | 5 +- source/extensions/dynamic_modules/BUILD | 22 ++++++ .../dynamic_modules/dynamic_modules.cc | 44 ++++++++++++ .../dynamic_modules/dynamic_modules.h | 65 ++++++++++++++++++ test/extensions/dynamic_modules/BUILD | 22 ++++++ .../dynamic_modules/dynamic_modules_test.cc | 68 +++++++++++++++++++ .../dynamic_modules/test_data/BUILD | 7 ++ .../dynamic_modules/test_data/no_op.c | 5 ++ .../dynamic_modules/test_data/test_data.bzl | 14 ++++ tools/spelling/spelling_dictionary.txt | 5 ++ 10 files changed, 255 insertions(+), 2 deletions(-) create mode 100644 source/extensions/dynamic_modules/BUILD create mode 100644 source/extensions/dynamic_modules/dynamic_modules.cc create mode 100644 source/extensions/dynamic_modules/dynamic_modules.h create mode 100644 test/extensions/dynamic_modules/BUILD create mode 100644 test/extensions/dynamic_modules/dynamic_modules_test.cc create mode 100644 test/extensions/dynamic_modules/test_data/BUILD create mode 100644 test/extensions/dynamic_modules/test_data/no_op.c create mode 100644 test/extensions/dynamic_modules/test_data/test_data.bzl diff --git a/CODEOWNERS b/CODEOWNERS index 68e00305fd21..d8f7be277fc2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -338,6 +338,7 @@ extensions/filters/http/oauth2 @derekargueta @mattklein123 /*/extensions/health_checkers/grpc @zuercher @botengyao /*/extensions/health_checkers/http @zuercher @botengyao /*/extensions/health_checkers/tcp @zuercher @botengyao +/*/extensions/health_checkers/common @zuercher @botengyao # Health check event sinks /*/extensions/health_check/event_sinks/file @botengyao @yanavlasov # IP Geolocation @@ -349,8 +350,8 @@ extensions/filters/http/oauth2 @derekargueta @mattklein123 /*/extensions/filters/http/match_delegate @wbpcode @jstraceski @tyxia # Generic proxy and related extensions /*/extensions/filters/network/generic_proxy/ @wbpcode @soulxu - -/*/extensions/health_checkers/common @zuercher @botengyao +# Dynamic Modules +/*/extensions/dynamic_modules @mattklein123 @mathetake @marc-barry # HTTP credential injector /*/extensions/filters/http/credential_injector @zhaohuabing @kyessenov diff --git a/source/extensions/dynamic_modules/BUILD b/source/extensions/dynamic_modules/BUILD new file mode 100644 index 000000000000..008e2ad84734 --- /dev/null +++ b/source/extensions/dynamic_modules/BUILD @@ -0,0 +1,22 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_library( + name = "dynamic_modules_lib", + srcs = [ + "dynamic_modules.cc", + ], + hdrs = [ + "dynamic_modules.h", + ], + deps = [ + "//envoy/common:exception_lib", + ], +) diff --git a/source/extensions/dynamic_modules/dynamic_modules.cc b/source/extensions/dynamic_modules/dynamic_modules.cc new file mode 100644 index 000000000000..e28917fac16f --- /dev/null +++ b/source/extensions/dynamic_modules/dynamic_modules.cc @@ -0,0 +1,44 @@ +#include "source/extensions/dynamic_modules/dynamic_modules.h" + +#include + +#include +#include + +#include "envoy/common/exception.h" + +namespace Envoy { +namespace Extensions { +namespace DynamicModules { + +absl::StatusOr newDynamicModule(const absl::string_view object_file_path, + const bool do_not_close) { + // RTLD_LOCAL is always needed to avoid collisions between multiple modules. + // RTLD_LAZY is required for not only performance but also simply to load the module, otherwise + // dlopen results in Invalid argument. + int mode = RTLD_LOCAL | RTLD_LAZY; + if (do_not_close) { + mode |= RTLD_NODELETE; + } + + const std::filesystem::path file_path_absolute = std::filesystem::absolute(object_file_path); + void* handle = dlopen(file_path_absolute.c_str(), mode); + if (handle == nullptr) { + return absl::InvalidArgumentError( + absl::StrCat("Failed to load dynamic module: ", object_file_path, " : ", dlerror())); + } + return std::make_shared(handle); +} + +DynamicModule::~DynamicModule() { dlclose(handle_); } + +void* DynamicModule::getSymbol(const absl::string_view symbol_ref) const { + // TODO(mathetake): maybe we should accept null-terminated const char* instead of string_view to + // avoid unnecessary copy because it is likely that this is only called for a constant string, + // though this is not a performance critical path. + return dlsym(handle_, std::string(symbol_ref).c_str()); +} + +} // namespace DynamicModules +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/dynamic_modules/dynamic_modules.h b/source/extensions/dynamic_modules/dynamic_modules.h new file mode 100644 index 000000000000..5e20f729cb2a --- /dev/null +++ b/source/extensions/dynamic_modules/dynamic_modules.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Extensions { +namespace DynamicModules { + +/** + * A class for loading and managing dynamic modules. This corresponds to a single dlopen handle. + * When the DynamicModule object is destroyed, the dlopen handle is closed. + * + * This class is supposed to be initialized once in the main thread and can be shared with other + * threads. + */ +class DynamicModule { +public: + DynamicModule(void* handle) : handle_(handle) {} + ~DynamicModule(); + + /** + * Get a function pointer from the dynamic module with a specific type. + * @param T the function pointer type to cast the symbol to. + * @param symbol_ref the symbol to look up. + * @return the symbol if found, otherwise nullptr. + */ + template T getFunctionPointer(const absl::string_view symbol_ref) const { + static_assert(std::is_pointer::value && + std::is_function::type>::value, + "T must be a function pointer type"); + return reinterpret_cast(getSymbol(symbol_ref)); + } + +private: + /** + * Get a symbol from the dynamic module. + * @param symbol_ref the symbol to look up. + * @return the symbol if found, otherwise nullptr. + */ + void* getSymbol(const absl::string_view symbol_ref) const; + + // The raw dlopen handle that can be used to look up symbols. + void* handle_; +}; + +using DynamicModuleSharedPtr = std::shared_ptr; + +/** + * Creates a new DynamicModule. + * @param object_file_path the path to the object file to load. + * @param do_not_close if true, the dlopen will be called with RTLD_NODELETE, so the loaded object + * will not be destroyed. This is useful when an object has some global state that should not be + * terminated. For example, c-shared objects compiled by Go doesn't support dlclose + * https://github.com/golang/go/issues/11100. + */ +absl::StatusOr newDynamicModule(const absl::string_view object_file_path, + const bool do_not_close); + +} // namespace DynamicModules +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/dynamic_modules/BUILD b/test/extensions/dynamic_modules/BUILD new file mode 100644 index 000000000000..8c831226c66a --- /dev/null +++ b/test/extensions/dynamic_modules/BUILD @@ -0,0 +1,22 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_test", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_test( + name = "dynamic_modules_test", + srcs = ["dynamic_modules_test.cc"], + data = [ + "//test/extensions/dynamic_modules/test_data:no_op", + ], + deps = [ + "//source/extensions/dynamic_modules:dynamic_modules_lib", + "//test/test_common:environment_lib", + "//test/test_common:utility_lib", + ], +) diff --git a/test/extensions/dynamic_modules/dynamic_modules_test.cc b/test/extensions/dynamic_modules/dynamic_modules_test.cc new file mode 100644 index 000000000000..6f5f21f5988f --- /dev/null +++ b/test/extensions/dynamic_modules/dynamic_modules_test.cc @@ -0,0 +1,68 @@ +#include + +#include "envoy/common/exception.h" + +#include "source/extensions/dynamic_modules/dynamic_modules.h" + +#include "test/test_common/environment.h" +#include "test/test_common/utility.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace DynamicModules { + +// This loads a shared object file from the test_data directory. +std::string testSharedObjectPath(std::string name) { + return TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/dynamic_modules/test_data/") + + "lib" + name + ".so"; +} + +TEST(DynamicModuleTest, InvalidPath) { + absl::StatusOr result = newDynamicModule("invalid_name", false); + EXPECT_FALSE(result.ok()); + EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument); +} + +TEST(DynamicModuleTest, LoadNoOp) { + using GetSomeVariableFuncType = int (*)(); + absl::StatusOr module = + newDynamicModule(testSharedObjectPath("no_op"), false); + EXPECT_TRUE(module.ok()); + const auto getSomeVariable = + module->get()->getFunctionPointer("getSomeVariable"); + EXPECT_EQ(getSomeVariable(), 1); + EXPECT_EQ(getSomeVariable(), 2); + EXPECT_EQ(getSomeVariable(), 3); + + // Release the module, and reload it. + module->reset(); + module = + newDynamicModule(testSharedObjectPath("no_op"), true); // This time, do not close the module. + EXPECT_TRUE(module.ok()); + + // This module must be reloaded and the variable must be reset. + const auto getSomeVariable2 = + (module->get()->getFunctionPointer("getSomeVariable")); + EXPECT_NE(getSomeVariable2, nullptr); + EXPECT_EQ(getSomeVariable2(), 1); // Start from 1 again. + EXPECT_EQ(getSomeVariable2(), 2); + EXPECT_EQ(getSomeVariable2(), 3); + + // Release the module, and reload it. + module->reset(); + module = newDynamicModule(testSharedObjectPath("no_op"), false); + EXPECT_TRUE(module.ok()); + + // This module must be the already loaded one, and the variable must be kept. + const auto getSomeVariable3 = + module->get()->getFunctionPointer("getSomeVariable"); + EXPECT_NE(getSomeVariable3, nullptr); + EXPECT_EQ(getSomeVariable3(), 4); // Start from 4. +} + +} // namespace DynamicModules +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/dynamic_modules/test_data/BUILD b/test/extensions/dynamic_modules/test_data/BUILD new file mode 100644 index 000000000000..f199f10d3218 --- /dev/null +++ b/test/extensions/dynamic_modules/test_data/BUILD @@ -0,0 +1,7 @@ +load("//test/extensions/dynamic_modules/test_data:test_data.bzl", "test_program") + +licenses(["notice"]) # Apache 2 + +package(default_visibility = ["//visibility:public"]) + +test_program(name = "no_op") diff --git a/test/extensions/dynamic_modules/test_data/no_op.c b/test/extensions/dynamic_modules/test_data/no_op.c new file mode 100644 index 000000000000..81815e007da7 --- /dev/null +++ b/test/extensions/dynamic_modules/test_data/no_op.c @@ -0,0 +1,5 @@ +int getSomeVariable() { + static int some_variable = 0; + some_variable++; + return some_variable; +} diff --git a/test/extensions/dynamic_modules/test_data/test_data.bzl b/test/extensions/dynamic_modules/test_data/test_data.bzl new file mode 100644 index 000000000000..fbe1af7f580e --- /dev/null +++ b/test/extensions/dynamic_modules/test_data/test_data.bzl @@ -0,0 +1,14 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") + +# This declares a cc_library target that is used to build a shared library. +# name + ".c" is the source file that is compiled to create the shared library. +def test_program(name): + cc_library( + name = name, + srcs = [name + ".c"], + linkopts = [ + "-shared", + "-fPIC", + ], + linkstatic = False, + ) diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index eff88c22e99e..04d4f7e0f3a3 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -93,6 +93,7 @@ OWS Preconnecting RCVBUF RTCP +RTLD RTP SOH SPC @@ -306,6 +307,7 @@ NOAUTH NOCHECKRESP NODATA NODELAY +NODELETE NOLINT NOLINTNEXTLINE NONAME @@ -721,6 +723,9 @@ dgst dir dirname djb +dlclose +dlopen +dlsym downcalls downcasted downcased From c9a65a13ecc9e5d463ad125b4a028417924de096 Mon Sep 17 00:00:00 2001 From: phlax Date: Wed, 7 Aug 2024 08:40:00 +0100 Subject: [PATCH 048/130] ci: Remove failing Docker cleanup (#35616) Signed-off-by: Ryan Northey --- .azure-pipelines/ci.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml index 0c407ac00ba0..d4454b618ba3 100644 --- a/.azure-pipelines/ci.yml +++ b/.azure-pipelines/ci.yml @@ -328,10 +328,6 @@ steps: else sudo chown -R azure-pipelines:azure-pipelines ${{ parameters.pathCacheTemp }}/all fi - sudo systemctl stop docker docker.socket - sudo umount /var/lib/docker - sudo rm -rf /mnt/docker - displayName: "Cache/save (${{ parameters.cacheName}})" condition: and(succeeded(), ne('${{ parameters.cacheName }}', ''), ne(variables.CACHE_RESTORED, 'true')) From 420ac6bd8ceb410f641da8eb95db4269e8f791ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 08:44:55 +0100 Subject: [PATCH 049/130] build(deps): bump actions/upload-artifact from 4.3.5 to 4.3.6 (#35604) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 40649c09044a..54e732e81292 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -33,7 +33,7 @@ jobs: publish_results: true - name: "Upload artifact" - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: SARIF file path: results.sarif From 4499057c3363d85990a91a2b014d5c36acee694a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 08:45:19 +0100 Subject: [PATCH 050/130] build(deps): bump github/codeql-action from 3.25.15 to 3.26.0 (#35613) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-daily.yml | 4 ++-- .github/workflows/codeql-push.yml | 4 ++-- .github/workflows/scorecard.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql-daily.yml b/.github/workflows/codeql-daily.yml index 23bbd77df104..f3038c666c16 100644 --- a/.github/workflows/codeql-daily.yml +++ b/.github/workflows/codeql-daily.yml @@ -34,7 +34,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # codeql-bundle-v3.25.15 + uses: github/codeql-action/init@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # codeql-bundle-v3.26.0 # Override language selection by uncommenting this and choosing your languages with: languages: cpp @@ -68,4 +68,4 @@ jobs: git clean -xdf - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # codeql-bundle-v3.25.15 + uses: github/codeql-action/analyze@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # codeql-bundle-v3.26.0 diff --git a/.github/workflows/codeql-push.yml b/.github/workflows/codeql-push.yml index 8b9b16a2f184..014c3368afa3 100644 --- a/.github/workflows/codeql-push.yml +++ b/.github/workflows/codeql-push.yml @@ -65,7 +65,7 @@ jobs: - name: Initialize CodeQL if: ${{ env.BUILD_TARGETS != '' }} - uses: github/codeql-action/init@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # codeql-bundle-v3.25.15 + uses: github/codeql-action/init@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # codeql-bundle-v3.26.0 with: languages: cpp @@ -109,4 +109,4 @@ jobs: - name: Perform CodeQL Analysis if: ${{ env.BUILD_TARGETS != '' }} - uses: github/codeql-action/analyze@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # codeql-bundle-v3.25.15 + uses: github/codeql-action/analyze@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # codeql-bundle-v3.26.0 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 54e732e81292..0b3ba9d388ad 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -40,6 +40,6 @@ jobs: retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 + uses: github/codeql-action/upload-sarif@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0 with: sarif_file: results.sarif From 11bba2ab740095c1b0699fde80bda47d30547755 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 08:45:37 +0100 Subject: [PATCH 051/130] build(deps): bump cffi from 1.16.0 to 1.17.0 in /tools/base (#35614) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/base/requirements.txt | 121 ++++++++++++++++++++---------------- 1 file changed, 68 insertions(+), 53 deletions(-) diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index 05683ddfc126..4b5dd3996395 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -239,59 +239,74 @@ certifi==2024.7.4 \ # via # aioquic # requests -cffi==1.16.0 \ - --hash=sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc \ - --hash=sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a \ - --hash=sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417 \ - --hash=sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab \ - --hash=sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520 \ - --hash=sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36 \ - --hash=sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743 \ - --hash=sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8 \ - --hash=sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed \ - --hash=sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684 \ - --hash=sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56 \ - --hash=sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324 \ - --hash=sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d \ - --hash=sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235 \ - --hash=sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e \ - --hash=sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088 \ - --hash=sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000 \ - --hash=sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7 \ - --hash=sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e \ - --hash=sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673 \ - --hash=sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c \ - --hash=sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe \ - --hash=sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2 \ - --hash=sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098 \ - --hash=sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8 \ - --hash=sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a \ - --hash=sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0 \ - --hash=sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b \ - --hash=sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896 \ - --hash=sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e \ - --hash=sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9 \ - --hash=sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2 \ - --hash=sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b \ - --hash=sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6 \ - --hash=sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404 \ - --hash=sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f \ - --hash=sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0 \ - --hash=sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4 \ - --hash=sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc \ - --hash=sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936 \ - --hash=sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba \ - --hash=sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872 \ - --hash=sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb \ - --hash=sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614 \ - --hash=sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1 \ - --hash=sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d \ - --hash=sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969 \ - --hash=sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b \ - --hash=sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4 \ - --hash=sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627 \ - --hash=sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956 \ - --hash=sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357 +cffi==1.17.0 \ + --hash=sha256:011aff3524d578a9412c8b3cfaa50f2c0bd78e03eb7af7aa5e0df59b158efb2f \ + --hash=sha256:0a048d4f6630113e54bb4b77e315e1ba32a5a31512c31a273807d0027a7e69ab \ + --hash=sha256:0bb15e7acf8ab35ca8b24b90af52c8b391690ef5c4aec3d31f38f0d37d2cc499 \ + --hash=sha256:0d46ee4764b88b91f16661a8befc6bfb24806d885e27436fdc292ed7e6f6d058 \ + --hash=sha256:0e60821d312f99d3e1569202518dddf10ae547e799d75aef3bca3a2d9e8ee693 \ + --hash=sha256:0fdacad9e0d9fc23e519efd5ea24a70348305e8d7d85ecbb1a5fa66dc834e7fb \ + --hash=sha256:14b9cbc8f7ac98a739558eb86fabc283d4d564dafed50216e7f7ee62d0d25377 \ + --hash=sha256:17c6d6d3260c7f2d94f657e6872591fe8733872a86ed1345bda872cfc8c74885 \ + --hash=sha256:1a2ddbac59dc3716bc79f27906c010406155031a1c801410f1bafff17ea304d2 \ + --hash=sha256:2404f3de742f47cb62d023f0ba7c5a916c9c653d5b368cc966382ae4e57da401 \ + --hash=sha256:24658baf6224d8f280e827f0a50c46ad819ec8ba380a42448e24459daf809cf4 \ + --hash=sha256:24aa705a5f5bd3a8bcfa4d123f03413de5d86e497435693b638cbffb7d5d8a1b \ + --hash=sha256:2770bb0d5e3cc0e31e7318db06efcbcdb7b31bcb1a70086d3177692a02256f59 \ + --hash=sha256:331ad15c39c9fe9186ceaf87203a9ecf5ae0ba2538c9e898e3a6967e8ad3db6f \ + --hash=sha256:3aa9d43b02a0c681f0bfbc12d476d47b2b2b6a3f9287f11ee42989a268a1833c \ + --hash=sha256:41f4915e09218744d8bae14759f983e466ab69b178de38066f7579892ff2a555 \ + --hash=sha256:4304d4416ff032ed50ad6bb87416d802e67139e31c0bde4628f36a47a3164bfa \ + --hash=sha256:435a22d00ec7d7ea533db494da8581b05977f9c37338c80bc86314bec2619424 \ + --hash=sha256:45f7cd36186db767d803b1473b3c659d57a23b5fa491ad83c6d40f2af58e4dbb \ + --hash=sha256:48b389b1fd5144603d61d752afd7167dfd205973a43151ae5045b35793232aa2 \ + --hash=sha256:4e67d26532bfd8b7f7c05d5a766d6f437b362c1bf203a3a5ce3593a645e870b8 \ + --hash=sha256:516a405f174fd3b88829eabfe4bb296ac602d6a0f68e0d64d5ac9456194a5b7e \ + --hash=sha256:5ba5c243f4004c750836f81606a9fcb7841f8874ad8f3bf204ff5e56332b72b9 \ + --hash=sha256:5bdc0f1f610d067c70aa3737ed06e2726fd9d6f7bfee4a351f4c40b6831f4e82 \ + --hash=sha256:6107e445faf057c118d5050560695e46d272e5301feffda3c41849641222a828 \ + --hash=sha256:6327b572f5770293fc062a7ec04160e89741e8552bf1c358d1a23eba68166759 \ + --hash=sha256:669b29a9eca6146465cc574659058ed949748f0809a2582d1f1a324eb91054dc \ + --hash=sha256:6ce01337d23884b21c03869d2f68c5523d43174d4fc405490eb0091057943118 \ + --hash=sha256:6d872186c1617d143969defeadac5a904e6e374183e07977eedef9c07c8953bf \ + --hash=sha256:6f76a90c345796c01d85e6332e81cab6d70de83b829cf1d9762d0a3da59c7932 \ + --hash=sha256:70d2aa9fb00cf52034feac4b913181a6e10356019b18ef89bc7c12a283bf5f5a \ + --hash=sha256:7cbc78dc018596315d4e7841c8c3a7ae31cc4d638c9b627f87d52e8abaaf2d29 \ + --hash=sha256:856bf0924d24e7f93b8aee12a3a1095c34085600aa805693fb7f5d1962393206 \ + --hash=sha256:8a98748ed1a1df4ee1d6f927e151ed6c1a09d5ec21684de879c7ea6aa96f58f2 \ + --hash=sha256:93a7350f6706b31f457c1457d3a3259ff9071a66f312ae64dc024f049055f72c \ + --hash=sha256:964823b2fc77b55355999ade496c54dde161c621cb1f6eac61dc30ed1b63cd4c \ + --hash=sha256:a003ac9edc22d99ae1286b0875c460351f4e101f8c9d9d2576e78d7e048f64e0 \ + --hash=sha256:a0ce71725cacc9ebf839630772b07eeec220cbb5f03be1399e0457a1464f8e1a \ + --hash=sha256:a47eef975d2b8b721775a0fa286f50eab535b9d56c70a6e62842134cf7841195 \ + --hash=sha256:a8b5b9712783415695663bd463990e2f00c6750562e6ad1d28e072a611c5f2a6 \ + --hash=sha256:a9015f5b8af1bb6837a3fcb0cdf3b874fe3385ff6274e8b7925d81ccaec3c5c9 \ + --hash=sha256:aec510255ce690d240f7cb23d7114f6b351c733a74c279a84def763660a2c3bc \ + --hash=sha256:b00e7bcd71caa0282cbe3c90966f738e2db91e64092a877c3ff7f19a1628fdcb \ + --hash=sha256:b50aaac7d05c2c26dfd50c3321199f019ba76bb650e346a6ef3616306eed67b0 \ + --hash=sha256:b7b6ea9e36d32582cda3465f54c4b454f62f23cb083ebc7a94e2ca6ef011c3a7 \ + --hash=sha256:bb9333f58fc3a2296fb1d54576138d4cf5d496a2cc118422bd77835e6ae0b9cb \ + --hash=sha256:c1c13185b90bbd3f8b5963cd8ce7ad4ff441924c31e23c975cb150e27c2bf67a \ + --hash=sha256:c3b8bd3133cd50f6b637bb4322822c94c5ce4bf0d724ed5ae70afce62187c492 \ + --hash=sha256:c5d97162c196ce54af6700949ddf9409e9833ef1003b4741c2b39ef46f1d9720 \ + --hash=sha256:c815270206f983309915a6844fe994b2fa47e5d05c4c4cef267c3b30e34dbe42 \ + --hash=sha256:cab2eba3830bf4f6d91e2d6718e0e1c14a2f5ad1af68a89d24ace0c6b17cced7 \ + --hash=sha256:d1df34588123fcc88c872f5acb6f74ae59e9d182a2707097f9e28275ec26a12d \ + --hash=sha256:d6bdcd415ba87846fd317bee0774e412e8792832e7805938987e4ede1d13046d \ + --hash=sha256:db9a30ec064129d605d0f1aedc93e00894b9334ec74ba9c6bdd08147434b33eb \ + --hash=sha256:dbc183e7bef690c9abe5ea67b7b60fdbca81aa8da43468287dae7b5c046107d4 \ + --hash=sha256:dca802c8db0720ce1c49cce1149ff7b06e91ba15fa84b1d59144fef1a1bc7ac2 \ + --hash=sha256:dec6b307ce928e8e112a6bb9921a1cb00a0e14979bf28b98e084a4b8a742bd9b \ + --hash=sha256:df8bb0010fdd0a743b7542589223a2816bdde4d94bb5ad67884348fa2c1c67e8 \ + --hash=sha256:e4094c7b464cf0a858e75cd14b03509e84789abf7b79f8537e6a72152109c76e \ + --hash=sha256:e4760a68cab57bfaa628938e9c2971137e05ce48e762a9cb53b76c9b569f1204 \ + --hash=sha256:eb09b82377233b902d4c3fbeeb7ad731cdab579c6c6fda1f763cd779139e47c3 \ + --hash=sha256:eb862356ee9391dc5a0b3cbc00f416b48c1b9a52d252d898e5b7696a5f9fe150 \ + --hash=sha256:ef9528915df81b8f4c7612b19b8628214c65c9b7f74db2e34a646a0a2a0da2d4 \ + --hash=sha256:f3157624b7558b914cb039fd1af735e5e8049a87c817cc215109ad1c8779df76 \ + --hash=sha256:f3e0992f23bbb0be00a921eae5363329253c3b86287db27092461c887b791e5e \ + --hash=sha256:f9338cc05451f1942d0d8203ec2c346c830f8e86469903d5126c1f0a13a2bcbb \ + --hash=sha256:ffef8fd58a36fb5f1196919638f73dd3ae0db1a878982b27a9a5a176ede4ba91 # via # -r requirements.in # cryptography From 9b1010de36cafebc6114b8e44791f316178e5ed9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 08:45:54 +0100 Subject: [PATCH 052/130] build(deps): bump pyyaml from 6.0.1 to 6.0.2 in /tools/base (#35615) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/base/requirements.txt | 106 ++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index 4b5dd3996395..d715dd3c0189 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -1157,58 +1157,60 @@ pytz==2024.1 \ pyu2f==0.1.5 \ --hash=sha256:a3caa3a11842fc7d5746376f37195e6af5f17c0a15737538bb1cebf656fb306b # via google-reauth -pyyaml==6.0.1 \ - --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \ - --hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \ - --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \ - --hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \ - --hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \ - --hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \ - --hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \ - --hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \ - --hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \ - --hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \ - --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \ - --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \ - --hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \ - --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \ - --hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \ - --hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \ - --hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \ - --hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \ - --hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \ - --hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \ - --hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \ - --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \ - --hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \ - --hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \ - --hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \ - --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \ - --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \ - --hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \ - --hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \ - --hash=sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef \ - --hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \ - --hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \ - --hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \ - --hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \ - --hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \ - --hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \ - --hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \ - --hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \ - --hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \ - --hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \ - --hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \ - --hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \ - --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \ - --hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \ - --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \ - --hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \ - --hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \ - --hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \ - --hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \ - --hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \ - --hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f +pyyaml==6.0.2 \ + --hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \ + --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \ + --hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \ + --hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \ + --hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \ + --hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \ + --hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \ + --hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \ + --hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \ + --hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \ + --hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \ + --hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \ + --hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \ + --hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \ + --hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \ + --hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \ + --hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \ + --hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \ + --hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \ + --hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \ + --hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \ + --hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \ + --hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \ + --hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \ + --hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \ + --hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \ + --hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \ + --hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \ + --hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \ + --hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \ + --hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \ + --hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \ + --hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \ + --hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \ + --hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \ + --hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \ + --hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \ + --hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \ + --hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \ + --hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \ + --hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \ + --hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \ + --hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \ + --hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \ + --hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \ + --hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \ + --hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \ + --hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \ + --hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \ + --hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \ + --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \ + --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \ + --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 # via # -r requirements.in # aio-core From 2e6c1e17b48c0cffb5256c4f071300f34226ecc3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 09:42:29 +0100 Subject: [PATCH 053/130] build(deps): bump envoyproxy/toolshed from actions-v0.2.33 to 0.2.34 (#35618) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/_cache.yml | 8 ++++---- .github/workflows/_finish.yml | 8 ++++---- .github/workflows/_load.yml | 10 +++++----- .github/workflows/_load_env.yml | 8 ++++---- .github/workflows/_request.yml | 10 +++++----- .github/workflows/_run.yml | 14 +++++++------- .github/workflows/_stage_publish.yml | 4 ++-- .github/workflows/_start.yml | 10 +++++----- .github/workflows/codeql-daily.yml | 2 +- .github/workflows/codeql-push.yml | 2 +- .github/workflows/command.yml | 6 +++--- .github/workflows/envoy-dependency.yml | 18 +++++++++--------- .github/workflows/envoy-release.yml | 22 +++++++++++----------- .github/workflows/envoy-sync.yml | 4 ++-- .github/workflows/garbage.yml | 2 +- 15 files changed, 64 insertions(+), 64 deletions(-) diff --git a/.github/workflows/_cache.yml b/.github/workflows/_cache.yml index c6f7e79a32e4..a630de390dee 100644 --- a/.github/workflows/_cache.yml +++ b/.github/workflows/_cache.yml @@ -39,20 +39,20 @@ jobs: docker: runs-on: ubuntu-22.04 steps: - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 id: appauth name: Appauth (mutex lock) with: app_id: ${{ secrets.app-id }} key: ${{ secrets.app-key }} - - uses: envoyproxy/toolshed/gh-actions/docker/cache/prime@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/docker/cache/prime@actions-v0.2.34 id: docker name: Prime Docker cache (${{ inputs.image-tag }}) with: image-tag: ${{ inputs.image-tag }} lock-token: ${{ steps.appauth.outputs.token }} lock-repository: ${{ inputs.lock-repository }} - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 id: data name: Cache data with: @@ -60,7 +60,7 @@ jobs: input: | cached: ${{ steps.docker.outputs.cached }} key: ${{ inputs.image-tag }} - - uses: envoyproxy/toolshed/gh-actions/json/table@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/json/table@actions-v0.2.34 name: Summary with: json: ${{ steps.data.outputs.value }} diff --git a/.github/workflows/_finish.yml b/.github/workflows/_finish.yml index d5b638c4dce8..df59e74c64de 100644 --- a/.github/workflows/_finish.yml +++ b/.github/workflows/_finish.yml @@ -36,7 +36,7 @@ jobs: actions: read contents: read steps: - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 name: Incoming data id: needs with: @@ -87,7 +87,7 @@ jobs: summary: "Check has finished", text: $text}}}} - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 name: Print summary with: input: ${{ toJSON(steps.needs.outputs.value).summary-title }} @@ -95,13 +95,13 @@ jobs: "## \(.)" options: -Rr output-path: GITHUB_STEP_SUMMARY - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 name: Appauth id: appauth with: app_id: ${{ secrets.app-id }} key: ${{ secrets.app-key }} - - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.34 name: Update check with: action: update diff --git a/.github/workflows/_load.yml b/.github/workflows/_load.yml index aa311ab3862f..60b2822be7fb 100644 --- a/.github/workflows/_load.yml +++ b/.github/workflows/_load.yml @@ -91,7 +91,7 @@ jobs: # Handle any failure in triggering job # Remove any `checks` we dont care about # Prepare a check request - - uses: envoyproxy/toolshed/gh-actions/github/env/load@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/github/env/load@actions-v0.2.34 name: Load env id: data with: @@ -102,13 +102,13 @@ jobs: GH_TOKEN: ${{ github.token }} # Update the check - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 name: Appauth id: appauth with: app_id: ${{ secrets.app-id }} key: ${{ secrets.app-key }} - - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.34 name: Update check if: ${{ fromJSON(steps.data.outputs.data).data.check.action == 'RUN' }} with: @@ -116,7 +116,7 @@ jobs: checks: ${{ toJSON(fromJSON(steps.data.outputs.data).checks) }} token: ${{ steps.appauth.outputs.token }} - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 name: Print request summary with: input: | @@ -136,7 +136,7 @@ jobs: | $summary.summary as $summary | "${{ inputs.template-request-summary }}" - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 id: request-output name: Load request with: diff --git a/.github/workflows/_load_env.yml b/.github/workflows/_load_env.yml index 8b10499d3eaa..a07e2ab8b16e 100644 --- a/.github/workflows/_load_env.yml +++ b/.github/workflows/_load_env.yml @@ -63,18 +63,18 @@ jobs: request: ${{ steps.env.outputs.data }} trusted: true steps: - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 id: started name: Create timestamp with: options: -r filter: | now - - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 id: checkout name: Checkout Envoy repository - name: Generate environment variables - uses: envoyproxy/toolshed/gh-actions/envoy/ci/env@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/envoy/ci/env@actions-v0.2.34 id: env with: branch-name: ${{ inputs.branch-name }} @@ -86,7 +86,7 @@ jobs: - name: Request summary id: summary - uses: envoyproxy/toolshed/gh-actions/github/env/summary@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/github/env/summary@actions-v0.2.34 with: actor: ${{ toJSON(fromJSON(steps.env.outputs.data).request.actor) }} base-sha: ${{ fromJSON(steps.env.outputs.data).request.base-sha }} diff --git a/.github/workflows/_request.yml b/.github/workflows/_request.yml index 68fa1e46706a..d14a37622e09 100644 --- a/.github/workflows/_request.yml +++ b/.github/workflows/_request.yml @@ -40,14 +40,14 @@ jobs: env: ${{ steps.data.outputs.value }} config: ${{ steps.config.outputs.config }} steps: - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 id: started name: Create timestamp with: options: -r filter: | now - - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 id: checkout name: Checkout Envoy repository with: @@ -60,7 +60,7 @@ jobs: # *ALL* variables collected should be treated as untrusted and should be sanitized before # use - name: Generate environment variables from commit - uses: envoyproxy/toolshed/gh-actions/envoy/ci/request@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/envoy/ci/request@actions-v0.2.34 id: env with: branch-name: ${{ steps.checkout.outputs.branch-name }} @@ -71,7 +71,7 @@ jobs: vars: ${{ toJSON(vars) }} - name: Request summary id: summary - uses: envoyproxy/toolshed/gh-actions/github/env/summary@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/github/env/summary@actions-v0.2.34 with: actor: ${{ toJSON(fromJSON(steps.env.outputs.data).request.actor) }} base-sha: ${{ fromJSON(steps.env.outputs.data).request.base-sha }} @@ -87,7 +87,7 @@ jobs: target-branch: ${{ fromJSON(steps.env.outputs.data).request.target-branch }} - name: Environment data - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 id: data with: input: | diff --git a/.github/workflows/_run.yml b/.github/workflows/_run.yml index 6cfc51d31cdf..83497741037c 100644 --- a/.github/workflows/_run.yml +++ b/.github/workflows/_run.yml @@ -158,7 +158,7 @@ jobs: name: ${{ inputs.command }} ${{ inputs.target }} timeout-minutes: ${{ inputs.timeout-minutes }} steps: - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 id: started name: Create timestamp with: @@ -166,7 +166,7 @@ jobs: filter: | now # This controls which input vars are exposed to the run action (and related steps) - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 name: Context id: context with: @@ -187,11 +187,11 @@ jobs: | . * {$config, $check} - if: ${{ inputs.cache-build-image }} name: Restore Docker cache ${{ inputs.cache-build-image && format('({0})', inputs.cache-build-image) || '' }} - uses: envoyproxy/toolshed/gh-actions/docker/cache/restore@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/docker/cache/restore@actions-v0.2.34 with: image_tag: ${{ inputs.cache-build-image }} - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 id: appauth name: Appauth if: ${{ inputs.trusted }} @@ -202,7 +202,7 @@ jobs: # - the workaround is to allow the token to be passed through. token: ${{ github.token }} token-ok: true - - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 id: checkout name: Checkout Envoy repository with: @@ -219,7 +219,7 @@ jobs: token: ${{ inputs.trusted && steps.appauth.outputs.token || github.token }} # This is currently only use by mobile-docs and can be removed once they are updated to the newer website - - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 id: checkout-extra name: Checkout extra repository (for publishing) if: ${{ inputs.checkout-extra }} @@ -227,7 +227,7 @@ jobs: config: ${{ inputs.checkout-extra }} ssh-key: ${{ inputs.trusted && inputs.ssh-key-extra || '' }} - - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.34 name: Run CI ${{ inputs.command }} ${{ inputs.target }} with: args: ${{ inputs.args != '--' && inputs.args || inputs.target }} diff --git a/.github/workflows/_stage_publish.yml b/.github/workflows/_stage_publish.yml index c2572d61eca9..240b93b2a20e 100644 --- a/.github/workflows/_stage_publish.yml +++ b/.github/workflows/_stage_publish.yml @@ -98,12 +98,12 @@ jobs: needs: - publish steps: - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 id: appauth with: app_id: ${{ secrets.ENVOY_CI_SYNC_APP_ID }} key: ${{ secrets.ENVOY_CI_SYNC_APP_KEY }} - - uses: envoyproxy/toolshed/gh-actions/dispatch@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/dispatch@actions-v0.2.34 with: ref: main repository: ${{ fromJSON(inputs.request).request.version.dev && 'envoyproxy/envoy-website' || 'envoyproxy/archive' }} diff --git a/.github/workflows/_start.yml b/.github/workflows/_start.yml index 6756eaa93d00..622dab7ae085 100644 --- a/.github/workflows/_start.yml +++ b/.github/workflows/_start.yml @@ -54,7 +54,7 @@ jobs: start: runs-on: ubuntu-22.04 steps: - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 id: check-config name: Prepare check data with: @@ -77,13 +77,13 @@ jobs: | .skipped.output.summary = "${{ inputs.skipped-summary }}" | .skipped.output.text = "" - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 name: Appauth id: appauth with: app_id: ${{ secrets.app-id }} key: ${{ secrets.app-key }} - - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.34 name: Start checks id: checks with: @@ -94,7 +94,7 @@ jobs: ${{ fromJSON(inputs.env).summary.summary }} token: ${{ steps.appauth.outputs.token }} - - uses: envoyproxy/toolshed/gh-actions/json/table@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/json/table@actions-v0.2.34 name: Summary with: collapse-open: true @@ -118,7 +118,7 @@ jobs: output-path: GITHUB_STEP_SUMMARY title: Checks started/skipped - - uses: envoyproxy/toolshed/gh-actions/github/env/save@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/github/env/save@actions-v0.2.34 name: Save env id: data with: diff --git a/.github/workflows/codeql-daily.yml b/.github/workflows/codeql-daily.yml index f3038c666c16..57c58aae0fa1 100644 --- a/.github/workflows/codeql-daily.yml +++ b/.github/workflows/codeql-daily.yml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Free disk space - uses: envoyproxy/toolshed/gh-actions/diskspace@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/diskspace@actions-v0.2.34 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/codeql-push.yml b/.github/workflows/codeql-push.yml index 014c3368afa3..ba7e85a5bb7c 100644 --- a/.github/workflows/codeql-push.yml +++ b/.github/workflows/codeql-push.yml @@ -61,7 +61,7 @@ jobs: - name: Free disk space if: ${{ env.BUILD_TARGETS != '' }} - uses: envoyproxy/toolshed/gh-actions/diskspace@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/diskspace@actions-v0.2.34 - name: Initialize CodeQL if: ${{ env.BUILD_TARGETS != '' }} diff --git a/.github/workflows/command.yml b/.github/workflows/command.yml index fdcd0722471a..f622983f4703 100644 --- a/.github/workflows/command.yml +++ b/.github/workflows/command.yml @@ -28,7 +28,7 @@ jobs: && github.actor != 'dependabot[bot]' }} steps: - - uses: envoyproxy/toolshed/gh-actions/github/command@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/github/command@actions-v0.2.34 name: Parse command from comment id: command with: @@ -37,14 +37,14 @@ jobs: ^/(retest) # /retest - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 if: ${{ steps.command.outputs.command == 'retest' }} id: appauth-retest name: Appauth (retest) with: key: ${{ secrets.ENVOY_CI_APP_KEY }} app_id: ${{ secrets.ENVOY_CI_APP_ID }} - - uses: envoyproxy/toolshed/gh-actions/retest@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/retest@actions-v0.2.34 if: ${{ steps.command.outputs.command == 'retest' }} name: Retest with: diff --git a/.github/workflows/envoy-dependency.yml b/.github/workflows/envoy-dependency.yml index faacf1d8b11f..ccdac300d33c 100644 --- a/.github/workflows/envoy-dependency.yml +++ b/.github/workflows/envoy-dependency.yml @@ -53,16 +53,16 @@ jobs: steps: - id: appauth name: Appauth - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 with: app_id: ${{ secrets.ENVOY_CI_DEP_APP_ID }} key: ${{ secrets.ENVOY_CI_DEP_APP_KEY }} - id: checkout name: Checkout Envoy repository - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 with: token: ${{ steps.appauth.outputs.token }} - - uses: envoyproxy/toolshed/gh-actions/bson@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/bson@actions-v0.2.34 id: update name: Update dependency (${{ inputs.dependency }}) with: @@ -97,13 +97,13 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: envoyproxy/toolshed/gh-actions/upload/diff@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/upload/diff@actions-v0.2.34 name: Upload diff with: name: ${{ inputs.dependency }}-${{ steps.update.outputs.output }} - name: Create a PR if: ${{ inputs.pr }} - uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.34 with: base: main body: | @@ -134,11 +134,11 @@ jobs: steps: - id: appauth name: Appauth - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 with: app_id: ${{ secrets.ENVOY_CI_DEP_APP_ID }} key: ${{ secrets.ENVOY_CI_DEP_APP_KEY }} - - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 id: checkout name: Checkout Envoy repository with: @@ -180,7 +180,7 @@ jobs: - name: Check Docker SHAs id: build-images - uses: envoyproxy/toolshed/gh-actions/docker/shas@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/docker/shas@actions-v0.2.34 with: images: | sha: envoyproxy/envoy-build-ubuntu:${{ steps.build-tools.outputs.tag }} @@ -209,7 +209,7 @@ jobs: name: Update SHAs working-directory: envoy - name: Create a PR - uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.34 with: base: main body: Created by Envoy dependency bot diff --git a/.github/workflows/envoy-release.yml b/.github/workflows/envoy-release.yml index 4a3fa9adabfb..b716a96d14dd 100644 --- a/.github/workflows/envoy-release.yml +++ b/.github/workflows/envoy-release.yml @@ -55,14 +55,14 @@ jobs: steps: - id: appauth name: App auth - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 with: app_id: ${{ secrets.ENVOY_CI_PUBLISH_APP_ID }} key: ${{ secrets.ENVOY_CI_PUBLISH_APP_KEY }} - id: checkout name: Checkout Envoy repository - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 with: committer-name: ${{ env.COMMITTER_NAME }} committer-email: ${{ env.COMMITTER_EMAIL }} @@ -83,10 +83,10 @@ jobs: name: Check changelog summary - if: ${{ inputs.author }} name: Validate signoff email - uses: envoyproxy/toolshed/gh-actions/email/validate@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/email/validate@actions-v0.2.34 with: email: ${{ inputs.author }} - - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.34 name: Create release with: source: | @@ -111,7 +111,7 @@ jobs: name: Release version id: release - name: Create a PR - uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.34 with: base: ${{ github.ref_name }} commit: false @@ -136,20 +136,20 @@ jobs: steps: - id: appauth name: App auth - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 with: app_id: ${{ secrets.ENVOY_CI_PUBLISH_APP_ID }} key: ${{ secrets.ENVOY_CI_PUBLISH_APP_KEY }} - id: checkout name: Checkout Envoy repository - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 with: committer-name: ${{ env.COMMITTER_NAME }} committer-email: ${{ env.COMMITTER_EMAIL }} strip-prefix: release/ token: ${{ steps.appauth.outputs.token }} - - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.34 name: Sync version histories with: command: >- @@ -159,7 +159,7 @@ jobs: -- --signoff="${{ env.COMMITTER_NAME }} <${{ env.COMMITTER_EMAIL }}>" - name: Create a PR - uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.34 with: append-commit-message: true base: ${{ github.ref_name }} @@ -189,13 +189,13 @@ jobs: steps: - id: appauth name: App auth - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 with: app_id: ${{ secrets.ENVOY_CI_PUBLISH_APP_ID }} key: ${{ secrets.ENVOY_CI_PUBLISH_APP_KEY }} - name: Checkout repository - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 with: committer-name: ${{ env.COMMITTER_NAME }} committer-email: ${{ env.COMMITTER_EMAIL }} diff --git a/.github/workflows/envoy-sync.yml b/.github/workflows/envoy-sync.yml index 65cc0fdd2161..74b094d3bd49 100644 --- a/.github/workflows/envoy-sync.yml +++ b/.github/workflows/envoy-sync.yml @@ -31,12 +31,12 @@ jobs: - data-plane-api - mobile-website steps: - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 id: appauth with: app_id: ${{ secrets.ENVOY_CI_SYNC_APP_ID }} key: ${{ secrets.ENVOY_CI_SYNC_APP_KEY }} - - uses: envoyproxy/toolshed/gh-actions/dispatch@actions-v0.2.33 + - uses: envoyproxy/toolshed/gh-actions/dispatch@actions-v0.2.34 with: repository: "envoyproxy/${{ matrix.downstream }}" ref: main diff --git a/.github/workflows/garbage.yml b/.github/workflows/garbage.yml index cb4be6ab5fff..58f95a967d11 100644 --- a/.github/workflows/garbage.yml +++ b/.github/workflows/garbage.yml @@ -33,7 +33,7 @@ jobs: pool-id: 17 steps: - name: Remove dead AZP agents (${{ matrix.target }}) - uses: envoyproxy/toolshed/gh-actions/azp/agent-cleanup@actions-v0.2.33 + uses: envoyproxy/toolshed/gh-actions/azp/agent-cleanup@actions-v0.2.34 with: azp-org: cncf azp-token: ${{ secrets.AZP_TOKEN }} From 52428874ee9586c4b886fbaccbaa8480343f94eb Mon Sep 17 00:00:00 2001 From: Raven Black Date: Wed, 7 Aug 2024 10:24:19 -0400 Subject: [PATCH 054/130] [cache filter] Bug fix, abort various functions on filter destruction (#35571) Commit Message: [cache filter] Bug fix, abort various functions on filter destruction Additional Description: While testing the aborted thundering herd mitigation PR, one thing that was revealed was several possible situations where the filter onDestroy could be called (most commonly by downstream disconnect) and then the filter could be "resurrected" by a change of state back to a state other than destroyed, if the destruction had occurred during a callback. This could lead to a variety of issues, including potentially crash bugs, if the non-destroyed state leads into doing something using member variables that were invalidated during onDestroy. Risk Level: Very small; change is to a WIP filter, and only impacts behavior when the filter state is 'destroyed'. Testing: Yes. Docs Changes: n/a Release Notes: n/a Platform Specific Features: n/a --------- Signed-off-by: Raven Black --- .../filters/http/cache/cache_filter.cc | 16 ++ .../filters/http/cache/cache_filter_test.cc | 166 ++++++++++++++++++ 2 files changed, 182 insertions(+) diff --git a/source/extensions/filters/http/cache/cache_filter.cc b/source/extensions/filters/http/cache/cache_filter.cc index 658b1446d7b8..03f5557a8538 100644 --- a/source/extensions/filters/http/cache/cache_filter.cc +++ b/source/extensions/filters/http/cache/cache_filter.cc @@ -485,8 +485,16 @@ void CacheFilter::onTrailers(Http::ResponseTrailerMapPtr&& trailers) { } if (filter_state_ == FilterState::DecodeServingFromCache) { decoder_callbacks_->encodeTrailers(std::move(trailers)); + // Filter can potentially be destroyed during encodeTrailers. + if (filter_state_ == FilterState::Destroyed) { + return; + } } else { Http::ResponseTrailerMap& response_trailers = encoder_callbacks_->addEncodedTrailers(); + // Filter can potentially be destroyed during addEncodedTrailers. + if (filter_state_ == FilterState::Destroyed) { + return; + } response_trailers = std::move(*trailers); } finalizeEncodingCachedResponse(); @@ -677,6 +685,10 @@ void CacheFilter::encodeCachedResponse() { if (filter_state_ == FilterState::DecodeServingFromCache) { decoder_callbacks_->encodeHeaders(std::move(lookup_result_->headers_), end_stream, CacheResponseCodeDetails::get().ResponseFromCacheFilter); + // Filter can potentially be destroyed during encodeHeaders. + if (filter_state_ == FilterState::Destroyed) { + return; + } } if (filter_state_ == FilterState::EncodeServingFromCache && is_head_request_) { filter_state_ = FilterState::ResponseServedFromCache; @@ -698,6 +710,10 @@ void CacheFilter::finalizeEncodingCachedResponse() { // encodeHeaders returned StopIteration waiting for finishing encoding the cached response -- // continue encoding. encoder_callbacks_->continueEncoding(); + // Filter can potentially be destroyed during continueEncoding. + if (filter_state_ == FilterState::Destroyed) { + return; + } } filter_state_ = FilterState::ResponseServedFromCache; } diff --git a/test/extensions/filters/http/cache/cache_filter_test.cc b/test/extensions/filters/http/cache/cache_filter_test.cc index d481e905799e..a0b0cd38457b 100644 --- a/test/extensions/filters/http/cache/cache_filter_test.cc +++ b/test/extensions/filters/http/cache/cache_filter_test.cc @@ -494,6 +494,88 @@ MATCHER_P2(RangeMatcher, begin, end, "") { testing::ExplainMatchResult(end, arg.end(), result_listener); } +TEST_F(CacheFilterTest, OnDestroyBeforeOnHeadersAbortsAction) { + request_headers_.setHost("CacheHitWithBody"); + auto mock_http_cache = std::make_shared(); + auto mock_lookup_context = std::make_unique>(); + EXPECT_CALL(*mock_http_cache, makeLookupContext(_, _)) + .WillOnce([&](LookupRequest&&, + Http::StreamDecoderFilterCallbacks&) -> std::unique_ptr { + return std::move(mock_lookup_context); + }); + EXPECT_CALL(*mock_lookup_context, getHeaders(_)).WillOnce([&](LookupHeadersCallback&& cb) { + std::unique_ptr response_headers = + std::make_unique(response_headers_); + cb(LookupResult{CacheEntryStatus::Ok, std::move(response_headers), 8, absl::nullopt}); + }); + auto filter = makeFilter(mock_http_cache, false); + EXPECT_EQ(filter->decodeHeaders(request_headers_, true), + Http::FilterHeadersStatus::StopAllIterationAndWatermark); + filter->onDestroy(); + // Nothing extra should happen when the posted lookup completion resolves, because + // the filter was destroyed. + dispatcher_->run(Event::Dispatcher::RunType::Block); +} + +TEST_F(CacheFilterTest, OnDestroyBeforeOnBodyAbortsAction) { + request_headers_.setHost("CacheHitWithBody"); + auto mock_http_cache = std::make_shared(); + auto mock_lookup_context = std::make_unique>(); + EXPECT_CALL(*mock_http_cache, makeLookupContext(_, _)) + .WillOnce([&](LookupRequest&&, + Http::StreamDecoderFilterCallbacks&) -> std::unique_ptr { + return std::move(mock_lookup_context); + }); + EXPECT_CALL(*mock_lookup_context, getHeaders(_)).WillOnce([&](LookupHeadersCallback&& cb) { + std::unique_ptr response_headers = + std::make_unique(response_headers_); + cb(LookupResult{CacheEntryStatus::Ok, std::move(response_headers), 5, absl::nullopt}); + }); + LookupBodyCallback body_callback; + EXPECT_CALL(*mock_lookup_context, getBody(RangeMatcher(0, 5), _)) + .WillOnce([&](const AdjustedByteRange&, LookupBodyCallback&& cb) { body_callback = cb; }); + auto filter = makeFilter(mock_http_cache, false); + EXPECT_EQ(filter->decodeHeaders(request_headers_, true), + Http::FilterHeadersStatus::StopAllIterationAndWatermark); + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + filter->onDestroy(); + // onBody should do nothing because the filter was destroyed. + body_callback(std::make_unique("abcde")); + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); +} + +TEST_F(CacheFilterTest, OnDestroyBeforeOnTrailersAbortsAction) { + request_headers_.setHost("CacheHitWithTrailers"); + auto mock_http_cache = std::make_shared(); + auto mock_lookup_context = std::make_unique>(); + EXPECT_CALL(*mock_http_cache, makeLookupContext(_, _)) + .WillOnce([&](LookupRequest&&, + Http::StreamDecoderFilterCallbacks&) -> std::unique_ptr { + return std::move(mock_lookup_context); + }); + EXPECT_CALL(*mock_lookup_context, getHeaders(_)).WillOnce([&](LookupHeadersCallback&& cb) { + std::unique_ptr response_headers = + std::make_unique(response_headers_); + cb(LookupResult{CacheEntryStatus::Ok, std::move(response_headers), 5, absl::nullopt, true}); + }); + EXPECT_CALL(*mock_lookup_context, getBody(RangeMatcher(0, 5), _)) + .WillOnce([&](const AdjustedByteRange&, LookupBodyCallback&& cb) { + cb(std::make_unique("abcde")); + }); + LookupTrailersCallback trailers_callback; + EXPECT_CALL(*mock_lookup_context, getTrailers(_)).WillOnce([&](LookupTrailersCallback&& cb) { + trailers_callback = cb; + }); + auto filter = makeFilter(mock_http_cache, false); + EXPECT_EQ(filter->decodeHeaders(request_headers_, true), + Http::FilterHeadersStatus::StopAllIterationAndWatermark); + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + filter->onDestroy(); + // onTrailers should do nothing because the filter was destroyed. + trailers_callback(std::make_unique()); + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); +} + TEST_F(CacheFilterTest, BodyReadFromCacheLimitedToBufferSizeChunks) { request_headers_.setHost("CacheHitWithBody"); // Set the buffer limit to 5 bytes, and we will have the file be of size @@ -799,6 +881,90 @@ TEST_F(CacheFilterTest, SuccessfulValidation) { } } +TEST_F(CacheFilterTest, SuccessfulValidationWithFilterDestroyedDuringContinueEncoding) { + request_headers_.setHost("SuccessfulValidation"); + const std::string body = "abc"; + const std::string etag = "abc123"; + const std::string last_modified_date = formatter_.now(time_source_); + { + // Create filter for request 1 + CacheFilterSharedPtr filter = makeFilter(simple_cache_); + + testDecodeRequestMiss(filter); + + // Encode response + // Add Etag & Last-Modified headers to the response for validation + response_headers_.setReferenceKey(Http::CustomHeaders::get().Etag, etag); + response_headers_.setReferenceKey(Http::CustomHeaders::get().LastModified, last_modified_date); + + Buffer::OwnedImpl buffer(body); + response_headers_.setContentLength(body.size()); + EXPECT_EQ(filter->encodeHeaders(response_headers_, false), Http::FilterHeadersStatus::Continue); + EXPECT_EQ(filter->encodeData(buffer, true), Http::FilterDataStatus::Continue); + // The cache getBody callback should be posted to the dispatcher. + // Run events on the dispatcher so that the callback is invoked. + dispatcher_->run(Event::Dispatcher::RunType::Block); + + filter->onStreamComplete(); + EXPECT_THAT(lookupStatus(), IsOkAndHolds(LookupStatus::CacheMiss)); + } + waitBeforeSecondRequest(); + { + // Create filter for request 2 + CacheFilterSharedPtr filter = makeFilter(simple_cache_, /*auto_destroy=*/false); + + // Make request require validation + request_headers_.setReferenceKey(Http::CustomHeaders::get().CacheControl, "no-cache"); + + // Decoding the request should find a cached response that requires validation. + // As far as decoding the request is concerned, this is the same as a cache miss with the + // exception of injecting validation precondition headers. + testDecodeRequestMiss(filter); + + // Make sure validation conditional headers are added + const Http::TestRequestHeaderMapImpl injected_headers = { + {"if-none-match", etag}, {"if-modified-since", last_modified_date}}; + EXPECT_THAT(request_headers_, IsSupersetOfHeaders(injected_headers)); + + // Encode 304 response + // Advance time to make sure the cached date is updated with the 304 date + const std::string not_modified_date = formatter_.now(time_source_); + Http::TestResponseHeaderMapImpl not_modified_response_headers = {{":status", "304"}, + {"date", not_modified_date}}; + + // The filter should stop encoding iteration when encodeHeaders is called as a cached response + // is being fetched and added to the encoding stream. StopIteration does not stop encodeData of + // the same filter from being called + EXPECT_EQ(filter->encodeHeaders(not_modified_response_headers, true), + Http::FilterHeadersStatus::StopIteration); + + // Check for the cached response headers with updated date + Http::TestResponseHeaderMapImpl updated_response_headers = response_headers_; + updated_response_headers.setDate(not_modified_date); + EXPECT_THAT(not_modified_response_headers, IsSupersetOfHeaders(updated_response_headers)); + + // A 304 response should not have a body, so encodeData should not be called + // However, if a body is present by mistake, encodeData should stop iteration until + // encoding the cached response is done + Buffer::OwnedImpl not_modified_body; + EXPECT_EQ(filter->encodeData(not_modified_body, true), + Http::FilterDataStatus::StopIterationAndBuffer); + + // The filter should add the cached response body to encoded data. + Buffer::OwnedImpl buffer(body); + EXPECT_CALL( + encoder_callbacks_, + addEncodedData(testing::Property(&Buffer::Instance::toString, testing::Eq(body)), true)); + EXPECT_CALL(encoder_callbacks_, continueEncoding()).WillOnce([&]() { filter->onDestroy(); }); + + // The cache getBody callback should be posted to the dispatcher. + // Run events on the dispatcher so that the callback is invoked. + dispatcher_->run(Event::Dispatcher::RunType::Block); + + ::testing::Mock::VerifyAndClearExpectations(&encoder_callbacks_); + } +} + TEST_F(CacheFilterTest, UnsuccessfulValidation) { request_headers_.setHost("UnsuccessfulValidation"); const std::string body = "abc"; From bc3f240f54c161448df96644b8a87b3f4404143d Mon Sep 17 00:00:00 2001 From: phlax Date: Wed, 7 Aug 2024 15:55:00 +0100 Subject: [PATCH 055/130] deps: Bump examples -> 0.0.2 (#35619) Signed-off-by: Ryan Northey --- bazel/repository_locations.bzl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 1e2ea67ec6d9..23879aef24bc 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -96,15 +96,15 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "envoy_examples", project_desc = "Envoy proxy examples", project_url = "https://github.com/envoyproxy/examples", - version = "6aa9c3730c91eddaf0c69cc7cbe5294d3da74310", - sha256 = "9bb97f256bedf68cc6696eb7bd8dbb9d0776c137fb88d028e5aa674cfc8ab467", + version = "0.0.2", + sha256 = "c19d5542eb1b105c60a13170c7cdc8f15b489ae06d49374962ea131a50c13c49", strip_prefix = "examples-{version}", - urls = ["https://github.com/envoyproxy/examples/archive/{version}.tar.gz"], + urls = ["https://github.com/envoyproxy/examples/archive/v{version}.tar.gz"], use_category = ["test_only"], - release_date = "2024-08-02", + release_date = "2024-08-07", cpe = "N/A", license = "Apache-2.0", - license_url = "https://github.com/envoyproxy/examples/blob/{version}/LICENSE", + license_url = "https://github.com/envoyproxy/examples/blob/v{version}/LICENSE", ), rules_fuzzing = dict( project_name = "Fuzzing Rules for Bazel", From b8054ef6da0f20e1e8371062cf03963492b91665 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Wed, 7 Aug 2024 11:54:38 -0400 Subject: [PATCH 056/130] coverage: ratcheting (#35593) Signed-off-by: Alyssa Wilk --- test/per_file_coverage.sh | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index 5258c8403c84..6cca2bbba585 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -6,23 +6,22 @@ declare -a KNOWN_LOW_COVERAGE=( "source/common:96.2" "source/common/api:84.5" # flaky due to posix: be careful adjusting "source/common/api/posix:83.8" # flaky (accept failover non-deterministic): be careful adjusting -"source/common/config:95.8" +"source/common/config:96.1" "source/common/crypto:95.5" "source/common/event:95.1" # Emulated edge events guards don't report LCOV "source/common/filesystem/posix:96.3" # FileReadToEndNotReadable fails in some env; createPath can't test all failure branches. -"source/common/http/http2:95.9" +"source/common/http/http2:96.0" "source/common/json:94.8" "source/common/matcher:94.4" "source/common/memory:74.5" # tcmalloc code path is not enabled in coverage build, only gperf tcmalloc, see PR#32589 "source/common/network:94.4" # Flaky, `activateFileEvents`, `startSecureTransport` and `ioctl`, listener_socket do not always report LCOV "source/common/network/dns_resolver:91.4" # A few lines of MacOS code not tested in linux scripts. Tested in MacOS scripts -"source/common/quic:93.7" +"source/common/quic:93.8" "source/common/secret:95.4" "source/common/signal:87.2" # Death tests don't report LCOV "source/common/thread:0.0" # Death tests don't report LCOV "source/common/watchdog:58.6" # Death tests don't report LCOV "source/exe:94.2" # increased by #32346, need coverage for terminate_handler and hot restart failures -"source/extensions/clusters/common:95.6" "source/extensions/common:93.0" #flaky: be careful adjusting "source/extensions/common/proxy_protocol:93.8" # Adjusted for security patch "source/extensions/common/tap:94.6" @@ -31,7 +30,7 @@ declare -a KNOWN_LOW_COVERAGE=( "source/extensions/filters/common/fault:94.5" "source/extensions/filters/common/rbac:90.8" "source/extensions/filters/http/cache:95.1" -"source/extensions/filters/http/grpc_json_transcoder:93.8" # TODO(#28232) +"source/extensions/filters/http/grpc_json_transcoder:94.2" # TODO(#28232) "source/extensions/filters/http/ip_tagging:88.2" "source/extensions/filters/http/kill_request:91.7" # Death tests don't report LCOV "source/extensions/filters/http/wasm:1.3" # Disabled due to issue (#24164) @@ -44,14 +43,11 @@ declare -a KNOWN_LOW_COVERAGE=( "source/extensions/rate_limit_descriptors/expr:95.0" "source/extensions/stat_sinks/graphite_statsd:82.8" # Death tests don't report LCOV "source/extensions/stat_sinks/statsd:85.2" # Death tests don't report LCOV -"source/extensions/tracers:96.5" -"source/extensions/tracers/common:75.0" -"source/extensions/tracers/common/ot:73.1" "source/extensions/tracers/opencensus:94.0" "source/extensions/tracers/zipkin:95.8" "source/extensions/transport_sockets:97.4" -"source/common/tls:94.7" -"source/common/tls/cert_validator:94.2" +"source/common/tls:94.8" +"source/common/tls/cert_validator:94.4" "source/common/tls/private_key:88.9" "source/extensions/wasm_runtime/wamr:0.0" # Not enabled in coverage build "source/extensions/wasm_runtime/wasmtime:0.0" # Not enabled in coverage build @@ -60,7 +56,7 @@ declare -a KNOWN_LOW_COVERAGE=( "source/extensions/listener_managers/validation_listener_manager:70.5" "source/extensions/watchdog/profile_action:83.3" "source/server:91.0" # flaky: be careful adjusting. See https://github.com/envoyproxy/envoy/issues/15239 -"source/server/config_validation:91.4" +"source/server/config_validation:91.8" "source/extensions/health_checkers:96.1" "source/extensions/health_checkers/http:93.9" "source/extensions/health_checkers/grpc:92.1" From dab11324bb39dcd516cc5a82940b2355ad307c58 Mon Sep 17 00:00:00 2001 From: Ali Beyad Date: Wed, 7 Aug 2024 12:10:09 -0400 Subject: [PATCH 057/130] runtime: Remove `normalize_host_for_preresolve_dfp_dns` runtime guard (#35601) Risk Level: low Testing: unit tests Release Notes: inline Fixes #35372 Signed-off-by: Ali Beyad --- changelogs/current.yaml | 3 ++ source/common/runtime/runtime_features.cc | 1 - .../dynamic_forward_proxy/dns_cache_impl.cc | 5 +-- .../dns_cache_impl_test.cc | 32 ++++++------------- 4 files changed, 13 insertions(+), 28 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index e59477c5e4e9..9d7c0eb3695f 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -109,6 +109,9 @@ removed_config_or_runtime: change: | Removed ``envoy.reloadable_features.upstream_wait_for_response_headers_before_disabling_read`` runtime flag and legacy code paths. +- area: dynamic forward proxy + change: | + Removed ``envoy.reloadable_features.normalize_host_for_preresolve_dfp_dns`` runtime flag and legacy code paths. new_features: - area: tls diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 25cd64d30ded..2ef124d5c9bf 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -68,7 +68,6 @@ RUNTIME_GUARD(envoy_reloadable_features_jwt_authn_validate_uri); RUNTIME_GUARD(envoy_reloadable_features_lua_flow_control_while_http_call); RUNTIME_GUARD(envoy_reloadable_features_no_extension_lookup_by_name); RUNTIME_GUARD(envoy_reloadable_features_no_timer_based_rate_limit_token_bucket); -RUNTIME_GUARD(envoy_reloadable_features_normalize_host_for_preresolve_dfp_dns); RUNTIME_GUARD(envoy_reloadable_features_original_dst_rely_on_idle_timeout); RUNTIME_GUARD(envoy_reloadable_features_prefer_ipv6_dns_on_macos); // Fixes fail-open behaviour of failure_mode_allow for external authz grpc servers. diff --git a/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc b/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc index 99b7efcc5b4e..494df335ea64 100644 --- a/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc +++ b/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc @@ -62,10 +62,7 @@ DnsCacheImpl::DnsCacheImpl( // potential optimization of having the entry be preresolved the first time a true consumer of // this DNS cache asks for it. const std::string host = - (Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.normalize_host_for_preresolve_dfp_dns")) - ? DnsHostInfo::normalizeHostForDfp(hostname.address(), hostname.port_value()) - : hostname.address(); + DnsHostInfo::normalizeHostForDfp(hostname.address(), hostname.port_value()); ENVOY_LOG(debug, "DNS pre-resolve starting for host {}", host); startCacheLoad(host, hostname.port_value(), false, false); } diff --git a/test/extensions/common/dynamic_forward_proxy/dns_cache_impl_test.cc b/test/extensions/common/dynamic_forward_proxy/dns_cache_impl_test.cc index dae49294ef84..85e4e72a53aa 100644 --- a/test/extensions/common/dynamic_forward_proxy/dns_cache_impl_test.cc +++ b/test/extensions/common/dynamic_forward_proxy/dns_cache_impl_test.cc @@ -135,19 +135,7 @@ void verifyCaresDnsConfigAndUnpack( typed_dns_resolver_config.typed_config().UnpackTo(&cares); } -class DnsCacheImplPreresolveTest : public DnsCacheImplTest, - public testing::WithParamInterface { -public: - bool normalizeDfpHost() { return GetParam(); } -}; - -INSTANTIATE_TEST_SUITE_P(DnsCachePreresolveNormalizedDfpHost, DnsCacheImplPreresolveTest, - testing::Bool()); - -TEST_P(DnsCacheImplPreresolveTest, PreresolveSuccess) { - scoped_runtime_.mergeValues({{"envoy.reloadable_features.normalize_host_for_preresolve_dfp_dns", - absl::StrCat(normalizeDfpHost())}}); - +TEST_F(DnsCacheImplTest, PreresolveSuccess) { Network::DnsResolver::ResolveCb resolve_cb; std::string host = "bar.baz.com"; uint32_t port = 443; @@ -162,7 +150,7 @@ TEST_P(DnsCacheImplPreresolveTest, PreresolveSuccess) { DnsHostInfoEquals("10.0.0.1:443", "bar.baz.com", false), Network::DnsResolver::ResolutionStatus::Success)); - initialize({{normalizeDfpHost() ? host : authority, port}} /* preresolve_hostnames */); + initialize({{host, port}} /* preresolve_hostnames */); resolve_cb(Network::DnsResolver::ResolutionStatus::Success, "", TestUtility::makeDnsResponse({"10.0.0.1"})); @@ -170,21 +158,19 @@ TEST_P(DnsCacheImplPreresolveTest, PreresolveSuccess) { 1 /* added */, 0 /* removed */, 1 /* num hosts */); MockLoadDnsCacheEntryCallbacks callbacks; - if (normalizeDfpHost()) { - // Retrieve with the hostname and port in the "host". - auto result = dns_cache_->loadDnsCacheEntry(authority, port, false, callbacks); - EXPECT_EQ(DnsCache::LoadDnsCacheEntryStatus::InCache, result.status_); - EXPECT_EQ(result.handle_, nullptr); - EXPECT_NE(absl::nullopt, result.host_info_); - } + // Retrieve with the hostname and port in the "host". + auto result = dns_cache_->loadDnsCacheEntry(authority, port, false, callbacks); + EXPECT_EQ(DnsCache::LoadDnsCacheEntryStatus::InCache, result.status_); + EXPECT_EQ(result.handle_, nullptr); + EXPECT_NE(absl::nullopt, result.host_info_); // Retrieve with the hostname only in the "host". - auto result = dns_cache_->loadDnsCacheEntry(host, port, false, callbacks); + result = dns_cache_->loadDnsCacheEntry(host, port, false, callbacks); EXPECT_EQ(DnsCache::LoadDnsCacheEntryStatus::InCache, result.status_); EXPECT_EQ(result.handle_, nullptr); EXPECT_NE(absl::nullopt, result.host_info_); } -TEST_P(DnsCacheImplPreresolveTest, PreresolveFailure) { +TEST_F(DnsCacheImplTest, PreresolveFailure) { EXPECT_THROW_WITH_MESSAGE( initialize({{"bar.baz.com", 443}} /* preresolve_hostnames */, 0 /* max_hosts */), EnvoyException, From 770b151d2c9245ea19fdc4304b33636250f0b5b7 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Wed, 7 Aug 2024 11:19:34 -0500 Subject: [PATCH 058/130] c-ares: Add c-ares status in the DNS response details (#35606) Similar to the `getaddrinfo` implementation, this PR adds the c-ares human readable status in the DNS response details. Risk Level: low Testing: unit test Docs Changes: n/a Release Notes: n/a Platform Specific Features: n/a --------- Signed-off-by: Fredy Wijaya --- .../network/dns_resolver/cares/dns_impl.cc | 6 +++--- .../proxy_filter_integration_test.cc | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/source/extensions/network/dns_resolver/cares/dns_impl.cc b/source/extensions/network/dns_resolver/cares/dns_impl.cc index 04fa3622e7cf..f3c662bcec7c 100644 --- a/source/extensions/network/dns_resolver/cares/dns_impl.cc +++ b/source/extensions/network/dns_resolver/cares/dns_impl.cc @@ -216,7 +216,7 @@ void DnsResolverImpl::AddrInfoPendingResolution::onAresGetAddrInfoCallback( if (status == ARES_SUCCESS) { pending_response_.status_ = ResolutionStatus::Success; - pending_response_.details_ = "cares_success"; + pending_response_.details_ = absl::StrCat("cares_success:", ares_strerror(status)); if (addrinfo != nullptr && addrinfo->nodes != nullptr) { bool can_process_v4 = @@ -265,10 +265,10 @@ void DnsResolverImpl::AddrInfoPendingResolution::onAresGetAddrInfoCallback( // Treat `ARES_ENODATA` or `ARES_ENOTFOUND` here as success to populate back the // "empty records" response. pending_response_.status_ = ResolutionStatus::Success; - pending_response_.details_ = "cares_norecords"; + pending_response_.details_ = absl::StrCat("cares_norecords:", ares_strerror(status)); ASSERT(addrinfo == nullptr); } else { - pending_response_.details_ = "cares_failure"; + pending_response_.details_ = absl::StrCat("cares_failure:", ares_strerror(status)); } if (timeouts > 0) { diff --git a/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_integration_test.cc b/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_integration_test.cc index 4cfa502e3e26..5bb5ea66e488 100644 --- a/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_integration_test.cc +++ b/test/extensions/filters/http/dynamic_forward_proxy/proxy_filter_integration_test.cc @@ -291,12 +291,16 @@ name: envoy.clusters.dynamic_forward_proxy auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); ASSERT_TRUE(response->waitForEndStream()); EXPECT_EQ("503", response->headers().getStatusValue()); - EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("dns_resolution_failure")); + std::string access_log = waitForAccessLog(access_log_name_); + EXPECT_THAT(access_log, HasSubstr("dns_resolution_failure")); + EXPECT_FALSE(StringUtil::hasEmptySpace(access_log)); response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); ASSERT_TRUE(response->waitForEndStream()); EXPECT_EQ("503", response->headers().getStatusValue()); - EXPECT_THAT(waitForAccessLog(access_log_name_, 1), HasSubstr("dns_resolution_failure")); + access_log = waitForAccessLog(access_log_name_, 1); + EXPECT_THAT(access_log, HasSubstr("dns_resolution_failure")); + EXPECT_FALSE(StringUtil::hasEmptySpace(access_log)); } void multipleRequestsMaybeReresolve(bool reresolve) { @@ -528,12 +532,16 @@ TEST_P(ProxyFilterIntegrationTest, RequestWithUnknownDomainAndNoCaching) { auto response = codec_client_->makeHeaderOnlyRequest(request_headers); ASSERT_TRUE(response->waitForEndStream()); EXPECT_EQ("503", response->headers().getStatusValue()); - EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("dns_resolution_failure")); + std::string access_log = waitForAccessLog(access_log_name_); + EXPECT_THAT(access_log, HasSubstr("dns_resolution_failure")); + EXPECT_FALSE(StringUtil::hasEmptySpace(access_log)); response = codec_client_->makeHeaderOnlyRequest(request_headers); ASSERT_TRUE(response->waitForEndStream()); EXPECT_EQ("503", response->headers().getStatusValue()); - EXPECT_THAT(waitForAccessLog(access_log_name_, 1), HasSubstr("dns_resolution_failure")); + access_log = waitForAccessLog(access_log_name_, 1); + EXPECT_THAT(access_log, HasSubstr("dns_resolution_failure")); + EXPECT_FALSE(StringUtil::hasEmptySpace(access_log)); } // Verify that after we populate the cache and reload the cluster we reattach to the cache with From d78e8138feb499147730a1c39da8e479a796a621 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Wed, 7 Aug 2024 12:28:52 -0400 Subject: [PATCH 059/130] mobile: direct response cleanup (#35621) Signed-off-by: Alyssa Wilk --- mobile/library/common/http/header_utility.cc | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/mobile/library/common/http/header_utility.cc b/mobile/library/common/http/header_utility.cc index b97da27b0f60..fc1ae9ec0e56 100644 --- a/mobile/library/common/http/header_utility.cc +++ b/mobile/library/common/http/header_utility.cc @@ -9,14 +9,9 @@ namespace Envoy { namespace Http { namespace Utility { -Http::LocalErrorStatus statusForOnLocalReply(const StreamDecoderFilter::LocalReplyData& reply, - const StreamInfo::StreamInfo& info) { - // This is a horrible hack to work around legacy swift direct response API. - // TODO(https://github.com/envoyproxy/envoy/issues/24428) remove. - if (reply.details_ == "direct_response" && info.getRequestHeaders() && - info.getRequestHeaders()->getHostValue() == "127.0.0.1") { - return Http::LocalErrorStatus::Continue; - } +Http::LocalErrorStatus statusForOnLocalReply(const StreamDecoderFilter::LocalReplyData&, + const StreamInfo::StreamInfo&) { + // Avoid sendLocalReply to differentiate Envoy generated errors from peer generated errors. return Http::LocalErrorStatus::ContinueAndResetStream; } From 84e1ed20c6833604411b7c0f872397defde09696 Mon Sep 17 00:00:00 2001 From: botengyao Date: Wed, 7 Aug 2024 12:55:46 -0400 Subject: [PATCH 060/130] opentelemetry: add the option to discard buffer response body for http async exporter (#35572) only the response code is used for logging purpose. Risk Level: low Testing: unit test Signed-off-by: Boteng Yao --- .../tracers/opentelemetry/http_trace_exporter.cc | 7 +++++-- .../tracers/opentelemetry/http_trace_exporter_test.cc | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/source/extensions/tracers/opentelemetry/http_trace_exporter.cc b/source/extensions/tracers/opentelemetry/http_trace_exporter.cc index 1ff2d2c7c867..98a121a21ff4 100644 --- a/source/extensions/tracers/opentelemetry/http_trace_exporter.cc +++ b/source/extensions/tracers/opentelemetry/http_trace_exporter.cc @@ -61,8 +61,11 @@ bool OpenTelemetryHttpTraceExporter::log(const ExportTraceServiceRequest& reques } message->body().add(request_body); - const auto options = Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds( - DurationUtil::durationToMilliseconds(http_service_.http_uri().timeout()))); + const auto options = + Http::AsyncClient::RequestOptions() + .setTimeout(std::chrono::milliseconds( + DurationUtil::durationToMilliseconds(http_service_.http_uri().timeout()))) + .setDiscardResponseBody(true); Http::AsyncClient::Request* in_flight_request = thread_local_cluster->httpAsyncClient().send(std::move(message), *this, options); diff --git a/test/extensions/tracers/opentelemetry/http_trace_exporter_test.cc b/test/extensions/tracers/opentelemetry/http_trace_exporter_test.cc index 3674f4fec219..e4df021f593a 100644 --- a/test/extensions/tracers/opentelemetry/http_trace_exporter_test.cc +++ b/test/extensions/tracers/opentelemetry/http_trace_exporter_test.cc @@ -70,9 +70,11 @@ TEST_F(OpenTelemetryHttpTraceExporterTest, CreateExporterAndExportSpan) { Http::MockAsyncClientRequest request(&cluster_manager_.thread_local_cluster_.async_client_); Http::AsyncClient::Callbacks* callback; - EXPECT_CALL( - cluster_manager_.thread_local_cluster_.async_client_, - send_(_, _, Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds(250)))) + EXPECT_CALL(cluster_manager_.thread_local_cluster_.async_client_, + send_(_, _, + Http::AsyncClient::RequestOptions() + .setTimeout(std::chrono::milliseconds(250)) + .setDiscardResponseBody(true))) .WillOnce( Invoke([&](Http::RequestMessagePtr& message, Http::AsyncClient::Callbacks& callbacks, const Http::AsyncClient::RequestOptions&) -> Http::AsyncClient::Request* { From d283802bf8a26e4cb5f8ecdc07809f48a9464b7d Mon Sep 17 00:00:00 2001 From: botengyao Date: Wed, 7 Aug 2024 13:58:18 -0400 Subject: [PATCH 061/130] http_conn_manager: clarify hcm accesslog docs (#35611) Signed-off-by: Boteng Yao --- .../v3/http_connection_manager.proto | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto b/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto index f4d0c78597b2..9217b6d0c933 100644 --- a/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto +++ b/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto @@ -589,26 +589,33 @@ message HttpConnectionManager { // emitted by the connection manager. repeated config.accesslog.v3.AccessLog access_log = 13; + // The interval to flush the above access logs. + // // .. attention:: - // This field is deprecated in favor of - // :ref:`access_log_flush_interval - // `. - // Note that if both this field and :ref:`access_log_flush_interval - // ` - // are specified, the former (deprecated field) is ignored. + // + // This field is deprecated in favor of + // :ref:`access_log_flush_interval + // `. + // Note that if both this field and :ref:`access_log_flush_interval + // ` + // are specified, the former (deprecated field) is ignored. google.protobuf.Duration access_log_flush_interval = 54 [ deprecated = true, (validate.rules).duration = {gte {nanos: 1000000}}, (envoy.annotations.deprecated_at_minor_version) = "3.0" ]; + // If set to true, HCM will flush an access log once when a new HTTP request is received, after the request + // headers have been evaluated, and before iterating through the HTTP filter chain. + // // .. attention:: - // This field is deprecated in favor of - // :ref:`flush_access_log_on_new_request - // `. - // Note that if both this field and :ref:`flush_access_log_on_new_request - // ` - // are specified, the former (deprecated field) is ignored. + // + // This field is deprecated in favor of + // :ref:`flush_access_log_on_new_request + // `. + // Note that if both this field and :ref:`flush_access_log_on_new_request + // ` + // are specified, the former (deprecated field) is ignored. bool flush_access_log_on_new_request = 55 [deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"]; From c9fd85f283870f8364f3daeaea35e94c8af9409f Mon Sep 17 00:00:00 2001 From: bsurber <73970703+bsurber@users.noreply.github.com> Date: Wed, 7 Aug 2024 15:26:19 -0700 Subject: [PATCH 062/130] BucketId Dynamic Metadata for RateLimitQuota filter (#35594) Commit Message: Add dynamic metadata to the rate_limit_quota filter to export the selected bucket for a given request for logging Additional Description: Functionality tested using an access log filter in integration testing Risk Level: low Testing: integration testing Docs Changes: Release Notes: Platform Specific Features: --------- Signed-off-by: Brian Surber --- .../filters/http/rate_limit_quota/filter.cc | 19 ++++-- .../http/rate_limit_quota/integration_test.cc | 61 ++++++++++++++++++- 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/source/extensions/filters/http/rate_limit_quota/filter.cc b/source/extensions/filters/http/rate_limit_quota/filter.cc index 28600208658c..60eede663322 100644 --- a/source/extensions/filters/http/rate_limit_quota/filter.cc +++ b/source/extensions/filters/http/rate_limit_quota/filter.cc @@ -9,6 +9,8 @@ namespace Extensions { namespace HttpFilters { namespace RateLimitQuota { +const char kBucketMetadataNamespace[] = "envoy.extensions.http_filters.rate_limit_quota.bucket"; + Http::FilterHeadersStatus RateLimitQuotaFilter::decodeHeaders(Http::RequestHeaderMap& headers, bool end_stream) { ENVOY_LOG(trace, "decodeHeaders: end_stream = {}", end_stream); @@ -28,7 +30,7 @@ Http::FilterHeadersStatus RateLimitQuotaFilter::decodeHeaders(Http::RequestHeade // succeeds. const RateLimitOnMatchAction& match_action = match_result.value()->getTyped(); - auto ret = match_action.generateBucketId(*data_ptr_, factory_context_, visitor_); + absl::StatusOr ret = match_action.generateBucketId(*data_ptr_, factory_context_, visitor_); if (!ret.ok()) { // When it failed to generate the bucket id for this specific request, the request is ALLOWED by // default (i.e., fail-open). @@ -36,19 +38,26 @@ Http::FilterHeadersStatus RateLimitQuotaFilter::decodeHeaders(Http::RequestHeade return Envoy::Http::FilterHeadersStatus::Continue; } - BucketId bucket_id_proto = ret.value(); + const BucketId& bucket_id_proto = *ret; const size_t bucket_id = MessageUtil::hash(bucket_id_proto); ENVOY_LOG(trace, "Generated the associated hashed bucket id: {} for bucket id proto:\n {}", bucket_id, bucket_id_proto.DebugString()); + + ProtobufWkt::Struct bucket_log; + auto* bucket_log_fields = bucket_log.mutable_fields(); + for (const auto& bucket : bucket_id_proto.bucket()) + (*bucket_log_fields)[bucket.first] = ValueUtil::stringValue(bucket.second); + + callbacks_->streamInfo().setDynamicMetadata(kBucketMetadataNamespace, bucket_log); + if (quota_buckets_.find(bucket_id) == quota_buckets_.end()) { // For first matched request, create a new bucket in the cache and sent the report to RLQS // server immediately. createNewBucket(bucket_id_proto, match_action, bucket_id); return sendImmediateReport(bucket_id, match_action); - } else { - // Found the cached bucket entry. - return processCachedBucket(bucket_id, match_action); } + + return processCachedBucket(bucket_id, match_action); } void RateLimitQuotaFilter::createMatcher() { diff --git a/test/extensions/filters/http/rate_limit_quota/integration_test.cc b/test/extensions/filters/http/rate_limit_quota/integration_test.cc index d342e3aca00c..5af41c95c49b 100644 --- a/test/extensions/filters/http/rate_limit_quota/integration_test.cc +++ b/test/extensions/filters/http/rate_limit_quota/integration_test.cc @@ -50,13 +50,18 @@ class RateLimitQuotaIntegrationTest } } - void initializeConfig(ConfigOption config_option = {}) { - config_helper_.addConfigModifier([this, config_option]( + void initializeConfig(ConfigOption config_option = {}, const std::string& log_format = "") { + config_helper_.addConfigModifier([this, config_option, log_format]( envoy::config::bootstrap::v3::Bootstrap& bootstrap) { // Ensure "HTTP2 with no prior knowledge." Necessary for gRPC and for headers ConfigHelper::setHttp2( *(bootstrap.mutable_static_resources()->mutable_clusters()->Mutable(0))); + // Enable access logging for testing dynamic metadata. + if (!log_format.empty()) { + HttpIntegrationTest::useAccessLog(log_format); + } + // Clusters for ExtProc gRPC servers, starting by copying an existing cluster for (size_t i = 0; i < grpc_upstreams_.size(); ++i) { auto* server_cluster = bootstrap.mutable_static_resources()->add_clusters(); @@ -298,6 +303,58 @@ TEST_P(RateLimitQuotaIntegrationTest, BasicFlowResponseMatched) { EXPECT_EQ(response_->headers().getStatusValue(), "200"); } +TEST_P(RateLimitQuotaIntegrationTest, TestBasicMetadataLogging) { + initializeConfig({}, "Whole Bucket " + "ID=%DYNAMIC_METADATA(envoy.extensions.http_filters.rate_" + "limit_quota.bucket)%\n" + "Name=%DYNAMIC_METADATA(envoy.extensions.http_filters.rate_" + "limit_quota.bucket:name)%"); + HttpIntegrationTest::initialize(); + absl::flat_hash_map custom_headers = {{"environment", "staging"}, + {"group", "envoy"}}; + // Send downstream client request to upstream. + sendClientRequest(&custom_headers); + + // Start the gRPC stream to RLQS server. + ASSERT_TRUE(grpc_upstreams_[0]->waitForHttpConnection(*dispatcher_, rlqs_connection_)); + ASSERT_TRUE(rlqs_connection_->waitForNewStream(*dispatcher_, rlqs_stream_)); + envoy::service::rate_limit_quota::v3::RateLimitQuotaUsageReports reports; + ASSERT_TRUE(rlqs_stream_->waitForGrpcMessage(*dispatcher_, reports)); + rlqs_stream_->startGrpcStream(); + + // Build the response whose bucket ID matches the sent report. + envoy::service::rate_limit_quota::v3::RateLimitQuotaResponse rlqs_response; + custom_headers.insert({"name", "prod"}); + auto* bucket_action = rlqs_response.add_bucket_action(); + for (const auto& [key, value] : custom_headers) { + (*bucket_action->mutable_bucket_id()->mutable_bucket()).insert({key, value}); + } + // Send the response from RLQS server. + rlqs_stream_->sendGrpcMessage(rlqs_response); + + // Handle the request received by upstream. + ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_)); + ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); + ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_)); + upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, false); + upstream_request_->encodeData(100, true); + + // Verify the response to downstream. + ASSERT_TRUE(response_->waitForEndStream()); + EXPECT_TRUE(response_->complete()); + EXPECT_EQ(response_->headers().getStatusValue(), "200"); + + std::string log_output0 = + HttpIntegrationTest::waitForAccessLog(HttpIntegrationTest::access_log_name_, 0, true); + EXPECT_THAT(log_output0, testing::HasSubstr("Whole Bucket ID")); + EXPECT_THAT(log_output0, testing::HasSubstr("\"name\":\"prod\"")); + EXPECT_THAT(log_output0, testing::HasSubstr("\"group\":\"envoy\"")); + EXPECT_THAT(log_output0, testing::HasSubstr("\"environment\":\"staging\"")); + std::string log_output1 = + HttpIntegrationTest::waitForAccessLog(HttpIntegrationTest::access_log_name_, 1, true); + EXPECT_THAT(log_output1, testing::HasSubstr("Name=prod")); +} + TEST_P(RateLimitQuotaIntegrationTest, BasicFlowMultiSameRequest) { initializeConfig(); HttpIntegrationTest::initialize(); From 19520394c5f43f617d16826a127b0f05177d0e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bence=20B=C3=A9ky?= Date: Wed, 7 Aug 2024 19:54:14 -0400 Subject: [PATCH 063/130] Remove QUICHE_EXPORT_PRIVATE_IMPL. (#35624) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QUICHE_EXPORT_PRIVATE has been removed from third_party/quiche/common/platform/api/quiche_export.h at https://quiche.googlesource.com/quiche/+/e124b7b1db81291dab6f3c87d5dd5b305146cb51%5E%21/#F12 therefore QUICHE_EXPORT_PRIVATE_IMPL is unused. Signed-off-by: Bence Béky --- source/common/quic/platform/quiche_export_impl.h | 1 - 1 file changed, 1 deletion(-) diff --git a/source/common/quic/platform/quiche_export_impl.h b/source/common/quic/platform/quiche_export_impl.h index 9cb2788428fe..ae460cf0b700 100644 --- a/source/common/quic/platform/quiche_export_impl.h +++ b/source/common/quic/platform/quiche_export_impl.h @@ -18,5 +18,4 @@ #define QUICHE_EXPORT_IMPL #endif -#define QUICHE_EXPORT_PRIVATE_IMPL QUICHE_EXPORT_IMPL #define QUICHE_NO_EXPORT_IMPL QUICHE_EXPORT_IMPL From ccae683835c19b91644e59fd93e8914273f30e62 Mon Sep 17 00:00:00 2001 From: Tianyu <72890320+tyxia@users.noreply.github.com> Date: Wed, 7 Aug 2024 20:41:00 -0400 Subject: [PATCH 064/130] ext_proc: Switch to use address of stream as the key (#34959) Switch to use address of stream as the key to avoid any potential key duplication --------- Signed-off-by: tyxia --- .../filters/http/ext_proc/ext_proc.cc | 23 ++++++---------- .../filters/http/ext_proc/ext_proc.h | 26 +++++++++---------- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/source/extensions/filters/http/ext_proc/ext_proc.cc b/source/extensions/filters/http/ext_proc/ext_proc.cc index a03923519754..5130208e1b5d 100644 --- a/source/extensions/filters/http/ext_proc/ext_proc.cc +++ b/source/extensions/filters/http/ext_proc/ext_proc.cc @@ -358,9 +358,7 @@ Filter::StreamOpenState Filter::openStream() { } stats_.streams_started_.inc(); - // TODO(tyxia) Switch to address of stream - stream_ = config_->threadLocalStreamManager().store(decoder_callbacks_->streamId(), - std::move(stream_object), config_->stats(), + stream_ = config_->threadLocalStreamManager().store(std::move(stream_object), config_->stats(), config_->deferredCloseTimeout()); // For custom access logging purposes. Applicable only for Envoy gRPC as Google gRPC does not // have a proper implementation of streamInfo. @@ -377,8 +375,8 @@ void Filter::closeStream() { if (stream_->close()) { stats_.streams_closed_.inc(); } + config_->threadLocalStreamManager().erase(stream_); stream_ = nullptr; - config_->threadLocalStreamManager().erase(decoder_callbacks_->streamId()); } else { ENVOY_LOG(debug, "Stream already closed"); } @@ -386,8 +384,7 @@ void Filter::closeStream() { void Filter::deferredCloseStream() { ENVOY_LOG(debug, "Calling deferred close on stream"); - config_->threadLocalStreamManager().deferredErase(decoder_callbacks_->streamId(), - filter_callbacks_->dispatcher()); + config_->threadLocalStreamManager().deferredErase(stream_, filter_callbacks_->dispatcher()); } void Filter::onDestroy() { @@ -1360,28 +1357,24 @@ void Filter::mergePerRouteConfig() { } } -void DeferredDeletableStream::closeStreamOnTimer(uint64_t stream_id) { +void DeferredDeletableStream::closeStreamOnTimer() { // Close the stream. if (stream_) { ENVOY_LOG(debug, "Closing the stream"); if (stream_->close()) { stats.streams_closed_.inc(); } - stream_.reset(); + // Erase this entry from the map; this will also reset the stream_ pointer. + parent.erase(stream_.get()); } else { ENVOY_LOG(debug, "Stream already closed"); } - - // Erase this entry from the map. - parent.erase(stream_id); } // In the deferred closure mode, stream closure is deferred upon filter destruction, with a timer // to prevent unbounded resource usage growth. -void DeferredDeletableStream::deferredClose(Envoy::Event::Dispatcher& dispatcher, - uint64_t stream_id) { - derferred_close_timer = - dispatcher.createTimer([this, stream_id] { closeStreamOnTimer(stream_id); }); +void DeferredDeletableStream::deferredClose(Envoy::Event::Dispatcher& dispatcher) { + derferred_close_timer = dispatcher.createTimer([this] { closeStreamOnTimer(); }); derferred_close_timer->enableTimer(std::chrono::milliseconds(deferred_close_timeout)); } diff --git a/source/extensions/filters/http/ext_proc/ext_proc.h b/source/extensions/filters/http/ext_proc/ext_proc.h index b798a29b5f82..070b60b58d8e 100644 --- a/source/extensions/filters/http/ext_proc/ext_proc.h +++ b/source/extensions/filters/http/ext_proc/ext_proc.h @@ -158,9 +158,9 @@ struct DeferredDeletableStream : public Logger::Loggable { : stream_(std::move(stream)), parent(stream_manager), stats(stat), deferred_close_timeout(timeout) {} - void deferredClose(Envoy::Event::Dispatcher& dispatcher, uint64_t stream_id); + void deferredClose(Envoy::Event::Dispatcher& dispatcher); + void closeStreamOnTimer(); - void closeStreamOnTimer(uint64_t stream_id); ExternalProcessorStreamPtr stream_; ThreadLocalStreamManager& parent; ExtProcFilterStats stats; @@ -174,28 +174,28 @@ class ThreadLocalStreamManager : public Envoy::ThreadLocal::ThreadLocalObject { public: // Store the ExternalProcessorStreamPtr (as a wrapper object) in the map and return the raw // pointer of ExternalProcessorStream. - ExternalProcessorStream* store(uint64_t stream_id, ExternalProcessorStreamPtr stream, - const ExtProcFilterStats& stat, + ExternalProcessorStream* store(ExternalProcessorStreamPtr stream, const ExtProcFilterStats& stat, const std::chrono::milliseconds& timeout) { - stream_manager_[stream_id] = + auto deferred_stream = std::make_unique(std::move(stream), *this, stat, timeout); - return stream_manager_[stream_id]->stream_.get(); + ExternalProcessorStream* raw_stream = deferred_stream->stream_.get(); + stream_manager_[raw_stream] = std::move(deferred_stream); + return stream_manager_[raw_stream]->stream_.get(); } - void erase(uint64_t stream_id) { stream_manager_.erase(stream_id); } - - void deferredErase(uint64_t stream_id, Envoy::Event::Dispatcher& dispatcher) { - auto it = stream_manager_.find(stream_id); + void erase(ExternalProcessorStream* stream) { stream_manager_.erase(stream); } + void deferredErase(ExternalProcessorStream* stream, Envoy::Event::Dispatcher& dispatcher) { + auto it = stream_manager_.find(stream); if (it == stream_manager_.end()) { return; } - it->second->deferredClose(dispatcher, stream_id); + it->second->deferredClose(dispatcher); } private: - // Map of DeferredDeletableStreamPtrs with stream id as key. - absl::flat_hash_map stream_manager_; + // Map of DeferredDeletableStreamPtrs with ExternalProcessorStream pointer as key. + absl::flat_hash_map stream_manager_; }; class FilterConfig { From bf65ad3ab24a6c3cb2b60cdb2e043dea6e32c2ac Mon Sep 17 00:00:00 2001 From: Yangmin Zhu Date: Wed, 7 Aug 2024 23:46:12 -0700 Subject: [PATCH 065/130] rbac: add delay_deny implementation in RBAC network filter (#33875) If specified, the RBAC network filter will delay the specified duration before actually closing the connection. This implements https://github.com/envoyproxy/envoy/issues/33771. --------- Signed-off-by: Yangmin Zhu --- .../filters/network/rbac/v3/rbac.proto | 10 ++++- changelogs/current.yaml | 4 ++ .../filters/network/rbac/rbac_filter.cc | 39 +++++++++++++++++- .../filters/network/rbac/rbac_filter.h | 15 +++++++ .../filters/network/rbac/filter_test.cc | 41 +++++++++++++++---- .../filters/network/rbac/integration_test.cc | 32 +++++++++++++++ test/integration/integration_tcp_client.h | 1 + 7 files changed, 131 insertions(+), 11 deletions(-) diff --git a/api/envoy/extensions/filters/network/rbac/v3/rbac.proto b/api/envoy/extensions/filters/network/rbac/v3/rbac.proto index 823e18277d1f..9032a65924ea 100644 --- a/api/envoy/extensions/filters/network/rbac/v3/rbac.proto +++ b/api/envoy/extensions/filters/network/rbac/v3/rbac.proto @@ -4,6 +4,8 @@ package envoy.extensions.filters.network.rbac.v3; import "envoy/config/rbac/v3/rbac.proto"; +import "google/protobuf/duration.proto"; + import "xds/annotations/v3/status.proto"; import "xds/type/matcher/v3/matcher.proto"; @@ -26,7 +28,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // // Header should not be used in rules/shadow_rules in RBAC network filter as // this information is only available in :ref:`RBAC http filter `. -// [#next-free-field: 8] +// [#next-free-field: 9] message RBAC { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.network.rbac.v2.RBAC"; @@ -87,4 +89,10 @@ message RBAC { // every payload (e.g., Mongo, MySQL, Kafka) set the enforcement type to // CONTINUOUS to enforce RBAC policies on every message boundary. EnforcementType enforcement_type = 4; + + // Delay the specified duration before closing the connection when the policy evaluation + // result is ``DENY``. If this is not present, the connection will be closed immediately. + // This is useful to provide a better protection for Envoy against clients that retries + // aggressively when the connection is rejected by the RBAC filter. + google.protobuf.Duration delay_deny = 8; } diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 9d7c0eb3695f..f7fa938c4040 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -159,5 +159,9 @@ new_features: change: | Prefer using IPv6 address when addresses from both families are available. Can be reverted by setting ``envoy.reloadable_features.prefer_ipv6_dns_on_macos`` to false. +- area: rbac + change: | + Added :ref:`delay_deny ` to support deny connection after + the configured duration. deprecated: diff --git a/source/extensions/filters/network/rbac/rbac_filter.cc b/source/extensions/filters/network/rbac/rbac_filter.cc index 7d07de0f6d18..4e67a4ec4c81 100644 --- a/source/extensions/filters/network/rbac/rbac_filter.cc +++ b/source/extensions/filters/network/rbac/rbac_filter.cc @@ -63,9 +63,14 @@ RoleBasedAccessControlFilterConfig::RoleBasedAccessControlFilterConfig( action_validation_visitor_)), shadow_engine_(Filters::Common::RBAC::createShadowEngine( proto_config, context, validation_visitor, action_validation_visitor_)), - enforcement_type_(proto_config.enforcement_type()) {} + enforcement_type_(proto_config.enforcement_type()), + delay_deny_ms_(PROTOBUF_GET_MS_OR_DEFAULT(proto_config, delay_deny, 0)) {} Network::FilterStatus RoleBasedAccessControlFilter::onData(Buffer::Instance&, bool) { + if (is_delay_denied_) { + return Network::FilterStatus::StopIteration; + } + ENVOY_LOG( debug, "checking connection: requestedServerName: {}, sourceIP: {}, directRemoteIP: {}," @@ -118,7 +123,19 @@ Network::FilterStatus RoleBasedAccessControlFilter::onData(Buffer::Instance&, bo } else if (engine_result_ == Deny) { callbacks_->connection().streamInfo().setConnectionTerminationDetails( Filters::Common::RBAC::responseDetail(log_policy_id)); - callbacks_->connection().close(Network::ConnectionCloseType::NoFlush, "rbac_deny_close"); + + std::chrono::milliseconds duration = config_->delayDenyMs(); + if (duration > std::chrono::milliseconds(0)) { + ENVOY_LOG(debug, "connection will be delay denied in {}ms", duration.count()); + delay_timer_ = callbacks_->connection().dispatcher().createTimer( + [this]() -> void { closeConnection(); }); + ASSERT(!is_delay_denied_); + is_delay_denied_ = true; + callbacks_->connection().readDisable(true); + delay_timer_->enableTimer(duration); + } else { + closeConnection(); + } return Network::FilterStatus::StopIteration; } @@ -126,6 +143,24 @@ Network::FilterStatus RoleBasedAccessControlFilter::onData(Buffer::Instance&, bo return Network::FilterStatus::Continue; } +void RoleBasedAccessControlFilter::closeConnection() { + callbacks_->connection().close(Network::ConnectionCloseType::NoFlush, "rbac_deny_close"); +} + +void RoleBasedAccessControlFilter::resetTimerState() { + if (delay_timer_) { + delay_timer_->disableTimer(); + delay_timer_.reset(); + } +} + +void RoleBasedAccessControlFilter::onEvent(Network::ConnectionEvent event) { + if (event == Network::ConnectionEvent::RemoteClose || + event == Network::ConnectionEvent::LocalClose) { + resetTimerState(); + } +} + void RoleBasedAccessControlFilter::setDynamicMetadata(std::string shadow_engine_result, std::string shadow_policy_id) { ProtobufWkt::Struct metrics; diff --git a/source/extensions/filters/network/rbac/rbac_filter.h b/source/extensions/filters/network/rbac/rbac_filter.h index a534f7c88010..b68aea4a56d6 100644 --- a/source/extensions/filters/network/rbac/rbac_filter.h +++ b/source/extensions/filters/network/rbac/rbac_filter.h @@ -1,5 +1,6 @@ #pragma once +#include "envoy/event/timer.h" #include "envoy/extensions/filters/network/rbac/v3/rbac.pb.h" #include "envoy/network/connection.h" #include "envoy/network/filter.h" @@ -58,6 +59,8 @@ class RoleBasedAccessControlFilterConfig { return enforcement_type_; } + std::chrono::milliseconds delayDenyMs() const { return delay_deny_ms_; } + private: Filters::Common::RBAC::RoleBasedAccessControlFilterStats stats_; const std::string shadow_rules_stat_prefix_; @@ -66,6 +69,7 @@ class RoleBasedAccessControlFilterConfig { std::unique_ptr engine_; std::unique_ptr shadow_engine_; const envoy::extensions::filters::network::rbac::v3::RBAC::EnforcementType enforcement_type_; + std::chrono::milliseconds delay_deny_ms_; }; using RoleBasedAccessControlFilterConfigSharedPtr = @@ -75,6 +79,7 @@ using RoleBasedAccessControlFilterConfigSharedPtr = * Implementation of a basic RBAC network filter. */ class RoleBasedAccessControlFilter : public Network::ReadFilter, + public Network::ConnectionCallbacks, public Logger::Loggable { public: @@ -87,8 +92,14 @@ class RoleBasedAccessControlFilter : public Network::ReadFilter, Network::FilterStatus onNewConnection() override { return Network::FilterStatus::Continue; }; void initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callbacks) override { callbacks_ = &callbacks; + callbacks_->connection().addConnectionCallbacks(*this); } + // Network::ConnectionCallbacks + void onEvent(Network::ConnectionEvent event) override; + void onAboveWriteBufferHighWatermark() override {} + void onBelowWriteBufferLowWatermark() override {} + void setDynamicMetadata(std::string shadow_engine_result, std::string shadow_policy_id); private: @@ -98,6 +109,10 @@ class RoleBasedAccessControlFilter : public Network::ReadFilter, EngineResult shadow_engine_result_{Unknown}; Result checkEngine(Filters::Common::RBAC::EnforcementMode mode); + void closeConnection(); + void resetTimerState(); + Event::TimerPtr delay_timer_{nullptr}; + bool is_delay_denied_{false}; }; } // namespace RBACFilter diff --git a/test/extensions/filters/network/rbac/filter_test.cc b/test/extensions/filters/network/rbac/filter_test.cc index 9ca7ad06483e..cdc9ed2f6b4e 100644 --- a/test/extensions/filters/network/rbac/filter_test.cc +++ b/test/extensions/filters/network/rbac/filter_test.cc @@ -10,6 +10,7 @@ #include "source/extensions/filters/network/rbac/rbac_filter.h" #include "source/extensions/filters/network/well_known_names.h" +#include "test/mocks/event/mocks.h" #include "test/mocks/network/mocks.h" #include "test/mocks/server/factory_context.h" @@ -29,7 +30,8 @@ class RoleBasedAccessControlNetworkFilterTest : public testing::Test { public: void setupPolicy(bool with_policy = true, bool continuous = false, - envoy::config::rbac::v3::RBAC::Action action = envoy::config::rbac::v3::RBAC::ALLOW) { + envoy::config::rbac::v3::RBAC::Action action = envoy::config::rbac::v3::RBAC::ALLOW, + int64_t delay_deny_duration_ms = 0) { envoy::extensions::filters::network::rbac::v3::RBAC config; config.set_stat_prefix("tcp."); @@ -58,11 +60,14 @@ class RoleBasedAccessControlNetworkFilterTest : public testing::Test { config.set_enforcement_type(envoy::extensions::filters::network::rbac::v3::RBAC::CONTINUOUS); } + if (delay_deny_duration_ms > 0) { + (*config.mutable_delay_deny()) = + ProtobufUtil::TimeUtil::MillisecondsToDuration(delay_deny_duration_ms); + } + config_ = std::make_shared( config, *store_.rootScope(), context_, ProtobufMessage::getStrictValidationVisitor()); - - filter_ = std::make_unique(config_); - filter_->initializeReadFilterCallbacks(callbacks_); + initFilter(); } void setupMatcher(bool with_matcher = true, bool continuous = false, std::string action = "ALLOW", @@ -163,12 +168,10 @@ class RoleBasedAccessControlNetworkFilterTest : public testing::Test { config_ = std::make_shared( config, *store_.rootScope(), context_, ProtobufMessage::getStrictValidationVisitor()); - - filter_ = std::make_unique(config_); - filter_->initializeReadFilterCallbacks(callbacks_); + initFilter(); } - RoleBasedAccessControlNetworkFilterTest() { + void initFilter() { EXPECT_CALL(callbacks_, connection()).WillRepeatedly(ReturnRef(callbacks_.connection_)); EXPECT_CALL(callbacks_.connection_, streamInfo()).WillRepeatedly(ReturnRef(stream_info_)); @@ -347,6 +350,28 @@ TEST_F(RoleBasedAccessControlNetworkFilterTest, Denied) { filter_meta.fields().at("shadow_rules_prefix_shadow_engine_result").string_value()); } +TEST_F(RoleBasedAccessControlNetworkFilterTest, DelayDenied) { + int64_t delay_deny_duration_ms = 500; + setupPolicy(true, false, envoy::config::rbac::v3::RBAC::ALLOW, delay_deny_duration_ms); + setDestinationPort(789); + + // Only call close() once since the connection is delay denied. + EXPECT_CALL(callbacks_.connection_, readDisable(true)); + EXPECT_CALL(callbacks_.connection_, close(Network::ConnectionCloseType::NoFlush, _)); + + Event::MockTimer* delay_timer = + new NiceMock(&callbacks_.connection_.dispatcher_); + EXPECT_CALL(*delay_timer, enableTimer(std::chrono::milliseconds(delay_deny_duration_ms), _)); + + // Call onData() twice, should only increase stats once. + EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onData(data_, false)); + EXPECT_EQ(Network::FilterStatus::StopIteration, filter_->onData(data_, false)); + EXPECT_EQ(0U, config_->stats().allowed_.value()); + EXPECT_EQ(1U, config_->stats().denied_.value()); + + delay_timer->invokeCallback(); +} + TEST_F(RoleBasedAccessControlNetworkFilterTest, MatcherAllowedWithOneTimeEnforcement) { setupMatcher(); diff --git a/test/extensions/filters/network/rbac/integration_test.cc b/test/extensions/filters/network/rbac/integration_test.cc index f8018eaff5db..c07ce6612552 100644 --- a/test/extensions/filters/network/rbac/integration_test.cc +++ b/test/extensions/filters/network/rbac/integration_test.cc @@ -7,6 +7,7 @@ #include "test/integration/integration.h" #include "test/test_common/environment.h" +#include "test/test_common/simulated_time_system.h" #include "fmt/printf.h" @@ -22,6 +23,7 @@ std::string rbac_config; class RoleBasedAccessControlNetworkFilterIntegrationTest : public testing::TestWithParam, + public Event::TestUsingSimulatedTime, public BaseIntegrationTest { public: RoleBasedAccessControlNetworkFilterIntegrationTest() @@ -133,6 +135,36 @@ name: rbac EXPECT_EQ(0U, test_server_->counter("tcp.rbac.shadow_denied")->value()); } +TEST_P(RoleBasedAccessControlNetworkFilterIntegrationTest, DelayDenied) { + initializeFilter(R"EOF( +name: rbac +typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC + stat_prefix: tcp. + rules: + policies: + "deny_all": + permissions: + - any: true + principals: + - not_id: + any: true + delay_deny: 5s +)EOF"); + IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("listener_0")); + ASSERT_TRUE(tcp_client->write("hello", false, false)); + ASSERT_TRUE(tcp_client->connected()); + + timeSystem().advanceTimeWait(std::chrono::seconds(3)); + ASSERT_TRUE(tcp_client->connected()); + + timeSystem().advanceTimeWait(std::chrono::seconds(6)); + tcp_client->waitForDisconnect(); + + EXPECT_EQ(0U, test_server_->counter("tcp.rbac.allowed")->value()); + EXPECT_EQ(1U, test_server_->counter("tcp.rbac.denied")->value()); +} + TEST_P(RoleBasedAccessControlNetworkFilterIntegrationTest, DeniedWithDenyAction) { useListenerAccessLog("%CONNECTION_TERMINATION_DETAILS%"); initializeFilter(R"EOF( diff --git a/test/integration/integration_tcp_client.h b/test/integration/integration_tcp_client.h index 5552c928d5f9..376bf7f01a4b 100644 --- a/test/integration/integration_tcp_client.h +++ b/test/integration/integration_tcp_client.h @@ -45,6 +45,7 @@ class IntegrationTcpClient { ABSL_MUST_USE_RESULT AssertionResult write(const std::string& data, bool end_stream = false, bool verify = true, std::chrono::milliseconds timeout = TestUtility::DefaultTimeout); + const std::string& data() { return payload_reader_->data(); } bool connected() const { return !disconnected_; } // clear up to the `count` number of bytes of received data From 121a541dd3fadef7131963f23e42a41e0c93e102 Mon Sep 17 00:00:00 2001 From: danzh Date: Thu, 8 Aug 2024 08:43:02 -0400 Subject: [PATCH 066/130] http: deprecate `envoy.reloadable_features.http1_connection_close_header_in_redirect` (#35603) Signed-off-by: Dan Zhang Co-authored-by: Dan Zhang --- changelogs/current.yaml | 4 ++ source/common/http/conn_manager_impl.cc | 16 ++--- source/common/runtime/runtime_features.cc | 1 - source/common/stream_info/stream_info_impl.h | 5 +- test/integration/redirect_integration_test.cc | 59 ------------------- 5 files changed, 10 insertions(+), 75 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index f7fa938c4040..5019a19f60ab 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -85,6 +85,10 @@ removed_config_or_runtime: change: | Removed runtime flag ``envoy.reloadable_features.abort_filter_chain_on_stream_reset`` and legacy code path. +- area: http + change: | + Removed runtime flag ``envoy.reloadable_features.http1_connection_close_header_in_redirect`` and + legacy code paths. - area: grpc reverse bridge change: | Removed ``envoy.reloadable_features.grpc_http1_reverse_bridge_handle_empty_response`` runtime diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc index 6a9bd3c0d446..a9e4a70cc25d 100644 --- a/source/common/http/conn_manager_impl.cc +++ b/source/common/http/conn_manager_impl.cc @@ -1152,17 +1152,11 @@ void ConnectionManagerImpl::ActiveStream::decodeHeaders(RequestHeaderMapSharedPt // Both shouldDrainConnectionUponCompletion() and is_head_request_ affect local replies: set them // as early as possible. const Protocol protocol = connection_manager_.codec_->protocol(); - if (Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.http1_connection_close_header_in_redirect")) { - if (HeaderUtility::shouldCloseConnection(protocol, *request_headers_)) { - // Only mark the connection to be closed if the request indicates so. The connection might - // already be marked so before this step, in which case if shouldCloseConnection() returns - // false, the stream info value shouldn't be overridden. - filter_manager_.streamInfo().setShouldDrainConnectionUponCompletion(true); - } - } else { - filter_manager_.streamInfo().setShouldDrainConnectionUponCompletion( - HeaderUtility::shouldCloseConnection(protocol, *request_headers_)); + if (HeaderUtility::shouldCloseConnection(protocol, *request_headers_)) { + // Only mark the connection to be closed if the request indicates so. The connection might + // already be marked so before this step, in which case if shouldCloseConnection() returns + // false, the stream info value shouldn't be overridden. + filter_manager_.streamInfo().setShouldDrainConnectionUponCompletion(true); } filter_manager_.streamInfo().protocol(protocol); diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 2ef124d5c9bf..60669e8d41d4 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -50,7 +50,6 @@ RUNTIME_GUARD(envoy_reloadable_features_gcp_authn_use_fixed_url); RUNTIME_GUARD(envoy_reloadable_features_grpc_side_stream_flow_control); RUNTIME_GUARD(envoy_reloadable_features_http1_balsa_delay_reset); RUNTIME_GUARD(envoy_reloadable_features_http1_balsa_disallow_lone_cr_in_chunk_extension); -RUNTIME_GUARD(envoy_reloadable_features_http1_connection_close_header_in_redirect); // Ignore the automated "remove this flag" issue: we should keep this for 1 year. RUNTIME_GUARD(envoy_reloadable_features_http1_use_balsa_parser); RUNTIME_GUARD(envoy_reloadable_features_http2_discard_host_header); diff --git a/source/common/stream_info/stream_info_impl.h b/source/common/stream_info/stream_info_impl.h index 15a6e46eccc4..bcd5d6cc17eb 100644 --- a/source/common/stream_info/stream_info_impl.h +++ b/source/common/stream_info/stream_info_impl.h @@ -377,10 +377,7 @@ struct StreamInfoImpl : public StreamInfo { downstream_transport_failure_reason_ = std::string(info.downstreamTransportFailureReason()); bytes_retransmitted_ = info.bytesRetransmitted(); packets_retransmitted_ = info.packetsRetransmitted(); - if (Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.http1_connection_close_header_in_redirect")) { - should_drain_connection_ = info.shouldDrainConnectionUponCompletion(); - } + should_drain_connection_ = info.shouldDrainConnectionUponCompletion(); } // This function is used to copy over every field exposed in the StreamInfo interface, with a diff --git a/test/integration/redirect_integration_test.cc b/test/integration/redirect_integration_test.cc index db289c321775..51537b20bbcf 100644 --- a/test/integration/redirect_integration_test.cc +++ b/test/integration/redirect_integration_test.cc @@ -208,68 +208,9 @@ TEST_P(RedirectIntegrationTest, BasicInternalRedirect) { EXPECT_THAT(waitForAccessLog(access_log_name_, 1), HasSubstr("200 via_upstream -")); } -// Test the buggy behavior where Envoy doesn't respond to "Connection: close" header in requests -// which get redirected. -// TODO(danzh) remove the test once the runtime guard is deprecated. -TEST_P(RedirectIntegrationTest, ConnectionCloseHeaderDroppedInInternalRedirect) { - config_helper_.addRuntimeOverride( - "envoy.reloadable_features.http1_connection_close_header_in_redirect", "false"); - - if (downstreamProtocol() != Envoy::Http::CodecClient::Type::HTTP1) { - return; - } - useAccessLog("%RESPONSE_FLAGS% %RESPONSE_CODE% %RESPONSE_CODE_DETAILS% %RESP(test-header)%"); - // Validate that header sanitization is only called once. - config_helper_.addConfigModifier( - [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& - hcm) { - // The delay should be ignored in light of "Connection: close". - hcm.mutable_delayed_close_timeout()->set_seconds(1); - hcm.set_via("via_value"); - hcm.mutable_common_http_protocol_options() - ->mutable_max_requests_per_connection() - ->set_value(10); - }); - initialize(); - - codec_client_ = makeHttpConnection(lookupPort("http")); - - default_request_headers_.setHost("handle.internal.redirect"); - default_request_headers_.setConnection("close"); - IntegrationStreamDecoderPtr response = - codec_client_->makeHeaderOnlyRequest(default_request_headers_); - - waitForNextUpstreamRequest(); - - upstream_request_->encodeHeaders(redirect_response_, true); - EXPECT_THAT(waitForAccessLog(access_log_name_, 0), - HasSubstr("302 internal_redirect test-header-value")); - - waitForNextUpstreamRequest(); - ASSERT(upstream_request_->headers().EnvoyOriginalUrl() != nullptr); - EXPECT_EQ("http://handle.internal.redirect/test/long/url", - upstream_request_->headers().getEnvoyOriginalUrlValue()); - EXPECT_EQ("/new/url", upstream_request_->headers().getPathValue()); - EXPECT_EQ("authority2", upstream_request_->headers().getHostValue()); - EXPECT_EQ("via_value", upstream_request_->headers().getViaValue()); - - upstream_request_->encodeHeaders(default_response_headers_, true); - - ASSERT_TRUE(response->waitForEndStream()); - ASSERT_TRUE(response->complete()); - EXPECT_EQ("200", response->headers().getStatusValue()); - EXPECT_TRUE(response->headers().get(Envoy::Http::Headers::get().Connection).empty()); - - // Envoy won't close the connection with out the fix. - ASSERT_FALSE(codec_client_->waitForDisconnect(std::chrono::milliseconds(2000))); -} - // Test that Envoy should correctly respond to "Connection: close" header in requests which get // redirected by echoing "Connection: close" in response and closing the connection immediately. TEST_P(RedirectIntegrationTest, ConnectionCloseHeaderHonoredInInternalRedirect) { - config_helper_.addRuntimeOverride( - "envoy.reloadable_features.http1_connection_close_header_in_redirect", "true"); - if (downstreamProtocol() != Envoy::Http::CodecClient::Type::HTTP1) { return; } From 751217c2bf6cdf0f48f3c7016ae8dfcb49097739 Mon Sep 17 00:00:00 2001 From: "Antonio V. Leonti" <53806445+antoniovleonti@users.noreply.github.com> Date: Thu, 8 Aug 2024 09:23:44 -0400 Subject: [PATCH 067/130] implement per-route override for rbac stat prefixes (#35531) Currently, if you only have stat prefixes configured in per-route config and not the base config, the stat prefix will be empty. this PR addresses that. --------- Signed-off-by: antoniovleonti --- changelogs/current.yaml | 4 + .../filters/http/rbac/rbac_filter.cc | 39 +++++- .../filters/http/rbac/rbac_filter.h | 25 ++-- .../filters/http/rbac/rbac_filter_test.cc | 128 ++++++++++++++++++ 4 files changed, 174 insertions(+), 22 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 5019a19f60ab..050c0abf0d6c 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -58,6 +58,10 @@ bug_fixes: change: | Add runtime guard for timeout error code 504 Gateway Timeout that is returned to downstream. If runtime flag ``envoy.reloadable_features.ext_proc_timeout_error`` is set to false, old error code 500 Internal Server Error will be returned. +- area: rbac + change: | + RBAC will now allow stat prefixes configured in per-route config to override the base config's + stat prefix. removed_config_or_runtime: # *Normally occurs at the end of the* :ref:`deprecation period ` diff --git a/source/extensions/filters/http/rbac/rbac_filter.cc b/source/extensions/filters/http/rbac/rbac_filter.cc index cefa97f010c4..e55924cfc861 100644 --- a/source/extensions/filters/http/rbac/rbac_filter.cc +++ b/source/extensions/filters/http/rbac/rbac_filter.cc @@ -1,3 +1,4 @@ +#include "rbac_filter.h" #include "source/extensions/filters/http/rbac/rbac_filter.h" #include "envoy/stats/scope.h" @@ -74,6 +75,29 @@ RoleBasedAccessControlFilterConfig::RoleBasedAccessControlFilterConfig( shadow_engine_(Filters::Common::RBAC::createShadowEngine( proto_config, context, validation_visitor, action_validation_visitor_)) {} +#define DEFINE_DYNAMIC_METADATA_STAT_KEY_GETTER(GETTER_NAME, PREFIX, ROUTE_LOCAL_PREFIX_OVERRIDE, \ + DYNAMIC_METADATA_KEY) \ + std::string RoleBasedAccessControlFilterConfig::GETTER_NAME( \ + const Http::StreamFilterCallbacks* callbacks) const { \ + const auto* route_local = Http::Utility::resolveMostSpecificPerFilterConfig< \ + RoleBasedAccessControlRouteSpecificFilterConfig>(callbacks); \ + std::string prefix = PREFIX; \ + if (route_local && !route_local->ROUTE_LOCAL_PREFIX_OVERRIDE().empty()) { \ + prefix = route_local->ROUTE_LOCAL_PREFIX_OVERRIDE(); \ + } \ + return prefix + \ + Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().DYNAMIC_METADATA_KEY; \ + } + +DEFINE_DYNAMIC_METADATA_STAT_KEY_GETTER(shadowEffectivePolicyIdField, shadow_rules_stat_prefix_, + shadowRulesStatPrefix, ShadowEffectivePolicyIdField) +DEFINE_DYNAMIC_METADATA_STAT_KEY_GETTER(shadowEngineResultField, shadow_rules_stat_prefix_, + shadowRulesStatPrefix, ShadowEngineResultField) +DEFINE_DYNAMIC_METADATA_STAT_KEY_GETTER(enforcedEffectivePolicyIdField, rules_stat_prefix_, + rulesStatPrefix, EnforcedEffectivePolicyIdField) +DEFINE_DYNAMIC_METADATA_STAT_KEY_GETTER(enforcedEngineResultField, rules_stat_prefix_, + rulesStatPrefix, EnforcedEngineResultField) + const Filters::Common::RBAC::RoleBasedAccessControlEngine* RoleBasedAccessControlFilterConfig::engine(const Http::StreamFilterCallbacks* callbacks, Filters::Common::RBAC::EnforcementMode mode) const { @@ -103,7 +127,9 @@ RoleBasedAccessControlRouteSpecificFilterConfig::RoleBasedAccessControlRouteSpec const envoy::extensions::filters::http::rbac::v3::RBACPerRoute& per_route_config, Server::Configuration::ServerFactoryContext& context, ProtobufMessage::ValidationVisitor& validation_visitor) - : per_rule_stats_(per_route_config.rbac().track_per_rule_stats()) { + : rules_stat_prefix_(per_route_config.rbac().rules_stat_prefix()), + shadow_rules_stat_prefix_(per_route_config.rbac().shadow_rules_stat_prefix()), + per_rule_stats_(per_route_config.rbac().track_per_rule_stats()) { // Moved from member initializer to ctor body to overcome clang false warning about memory // leak (clang-analyzer-cplusplus.NewDeleteLeaks,-warnings-as-errors). // Potentially https://lists.llvm.org/pipermail/llvm-bugs/2018-July/066769.html @@ -165,10 +191,11 @@ RoleBasedAccessControlFilter::decodeHeaders(Http::RequestHeaderMap& headers, boo } if (!effective_policy_id.empty()) { - *fields[config_->shadowEffectivePolicyIdField()].mutable_string_value() = effective_policy_id; + *fields[config_->shadowEffectivePolicyIdField(callbacks_)].mutable_string_value() = + effective_policy_id; } - *fields[config_->shadowEngineResultField()].mutable_string_value() = shadow_resp_code; + *fields[config_->shadowEngineResultField(callbacks_)].mutable_string_value() = shadow_resp_code; } const auto engine = config_->engine(callbacks_, Filters::Common::RBAC::EnforcementMode::Enforced); @@ -178,7 +205,7 @@ RoleBasedAccessControlFilter::decodeHeaders(Http::RequestHeaderMap& headers, boo callbacks_->streamInfo(), &effective_policy_id); const std::string log_policy_id = effective_policy_id.empty() ? "none" : effective_policy_id; if (!effective_policy_id.empty()) { - *fields[config_->enforcedEffectivePolicyIdField()].mutable_string_value() = + *fields[config_->enforcedEffectivePolicyIdField(callbacks_)].mutable_string_value() = effective_policy_id; } if (allowed) { @@ -188,7 +215,7 @@ RoleBasedAccessControlFilter::decodeHeaders(Http::RequestHeaderMap& headers, boo config_->stats().incPolicyAllowed(effective_policy_id); } - *fields[config_->enforcedEngineResultField()].mutable_string_value() = + *fields[config_->enforcedEngineResultField(callbacks_)].mutable_string_value() = Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().EngineResultAllowed; callbacks_->streamInfo().setDynamicMetadata("envoy.filters.http.rbac", metrics); @@ -203,7 +230,7 @@ RoleBasedAccessControlFilter::decodeHeaders(Http::RequestHeaderMap& headers, boo config_->stats().incPolicyDenied(effective_policy_id); } - *fields[config_->enforcedEngineResultField()].mutable_string_value() = + *fields[config_->enforcedEngineResultField(callbacks_)].mutable_string_value() = Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().EngineResultDenied; callbacks_->streamInfo().setDynamicMetadata("envoy.filters.http.rbac", metrics); diff --git a/source/extensions/filters/http/rbac/rbac_filter.h b/source/extensions/filters/http/rbac/rbac_filter.h index 9f021cb94e98..bb6748691fd2 100644 --- a/source/extensions/filters/http/rbac/rbac_filter.h +++ b/source/extensions/filters/http/rbac/rbac_filter.h @@ -35,10 +35,15 @@ class RoleBasedAccessControlRouteSpecificFilterConfig : public Router::RouteSpec return mode == Filters::Common::RBAC::EnforcementMode::Enforced ? engine_.get() : shadow_engine_.get(); } + std::string rulesStatPrefix() const { return rules_stat_prefix_; } + + std::string shadowRulesStatPrefix() const { return shadow_rules_stat_prefix_; } bool perRuleStatsEnabled() const { return per_rule_stats_; } private: + const std::string rules_stat_prefix_; + const std::string shadow_rules_stat_prefix_; ActionValidationVisitor action_validation_visitor_; std::unique_ptr engine_; std::unique_ptr shadow_engine_; @@ -57,23 +62,11 @@ class RoleBasedAccessControlFilterConfig { ProtobufMessage::ValidationVisitor& validation_visitor); Filters::Common::RBAC::RoleBasedAccessControlFilterStats& stats() { return stats_; } - std::string shadowEffectivePolicyIdField() const { - return shadow_rules_stat_prefix_ + - Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().ShadowEffectivePolicyIdField; - } - std::string shadowEngineResultField() const { - return shadow_rules_stat_prefix_ + - Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().ShadowEngineResultField; - } + std::string shadowEffectivePolicyIdField(const Http::StreamFilterCallbacks* callbacks) const; + std::string shadowEngineResultField(const Http::StreamFilterCallbacks* callbacks) const; - std::string enforcedEffectivePolicyIdField() const { - return rules_stat_prefix_ + Filters::Common::RBAC::DynamicMetadataKeysSingleton::get() - .EnforcedEffectivePolicyIdField; - } - std::string enforcedEngineResultField() const { - return rules_stat_prefix_ + - Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().EnforcedEngineResultField; - } + std::string enforcedEffectivePolicyIdField(const Http::StreamFilterCallbacks* callbacks) const; + std::string enforcedEngineResultField(const Http::StreamFilterCallbacks* callbacks) const; const Filters::Common::RBAC::RoleBasedAccessControlEngine* engine(const Http::StreamFilterCallbacks* callbacks, diff --git a/test/extensions/filters/http/rbac/rbac_filter_test.cc b/test/extensions/filters/http/rbac/rbac_filter_test.cc index 2d772457a176..3877ab4046bc 100644 --- a/test/extensions/filters/http/rbac/rbac_filter_test.cc +++ b/test/extensions/filters/http/rbac/rbac_filter_test.cc @@ -576,6 +576,134 @@ TEST_F(RoleBasedAccessControlFilterTest, RouteLocalOverride) { checkAccessLogMetadata(LogResult::Undecided); } +TEST_F(RoleBasedAccessControlFilterTest, RouteLocalOverrideDynamicMetadataStats) { + setupPolicy(envoy::config::rbac::v3::RBAC::DENY, "original_rbac_rules_prefix_"); + + setDestinationPort(123); + setMetadata(); + + // Set up an allow route_config that overrides the deny policy. + envoy::extensions::filters::http::rbac::v3::RBACPerRoute route_config; + route_config.mutable_rbac()->mutable_rules()->set_action(envoy::config::rbac::v3::RBAC::ALLOW); + route_config.mutable_rbac()->mutable_shadow_rules()->set_action( + envoy::config::rbac::v3::RBAC::ALLOW); + route_config.mutable_rbac()->set_rules_stat_prefix("override_rules_stat_prefix_"); + route_config.mutable_rbac()->set_shadow_rules_stat_prefix("override_shadow_rules_stat_prefix_"); + + envoy::config::rbac::v3::Policy policy; + auto policy_rules = policy.add_permissions()->mutable_or_rules(); + policy_rules->add_rules()->set_destination_port(123); + policy.add_principals()->set_any(true); + + envoy::config::rbac::v3::Policy shadow_policy; + auto shadow_policy_rules = shadow_policy.add_permissions()->mutable_or_rules(); + shadow_policy_rules->add_rules()->set_destination_port(123); + shadow_policy.add_principals()->set_any(true); + + (*route_config.mutable_rbac()->mutable_rules()->mutable_policies())["foobar"] = policy; + (*route_config.mutable_rbac()->mutable_shadow_rules()->mutable_policies())["foobar"] = + shadow_policy; + NiceMock factory_context; + NiceMock engine{route_config.rbac().rules(), factory_context}; + NiceMock per_route_config_{route_config, + context_}; + + EXPECT_CALL(engine, handleAction(_, _, _, _)).WillRepeatedly(Return(true)); + EXPECT_CALL(per_route_config_, engine()).WillRepeatedly(ReturnRef(engine)); + + EXPECT_CALL(*callbacks_.route_, mostSpecificPerFilterConfig(_)) + .WillRepeatedly(Return(&per_route_config_)); + + // Filter iteration should continue since the route-specific policy is ALLOW + // and there are enforced and shadow rules. + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(headers_, true)); + ASSERT_TRUE(req_info_.dynamicMetadata().filter_metadata().contains("envoy.filters.http.rbac")); + auto filter_meta = req_info_.dynamicMetadata().filter_metadata().at("envoy.filters.http.rbac"); + + // We expect the route-specific rules and prefix to be used for the enforced + // engine and the shadow rules and prefix to be used for the shadow engine. + ASSERT_TRUE(filter_meta.fields().contains("override_rules_stat_prefix_enforced_engine_result")); + EXPECT_EQ( + "allowed", + filter_meta.fields().at("override_rules_stat_prefix_enforced_engine_result").string_value()); + ASSERT_TRUE( + filter_meta.fields().contains("override_rules_stat_prefix_enforced_effective_policy_id")); + EXPECT_EQ("foobar", filter_meta.fields() + .at("override_rules_stat_prefix_enforced_effective_policy_id") + .string_value()); + + ASSERT_TRUE( + filter_meta.fields().contains("override_shadow_rules_stat_prefix_shadow_engine_result")); + EXPECT_EQ("allowed", filter_meta.fields() + .at("override_shadow_rules_stat_prefix_shadow_engine_result") + .string_value()); + ASSERT_TRUE(filter_meta.fields().contains( + "override_shadow_rules_stat_prefix_shadow_effective_policy_id")); + EXPECT_EQ("foobar", filter_meta.fields() + .at("override_shadow_rules_stat_prefix_shadow_effective_policy_id") + .string_value()); +} + +TEST_F(RoleBasedAccessControlFilterTest, NoRouteLocalOverrideDynamicMetadataStatsEmpty) { + setupPolicy(envoy::config::rbac::v3::RBAC::DENY, "rules_stat_prefix_"); + + setDestinationPort(123); + setMetadata(); + + // Set up an allow route_config that overrides the deny policy. But do not set up stat prefixes. + envoy::extensions::filters::http::rbac::v3::RBACPerRoute route_config; + route_config.mutable_rbac()->mutable_rules()->set_action(envoy::config::rbac::v3::RBAC::ALLOW); + route_config.mutable_rbac()->mutable_shadow_rules()->set_action( + envoy::config::rbac::v3::RBAC::ALLOW); + + envoy::config::rbac::v3::Policy policy; + auto policy_rules = policy.add_permissions()->mutable_or_rules(); + policy_rules->add_rules()->set_destination_port(123); + policy.add_principals()->set_any(true); + + envoy::config::rbac::v3::Policy shadow_policy; + auto shadow_policy_rules = shadow_policy.add_permissions()->mutable_or_rules(); + shadow_policy_rules->add_rules()->set_destination_port(123); + shadow_policy.add_principals()->set_any(true); + + (*route_config.mutable_rbac()->mutable_rules()->mutable_policies())["foobar"] = policy; + (*route_config.mutable_rbac()->mutable_shadow_rules()->mutable_policies())["foobar"] = + shadow_policy; + NiceMock factory_context; + NiceMock engine{route_config.rbac().rules(), factory_context}; + NiceMock per_route_config_{route_config, + context_}; + + EXPECT_CALL(engine, handleAction(_, _, _, _)).WillRepeatedly(Return(true)); + EXPECT_CALL(per_route_config_, engine()).WillRepeatedly(ReturnRef(engine)); + + EXPECT_CALL(*callbacks_.route_, mostSpecificPerFilterConfig(_)) + .WillRepeatedly(Return(&per_route_config_)); + + // Filter iteration should continue since the route-specific policy is ALLOW and there are + // enforced and shadow rules. + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(headers_, true)); + ASSERT_TRUE(req_info_.dynamicMetadata().filter_metadata().contains("envoy.filters.http.rbac")); + auto filter_meta = req_info_.dynamicMetadata().filter_metadata().at("envoy.filters.http.rbac"); + + // We expect the base rules and prefix to be used since no route-specific stat was set up. + ASSERT_TRUE(filter_meta.fields().contains("rules_stat_prefix_enforced_engine_result")); + EXPECT_EQ("allowed", + filter_meta.fields().at("rules_stat_prefix_enforced_engine_result").string_value()); + ASSERT_TRUE(filter_meta.fields().contains("rules_stat_prefix_enforced_effective_policy_id")); + EXPECT_EQ( + "foobar", + filter_meta.fields().at("rules_stat_prefix_enforced_effective_policy_id").string_value()); + + ASSERT_TRUE(filter_meta.fields().contains("shadow_rules_prefix_shadow_engine_result")); + EXPECT_EQ("allowed", + filter_meta.fields().at("shadow_rules_prefix_shadow_engine_result").string_value()); + ASSERT_TRUE(filter_meta.fields().contains("shadow_rules_prefix_shadow_effective_policy_id")); + EXPECT_EQ( + "foobar", + filter_meta.fields().at("shadow_rules_prefix_shadow_effective_policy_id").string_value()); +} + TEST_F(RoleBasedAccessControlFilterTest, RouteLocalOverrideWithPerRuleStats) { setupPolicy(envoy::config::rbac::v3::RBAC::ALLOW); From 511e4e5495dd354ddc1e83cd61ab9b2228294728 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Aug 2024 14:32:38 +0000 Subject: [PATCH 068/130] build(deps): bump envoyproxy/toolshed from actions-v0.2.34 to 0.2.35 (#35636) Signed-off-by: dependabot[bot] Signed-off-by: Ryan Northey --- .github/workflows/_cache.yml | 10 +++++----- .github/workflows/_finish.yml | 8 ++++---- .github/workflows/_load.yml | 10 +++++----- .github/workflows/_load_env.yml | 8 ++++---- .github/workflows/_request.yml | 10 +++++----- .github/workflows/_run.yml | 16 ++++++++-------- .github/workflows/_stage_publish.yml | 8 ++++---- .github/workflows/_stage_verify.yml | 8 ++++---- .github/workflows/_start.yml | 10 +++++----- .github/workflows/codeql-daily.yml | 2 +- .github/workflows/codeql-push.yml | 2 +- .github/workflows/command.yml | 6 +++--- .github/workflows/envoy-dependency.yml | 18 +++++++++--------- .github/workflows/envoy-release.yml | 22 +++++++++++----------- .github/workflows/envoy-sync.yml | 4 ++-- .github/workflows/garbage.yml | 2 +- .github/workflows/mobile-android_build.yml | 12 ++++++------ .github/workflows/mobile-ios_build.yml | 4 ++-- 18 files changed, 80 insertions(+), 80 deletions(-) diff --git a/.github/workflows/_cache.yml b/.github/workflows/_cache.yml index a630de390dee..a21194312df9 100644 --- a/.github/workflows/_cache.yml +++ b/.github/workflows/_cache.yml @@ -29,7 +29,7 @@ on: # For a job that does, you can restore with something like: # # steps: -# - uses: envoyproxy/toolshed/gh-actions/docker/cache/restore@actions-v0.2.30 +# - uses: envoyproxy/toolshed/gh-actions/docker/cache/restore@actions-v0.2.35 # with: # key: "${{ needs.env.outputs.build-image }}" # @@ -39,20 +39,20 @@ jobs: docker: runs-on: ubuntu-22.04 steps: - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.35 id: appauth name: Appauth (mutex lock) with: app_id: ${{ secrets.app-id }} key: ${{ secrets.app-key }} - - uses: envoyproxy/toolshed/gh-actions/docker/cache/prime@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/docker/cache/prime@actions-v0.2.35 id: docker name: Prime Docker cache (${{ inputs.image-tag }}) with: image-tag: ${{ inputs.image-tag }} lock-token: ${{ steps.appauth.outputs.token }} lock-repository: ${{ inputs.lock-repository }} - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.35 id: data name: Cache data with: @@ -60,7 +60,7 @@ jobs: input: | cached: ${{ steps.docker.outputs.cached }} key: ${{ inputs.image-tag }} - - uses: envoyproxy/toolshed/gh-actions/json/table@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/json/table@actions-v0.2.35 name: Summary with: json: ${{ steps.data.outputs.value }} diff --git a/.github/workflows/_finish.yml b/.github/workflows/_finish.yml index df59e74c64de..62a7a884b05e 100644 --- a/.github/workflows/_finish.yml +++ b/.github/workflows/_finish.yml @@ -36,7 +36,7 @@ jobs: actions: read contents: read steps: - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.35 name: Incoming data id: needs with: @@ -87,7 +87,7 @@ jobs: summary: "Check has finished", text: $text}}}} - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.35 name: Print summary with: input: ${{ toJSON(steps.needs.outputs.value).summary-title }} @@ -95,13 +95,13 @@ jobs: "## \(.)" options: -Rr output-path: GITHUB_STEP_SUMMARY - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.35 name: Appauth id: appauth with: app_id: ${{ secrets.app-id }} key: ${{ secrets.app-key }} - - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.35 name: Update check with: action: update diff --git a/.github/workflows/_load.yml b/.github/workflows/_load.yml index 60b2822be7fb..acbf553f4113 100644 --- a/.github/workflows/_load.yml +++ b/.github/workflows/_load.yml @@ -91,7 +91,7 @@ jobs: # Handle any failure in triggering job # Remove any `checks` we dont care about # Prepare a check request - - uses: envoyproxy/toolshed/gh-actions/github/env/load@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/github/env/load@actions-v0.2.35 name: Load env id: data with: @@ -102,13 +102,13 @@ jobs: GH_TOKEN: ${{ github.token }} # Update the check - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.35 name: Appauth id: appauth with: app_id: ${{ secrets.app-id }} key: ${{ secrets.app-key }} - - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.35 name: Update check if: ${{ fromJSON(steps.data.outputs.data).data.check.action == 'RUN' }} with: @@ -116,7 +116,7 @@ jobs: checks: ${{ toJSON(fromJSON(steps.data.outputs.data).checks) }} token: ${{ steps.appauth.outputs.token }} - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.35 name: Print request summary with: input: | @@ -136,7 +136,7 @@ jobs: | $summary.summary as $summary | "${{ inputs.template-request-summary }}" - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.35 id: request-output name: Load request with: diff --git a/.github/workflows/_load_env.yml b/.github/workflows/_load_env.yml index a07e2ab8b16e..6c9f4f7d5ded 100644 --- a/.github/workflows/_load_env.yml +++ b/.github/workflows/_load_env.yml @@ -63,18 +63,18 @@ jobs: request: ${{ steps.env.outputs.data }} trusted: true steps: - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.35 id: started name: Create timestamp with: options: -r filter: | now - - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.35 id: checkout name: Checkout Envoy repository - name: Generate environment variables - uses: envoyproxy/toolshed/gh-actions/envoy/ci/env@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/envoy/ci/env@actions-v0.2.35 id: env with: branch-name: ${{ inputs.branch-name }} @@ -86,7 +86,7 @@ jobs: - name: Request summary id: summary - uses: envoyproxy/toolshed/gh-actions/github/env/summary@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/github/env/summary@actions-v0.2.35 with: actor: ${{ toJSON(fromJSON(steps.env.outputs.data).request.actor) }} base-sha: ${{ fromJSON(steps.env.outputs.data).request.base-sha }} diff --git a/.github/workflows/_request.yml b/.github/workflows/_request.yml index d14a37622e09..1b44ae81019a 100644 --- a/.github/workflows/_request.yml +++ b/.github/workflows/_request.yml @@ -40,14 +40,14 @@ jobs: env: ${{ steps.data.outputs.value }} config: ${{ steps.config.outputs.config }} steps: - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.35 id: started name: Create timestamp with: options: -r filter: | now - - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.35 id: checkout name: Checkout Envoy repository with: @@ -60,7 +60,7 @@ jobs: # *ALL* variables collected should be treated as untrusted and should be sanitized before # use - name: Generate environment variables from commit - uses: envoyproxy/toolshed/gh-actions/envoy/ci/request@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/envoy/ci/request@actions-v0.2.35 id: env with: branch-name: ${{ steps.checkout.outputs.branch-name }} @@ -71,7 +71,7 @@ jobs: vars: ${{ toJSON(vars) }} - name: Request summary id: summary - uses: envoyproxy/toolshed/gh-actions/github/env/summary@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/github/env/summary@actions-v0.2.35 with: actor: ${{ toJSON(fromJSON(steps.env.outputs.data).request.actor) }} base-sha: ${{ fromJSON(steps.env.outputs.data).request.base-sha }} @@ -87,7 +87,7 @@ jobs: target-branch: ${{ fromJSON(steps.env.outputs.data).request.target-branch }} - name: Environment data - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.35 id: data with: input: | diff --git a/.github/workflows/_run.yml b/.github/workflows/_run.yml index 83497741037c..ce82d5ac8ad1 100644 --- a/.github/workflows/_run.yml +++ b/.github/workflows/_run.yml @@ -96,7 +96,7 @@ on: summary-post: type: string default: | - - uses: envoyproxy/toolshed/gh-actions/envoy/run/summary@actions-v0.2.30 + - uses: envoyproxy/toolshed/gh-actions/envoy/run/summary@actions-v0.2.35 with: context: %{{ inputs.context }} steps-pre: @@ -158,7 +158,7 @@ jobs: name: ${{ inputs.command }} ${{ inputs.target }} timeout-minutes: ${{ inputs.timeout-minutes }} steps: - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.35 id: started name: Create timestamp with: @@ -166,7 +166,7 @@ jobs: filter: | now # This controls which input vars are exposed to the run action (and related steps) - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.35 name: Context id: context with: @@ -187,11 +187,11 @@ jobs: | . * {$config, $check} - if: ${{ inputs.cache-build-image }} name: Restore Docker cache ${{ inputs.cache-build-image && format('({0})', inputs.cache-build-image) || '' }} - uses: envoyproxy/toolshed/gh-actions/docker/cache/restore@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/docker/cache/restore@actions-v0.2.35 with: image_tag: ${{ inputs.cache-build-image }} - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.35 id: appauth name: Appauth if: ${{ inputs.trusted }} @@ -202,7 +202,7 @@ jobs: # - the workaround is to allow the token to be passed through. token: ${{ github.token }} token-ok: true - - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.35 id: checkout name: Checkout Envoy repository with: @@ -219,7 +219,7 @@ jobs: token: ${{ inputs.trusted && steps.appauth.outputs.token || github.token }} # This is currently only use by mobile-docs and can be removed once they are updated to the newer website - - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.35 id: checkout-extra name: Checkout extra repository (for publishing) if: ${{ inputs.checkout-extra }} @@ -227,7 +227,7 @@ jobs: config: ${{ inputs.checkout-extra }} ssh-key: ${{ inputs.trusted && inputs.ssh-key-extra || '' }} - - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.35 name: Run CI ${{ inputs.command }} ${{ inputs.target }} with: args: ${{ inputs.args != '--' && inputs.args || inputs.target }} diff --git a/.github/workflows/_stage_publish.yml b/.github/workflows/_stage_publish.yml index 240b93b2a20e..be26f8bdff4c 100644 --- a/.github/workflows/_stage_publish.yml +++ b/.github/workflows/_stage_publish.yml @@ -63,7 +63,7 @@ jobs: export ENVOY_PUBLISH_DRY_RUN=${{ (fromJSON(inputs.request).request.version.dev || ! inputs.trusted) && 1 || '' }} steps-pre: | - id: url - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.30 + uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.35 with: options: -Rr input: >- @@ -80,7 +80,7 @@ jobs: end | . as $bucket | "https://storage.googleapis.com/\($bucket)/\($sha)/\($path)" - - uses: envoyproxy/toolshed/gh-actions/fetch@actions-v0.2.30 + - uses: envoyproxy/toolshed/gh-actions/fetch@actions-v0.2.35 with: url: %{{ steps.url.outputs.value }} path: %{{ runner.temp }}/release.signed @@ -98,12 +98,12 @@ jobs: needs: - publish steps: - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.35 id: appauth with: app_id: ${{ secrets.ENVOY_CI_SYNC_APP_ID }} key: ${{ secrets.ENVOY_CI_SYNC_APP_KEY }} - - uses: envoyproxy/toolshed/gh-actions/dispatch@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/dispatch@actions-v0.2.35 with: ref: main repository: ${{ fromJSON(inputs.request).request.version.dev && 'envoyproxy/envoy-website' || 'envoyproxy/archive' }} diff --git a/.github/workflows/_stage_verify.yml b/.github/workflows/_stage_verify.yml index 2110aec94924..3011bbc9bd15 100644 --- a/.github/workflows/_stage_verify.yml +++ b/.github/workflows/_stage_verify.yml @@ -50,7 +50,7 @@ jobs: rbe: false steps-pre: | - id: url - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.30 + uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.35 with: options: -Rr input: >- @@ -66,15 +66,15 @@ jobs: end | . as $bucket | "https://storage.googleapis.com/\($bucket)/\($sha)" - - uses: envoyproxy/toolshed/gh-actions/docker/fetch@actions-v0.2.30 + - uses: envoyproxy/toolshed/gh-actions/docker/fetch@actions-v0.2.35 with: url: %{{ steps.url.outputs.value }}/docker/envoy.tar variant: dev - - uses: envoyproxy/toolshed/gh-actions/docker/fetch@actions-v0.2.30 + - uses: envoyproxy/toolshed/gh-actions/docker/fetch@actions-v0.2.35 with: url: %{{ steps.url.outputs.value }}/docker/envoy-contrib.tar variant: contrib-dev - - uses: envoyproxy/toolshed/gh-actions/docker/fetch@actions-v0.2.30 + - uses: envoyproxy/toolshed/gh-actions/docker/fetch@actions-v0.2.35 with: url: %{{ steps.url.outputs.value }}/docker/envoy-google-vrp.tar variant: google-vrp-dev diff --git a/.github/workflows/_start.yml b/.github/workflows/_start.yml index 622dab7ae085..9b475214da3b 100644 --- a/.github/workflows/_start.yml +++ b/.github/workflows/_start.yml @@ -54,7 +54,7 @@ jobs: start: runs-on: ubuntu-22.04 steps: - - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.35 id: check-config name: Prepare check data with: @@ -77,13 +77,13 @@ jobs: | .skipped.output.summary = "${{ inputs.skipped-summary }}" | .skipped.output.text = "" - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.35 name: Appauth id: appauth with: app_id: ${{ secrets.app-id }} key: ${{ secrets.app-key }} - - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/github/checks@actions-v0.2.35 name: Start checks id: checks with: @@ -94,7 +94,7 @@ jobs: ${{ fromJSON(inputs.env).summary.summary }} token: ${{ steps.appauth.outputs.token }} - - uses: envoyproxy/toolshed/gh-actions/json/table@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/json/table@actions-v0.2.35 name: Summary with: collapse-open: true @@ -118,7 +118,7 @@ jobs: output-path: GITHUB_STEP_SUMMARY title: Checks started/skipped - - uses: envoyproxy/toolshed/gh-actions/github/env/save@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/github/env/save@actions-v0.2.35 name: Save env id: data with: diff --git a/.github/workflows/codeql-daily.yml b/.github/workflows/codeql-daily.yml index 57c58aae0fa1..a1d208e6feea 100644 --- a/.github/workflows/codeql-daily.yml +++ b/.github/workflows/codeql-daily.yml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Free disk space - uses: envoyproxy/toolshed/gh-actions/diskspace@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/diskspace@actions-v0.2.35 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/codeql-push.yml b/.github/workflows/codeql-push.yml index ba7e85a5bb7c..5a763a564744 100644 --- a/.github/workflows/codeql-push.yml +++ b/.github/workflows/codeql-push.yml @@ -61,7 +61,7 @@ jobs: - name: Free disk space if: ${{ env.BUILD_TARGETS != '' }} - uses: envoyproxy/toolshed/gh-actions/diskspace@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/diskspace@actions-v0.2.35 - name: Initialize CodeQL if: ${{ env.BUILD_TARGETS != '' }} diff --git a/.github/workflows/command.yml b/.github/workflows/command.yml index f622983f4703..99f78cc63d4f 100644 --- a/.github/workflows/command.yml +++ b/.github/workflows/command.yml @@ -28,7 +28,7 @@ jobs: && github.actor != 'dependabot[bot]' }} steps: - - uses: envoyproxy/toolshed/gh-actions/github/command@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/github/command@actions-v0.2.35 name: Parse command from comment id: command with: @@ -37,14 +37,14 @@ jobs: ^/(retest) # /retest - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.35 if: ${{ steps.command.outputs.command == 'retest' }} id: appauth-retest name: Appauth (retest) with: key: ${{ secrets.ENVOY_CI_APP_KEY }} app_id: ${{ secrets.ENVOY_CI_APP_ID }} - - uses: envoyproxy/toolshed/gh-actions/retest@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/retest@actions-v0.2.35 if: ${{ steps.command.outputs.command == 'retest' }} name: Retest with: diff --git a/.github/workflows/envoy-dependency.yml b/.github/workflows/envoy-dependency.yml index ccdac300d33c..0bab604492e2 100644 --- a/.github/workflows/envoy-dependency.yml +++ b/.github/workflows/envoy-dependency.yml @@ -53,16 +53,16 @@ jobs: steps: - id: appauth name: Appauth - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.35 with: app_id: ${{ secrets.ENVOY_CI_DEP_APP_ID }} key: ${{ secrets.ENVOY_CI_DEP_APP_KEY }} - id: checkout name: Checkout Envoy repository - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.35 with: token: ${{ steps.appauth.outputs.token }} - - uses: envoyproxy/toolshed/gh-actions/bson@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/bson@actions-v0.2.35 id: update name: Update dependency (${{ inputs.dependency }}) with: @@ -97,13 +97,13 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: envoyproxy/toolshed/gh-actions/upload/diff@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/upload/diff@actions-v0.2.35 name: Upload diff with: name: ${{ inputs.dependency }}-${{ steps.update.outputs.output }} - name: Create a PR if: ${{ inputs.pr }} - uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.35 with: base: main body: | @@ -134,11 +134,11 @@ jobs: steps: - id: appauth name: Appauth - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.35 with: app_id: ${{ secrets.ENVOY_CI_DEP_APP_ID }} key: ${{ secrets.ENVOY_CI_DEP_APP_KEY }} - - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.35 id: checkout name: Checkout Envoy repository with: @@ -180,7 +180,7 @@ jobs: - name: Check Docker SHAs id: build-images - uses: envoyproxy/toolshed/gh-actions/docker/shas@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/docker/shas@actions-v0.2.35 with: images: | sha: envoyproxy/envoy-build-ubuntu:${{ steps.build-tools.outputs.tag }} @@ -209,7 +209,7 @@ jobs: name: Update SHAs working-directory: envoy - name: Create a PR - uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.35 with: base: main body: Created by Envoy dependency bot diff --git a/.github/workflows/envoy-release.yml b/.github/workflows/envoy-release.yml index b716a96d14dd..104df712e7e1 100644 --- a/.github/workflows/envoy-release.yml +++ b/.github/workflows/envoy-release.yml @@ -55,14 +55,14 @@ jobs: steps: - id: appauth name: App auth - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.35 with: app_id: ${{ secrets.ENVOY_CI_PUBLISH_APP_ID }} key: ${{ secrets.ENVOY_CI_PUBLISH_APP_KEY }} - id: checkout name: Checkout Envoy repository - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.35 with: committer-name: ${{ env.COMMITTER_NAME }} committer-email: ${{ env.COMMITTER_EMAIL }} @@ -83,10 +83,10 @@ jobs: name: Check changelog summary - if: ${{ inputs.author }} name: Validate signoff email - uses: envoyproxy/toolshed/gh-actions/email/validate@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/email/validate@actions-v0.2.35 with: email: ${{ inputs.author }} - - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.35 name: Create release with: source: | @@ -111,7 +111,7 @@ jobs: name: Release version id: release - name: Create a PR - uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.35 with: base: ${{ github.ref_name }} commit: false @@ -136,20 +136,20 @@ jobs: steps: - id: appauth name: App auth - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.35 with: app_id: ${{ secrets.ENVOY_CI_PUBLISH_APP_ID }} key: ${{ secrets.ENVOY_CI_PUBLISH_APP_KEY }} - id: checkout name: Checkout Envoy repository - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.35 with: committer-name: ${{ env.COMMITTER_NAME }} committer-email: ${{ env.COMMITTER_EMAIL }} strip-prefix: release/ token: ${{ steps.appauth.outputs.token }} - - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/github/run@actions-v0.2.35 name: Sync version histories with: command: >- @@ -159,7 +159,7 @@ jobs: -- --signoff="${{ env.COMMITTER_NAME }} <${{ env.COMMITTER_EMAIL }}>" - name: Create a PR - uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/github/pr@actions-v0.2.35 with: append-commit-message: true base: ${{ github.ref_name }} @@ -189,13 +189,13 @@ jobs: steps: - id: appauth name: App auth - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.35 with: app_id: ${{ secrets.ENVOY_CI_PUBLISH_APP_ID }} key: ${{ secrets.ENVOY_CI_PUBLISH_APP_KEY }} - name: Checkout repository - uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/github/checkout@actions-v0.2.35 with: committer-name: ${{ env.COMMITTER_NAME }} committer-email: ${{ env.COMMITTER_EMAIL }} diff --git a/.github/workflows/envoy-sync.yml b/.github/workflows/envoy-sync.yml index 74b094d3bd49..acd3de4ef854 100644 --- a/.github/workflows/envoy-sync.yml +++ b/.github/workflows/envoy-sync.yml @@ -31,12 +31,12 @@ jobs: - data-plane-api - mobile-website steps: - - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.35 id: appauth with: app_id: ${{ secrets.ENVOY_CI_SYNC_APP_ID }} key: ${{ secrets.ENVOY_CI_SYNC_APP_KEY }} - - uses: envoyproxy/toolshed/gh-actions/dispatch@actions-v0.2.34 + - uses: envoyproxy/toolshed/gh-actions/dispatch@actions-v0.2.35 with: repository: "envoyproxy/${{ matrix.downstream }}" ref: main diff --git a/.github/workflows/garbage.yml b/.github/workflows/garbage.yml index 58f95a967d11..01783dfd5a70 100644 --- a/.github/workflows/garbage.yml +++ b/.github/workflows/garbage.yml @@ -33,7 +33,7 @@ jobs: pool-id: 17 steps: - name: Remove dead AZP agents (${{ matrix.target }}) - uses: envoyproxy/toolshed/gh-actions/azp/agent-cleanup@actions-v0.2.34 + uses: envoyproxy/toolshed/gh-actions/azp/agent-cleanup@actions-v0.2.35 with: azp-org: cncf azp-token: ${{ secrets.AZP_TOKEN }} diff --git a/.github/workflows/mobile-android_build.yml b/.github/workflows/mobile-android_build.yml index 509ba5eebac5..d262a6f19c95 100644 --- a/.github/workflows/mobile-android_build.yml +++ b/.github/workflows/mobile-android_build.yml @@ -75,9 +75,9 @@ jobs: target: kotlin-hello-world runs-on: envoy-x64-small steps-pre: | - - uses: envoyproxy/toolshed/gh-actions/envoy/android/pre@actions-v0.2.30 + - uses: envoyproxy/toolshed/gh-actions/envoy/android/pre@actions-v0.2.35 steps-post: | - - uses: envoyproxy/toolshed/gh-actions/envoy/android/post@actions-v0.2.30 + - uses: envoyproxy/toolshed/gh-actions/envoy/android/post@actions-v0.2.35 with: apk: bazel-bin/examples/kotlin/hello_world/hello_envoy_kt.apk app: io.envoyproxy.envoymobile.helloenvoykotlin/.MainActivity @@ -104,7 +104,7 @@ jobs: target: ${{ matrix.target }} runs-on: envoy-x64-small steps-pre: | - - uses: envoyproxy/toolshed/gh-actions/envoy/android/pre@actions-v0.2.30 + - uses: envoyproxy/toolshed/gh-actions/envoy/android/pre@actions-v0.2.35 steps-post: ${{ matrix.steps-post }} timeout-minutes: 50 trusted: ${{ fromJSON(needs.load.outputs.trusted) }} @@ -115,7 +115,7 @@ jobs: include: - name: java-hello-world steps-post: | - - uses: envoyproxy/toolshed/gh-actions/envoy/android/post@actions-v0.2.30 + - uses: envoyproxy/toolshed/gh-actions/envoy/android/post@actions-v0.2.35 with: apk: bazel-bin/examples/java/hello_world/hello_envoy.apk app: io.envoyproxy.envoymobile.helloenvoy/.MainActivity @@ -134,7 +134,7 @@ jobs: --config=mobile-remote-release-clang-android //test/kotlin/apps/baseline:hello_envoy_kt steps-post: | - - uses: envoyproxy/toolshed/gh-actions/envoy/android/post@actions-v0.2.30 + - uses: envoyproxy/toolshed/gh-actions/envoy/android/post@actions-v0.2.35 with: apk: bazel-bin/test/kotlin/apps/baseline/hello_envoy_kt.apk app: io.envoyproxy.envoymobile.helloenvoybaselinetest/.MainActivity @@ -149,7 +149,7 @@ jobs: --config=mobile-remote-release-clang-android //test/kotlin/apps/experimental:hello_envoy_kt steps-post: | - - uses: envoyproxy/toolshed/gh-actions/envoy/android/post@actions-v0.2.30 + - uses: envoyproxy/toolshed/gh-actions/envoy/android/post@actions-v0.2.35 with: apk: bazel-bin/test/kotlin/apps/experimental/hello_envoy_kt.apk app: io.envoyproxy.envoymobile.helloenvoyexperimentaltest/.MainActivity diff --git a/.github/workflows/mobile-ios_build.yml b/.github/workflows/mobile-ios_build.yml index a9ed28375056..023eca732056 100644 --- a/.github/workflows/mobile-ios_build.yml +++ b/.github/workflows/mobile-ios_build.yml @@ -86,7 +86,7 @@ jobs: source ./ci/mac_ci_setup.sh ./bazelw shutdown steps-post: | - - uses: envoyproxy/toolshed/gh-actions/envoy/ios/post@actions-v0.2.30 + - uses: envoyproxy/toolshed/gh-actions/envoy/ios/post@actions-v0.2.35 with: app: ${{ matrix.app }} args: ${{ matrix.args || '--config=mobile-remote-ci-macos-ios' }} @@ -130,7 +130,7 @@ jobs: source: | source ./ci/mac_ci_setup.sh steps-post: | - - uses: envoyproxy/toolshed/gh-actions/envoy/ios/post@actions-v0.2.30 + - uses: envoyproxy/toolshed/gh-actions/envoy/ios/post@actions-v0.2.35 with: app: ${{ matrix.app }} args: ${{ matrix.args || '--config=mobile-remote-ci-macos-ios' }} From b1dca5a656630b1073825dbbfc4a50c831b521f9 Mon Sep 17 00:00:00 2001 From: kozjan <138656232+kozjan@users.noreply.github.com> Date: Thu, 8 Aug 2024 16:45:35 +0200 Subject: [PATCH 069/130] jwt_authn: add missing path_match_policy matcher implementation (#35072) Commit Message: Added a matcher implementation for JWT. Right now the [documentation](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto.html#config-route-v3-routematch) says that `path_match_policy` may be used, but currently it throws exception. Additional Description: Previously I implemented it for [rbac only](https://github.com/envoyproxy/envoy/pull/31447). Would be nice if it could be included as a bug fix in the current Envoy version. Risk Level: Low? Testing: unit test Docs Changes: do not know how to handle it if it would be merged before new major release. Release Notes: Platform Specific Features: --------- Signed-off-by: jan.kozlowski Signed-off-by: kozjan <138656232+kozjan@users.noreply.github.com> Co-authored-by: yanavlasov --- changelogs/current.yaml | 3 ++ .../filters/http/jwt_authn/matcher.cc | 45 +++++++++++++++++-- .../filters/http/jwt_authn/matcher_test.cc | 24 ++++++++++ 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 050c0abf0d6c..934894d405d7 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -122,6 +122,9 @@ removed_config_or_runtime: Removed ``envoy.reloadable_features.normalize_host_for_preresolve_dfp_dns`` runtime flag and legacy code paths. new_features: +- area: jwt_authn + change: | + Added missing implementation to jwt_authn matchers to allow glob pattern matching. - area: tls change: | Added :ref:`prefer_client_ciphers diff --git a/source/extensions/filters/http/jwt_authn/matcher.cc b/source/extensions/filters/http/jwt_authn/matcher.cc index bab21c5441d4..1bdb41fabb9d 100644 --- a/source/extensions/filters/http/jwt_authn/matcher.cc +++ b/source/extensions/filters/http/jwt_authn/matcher.cc @@ -7,6 +7,7 @@ #include "source/common/common/matchers.h" #include "source/common/common/regex.h" #include "source/common/http/path_utility.h" +#include "source/common/protobuf/message_validator_impl.h" #include "source/common/router/config_impl.h" #include "absl/strings/match.h" @@ -188,6 +189,43 @@ class PathSeparatedPrefixMatcherImpl : public BaseMatcherImpl { }; } // namespace +class PathMatchPolicyMatcherImpl : public BaseMatcherImpl { +public: + PathMatchPolicyMatcherImpl(const RequirementRule& rule, + Server::Configuration::CommonFactoryContext& context) + : BaseMatcherImpl(rule, context), uri_template_matcher_(createUriTemplateMatcher(rule)) {} + + bool matches(const Http::RequestHeaderMap& headers) const override { + if (BaseMatcherImpl::matchRoute(headers) && + uri_template_matcher_->match(headers.getPathValue())) { + ENVOY_LOG(debug, "Path match policy requirement '{}' matched.", + uri_template_matcher_->uriTemplate()); + return true; + } + + return false; + } + +private: + const Router::PathMatcherSharedPtr uri_template_matcher_; + + static Router::PathMatcherSharedPtr createUriTemplateMatcher(const RequirementRule& rule) { + auto& factory = Config::Utility::getAndCheckFactory( + rule.match().path_match_policy()); + ProtobufTypes::MessagePtr config = Envoy::Config::Utility::translateAnyToFactoryConfig( + rule.match().path_match_policy().typed_config(), + ProtobufMessage::getStrictValidationVisitor(), factory); + + absl::StatusOr matcher = factory.createPathMatcher(*config); + + if (!matcher.ok()) { + throw EnvoyException(std::string(matcher.status().message())); + } + + return matcher.value(); + } +}; + MatcherConstPtr Matcher::create(const RequirementRule& rule, Server::Configuration::CommonFactoryContext& context) { switch (rule.match().path_specifier_case()) { @@ -201,10 +239,9 @@ MatcherConstPtr Matcher::create(const RequirementRule& rule, return std::make_unique(rule, context); case RouteMatch::PathSpecifierCase::kPathSeparatedPrefix: return std::make_unique(rule, context); - case RouteMatch::PathSpecifierCase::kPathMatchPolicy: - // TODO(silverstar194): Implement matcher for template based match - throw EnvoyException("RouteMatch: path_match_policy is not supported"); - break; + case RouteMatch::PathSpecifierCase::kPathMatchPolicy: { + return std::make_unique(rule, context); + } case RouteMatch::PathSpecifierCase::PATH_SPECIFIER_NOT_SET: break; // Fall through to PANIC. } diff --git a/test/extensions/filters/http/jwt_authn/matcher_test.cc b/test/extensions/filters/http/jwt_authn/matcher_test.cc index 885cba5a57bd..916b8e993a4c 100644 --- a/test/extensions/filters/http/jwt_authn/matcher_test.cc +++ b/test/extensions/filters/http/jwt_authn/matcher_test.cc @@ -253,6 +253,30 @@ TEST_F(MatcherTest, TestMatchPathSeparatedPrefixBaseCondition) { EXPECT_FALSE(matcher->matches(headers)); } +TEST_F(MatcherTest, TestMatchPathMatchPolicy) { + const char config[] = R"(match: + path_match_policy: + name: envoy.path.match.uri_template.uri_template_matcher + typed_config: + "@type": type.googleapis.com/envoy.extensions.path.match.uri_template.v3.UriTemplateMatchConfig + path_template: "/bar/*/foo" + )"; + MatcherConstPtr matcher = createMatcher(config); + auto headers = TestRequestHeaderMapImpl{{":path", "/bar/test/foo"}}; + EXPECT_TRUE(matcher->matches(headers)); +} + +TEST_F(MatcherTest, TestMatchPathMatchPolicyError) { + const char config[] = R"(match: + path_match_policy: + name: envoy.path.match.uri_template.uri_template_matcher + typed_config: + "@type": type.googleapis.com/envoy.extensions.path.match.uri_template.v3.UriTemplateMatchConfig + wrong_key: "/bar/*/foo" + )"; + EXPECT_THROW_WITH_REGEX(createMatcher(config), EnvoyException, "INVALID_ARGUMENT") +} + } // namespace } // namespace JwtAuthn } // namespace HttpFilters From 3fd1a7aa42c5c7e721645ef50c0fa9af79e13328 Mon Sep 17 00:00:00 2001 From: phlax Date: Thu, 8 Aug 2024 16:27:55 +0100 Subject: [PATCH 070/130] mobile/ci: Minor fix for download paths (#35638) Signed-off-by: Ryan Northey --- .github/workflows/mobile-perf.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/mobile-perf.yml b/.github/workflows/mobile-perf.yml index f0d9d6e97325..02787de5587f 100644 --- a/.github/workflows/mobile-perf.yml +++ b/.github/workflows/mobile-perf.yml @@ -94,8 +94,9 @@ jobs: request: ${{ needs.load.outputs.request }} runs-on: ubuntu-22.04 source: | - strip -s -o dist/main.stripped dist/sizemain/size-main - strip -s -o dist/current.stripped dist/sizecurrent/size-current + mkdir dist + strip -s -o dist/main.stripped /home/runner/work/_temp/dist/sizemain/size-main + strip -s -o dist/current.stripped /home/runner/work/_temp/dist/sizecurrent/size-current zip -9 dist/main.zip dist/main.stripped zip -9 dist/current.zip dist/current.stripped target: size-compare From 5b22dced91c062fc409153777c287f053dac0cbe Mon Sep 17 00:00:00 2001 From: "Antonio V. Leonti" <53806445+antoniovleonti@users.noreply.github.com> Date: Thu, 8 Aug 2024 12:26:58 -0400 Subject: [PATCH 071/130] add http1-safe max_connection_duration for http connection manager (#35209) This PR adds an option to change the http connection manager's draining behavior for max_connection_duration for http1. The new behavior is that envoy will wait indefinitely for one more request, add connection:close to the response headers, then close the connection once the stream ends. This is to avoid a networking race condition which results in the client not receiving a response to their last request if they send it right when envoy is closing the connection. Fixes #34356 (check for context) --------- Signed-off-by: antoniovleonti --- .../v3/http_connection_manager.proto | 17 +- changelogs/current.yaml | 9 + .../http/http_conn_man/stats.rst | 1 + source/common/http/conn_manager_config.h | 7 + source/common/http/conn_manager_impl.cc | 20 +- source/common/http/conn_manager_impl.h | 2 + .../network/http_connection_manager/config.cc | 1 + .../network/http_connection_manager/config.h | 4 + source/server/admin/admin.h | 1 + .../http/conn_manager_impl_fuzz_test.cc | 4 + test/common/http/conn_manager_impl_test.cc | 179 +++++++------- test/common/http/conn_manager_impl_test_2.cc | 231 +++++++++++------- .../http/conn_manager_impl_test_base.cc | 17 +- .../common/http/conn_manager_impl_test_base.h | 38 ++- .../filters/http/ext_proc/filter_test.cc | 4 +- test/integration/protocol_integration_test.cc | 63 +++++ test/mocks/http/mocks.h | 1 + 17 files changed, 405 insertions(+), 194 deletions(-) diff --git a/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto b/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto index 9217b6d0c933..9b9e61edb839 100644 --- a/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto +++ b/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto @@ -37,7 +37,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // HTTP connection manager :ref:`configuration overview `. // [#extension: envoy.filters.network.http_connection_manager] -// [#next-free-field: 58] +// [#next-free-field: 59] message HttpConnectionManager { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager"; @@ -447,6 +447,21 @@ message HttpConnectionManager { config.core.v3.HttpProtocolOptions common_http_protocol_options = 35 [(udpa.annotations.security).configure_for_untrusted_downstream = true]; + // If set to true, Envoy will not start a drain timer for downstream HTTP1 connections after + // :ref:`common_http_protocol_options.max_connection_duration + // ` passes. + // Instead, Envoy will wait for the next downstream request, add connection:close to the response + // headers, then close the connection after the stream ends. + // + // This behavior is compliant with `RFC 9112 section 9.6 `_ + // + // If set to false, ``max_connection_duration`` will cause Envoy to enter the normal drain + // sequence for HTTP1 with Envoy eventually closing the connection (once there are no active + // streams). + // + // Has no effect if ``max_connection_duration`` is unset. Defaults to false. + bool http1_safe_max_connection_duration = 58; + // Additional HTTP/1 settings that are passed to the HTTP/1 codec. // [#comment:TODO: The following fields are ignored when the // :ref:`header validation configuration ` diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 934894d405d7..e9f0c7bda101 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -6,6 +6,15 @@ behavior_changes: change: | Removed support for (long deprecated) opentracing. See `issue 27401 `_ for details. +- area: http + change: | + Added HTTP1-safe option for :ref:`max_connection_duration + ` in + HttpConnectionManager. When enabled, ``max_connection_duration`` will only drain downstream + HTTP1 connections by adding the Connection:close response header; it will never cause the + HttpConnectionManager to close the connection itself. Defaults to off ("unsafe" -- check + \#34356) and is configurable via :ref:`http1_safe_max_connection_duration + `. - area: stats scoped_rds change: | Added new tag extraction so that scoped rds stats have their :ref:'scope_route_config_name diff --git a/docs/root/configuration/http/http_conn_man/stats.rst b/docs/root/configuration/http/http_conn_man/stats.rst index 2605414255a1..fbd4e3ba4a69 100644 --- a/docs/root/configuration/http/http_conn_man/stats.rst +++ b/docs/root/configuration/http/http_conn_man/stats.rst @@ -26,6 +26,7 @@ statistics: ``downstream_cx_ssl_active``, Gauge, Total active TLS connections ``downstream_cx_http1_active``, Gauge, Total active HTTP/1.1 connections ``downstream_cx_upgrades_active``, Gauge, Total active upgraded connections. These are also counted as active http1/http2 connections. + ``downstream_cx_http1_soft_drain``, Gauge, Total active HTTP/1.x connections waiting for another downstream request to safely close the connection. ``downstream_cx_http2_active``, Gauge, Total active HTTP/2 connections ``downstream_cx_http3_active``, Gauge, Total active HTTP/3 connections ``downstream_cx_protocol_error``, Counter, Total protocol errors diff --git a/source/common/http/conn_manager_config.h b/source/common/http/conn_manager_config.h index a02a45ddc988..7a2b300b839f 100644 --- a/source/common/http/conn_manager_config.h +++ b/source/common/http/conn_manager_config.h @@ -83,6 +83,7 @@ namespace Http { GAUGE(downstream_cx_ssl_active, Accumulate) \ GAUGE(downstream_cx_tx_bytes_buffered, Accumulate) \ GAUGE(downstream_cx_upgrades_active, Accumulate) \ + GAUGE(downstream_cx_http1_soft_drain, Accumulate) \ GAUGE(downstream_rq_active, Accumulate) \ HISTOGRAM(downstream_cx_length_ms, Milliseconds) \ HISTOGRAM(downstream_rq_time, Milliseconds) @@ -290,6 +291,12 @@ class ConnectionManagerConfig { */ virtual absl::optional maxConnectionDuration() const PURE; + /** + * @return whether maxConnectionDuration allows HTTP1 clients to choose when to close connection + * (rather than Envoy closing the connection itself when there are no active streams). + */ + virtual bool http1SafeMaxConnectionDuration() const PURE; + /** * @return maximum request headers size the connection manager will accept. */ diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc index a9e4a70cc25d..65a2b032ec4e 100644 --- a/source/common/http/conn_manager_impl.cc +++ b/source/common/http/conn_manager_impl.cc @@ -213,6 +213,10 @@ ConnectionManagerImpl::~ConnectionManagerImpl() { } } + if (soft_drain_http1_) { + stats_.named_.downstream_cx_http1_soft_drain_.dec(); + } + conn_length_->complete(); user_agent_.completeConnectionLength(*conn_length_); } @@ -413,6 +417,12 @@ RequestDecoder& ConnectionManagerImpl::newStream(ResponseEncoder& response_encod stats_.named_.downstream_cx_max_requests_reached_.inc(); } + if (soft_drain_http1_) { + new_stream->filter_manager_.streamInfo().setShouldDrainConnectionUponCompletion(true); + // Prevent erroneous debug log of closing due to incoming connection close header. + drain_state_ = DrainState::Closing; + } + new_stream->state_.is_internally_created_ = is_internally_created; new_stream->response_encoder_ = &response_encoder; new_stream->response_encoder_->getStream().addCallbacks(*new_stream); @@ -724,7 +734,15 @@ void ConnectionManagerImpl::onConnectionDurationTimeout() { StreamInfo::CoreResponseFlag::DurationTimeout, StreamInfo::ResponseCodeDetails::get().DurationTimeout); } else if (drain_state_ == DrainState::NotDraining) { - startDrainSequence(); + if (config_->http1SafeMaxConnectionDuration() && codec_->protocol() < Protocol::Http2) { + ENVOY_CONN_LOG(debug, + "HTTP1-safe max connection duration is configured -- skipping drain sequence.", + read_callbacks_->connection()); + stats_.named_.downstream_cx_http1_soft_drain_.inc(); + soft_drain_http1_ = true; + } else { + startDrainSequence(); + } } } diff --git a/source/common/http/conn_manager_impl.h b/source/common/http/conn_manager_impl.h index a325dff0dfb2..34c96fab02f1 100644 --- a/source/common/http/conn_manager_impl.h +++ b/source/common/http/conn_manager_impl.h @@ -606,6 +606,8 @@ class ConnectionManagerImpl : Logger::Loggable, // A connection duration timer. Armed during handling new connection if enabled in config. Event::TimerPtr connection_duration_timer_; Event::TimerPtr drain_timer_; + // When set to true, add Connection:close response header to nudge downstream client to reconnect. + bool soft_drain_http1_{false}; Random::RandomGenerator& random_generator_; Runtime::Loader& runtime_; const LocalInfo::LocalInfo& local_info_; diff --git a/source/extensions/filters/network/http_connection_manager/config.cc b/source/extensions/filters/network/http_connection_manager/config.cc index e538fbe89e72..e54182218b1c 100644 --- a/source/extensions/filters/network/http_connection_manager/config.cc +++ b/source/extensions/filters/network/http_connection_manager/config.cc @@ -375,6 +375,7 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( idle_timeout_(PROTOBUF_GET_OPTIONAL_MS(config.common_http_protocol_options(), idle_timeout)), max_connection_duration_( PROTOBUF_GET_OPTIONAL_MS(config.common_http_protocol_options(), max_connection_duration)), + http1_safe_max_connection_duration_(config.http1_safe_max_connection_duration()), max_stream_duration_( PROTOBUF_GET_OPTIONAL_MS(config.common_http_protocol_options(), max_stream_duration)), stream_idle_timeout_( diff --git a/source/extensions/filters/network/http_connection_manager/config.h b/source/extensions/filters/network/http_connection_manager/config.h index 34dbfafcc2f9..c7a25b431d1d 100644 --- a/source/extensions/filters/network/http_connection_manager/config.h +++ b/source/extensions/filters/network/http_connection_manager/config.h @@ -180,6 +180,9 @@ class HttpConnectionManagerConfig : Logger::Loggable, absl::optional maxConnectionDuration() const override { return max_connection_duration_; } + bool http1SafeMaxConnectionDuration() const override { + return http1_safe_max_connection_duration_; + } std::chrono::milliseconds streamIdleTimeout() const override { return stream_idle_timeout_; } std::chrono::milliseconds requestTimeout() const override { return request_timeout_; } std::chrono::milliseconds requestHeadersTimeout() const override { @@ -324,6 +327,7 @@ class HttpConnectionManagerConfig : Logger::Loggable, const uint32_t max_request_headers_count_; absl::optional idle_timeout_; absl::optional max_connection_duration_; + const bool http1_safe_max_connection_duration_; absl::optional max_stream_duration_; std::chrono::milliseconds stream_idle_timeout_; std::chrono::milliseconds request_timeout_; diff --git a/source/server/admin/admin.h b/source/server/admin/admin.h index 92db9a75a6ad..80a91efc3ea0 100644 --- a/source/server/admin/admin.h +++ b/source/server/admin/admin.h @@ -148,6 +148,7 @@ class AdminImpl : public Admin, absl::optional maxConnectionDuration() const override { return max_connection_duration_; } + bool http1SafeMaxConnectionDuration() const override { return false; } uint32_t maxRequestHeadersKb() const override { return max_request_headers_kb_; } uint32_t maxRequestHeadersCount() const override { return max_request_headers_count_; } std::chrono::milliseconds streamIdleTimeout() const override { return {}; } diff --git a/test/common/http/conn_manager_impl_fuzz_test.cc b/test/common/http/conn_manager_impl_fuzz_test.cc index 065b4d53f377..59a5012ae857 100644 --- a/test/common/http/conn_manager_impl_fuzz_test.cc +++ b/test/common/http/conn_manager_impl_fuzz_test.cc @@ -153,6 +153,9 @@ class FuzzConfig : public ConnectionManagerConfig { absl::optional maxConnectionDuration() const override { return max_connection_duration_; } + bool http1SafeMaxConnectionDuration() const override { + return http1_safe_max_connection_duration_; + } absl::optional maxStreamDuration() const override { return max_stream_duration_; } @@ -276,6 +279,7 @@ class FuzzConfig : public ConnectionManagerConfig { uint32_t max_request_headers_count_{Http::DEFAULT_MAX_HEADERS_COUNT}; absl::optional idle_timeout_; absl::optional max_connection_duration_; + bool http1_safe_max_connection_duration_{false}; absl::optional max_stream_duration_; std::chrono::milliseconds stream_idle_timeout_{}; std::chrono::milliseconds request_timeout_{}; diff --git a/test/common/http/conn_manager_impl_test.cc b/test/common/http/conn_manager_impl_test.cc index e9094f6bcdf9..d98b1008e2fd 100644 --- a/test/common/http/conn_manager_impl_test.cc +++ b/test/common/http/conn_manager_impl_test.cc @@ -5,6 +5,8 @@ #include "test/test_common/logging.h" #include "test/test_common/test_runtime.h" +#include "conn_manager_impl_test_base.h" + using testing::_; using testing::An; using testing::AnyNumber; @@ -22,7 +24,7 @@ namespace Envoy { namespace Http { TEST_F(HttpConnectionManagerImplTest, HeaderOnlyRequestAndResponse) { - setup(false, "envoy-custom-server", false); + setup(SetupOpts().setTracing(false)); // Store the basic request encoder during filter chain setup. std::shared_ptr filter(new NiceMock()); @@ -92,7 +94,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderOnlyRequestAndResponse) { // Similar to HeaderOnlyRequestAndResponse but uses newStreamHandle and has // lifetime checks. TEST_F(HttpConnectionManagerImplTest, HandleLifetime) { - setup(false, "envoy-custom-server", false); + setup(SetupOpts().setTracing(false)); Http::RequestDecoderHandlePtr decoder_handle; // Store the basic request encoder during filter chain setup. @@ -169,7 +171,7 @@ TEST_F(HttpConnectionManagerImplTest, HandleLifetime) { } TEST_F(HttpConnectionManagerImplTest, HeaderOnlyRequestAndResponseWithEarlyHeaderMutation) { - setup(false, "envoy-custom-server", false); + setup(SetupOpts().setTracing(false)); auto mock_early_header_mutation_1 = std::make_unique>(); auto mock_early_header_mutation_2 = std::make_unique>(); @@ -260,7 +262,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderOnlyRequestAndResponseWithEarlyHeade TEST_F(HttpConnectionManagerImplTest, 1xxResponse) { proxy_100_continue_ = true; - setup(false, "envoy-custom-server", false); + setup(SetupOpts().setTracing(false)); // Store the basic request encoder during filter chain setup. std::shared_ptr filter(new NiceMock()); @@ -318,7 +320,7 @@ TEST_F(HttpConnectionManagerImplTest, 1xxResponse) { TEST_F(HttpConnectionManagerImplTest, 1xxResponseWithEncoderFiltersProxyingDisabled) { proxy_100_continue_ = false; - setup(false, "envoy-custom-server", false); + setup(SetupOpts().setTracing(false)); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); @@ -345,7 +347,7 @@ TEST_F(HttpConnectionManagerImplTest, 1xxResponseWithEncoderFiltersProxyingDisab TEST_F(HttpConnectionManagerImplTest, 1xxResponseWithEncoderFilters) { proxy_100_continue_ = true; - setup(false, "envoy-custom-server", false); + setup(SetupOpts().setTracing(false)); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); @@ -371,7 +373,7 @@ TEST_F(HttpConnectionManagerImplTest, 1xxResponseWithEncoderFilters) { TEST_F(HttpConnectionManagerImplTest, PauseResume1xx) { proxy_100_continue_ = true; - setup(false, "envoy-custom-server", false); + setup(SetupOpts().setTracing(false)); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); @@ -405,7 +407,7 @@ TEST_F(HttpConnectionManagerImplTest, PauseResume1xx) { // Regression test for https://github.com/envoyproxy/envoy/issues/10923. TEST_F(HttpConnectionManagerImplTest, 1xxResponseWithDecoderPause) { proxy_100_continue_ = true; - setup(false, "envoy-custom-server", false); + setup(SetupOpts().setTracing(false)); std::shared_ptr filter(new NiceMock()); @@ -472,7 +474,7 @@ TEST_F(HttpConnectionManagerImplTest, 1xxResponseWithDecoderPause) { // When create new stream, the stream info will be populated from the connection. TEST_F(HttpConnectionManagerImplTest, PopulateStreamInfo) { - setup(true, "", false); + setup(SetupOpts().setSsl(true).setTracing(false)); // Set up the codec. Buffer::OwnedImpl fake_input("input"); @@ -496,7 +498,7 @@ TEST_F(HttpConnectionManagerImplTest, PopulateStreamInfo) { // By default, Envoy will set the server header to the server name, here "custom-value" TEST_F(HttpConnectionManagerImplTest, ServerHeaderOverwritten) { - setup(false, "custom-value", false); + setup(SetupOpts().setServerName("custom-value").setTracing(false)); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); @@ -510,7 +512,7 @@ TEST_F(HttpConnectionManagerImplTest, ServerHeaderOverwritten) { // When configured APPEND_IF_ABSENT if the server header is present it will be retained. TEST_F(HttpConnectionManagerImplTest, ServerHeaderAppendPresent) { server_transformation_ = HttpConnectionManagerProto::APPEND_IF_ABSENT; - setup(false, "custom-value", false); + setup(SetupOpts().setServerName("custom-value").setTracing(false)); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); @@ -524,7 +526,7 @@ TEST_F(HttpConnectionManagerImplTest, ServerHeaderAppendPresent) { // When configured APPEND_IF_ABSENT if the server header is absent the server name will be set. TEST_F(HttpConnectionManagerImplTest, ServerHeaderAppendAbsent) { server_transformation_ = HttpConnectionManagerProto::APPEND_IF_ABSENT; - setup(false, "custom-value", false); + setup(SetupOpts().setServerName("custom-value").setTracing(false)); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); @@ -538,7 +540,7 @@ TEST_F(HttpConnectionManagerImplTest, ServerHeaderAppendAbsent) { // When configured PASS_THROUGH, the server name will pass through. TEST_F(HttpConnectionManagerImplTest, ServerHeaderPassthroughPresent) { server_transformation_ = HttpConnectionManagerProto::PASS_THROUGH; - setup(false, "custom-value", false); + setup(SetupOpts().setServerName("custom-value").setTracing(false)); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); @@ -552,7 +554,7 @@ TEST_F(HttpConnectionManagerImplTest, ServerHeaderPassthroughPresent) { // When configured PASS_THROUGH, the server header will not be added if absent. TEST_F(HttpConnectionManagerImplTest, ServerHeaderPassthroughAbsent) { server_transformation_ = HttpConnectionManagerProto::PASS_THROUGH; - setup(false, "custom-value", false); + setup(SetupOpts().setServerName("custom-value").setTracing(false)); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); @@ -565,7 +567,7 @@ TEST_F(HttpConnectionManagerImplTest, ServerHeaderPassthroughAbsent) { TEST_F(HttpConnectionManagerImplTest, InvalidPathWithDualFilter) { InSequence s; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); @@ -604,7 +606,7 @@ TEST_F(HttpConnectionManagerImplTest, InvalidPathWithDualFilter) { // Invalid paths are rejected with 400. TEST_F(HttpConnectionManagerImplTest, PathFailedtoSanitize) { - setup(false, ""); + setup(); // Enable path sanitizer normalize_path_ = true; @@ -652,7 +654,7 @@ TEST_F(HttpConnectionManagerImplTest, PathFailedtoSanitize) { // Filters observe normalized paths, not the original path, when path // normalization is configured. TEST_F(HttpConnectionManagerImplTest, FilterShouldUseSantizedPath) { - setup(false, ""); + setup(); // Enable path sanitizer normalize_path_ = true; const std::string original_path = "/x/%2E%2e/z"; @@ -699,7 +701,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterShouldUseSantizedPath) { // The router observes normalized paths, not the original path, when path // normalization is configured. TEST_F(HttpConnectionManagerImplTest, RouteShouldUseSantizedPath) { - setup(false, ""); + setup(); // Enable path sanitizer normalize_path_ = true; const std::string original_path = "/x/%2E%2e/z"; @@ -795,7 +797,7 @@ TEST_F(HttpConnectionManagerImplTest, EscapedSlashesRedirectedAfterOtherNormaliz } TEST_F(HttpConnectionManagerImplTest, AllNormalizationsWithEscapedSlashesForwarded) { - setup(false, ""); + setup(); // Enable path sanitizer normalize_path_ = true; merge_slashes_ = true; @@ -844,7 +846,7 @@ TEST_F(HttpConnectionManagerImplTest, AllNormalizationsWithEscapedSlashesForward } TEST_F(HttpConnectionManagerImplTest, RouteOverride) { - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); @@ -1045,7 +1047,7 @@ TEST_F(HttpConnectionManagerImplTest, RouteOverride) { // original route returned by route config (upstream cluster default), when the setRoute filter // callback is applied. TEST_F(HttpConnectionManagerImplTest, FilterSetRouteToDelegatingRouteWithClusterOverride) { - setup(false, ""); + setup(); setupFilterChain(2, 0); // Cluster mocks: default and foo @@ -1131,7 +1133,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterSetRouteToDelegatingRouteWithCluster // Test that all methods supported by DelegatingRouteEntry delegate correctly TEST_F(HttpConnectionManagerImplTest, DelegatingRouteEntryAllCalls) { - setup(false, ""); + setup(); setupFilterChain(2, 0); // Cluster mock: foo @@ -1326,7 +1328,7 @@ TEST_F(HttpConnectionManagerImplTest, DelegatingRouteEntryAllCalls) { // Filters observe host header w/o port's part when port's removal is configured TEST_F(HttpConnectionManagerImplTest, FilterShouldUseNormalizedHost) { - setup(false, ""); + setup(); // Enable port removal strip_port_type_ = Http::StripPortType::MatchingHost; const std::string original_host = "host:443"; @@ -1371,7 +1373,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterShouldUseNormalizedHost) { // The router observes host header w/o port, not the original host, when // remove_port is configured TEST_F(HttpConnectionManagerImplTest, RouteShouldUseNormalizedHost) { - setup(false, ""); + setup(); // Enable port removal strip_port_type_ = Http::StripPortType::MatchingHost; const std::string original_host = "host:443"; @@ -1411,7 +1413,7 @@ TEST_F(HttpConnectionManagerImplTest, RouteShouldUseNormalizedHost) { // Observe that we strip the trailing dot. TEST_F(HttpConnectionManagerImplTest, StripTrailingHostDot) { - setup(false, ""); + setup(); // Enable removal of host's trailing dot. strip_trailing_host_dot_ = true; const std::string original_host = "host."; @@ -1431,7 +1433,7 @@ TEST_F(HttpConnectionManagerImplTest, StripTrailingHostDot) { } TEST_F(HttpConnectionManagerImplTest, HostWithoutTrailingDot) { - setup(false, ""); + setup(); // Enable removal of host's trailing dot. strip_trailing_host_dot_ = true; const std::string original_host = "host"; @@ -1451,7 +1453,7 @@ TEST_F(HttpConnectionManagerImplTest, HostWithoutTrailingDot) { } TEST_F(HttpConnectionManagerImplTest, DateHeaderNotPresent) { - setup(false, ""); + setup(); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); const auto* modified_headers = sendResponseHeaders( @@ -1462,7 +1464,7 @@ TEST_F(HttpConnectionManagerImplTest, DateHeaderNotPresent) { } TEST_F(HttpConnectionManagerImplTest, DateHeaderPresent) { - setup(false, ""); + setup(); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); const std::string expected_date{"Tue, 15 Nov 1994 08:12:31 GMT"}; @@ -1476,7 +1478,7 @@ TEST_F(HttpConnectionManagerImplTest, DateHeaderPresent) { } TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlow) { - setup(false, ""); + setup(); auto* span = new NiceMock(); EXPECT_CALL(*tracer_, startSpan_(_, _, _, _)) @@ -1636,7 +1638,7 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlow) { } TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowIngressDecorator) { - setup(false, ""); + setup(); auto* span = new NiceMock(); EXPECT_CALL(*tracer_, startSpan_(_, _, _, _)) @@ -1704,7 +1706,7 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowIngressDecorat } TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowIngressDecoratorPropagateFalse) { - setup(false, ""); + setup(); auto* span = new NiceMock(); EXPECT_CALL(*tracer_, startSpan_(_, _, _, _)) @@ -1773,7 +1775,7 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowIngressDecorat } TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowIngressDecoratorOverrideOp) { - setup(false, ""); + setup(); auto* span = new NiceMock(); EXPECT_CALL(*tracer_, startSpan_(_, _, _, _)) @@ -1843,7 +1845,7 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowIngressDecorat } TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowEgressDecorator) { - setup(false, ""); + setup(); envoy::type::v3::FractionalPercent percent1; percent1.set_numerator(100); envoy::type::v3::FractionalPercent percent2; @@ -1927,7 +1929,7 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowEgressDecorato } TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowEgressDecoratorPropagateFalse) { - setup(false, ""); + setup(); envoy::type::v3::FractionalPercent percent1; percent1.set_numerator(100); envoy::type::v3::FractionalPercent percent2; @@ -2013,7 +2015,7 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowEgressDecorato } TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowEgressDecoratorOverrideOp) { - setup(false, ""); + setup(); envoy::type::v3::FractionalPercent percent1; percent1.set_numerator(100); envoy::type::v3::FractionalPercent percent2; @@ -2091,7 +2093,7 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowEgressDecorato TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlowEgressDecoratorOverrideOpNoActiveSpan) { - setup(false, ""); + setup(); envoy::type::v3::FractionalPercent percent1; percent1.set_numerator(100); envoy::type::v3::FractionalPercent percent2; @@ -2148,7 +2150,7 @@ TEST_F(HttpConnectionManagerImplTest, } TEST_F(HttpConnectionManagerImplTest, NoHCMTracingConfigAndActiveSpanWouldBeNullSpan) { - setup(false, ""); + setup(); // Null HCM tracing config. tracing_config_ = nullptr; @@ -2206,7 +2208,7 @@ TEST_F(HttpConnectionManagerImplTest, TestAccessLog) { // stream_info.downstreamRemoteAddress will infer the address from request // headers instead of the physical connection use_remote_address_ = false; - setup(false, ""); + setup(); std::shared_ptr filter(new NiceMock()); std::shared_ptr handler(new NiceMock()); @@ -2264,7 +2266,7 @@ TEST_F(HttpConnectionManagerImplTest, TestAccessLog) { } TEST_F(HttpConnectionManagerImplTest, TestFilterCanEnrichAccessLogs) { - setup(false, ""); + setup(); std::shared_ptr filter(new NiceMock()); std::shared_ptr handler(new NiceMock()); @@ -2316,7 +2318,7 @@ TEST_F(HttpConnectionManagerImplTest, TestFilterCanEnrichAccessLogs) { } TEST_F(HttpConnectionManagerImplTest, TestRemoteDownstreamDisconnectAccessLog) { - setup(false, ""); + setup(); std::shared_ptr filter(new NiceMock()); std::shared_ptr handler(new NiceMock()); @@ -2360,7 +2362,7 @@ TEST_F(HttpConnectionManagerImplTest, TestRemoteDownstreamDisconnectAccessLog) { } TEST_F(HttpConnectionManagerImplTest, TestLocalDownstreamDisconnectAccessLog) { - setup(false, ""); + setup(); std::shared_ptr filter(new NiceMock()); std::shared_ptr handler(new NiceMock()); @@ -2402,7 +2404,7 @@ TEST_F(HttpConnectionManagerImplTest, TestLocalDownstreamDisconnectAccessLog) { } TEST_F(HttpConnectionManagerImplTest, TestAccessLogWithTrailers) { - setup(false, ""); + setup(); std::shared_ptr filter(new NiceMock()); std::shared_ptr handler(new NiceMock()); @@ -2455,7 +2457,7 @@ TEST_F(HttpConnectionManagerImplTest, TestAccessLogWithTrailers) { } TEST_F(HttpConnectionManagerImplTest, TestAccessLogWithInvalidRequest) { - setup(false, ""); + setup(); std::shared_ptr filter(new NiceMock()); std::shared_ptr handler(new NiceMock()); @@ -2501,7 +2503,7 @@ TEST_F(HttpConnectionManagerImplTest, TestAccessLogWithInvalidRequest) { } TEST_F(HttpConnectionManagerImplTest, TestAccessLogOnNewRequest) { - setup(false, ""); + setup(); std::shared_ptr filter(new NiceMock()); std::shared_ptr handler(new NiceMock()); @@ -2563,7 +2565,7 @@ TEST_F(HttpConnectionManagerImplTest, TestAccessLogOnNewRequest) { } TEST_F(HttpConnectionManagerImplTest, TestAccessLogOnTunnelEstablished) { - setup(false, ""); + setup(); std::shared_ptr filter(new NiceMock()); std::shared_ptr handler(new NiceMock()); @@ -2627,7 +2629,8 @@ TEST_F(HttpConnectionManagerImplTest, TestPeriodicAccessLogging) { request_timeout_ = std::chrono::milliseconds(0); request_headers_timeout_ = std::chrono::milliseconds(0); max_stream_duration_ = std::nullopt; - setup(false, "server_name"); + + setup(SetupOpts().setServerName("server-opts")); std::shared_ptr filter(new NiceMock()); std::shared_ptr handler(new NiceMock()); @@ -2708,7 +2711,7 @@ class StreamErrorOnInvalidHttpMessageTest : public HttpConnectionManagerImplTest public: void sendInvalidRequestAndVerifyConnectionState(bool stream_error_on_invalid_http_message, bool send_complete_request = true) { - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)) .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { @@ -2786,7 +2789,7 @@ TEST_F(StreamErrorOnInvalidHttpMessageTest, ConnectionOpenIfCodecStreamErrorIsTr } TEST_F(HttpConnectionManagerImplTest, TestAccessLogSsl) { - setup(true, ""); + setup(SetupOpts().setSsl(true)); std::shared_ptr filter(new NiceMock()); std::shared_ptr handler(new NiceMock()); @@ -2840,7 +2843,7 @@ TEST_F(HttpConnectionManagerImplTest, TestAccessLogSsl) { } TEST_F(HttpConnectionManagerImplTest, DoNotStartSpanIfTracingIsNotEnabled) { - setup(false, ""); + setup(); // Disable tracing. tracing_config_.reset(); @@ -2883,7 +2886,7 @@ TEST_F(HttpConnectionManagerImplTest, DoNotStartSpanIfTracingIsNotEnabled) { } TEST_F(HttpConnectionManagerImplTest, NoPath) { - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); @@ -2907,7 +2910,7 @@ TEST_F(HttpConnectionManagerImplTest, NoPath) { // per-route level. The connection manager config is responsible for managing // the default configuration aspects. TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutNotConfigured) { - setup(false, ""); + setup(); EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, createTimer_(_)).Times(0); EXPECT_CALL(*codec_, dispatch(_)) @@ -2933,7 +2936,7 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutNotConfigured) { // headers, if it fires we don't faceplant. TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutGlobal) { stream_idle_timeout_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillRepeatedly(Invoke([&](Buffer::Instance&) -> Http::Status { Event::MockTimer* idle_timer = setUpTimer(); @@ -2965,7 +2968,7 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutGlobal) { TEST_F(HttpConnectionManagerImplTest, AccessEncoderRouteBeforeHeadersArriveOnIdleTimeout) { stream_idle_timeout_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); std::shared_ptr filter(new NiceMock()); @@ -3017,7 +3020,7 @@ TEST_F(HttpConnectionManagerImplTest, AccessEncoderRouteBeforeHeadersArriveOnIdl TEST_F(HttpConnectionManagerImplTest, TestStreamIdleAccessLog) { stream_idle_timeout_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillRepeatedly(Invoke([&](Buffer::Instance&) -> Http::Status { Event::MockTimer* idle_timer = setUpTimer(); @@ -3072,7 +3075,7 @@ TEST_F(HttpConnectionManagerImplTest, TestStreamIdleAccessLog) { // Test timeout variants. TEST_F(HttpConnectionManagerImplTest, DurationTimeout) { stream_idle_timeout_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); setupFilterChain(1, 0); RequestHeaderMap* latched_headers = nullptr; @@ -3272,7 +3275,7 @@ TEST_F(HttpConnectionManagerImplTest, DurationTimeout) { // Per-route timeouts override the global stream idle timeout. TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutRouteOverride) { stream_idle_timeout_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); ON_CALL(route_config_provider_.route_config_->route_->route_entry_, idleTimeout()) .WillByDefault(Return(std::chrono::milliseconds(30))); @@ -3302,7 +3305,7 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutRouteOverride) { // Per-route zero timeout overrides the global stream idle timeout. TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutRouteZeroOverride) { stream_idle_timeout_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); ON_CALL(route_config_provider_.route_config_->route_->route_entry_, idleTimeout()) .WillByDefault(Return(std::chrono::milliseconds(0))); @@ -3331,7 +3334,7 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutRouteZeroOverride) { // Validate the per-stream idle timeout after having sent downstream headers. TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterDownstreamHeaders) { - setup(false, ""); + setup(); ON_CALL(route_config_provider_.route_config_->route_->route_entry_, idleTimeout()) .WillByDefault(Return(std::chrono::milliseconds(10))); @@ -3372,7 +3375,7 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterDownstreamHeaders // Validate the per-stream idle timer is properly disabled when the stream terminates normally. TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutNormalTermination) { - setup(false, ""); + setup(); ON_CALL(route_config_provider_.route_config_->route_->route_entry_, idleTimeout()) .WillByDefault(Return(std::chrono::milliseconds(10))); @@ -3402,7 +3405,7 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutNormalTermination) { // Validate the per-stream idle timeout after having sent downstream // headers+body. TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterDownstreamHeadersAndBody) { - setup(false, ""); + setup(); ON_CALL(route_config_provider_.route_config_->route_->route_entry_, idleTimeout()) .WillByDefault(Return(std::chrono::milliseconds(10))); @@ -3446,7 +3449,7 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterDownstreamHeaders // Validate the per-stream idle timeout after upstream headers have been sent. TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterUpstreamHeaders) { - setup(false, ""); + setup(); ON_CALL(route_config_provider_.route_config_->route_->route_entry_, idleTimeout()) .WillByDefault(Return(std::chrono::milliseconds(10))); @@ -3497,7 +3500,7 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterUpstreamHeaders) // Validate the per-stream idle timeout after a sequence of header/data events. TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterBidiData) { - setup(false, ""); + setup(); ON_CALL(route_config_provider_.route_config_->route_->route_entry_, idleTimeout()) .WillByDefault(Return(std::chrono::milliseconds(10))); proxy_100_continue_ = true; @@ -3571,7 +3574,7 @@ TEST_F(HttpConnectionManagerImplTest, PerStreamIdleTimeoutAfterBidiData) { } TEST_F(HttpConnectionManagerImplTest, RequestTimeoutDisabledByDefault) { - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, createTimer_).Times(0); @@ -3588,7 +3591,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutDisabledByDefault) { TEST_F(HttpConnectionManagerImplTest, RequestTimeoutDisabledIfSetToZero) { request_timeout_ = std::chrono::milliseconds(0); - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, createTimer_).Times(0); @@ -3603,7 +3606,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutDisabledIfSetToZero) { TEST_F(HttpConnectionManagerImplTest, RequestTimeoutValidlyConfigured) { request_timeout_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { Event::MockTimer* request_timer = setUpTimer(); @@ -3623,7 +3626,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutValidlyConfigured) { TEST_F(HttpConnectionManagerImplTest, RequestTimeoutCallbackDisarmsAndReturns408) { request_timeout_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); std::string response_body; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { @@ -3653,7 +3656,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutCallbackDisarmsAndReturns408 TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsNotDisarmedOnIncompleteRequestWithHeader) { request_timeout_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { Event::MockTimer* request_timer = setUpTimer(); @@ -3680,7 +3683,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsNotDisarmedOnIncompleteReq TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnCompleteRequestWithHeader) { request_timeout_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { Event::MockTimer* request_timer = setUpTimer(); @@ -3706,7 +3709,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnCompleteRequestW TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnCompleteRequestWithData) { request_timeout_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { Event::MockTimer* request_timer = setUpTimer(); @@ -3733,7 +3736,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnCompleteRequestW TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnCompleteRequestWithTrailers) { request_timeout_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { Event::MockTimer* request_timer = setUpTimer(); @@ -3762,7 +3765,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnCompleteRequestW TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnEncodeHeaders) { request_timeout_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); std::shared_ptr filter(new NiceMock()); EXPECT_CALL(filter_factory_, createFilterChain(_)) .WillRepeatedly(Invoke([&](FilterChainManager& manager) -> bool { @@ -3799,7 +3802,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnEncodeHeaders) { TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnConnectionTermination) { request_timeout_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); Event::MockTimer* request_timer = setUpTimer(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { @@ -3825,7 +3828,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnConnectionTermin TEST_F(HttpConnectionManagerImplTest, RequestHeaderTimeoutDisarmedAfterHeaders) { request_headers_timeout_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); Event::MockTimer* request_header_timer; EXPECT_CALL(*codec_, dispatch(_)) @@ -3863,7 +3866,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestHeaderTimeoutDisarmedAfterHeaders) TEST_F(HttpConnectionManagerImplTest, RequestHeaderTimeoutCallbackDisarmsAndReturns408) { request_headers_timeout_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); Event::MockTimer* request_header_timer; EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { @@ -3888,7 +3891,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestHeaderTimeoutCallbackDisarmsAndRetu TEST_F(HttpConnectionManagerImplTest, MaxStreamDurationDisabledIfSetToZero) { max_stream_duration_ = std::chrono::milliseconds(0); - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, createTimer_).Times(0); @@ -3903,7 +3906,7 @@ TEST_F(HttpConnectionManagerImplTest, MaxStreamDurationDisabledIfSetToZero) { TEST_F(HttpConnectionManagerImplTest, MaxStreamDurationValidlyConfigured) { max_stream_duration_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { Event::MockTimer* duration_timer = setUpTimer(); @@ -3921,7 +3924,7 @@ TEST_F(HttpConnectionManagerImplTest, MaxStreamDurationValidlyConfigured) { TEST_F(HttpConnectionManagerImplTest, MaxStreamDurationCallbackResetStream) { max_stream_duration_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); Event::MockTimer* duration_timer = setUpTimer(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { @@ -3944,7 +3947,7 @@ TEST_F(HttpConnectionManagerImplTest, MaxStreamDurationCallbackResetStream) { TEST_F(HttpConnectionManagerImplTest, MaxStreamDurationFiredReturn408IfRequestWasNotComplete) { max_stream_duration_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); Event::MockTimer* duration_timer = setUpTimer(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { @@ -3974,7 +3977,7 @@ TEST_F(HttpConnectionManagerImplTest, MaxStreamDurationFiredReturn408IfRequestWa TEST_F(HttpConnectionManagerImplTest, MaxStreamDurationFiredReturn504IfRequestWasFullyRead) { max_stream_duration_ = std::chrono::milliseconds(10); - setup(false, ""); + setup(); Event::MockTimer* duration_timer = setUpTimer(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { @@ -4003,7 +4006,7 @@ TEST_F(HttpConnectionManagerImplTest, MaxStreamDurationFiredReturn504IfRequestWa TEST_F(HttpConnectionManagerImplTest, MaxStreamDurationCallbackNotCalledIfResetStreamValidly) { max_stream_duration_ = std::chrono::milliseconds(5000); - setup(false, ""); + setup(); Event::MockTimer* duration_timer = setUpTimer(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { @@ -4023,7 +4026,7 @@ TEST_F(HttpConnectionManagerImplTest, MaxStreamDurationCallbackNotCalledIfResetS } TEST_F(HttpConnectionManagerImplTest, RejectWebSocketOnNonWebSocketRoute) { - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, @@ -4054,7 +4057,7 @@ TEST_F(HttpConnectionManagerImplTest, RejectWebSocketOnNonWebSocketRoute) { // Make sure for upgrades, we do not append Connection: Close when draining. TEST_F(HttpConnectionManagerImplTest, FooUpgradeDrainClose) { - setup(false, "envoy-custom-server", false); + setup(SetupOpts().setTracing(false)); // Store the basic request encoder during filter chain setup. auto* filter = new MockStreamFilter(); @@ -4120,7 +4123,7 @@ TEST_F(HttpConnectionManagerImplTest, FooUpgradeDrainClose) { // Make sure CONNECT requests hit the upgrade filter path. TEST_F(HttpConnectionManagerImplTest, ConnectAsUpgrade) { - setup(false, "envoy-custom-server", false); + setup(SetupOpts().setTracing(false)); EXPECT_CALL(filter_factory_, createUpgradeFilterChain("CONNECT", _, _, _)) .WillRepeatedly(Return(true)); @@ -4144,7 +4147,7 @@ TEST_F(HttpConnectionManagerImplTest, ConnectAsUpgrade) { } TEST_F(HttpConnectionManagerImplTest, ConnectWithEmptyPath) { - setup(false, "envoy-custom-server", false); + setup(SetupOpts().setTracing(false)); EXPECT_CALL(filter_factory_, createUpgradeFilterChain("CONNECT", _, _, _)) .WillRepeatedly(Return(true)); @@ -4170,7 +4173,7 @@ TEST_F(HttpConnectionManagerImplTest, ConnectWithEmptyPath) { // Regression test for https://github.com/envoyproxy/envoy/issues/10138 TEST_F(HttpConnectionManagerImplTest, DrainCloseRaceWithClose) { InSequence s; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); @@ -4215,7 +4218,7 @@ TEST_F(HttpConnectionManagerImplTest, DrainCloseRaceWithClose) { TEST_F(HttpConnectionManagerImplTest, FilterThatWaitsForBodyCanBeCalledAfterFilterThatAddsBodyEvenIfItIsNotLast) { InSequence s; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); @@ -4263,7 +4266,7 @@ TEST_F(HttpConnectionManagerImplTest, } TEST_F(HttpConnectionManagerImplTest, DrainClose) { - setup(true, ""); + setup(SetupOpts().setSsl(true)); MockStreamDecoderFilter* filter = new NiceMock(); EXPECT_CALL(filter_factory_, createFilterChain(_)) @@ -4319,7 +4322,7 @@ TEST_F(HttpConnectionManagerImplTest, DrainClose) { class ProxyStatusTest : public HttpConnectionManagerImplTest { public: void initialize() { - setup(/*ssl=*/false, servername_, /*tracing=*/false); + setup(SetupOpts().setServerName(servername_).setTracing(false)); setUpEncoderAndDecoder(/*request_with_data_and_trailers=*/false, /*decode_headers_stop_all=*/false); sendRequestHeadersAndData(); diff --git a/test/common/http/conn_manager_impl_test_2.cc b/test/common/http/conn_manager_impl_test_2.cc index e829a42e42ab..fead362c3328 100644 --- a/test/common/http/conn_manager_impl_test_2.cc +++ b/test/common/http/conn_manager_impl_test_2.cc @@ -20,7 +20,7 @@ namespace Envoy { namespace Http { TEST_F(HttpConnectionManagerImplTest, ResponseBeforeRequestComplete) { - setup(false, "envoy-server-test"); + setup(); setupFilterChain(1, 0); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) @@ -44,7 +44,7 @@ TEST_F(HttpConnectionManagerImplTest, ResponseBeforeRequestComplete) { TEST_F(HttpConnectionManagerImplTest, ResponseBeforeRequestComplete10) { EXPECT_CALL(*codec_, protocol()).WillRepeatedly(Return(Protocol::Http10)); - setup(false, "envoy-server-test"); + setup(); setupFilterChain(1, 0); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) @@ -69,7 +69,7 @@ TEST_F(HttpConnectionManagerImplTest, ResponseBeforeRequestComplete10) { TEST_F(HttpConnectionManagerImplTest, ResponseBeforeRequestComplete10NoOptimize) { EXPECT_CALL(runtime_.snapshot_, getBoolean(_, _)).WillRepeatedly(Return(false)); EXPECT_CALL(*codec_, protocol()).WillRepeatedly(Return(Protocol::Http10)); - setup(false, "envoy-server-test"); + setup(); setupFilterChain(1, 0); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) @@ -94,7 +94,7 @@ TEST_F(HttpConnectionManagerImplTest, ResponseBeforeRequestComplete10NoOptimize) } TEST_F(HttpConnectionManagerImplTest, DisconnectOnProxyConnectionDisconnect) { - setup(false, "envoy-server-test"); + setup(); setupFilterChain(1, 0); @@ -120,7 +120,7 @@ TEST_F(HttpConnectionManagerImplTest, DisconnectOnProxyConnectionDisconnect) { } TEST_F(HttpConnectionManagerImplTest, ResponseStartBeforeRequestComplete) { - setup(false, ""); + setup(SetupOpts().setServerName("")); // This is like ResponseBeforeRequestComplete, but it tests the case where we start the reply // before the request completes, but don't finish the reply until after the request completes. @@ -181,7 +181,7 @@ TEST_F(HttpConnectionManagerImplTest, ResponseStartBeforeRequestComplete) { TEST_F(HttpConnectionManagerImplTest, DownstreamDisconnect) { InSequence s; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { conn_manager_->newStream(response_encoder_); @@ -201,7 +201,7 @@ TEST_F(HttpConnectionManagerImplTest, DownstreamDisconnect) { TEST_F(HttpConnectionManagerImplTest, DownstreamProtocolError) { InSequence s; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { conn_manager_->newStream(response_encoder_); @@ -225,7 +225,7 @@ TEST_F(HttpConnectionManagerImplTest, DownstreamProtocolError) { TEST_F(HttpConnectionManagerImplTest, TestDownstreamProtocolErrorAccessLog) { std::shared_ptr handler(new NiceMock()); access_logs_ = {handler}; - setup(false, ""); + setup(); EXPECT_CALL(*handler, log(_, _)) .WillOnce(Invoke( @@ -246,7 +246,7 @@ TEST_F(HttpConnectionManagerImplTest, TestDownstreamProtocolErrorAccessLog) { } TEST_F(HttpConnectionManagerImplTest, TestDownstreamProtocolErrorAfterHeadersAccessLog) { - setup(false, ""); + setup(); std::shared_ptr filter(new NiceMock()); std::shared_ptr handler(new NiceMock()); @@ -289,7 +289,7 @@ TEST_F(HttpConnectionManagerImplTest, FrameFloodError) { std::shared_ptr log_handler = std::make_shared>(); access_logs_ = {log_handler}; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { conn_manager_->newStream(response_encoder_); @@ -323,7 +323,7 @@ TEST_F(HttpConnectionManagerImplTest, EnvoyOverloadError) { std::shared_ptr log_handler = std::make_shared>(); access_logs_ = {log_handler}; - setup(false, ""); + setup(); ASSERT_EQ(0U, stats_.named_.downstream_rq_overload_close_.value()); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { @@ -360,7 +360,7 @@ TEST_F(HttpConnectionManagerImplTest, IdleTimeoutNoCodec) { idle_timeout_ = (std::chrono::milliseconds(10)); Event::MockTimer* idle_timer = setUpTimer(); EXPECT_CALL(*idle_timer, enableTimer(_, _)); - setup(false, ""); + setup(); EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite, _)); EXPECT_CALL(*idle_timer, disableTimer()); @@ -373,7 +373,7 @@ TEST_F(HttpConnectionManagerImplTest, IdleTimeout) { idle_timeout_ = (std::chrono::milliseconds(10)); Event::MockTimer* idle_timer = setUpTimer(); EXPECT_CALL(*idle_timer, enableTimer(_, _)); - setup(false, ""); + setup(); MockStreamDecoderFilter* filter = new NiceMock(); EXPECT_CALL(filter_factory_, createFilterChain(_)) @@ -418,7 +418,7 @@ TEST_F(HttpConnectionManagerImplTest, ConnectionDurationResponseFlag) { max_connection_duration_ = (std::chrono::milliseconds(10)); Event::MockTimer* connection_duration_timer = setUpTimer(); EXPECT_CALL(*connection_duration_timer, enableTimer(_, _)); - setup(false, ""); + setup(); EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite, _)); filter_callbacks_.connection_.streamInfo().setResponseFlag( @@ -440,7 +440,7 @@ TEST_F(HttpConnectionManagerImplTest, ConnectionDurationNoCodec) { max_connection_duration_ = (std::chrono::milliseconds(10)); Event::MockTimer* connection_duration_timer = setUpTimer(); EXPECT_CALL(*connection_duration_timer, enableTimer(_, _)); - setup(false, ""); + setup(); EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite, _)); EXPECT_CALL(*connection_duration_timer, disableTimer()); @@ -454,7 +454,7 @@ TEST_F(HttpConnectionManagerImplTest, ConnectionDurationNoCodec) { TEST_F(HttpConnectionManagerImplTest, MaxRequests) { max_requests_per_connection_ = 1; codec_->protocol_ = Protocol::Http2; - setup(false, ""); + setup(); Event::MockTimer* drain_timer = setUpTimer(); EXPECT_CALL(*drain_timer, enableTimer(_, _)); @@ -483,7 +483,7 @@ TEST_F(HttpConnectionManagerImplTest, ConnectionDuration) { max_connection_duration_ = (std::chrono::milliseconds(10)); Event::MockTimer* connection_duration_timer = setUpTimer(); EXPECT_CALL(*connection_duration_timer, enableTimer(_, _)); - setup(false, ""); + setup(); MockStreamDecoderFilter* filter = new NiceMock(); EXPECT_CALL(filter_factory_, createFilterChain(_)) @@ -520,8 +520,51 @@ TEST_F(HttpConnectionManagerImplTest, ConnectionDuration) { EXPECT_EQ(1U, stats_.named_.downstream_cx_max_duration_reached_.value()); } +TEST_F(HttpConnectionManagerImplTest, ConnectionDurationSafeHttp1) { + EXPECT_CALL(*codec_, protocol()).WillRepeatedly(Return(Protocol::Http10)); + max_connection_duration_ = std::chrono::milliseconds(10); + Event::MockTimer* connection_duration_timer = setUpTimer(); + EXPECT_CALL(*connection_duration_timer, enableTimer(_, _)); + setup(SetupOpts().setHttp1SafeMaxConnectionDuration(true)); + + MockStreamDecoderFilter* filter = new NiceMock(); + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { + auto factory = createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{filter}); + manager.applyFilterFactoryCb({}, factory); + return true; + })); + + EXPECT_CALL(*filter, decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + EXPECT_CALL(*filter, decodeData(_, true)) + .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); + startRequest(true, "hello"); + + EXPECT_CALL(*connection_duration_timer, disableTimer()); + connection_duration_timer->invokeCallback(); + EXPECT_EQ(1U, stats_.named_.downstream_cx_http1_soft_drain_.value()); + EXPECT_EQ(1U, stats_.named_.downstream_cx_max_duration_reached_.value()); + + // Connection manager now waits to send another response, adds the Connection:close header to it, + // then closes the connection. + EXPECT_CALL(response_encoder_, encodeHeaders(_, _)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) { + // Check that the connection:close header is present. + ASSERT_NE(headers.Connection(), nullptr); + EXPECT_EQ(headers.getConnectionValue(), Headers::get().ConnectionValues.Close); + response_encoder_.stream_.codec_callbacks_->onCodecEncodeComplete(); + })); + // Expect stream & connection to close after response is sent. + expectOnDestroy(); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + filter->callbacks_->streamInfo().setResponseCodeDetails(""); + filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); +} + TEST_F(HttpConnectionManagerImplTest, IntermediateBufferingEarlyResponse) { - setup(false, ""); + setup(); setupFilterChain(2, 0); @@ -554,7 +597,7 @@ TEST_F(HttpConnectionManagerImplTest, IntermediateBufferingEarlyResponse) { } TEST_F(HttpConnectionManagerImplTest, DoubleBuffering) { - setup(false, ""); + setup(); setupFilterChain(3, 0); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) @@ -589,7 +632,7 @@ TEST_F(HttpConnectionManagerImplTest, DoubleBuffering) { } TEST_F(HttpConnectionManagerImplTest, ZeroByteDataFiltering) { - setup(false, ""); + setup(); setupFilterChain(2, 0); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) @@ -621,7 +664,7 @@ TEST_F(HttpConnectionManagerImplTest, ZeroByteDataFiltering) { TEST_F(HttpConnectionManagerImplTest, FilterAddTrailersInTrailersCallback) { InSequence s; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); @@ -708,7 +751,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddTrailersInTrailersCallback) { } TEST_F(HttpConnectionManagerImplTest, FilterAddTrailersInDataCallbackNoTrailers) { - setup(false, ""); + setup(); setupFilterChain(2, 2); std::string trailers_data("trailers"); @@ -788,7 +831,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddTrailersInDataCallbackNoTrailers) TEST_F(HttpConnectionManagerImplTest, FilterAddBodyInTrailersCallback) { InSequence s; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); @@ -871,7 +914,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddBodyInTrailersCallback) { // Don't send data frames, only headers and trailers. TEST_F(HttpConnectionManagerImplTest, FilterAddBodyInTrailersCallback_NoDataFrames) { InSequence s; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); @@ -932,7 +975,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddBodyInTrailersCallback_NoDataFram // Don't send data frames, only headers and trailers. TEST_F(HttpConnectionManagerImplTest, FilterAddBodyInTrailersCallback_ContinueAfterCallback) { InSequence s; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); @@ -999,7 +1042,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddBodyInTrailersCallback_ContinueAf // Add*Data during the *Data callbacks. TEST_F(HttpConnectionManagerImplTest, FilterAddBodyDuringDecodeData) { InSequence s; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); @@ -1068,7 +1111,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddBodyDuringDecodeData) { } TEST_F(HttpConnectionManagerImplTest, FilterAddBodyInline) { - setup(false, ""); + setup(); setupFilterChain(2, 2); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) @@ -1110,7 +1153,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddBodyInline) { } TEST_F(HttpConnectionManagerImplTest, BlockRouteCacheTest) { - setup(false, ""); + setup(); MockStreamDecoderFilter* filter = new NiceMock(); EXPECT_CALL(filter_factory_, createFilterChain(_)) @@ -1183,7 +1226,7 @@ TEST_F(HttpConnectionManagerImplTest, BlockRouteCacheTest) { } TEST_F(HttpConnectionManagerImplTest, Filter) { - setup(false, ""); + setup(); setupFilterChain(3, 2); const std::string fake_cluster1_name = "fake_cluster1"; @@ -1244,7 +1287,7 @@ TEST_F(HttpConnectionManagerImplTest, Filter) { // the line. Also tests that setRoute(nullptr) is equivalent to attempting route resolution and // failing to find a route. TEST_F(HttpConnectionManagerImplTest, FilterSetRouteToNullPtr) { - setup(false, ""); + setup(); setupFilterChain(2, 1); const std::string fake_cluster1_name = "fake_cluster1"; @@ -1288,7 +1331,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterSetRouteToNullPtr) { } TEST_F(HttpConnectionManagerImplTest, UpstreamWatermarkCallbacks) { - setup(false, ""); + setup(); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); @@ -1327,7 +1370,7 @@ TEST_F(HttpConnectionManagerImplTest, UpstreamWatermarkCallbacks) { } TEST_F(HttpConnectionManagerImplTest, UnderlyingConnectionWatermarksPassedOnWithLazyCreation) { - setup(false, ""); + setup(); // Make sure codec_ is created. EXPECT_CALL(*codec_, dispatch(_)); @@ -1389,7 +1432,7 @@ TEST_F(HttpConnectionManagerImplTest, UnderlyingConnectionWatermarksPassedOnWith } TEST_F(HttpConnectionManagerImplTest, UnderlyingConnectionWatermarksUnwoundWithLazyCreation) { - setup(false, ""); + setup(); // Make sure codec_ is created. EXPECT_CALL(*codec_, dispatch(_)); @@ -1454,7 +1497,7 @@ TEST_F(HttpConnectionManagerImplTest, UnderlyingConnectionWatermarksUnwoundWithL TEST_F(HttpConnectionManagerImplTest, AlterFilterWatermarkLimits) { initial_buffer_limit_ = 100; - setup(false, ""); + setup(); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); @@ -1487,7 +1530,7 @@ TEST_F(HttpConnectionManagerImplTest, HitFilterWatermarkLimits) { initial_buffer_limit_ = 1; streaming_filter_ = true; - setup(false, ""); + setup(); setUpEncoderAndDecoder(false, false); // The filter is a streaming filter. Sending 4 bytes should hit the @@ -1547,7 +1590,7 @@ TEST_F(HttpConnectionManagerImplTest, HitFilterWatermarkLimits) { TEST_F(HttpConnectionManagerImplTest, HitRequestBufferLimits) { initial_buffer_limit_ = 10; streaming_filter_ = false; - setup(false, ""); + setup(); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); @@ -1572,7 +1615,7 @@ TEST_F(HttpConnectionManagerImplTest, DownstreamConnectionTermination) { std::shared_ptr handler(new NiceMock()); access_logs_ = {handler}; - setup(false, ""); + setup(); EXPECT_CALL(*handler, log(_, _)) .WillOnce(Invoke( [](const Formatter::HttpFormatterContext&, const StreamInfo::StreamInfo& stream_info) { @@ -1598,7 +1641,7 @@ TEST_F(HttpConnectionManagerImplTest, HitRequestBufferLimitsIntermediateFilter) { InSequence s; initial_buffer_limit_ = 10; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); @@ -1642,7 +1685,7 @@ TEST_F(HttpConnectionManagerImplTest, HitRequestBufferLimitsIntermediateFilter) TEST_F(HttpConnectionManagerImplTest, HitResponseBufferLimitsBeforeHeaders) { initial_buffer_limit_ = 10; - setup(false, ""); + setup(); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); @@ -1683,7 +1726,7 @@ TEST_F(HttpConnectionManagerImplTest, HitResponseBufferLimitsBeforeHeaders) { TEST_F(HttpConnectionManagerImplTest, HitResponseBufferLimitsAfterHeaders) { initial_buffer_limit_ = 10; - setup(false, ""); + setup(); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); @@ -1720,7 +1763,7 @@ TEST_F(HttpConnectionManagerImplTest, HitResponseBufferLimitsAfterHeaders) { TEST_F(HttpConnectionManagerImplTest, FilterHeadReply) { InSequence s; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); @@ -1763,7 +1806,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterHeadReply) { // up resetting the stream in the doEndStream() path (e.g., via filter reset due to timeout, etc.), // we emit a reset to the codec. TEST_F(HttpConnectionManagerImplTest, ResetWithStoppedFilter) { - setup(false, ""); + setup(); setupFilterChain(1, 1); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) @@ -1802,7 +1845,7 @@ TEST_F(HttpConnectionManagerImplTest, DownstreamRemoteResetConnectError) { std::shared_ptr handler(new NiceMock()); access_logs_ = {handler}; - setup(false, ""); + setup(); codec_->protocol_ = Protocol::Http2; EXPECT_CALL(*handler, log(_, _)) .WillOnce(Invoke( @@ -1831,7 +1874,7 @@ TEST_F(HttpConnectionManagerImplTest, DownstreamRemoteReset) { std::shared_ptr handler(new NiceMock()); access_logs_ = {handler}; - setup(false, ""); + setup(); codec_->protocol_ = Protocol::Http2; EXPECT_CALL(*handler, log(_, _)) .WillOnce(Invoke( @@ -1860,7 +1903,7 @@ TEST_F(HttpConnectionManagerImplTest, DownstreamRemoteResetRefused) { std::shared_ptr handler(new NiceMock()); access_logs_ = {handler}; - setup(false, ""); + setup(); codec_->protocol_ = Protocol::Http2; EXPECT_CALL(*handler, log(_, _)) .WillOnce(Invoke( @@ -1885,7 +1928,7 @@ TEST_F(HttpConnectionManagerImplTest, DownstreamRemoteResetRefused) { // Filter stops headers iteration without ending the stream, then injects a body later. TEST_F(HttpConnectionManagerImplTest, FilterStopIterationInjectBody) { - setup(false, ""); + setup(); setupFilterChain(2, 2); // Decode filter 0 changes end_stream to false. @@ -1931,7 +1974,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterStopIterationInjectBody) { // Filter continues headers iteration without ending the stream, then injects a body later. TEST_F(HttpConnectionManagerImplTest, FilterContinueDontEndStreamInjectBody) { - setup(false, ""); + setup(); setupFilterChain(2, 2); // Decode filter 0 changes end_stream to false. @@ -1976,7 +2019,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterContinueDontEndStreamInjectBody) { } TEST_F(HttpConnectionManagerImplTest, FilterAddBodyContinuation) { - setup(false, ""); + setup(); setupFilterChain(2, 2); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) @@ -2052,7 +2095,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterAddBodyContinuation) { // filter1->encodeData(, true) is NOT called. // TEST_F(HttpConnectionManagerImplTest, AddDataWithAllContinue) { - setup(false, ""); + setup(); setupFilterChain(3, 3); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) @@ -2145,7 +2188,7 @@ TEST_F(HttpConnectionManagerImplTest, AddDataWithAllContinue) { // filter1->encodeData(, true) is NOT called. // TEST_F(HttpConnectionManagerImplTest, AddDataWithStopAndContinue) { - setup(false, ""); + setup(); setupFilterChain(3, 3); @@ -2212,7 +2255,7 @@ TEST_F(HttpConnectionManagerImplTest, AddDataWithStopAndContinue) { // Use filter direct decode/encodeData() calls without trailers. TEST_F(HttpConnectionManagerImplTest, FilterDirectDecodeEncodeDataNoTrailers) { - setup(false, ""); + setup(); EXPECT_CALL(*route_config_provider_.route_config_, route(_, _, _, _)); setupFilterChain(2, 2); @@ -2281,7 +2324,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterDirectDecodeEncodeDataNoTrailers) { // Use filter direct decode/encodeData() calls with trailers. TEST_F(HttpConnectionManagerImplTest, FilterDirectDecodeEncodeDataTrailers) { InSequence s; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); @@ -2377,7 +2420,7 @@ TEST_F(HttpConnectionManagerImplTest, FilterDirectDecodeEncodeDataTrailers) { TEST_F(HttpConnectionManagerImplTest, MultipleFilters) { InSequence s; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); @@ -2492,7 +2535,7 @@ TEST_F(HttpConnectionManagerImplTest, NoNewStreamWhenOverloaded) { getState(Server::OverloadActionNames::get().StopAcceptingRequests)) .WillByDefault(ReturnRef(stop_accepting_requests)); - setup(false, ""); + setup(); EXPECT_CALL(random_, random()) .WillRepeatedly(Return(static_cast(Random::RandomGenerator::max()) * 0.5)); @@ -2518,7 +2561,7 @@ TEST_F(HttpConnectionManagerImplTest, DisableHttp1KeepAliveWhenOverloaded) { .WillByDefault(ReturnRef(disable_http_keep_alive)); codec_->protocol_ = Protocol::Http11; - setup(false, ""); + setup(); EXPECT_CALL(random_, random()) .WillRepeatedly(Return(static_cast(Random::RandomGenerator::max()) * 0.5)); @@ -2567,7 +2610,7 @@ TEST_F(HttpConnectionManagerImplTest, DisableHttp2KeepAliveWhenOverloaded) { .WillByDefault(ReturnRef(disable_http_keep_alive)); codec_->protocol_ = Protocol::Http2; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, shutdownNotice); std::shared_ptr filter(new NiceMock()); @@ -2612,7 +2655,7 @@ TEST_F(HttpConnectionManagerImplTest, CodecCreationLoadShedPointCanCloseConnecti getLoadShedPoint(Server::LoadShedPointName::get().HcmDecodeHeaders)) .WillOnce(Return(nullptr)); - setup(false, ""); + setup(); EXPECT_CALL(close_connection_creating_codec_point, shouldShedLoad()).WillOnce(Return(true)); EXPECT_CALL(filter_callbacks_.connection_, close(_, _)); @@ -2638,7 +2681,7 @@ TEST_F(HttpConnectionManagerImplTest, CodecCreationLoadShedPointBypasscheck) { getLoadShedPoint(Server::LoadShedPointName::get().HttpDownstreamFilterCheck)) .WillOnce(Return(nullptr)); - setup(false, ""); + setup(); EXPECT_CALL(close_connection_creating_codec_point, shouldShedLoad()).WillOnce(Return(false)); @@ -2668,7 +2711,7 @@ TEST_F(HttpConnectionManagerImplTest, DecodeHeaderLoadShedPointCanRejectNewStrea getLoadShedPoint(Server::LoadShedPointName::get().HttpDownstreamFilterCheck)) .WillRepeatedly(Return(nullptr)); - setup(false, ""); + setup(); setupFilterChain(1, 0); EXPECT_CALL(accept_new_stream_point, shouldShedLoad()).WillOnce(Return(true)); @@ -2702,7 +2745,7 @@ TEST_F(HttpConnectionManagerImplTest, DecodeHeaderLoadShedPointCanRejectNewStrea } TEST_F(HttpConnectionManagerImplTest, TestStopAllIterationAndBufferOnDecodingPathFirstFilter) { - setup(false, "envoy-custom-server", false); + setup(SetupOpts().setTracing(false)); setUpEncoderAndDecoder(true, true); // Kick off the incoming data. @@ -2726,7 +2769,7 @@ TEST_F(HttpConnectionManagerImplTest, TestStopAllIterationAndBufferOnDecodingPat } TEST_F(HttpConnectionManagerImplTest, TestStopAllIterationAndBufferOnDecodingPathSecondFilter) { - setup(false, "envoy-custom-server", false); + setup(SetupOpts().setTracing(false)); setUpEncoderAndDecoder(true, false); // Verify headers go through both filters, and data and trailers go through the first filter only. @@ -2751,7 +2794,7 @@ TEST_F(HttpConnectionManagerImplTest, TestStopAllIterationAndBufferOnDecodingPat } TEST_F(HttpConnectionManagerImplTest, TestStopAllIterationAndBufferOnEncodingPath) { - setup(false, "envoy-custom-server", false); + setup(SetupOpts().setTracing(false)); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); @@ -2793,7 +2836,7 @@ TEST_F(HttpConnectionManagerImplTest, TestStopAllIterationAndBufferOnEncodingPat } TEST_F(HttpConnectionManagerImplTest, DisableKeepAliveWhenDraining) { - setup(false, ""); + setup(); EXPECT_CALL(drain_close_, drainClose()).WillOnce(Return(true)); @@ -2832,7 +2875,7 @@ TEST_F(HttpConnectionManagerImplTest, DisableKeepAliveWhenDraining) { } TEST_F(HttpConnectionManagerImplTest, TestSessionTrace) { - setup(false, ""); + setup(); // Set up the codec. EXPECT_CALL(*codec_, dispatch(_)) @@ -2899,7 +2942,7 @@ TEST_F(HttpConnectionManagerImplTest, TestSessionTrace) { // SRDS no scope found. TEST_F(HttpConnectionManagerImplTest, TestSrdsRouteNotFound) { - setup(false, "", true, true); + setup(SetupOpts().setUseSrds(true)); setupFilterChain(1, 0); // Recreate the chain for second stream. EXPECT_CALL(*static_cast(scopeKeyBuilder().ptr()), @@ -2935,7 +2978,7 @@ TEST_F(HttpConnectionManagerImplTest, TestSrdsRouteNotFound) { // SRDS updating scopes affects routing. TEST_F(HttpConnectionManagerImplTest, TestSrdsUpdate) { - setup(false, "", true, true); + setup(SetupOpts().setUseSrds(true)); EXPECT_CALL(*static_cast(scopeKeyBuilder().ptr()), computeScopeKey(_)) @@ -2990,7 +3033,7 @@ TEST_F(HttpConnectionManagerImplTest, TestSrdsUpdate) { // SRDS Scope header update cause cross-scope reroute. TEST_F(HttpConnectionManagerImplTest, TestSrdsCrossScopeReroute) { - setup(false, "", true, true); + setup(SetupOpts().setUseSrds(true)); std::shared_ptr route_config1 = std::make_shared>(); @@ -3063,7 +3106,7 @@ TEST_F(HttpConnectionManagerImplTest, TestSrdsCrossScopeReroute) { // SRDS scoped RouteConfiguration found and route found. TEST_F(HttpConnectionManagerImplTest, TestSrdsRouteFound) { - setup(false, "", true, true); + setup(SetupOpts().setUseSrds(true)); setupFilterChain(1, 0); const std::string fake_cluster1_name = "fake_cluster1"; @@ -3109,7 +3152,7 @@ TEST_F(HttpConnectionManagerImplTest, TestSrdsRouteFound) { } TEST_F(HttpConnectionManagerImplTest, NewConnection) { - setup(false, "", true, true); + setup(SetupOpts().setUseSrds(true)); filter_callbacks_.connection_.stream_info_.protocol_ = absl::nullopt; EXPECT_CALL(filter_callbacks_.connection_.stream_info_, protocol()); @@ -3127,7 +3170,7 @@ TEST_F(HttpConnectionManagerImplTest, NewConnection) { } TEST_F(HttpConnectionManagerImplTest, HeaderOnlyRequestAndResponseUsingHttp3) { - setup(false, "envoy-custom-server", false); + setup(SetupOpts().setTracing(false)); filter_callbacks_.connection_.stream_info_.protocol_ = Envoy::Http::Protocol::Http3; codec_->protocol_ = Http::Protocol::Http3; @@ -3195,7 +3238,7 @@ TEST_F(HttpConnectionManagerImplTest, ConnectionFilterState) { "connection_provided_data", std::make_shared(555), StreamInfo::FilterState::StateType::ReadOnly); - setup(false, "envoy-custom-server", false); + setup(SetupOpts().setTracing(false)); setupFilterChain(1, 0, /* num_requests = */ 3); EXPECT_CALL(*codec_, dispatch(_)) @@ -3299,7 +3342,7 @@ class HttpConnectionManagerImplDeathTest : public HttpConnectionManagerImplTest // HCM config can only have either RouteConfigProvider or ScopedRoutesConfigProvider. TEST_F(HttpConnectionManagerImplDeathTest, InvalidConnectionManagerConfig) { - setup(false, ""); + setup(); Buffer::OwnedImpl fake_input("1234"); EXPECT_CALL(*codec_, dispatch(_)).WillRepeatedly(Invoke([&](Buffer::Instance&) -> Http::Status { @@ -3337,7 +3380,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestRejectedViaIPDetection) { use_remote_address_ = false; - setup(false, ""); + setup(); // 403 direct response when IP detection fails. EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) @@ -3354,7 +3397,7 @@ TEST_F(HttpConnectionManagerImplTest, RequestRejectedViaIPDetection) { } TEST_F(HttpConnectionManagerImplTest, DisconnectDuringEncodeHeader) { - setup(false, "envoy-server-test"); + setup(); setupFilterChain(1, 0); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) @@ -3376,7 +3419,7 @@ TEST_F(HttpConnectionManagerImplTest, DisconnectDuringEncodeHeader) { } TEST_F(HttpConnectionManagerImplTest, DisconnectDuringEncodeBody) { - setup(false, "envoy-server-test"); + setup(); setupFilterChain(1, 0); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) @@ -3405,7 +3448,7 @@ TEST_F(HttpConnectionManagerImplTest, DisconnectDuringEncodeBody) { // Verify that trailers added during a data encoding continuation are not double continued. TEST_F(HttpConnectionManagerImplTest, AddTrailersDuringdDecodingContinue) { InSequence s; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); @@ -3449,7 +3492,7 @@ TEST_F(HttpConnectionManagerImplTest, AddTrailersDuringdDecodingContinue) { // Verify that trailers added during a data decoding continuation are not double continued. TEST_F(HttpConnectionManagerImplTest, AddTrailersDuringEncodingContinue) { InSequence s; - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); @@ -3489,7 +3532,7 @@ TEST_F(HttpConnectionManagerImplTest, AddTrailersDuringEncodingContinue) { } TEST_F(HttpConnectionManagerImplTest, DisconnectDuringEncodeTrailer) { - setup(false, "envoy-server-test"); + setup(); setupFilterChain(1, 0); EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) @@ -3520,7 +3563,7 @@ TEST_F(HttpConnectionManagerImplTest, DisconnectDuringEncodeTrailer) { TEST_F(HttpConnectionManagerImplTest, DirectLocalReplyCausesDisconnect) { initial_buffer_limit_ = 10; - setup(false, ""); + setup(); setUpEncoderAndDecoder(false, false); sendRequestHeadersAndData(); @@ -3563,7 +3606,7 @@ TEST_F(HttpConnectionManagerImplTest, DirectLocalReplyCausesDisconnect) { // Header validator rejects header map for HTTP/1.x protocols TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectHttp1) { - setup(false, ""); + setup(); expectUhvHeaderCheck(HeaderValidator::ValidationResult( HeaderValidator::ValidationResult::Action::Reject, "bad_header_map"), ServerHeaderValidator::RequestHeadersTransformationResult::success()); @@ -3609,7 +3652,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectHttp1) { // Header validator rejects header map for HTTP/2 protocols TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectHttp2) { codec_->protocol_ = Protocol::Http2; - setup(false, ""); + setup(); expectUhvHeaderCheck(HeaderValidator::ValidationResult( HeaderValidator::ValidationResult::Action::Reject, "bad_header_map"), ServerHeaderValidator::RequestHeadersTransformationResult::success()); @@ -3638,7 +3681,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectHttp2) { // Header validator rejects gRPC request TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectGrpcRequest) { codec_->protocol_ = Protocol::Http2; - setup(false, ""); + setup(); expectUhvHeaderCheck(HeaderValidator::ValidationResult( HeaderValidator::ValidationResult::Action::Reject, "bad_header_map"), ServerHeaderValidator::RequestHeadersTransformationResult::success()); @@ -3668,7 +3711,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectGrpcRequest) { // Header validator redirects TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRedirect) { - setup(false, ""); + setup(); expectUhvHeaderCheck( HeaderValidator::ValidationResult::success(), ServerHeaderValidator::RequestHeadersTransformationResult( @@ -3698,7 +3741,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRedirect) { // Header validator redirects gRPC request TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRedirectGrpcRequest) { codec_->protocol_ = Protocol::Http2; - setup(false, ""); + setup(); expectUhvHeaderCheck( HeaderValidator::ValidationResult::success(), ServerHeaderValidator::RequestHeadersTransformationResult( @@ -3731,7 +3774,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRedirectGrpcRequest) { // Header validator rejects trailer map before response has started TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectTrailersBeforeResponseHttp1) { codec_->protocol_ = Protocol::Http11; - setup(false, ""); + setup(); expectUhvTrailerCheck(HeaderValidator::ValidationResult( HeaderValidator::ValidationResult::Action::Reject, "bad_trailer_map"), HeaderValidator::TransformationResult::success()); @@ -3760,7 +3803,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectTrailersBeforeRespons TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectTrailersBeforeResponseHttp2) { codec_->protocol_ = Protocol::Http2; - setup(false, ""); + setup(); expectUhvTrailerCheck(HeaderValidator::ValidationResult( HeaderValidator::ValidationResult::Action::Reject, "bad_trailer_map"), HeaderValidator::TransformationResult::success(), false); @@ -3785,7 +3828,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectTrailersBeforeRespons TEST_F(HttpConnectionManagerImplTest, HeaderValidatorFailTrailersTransformationBeforeResponse) { codec_->protocol_ = Protocol::Http11; - setup(false, ""); + setup(); expectUhvTrailerCheck(HeaderValidator::ValidationResult( HeaderValidator::ValidationResult::Action::Reject, "bad_trailer_map"), HeaderValidator::TransformationResult::success()); @@ -3815,7 +3858,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderValidatorFailTrailersTransformationB // Header validator rejects trailer map after response has started TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectTrailersAfterResponse) { codec_->protocol_ = Protocol::Http2; - setup(false, ""); + setup(); setupFilterChain(1, 0, 1); expectUhvTrailerCheck(HeaderValidator::ValidationResult( HeaderValidator::ValidationResult::Action::Reject, "bad_trailer_map"), @@ -3855,7 +3898,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectTrailersAfterResponse // Request completes normally if header validator accepts it TEST_F(HttpConnectionManagerImplTest, HeaderValidatorAccept) { - setup(false, ""); + setup(); expectUhvHeaderCheck(HeaderValidator::ValidationResult::success(), ServerHeaderValidator::RequestHeadersTransformationResult::success()); @@ -3908,7 +3951,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderValidatorAccept) { TEST_F(HttpConnectionManagerImplTest, NoProxyProtocolAdded) { add_proxy_protocol_connection_state_ = false; - setup(false, "server_name"); + setup(); Buffer::OwnedImpl fake_input("input"); conn_manager_->createCodec(fake_input); @@ -3927,7 +3970,7 @@ TEST_F(HttpConnectionManagerImplTest, LimitWorkPerIOCycle) { EXPECT_CALL(runtime_.snapshot_, getInteger(_, _)).WillRepeatedly(ReturnArg<1>()); // Process 1 request per I/O cycle auto* deferred_request_callback = enableStreamsPerIoLimit(1); - setup(false, ""); + setup(); // Store the basic request encoder during filter chain setup. std::vector> decoder_filters; @@ -4069,7 +4112,7 @@ TEST_F(HttpConnectionManagerImplTest, StreamDeferralPreservesOrder) { EXPECT_CALL(runtime_.snapshot_, getInteger(_, _)).WillRepeatedly(ReturnArg<1>()); // Process 1 request per I/O cycle auto* deferred_request_callback = enableStreamsPerIoLimit(1); - setup(false, ""); + setup(); std::vector> encoder_filters; int expected_request_id = 0; @@ -4179,7 +4222,7 @@ TEST_F(HttpConnectionManagerImplTest, StreamDeferralPreservesOrder) { } TEST_F(HttpConnectionManagerImplTest, DownstreamTimingsRecordWhenRequestHeaderProcessingIsDone) { - setup(/*ssl=*/true, /*server_name=*/"", /*tracing=*/false); + setup(SetupOpts().setSsl(true).setTracing(false)); // Set up the codec. Buffer::OwnedImpl fake_input("input"); @@ -4211,7 +4254,7 @@ TEST_F(HttpConnectionManagerImplTest, DownstreamTimingsRecordWhenRequestHeaderPr } TEST_F(HttpConnectionManagerImplTest, PassMatchUpstreamSchemeHintToStreamInfo) { - setup(/*ssl=*/false, /*server_name=*/"", /*tracing=*/false); + setup(SetupOpts().setTracing(false)); scheme_match_upstream_ = true; // Store the basic request encoder during filter chain setup. diff --git a/test/common/http/conn_manager_impl_test_base.cc b/test/common/http/conn_manager_impl_test_base.cc index 4294d2992955..32cca79b165f 100644 --- a/test/common/http/conn_manager_impl_test_base.cc +++ b/test/common/http/conn_manager_impl_test_base.cc @@ -53,6 +53,9 @@ class ConnectionManagerConfigProxyObject : public ConnectionManagerConfig { absl::optional maxConnectionDuration() const override { return parent_.maxConnectionDuration(); } + bool http1SafeMaxConnectionDuration() const override { + return parent_.http1SafeMaxConnectionDuration(); + } uint32_t maxRequestHeadersKb() const override { return parent_.maxRequestHeadersKb(); } uint32_t maxRequestHeadersCount() const override { return parent_.maxRequestHeadersCount(); } std::chrono::milliseconds streamIdleTimeout() const override { @@ -183,14 +186,14 @@ HttpConnectionManagerImplMixin::requestHeaderCustomTag(const std::string& header return std::make_shared(header, headerTag); } -void HttpConnectionManagerImplMixin::setup(bool ssl, const std::string& server_name, bool tracing, - bool use_srds) { - use_srds_ = use_srds; - if (ssl) { +void HttpConnectionManagerImplMixin::setup(const SetupOpts& opts) { + use_srds_ = opts.use_srds_; + http1_safe_max_connection_duration_ = opts.http1_safe_max_connection_duration_; + if (opts.ssl_) { ssl_connection_ = std::make_shared(); } - server_name_ = server_name; + server_name_ = opts.server_name_; ON_CALL(filter_callbacks_.connection_, ssl()).WillByDefault(Return(ssl_connection_)); ON_CALL(Const(filter_callbacks_.connection_), ssl()).WillByDefault(Return(ssl_connection_)); ON_CALL(filter_callbacks_.connection_.dispatcher_, createScaledTypedTimer_) @@ -214,7 +217,7 @@ void HttpConnectionManagerImplMixin::setup(bool ssl, const std::string& server_n conn_manager_->initializeReadFilterCallbacks(filter_callbacks_); - if (tracing) { + if (opts.tracing_) { envoy::type::v3::FractionalPercent percent1; percent1.set_numerator(100); envoy::type::v3::FractionalPercent percent2; @@ -421,7 +424,7 @@ void HttpConnectionManagerImplMixin::doRemoteClose(bool deferred) { void HttpConnectionManagerImplMixin::testPathNormalization( const RequestHeaderMap& request_headers, const ResponseHeaderMap& expected_response) { - setup(false, ""); + setup(); EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { decoder_ = &conn_manager_->newStream(response_encoder_); diff --git a/test/common/http/conn_manager_impl_test_base.h b/test/common/http/conn_manager_impl_test_base.h index 5e24d36a926f..4be397ffb578 100644 --- a/test/common/http/conn_manager_impl_test_base.h +++ b/test/common/http/conn_manager_impl_test_base.h @@ -29,6 +29,38 @@ using testing::NiceMock; namespace Envoy { namespace Http { +struct SetupOpts { + SetupOpts& setSsl(bool ssl) { + ssl_ = ssl; + return *this; + } + + SetupOpts& setServerName(absl::string_view server_name) { + server_name_ = server_name; + return *this; + } + + SetupOpts& setTracing(bool tracing) { + tracing_ = tracing; + return *this; + } + + SetupOpts& setUseSrds(bool use_srds) { + use_srds_ = use_srds; + return *this; + } + + SetupOpts& setHttp1SafeMaxConnectionDuration(bool http1_safe_max_connection_duration) { + http1_safe_max_connection_duration_ = http1_safe_max_connection_duration; + return *this; + } + + bool ssl_{false}; + std::string server_name_{"envoy-server-test"}; + bool tracing_{true}; + bool use_srds_{false}; + bool http1_safe_max_connection_duration_{false}; +}; // Base class for HttpConnectionManagerImpl related tests. This base class is used by tests under // common/http as well as test/extensions/filters/http/ext_proc/, to reuse the many mocks/default // impls of ConnectionManagerConfig that we need to provide to HttpConnectionManagerImpl. @@ -37,7 +69,7 @@ class HttpConnectionManagerImplMixin : public ConnectionManagerConfig { HttpConnectionManagerImplMixin(); ~HttpConnectionManagerImplMixin() override; Tracing::CustomTagConstSharedPtr requestHeaderCustomTag(const std::string& header); - void setup(bool ssl, const std::string& server_name, bool tracing = true, bool use_srds = false); + void setup(const SetupOpts& opts = {}); void setupFilterChain(int num_decoder_filters, int num_encoder_filters, int num_requests = 1); void setUpBufferLimits(); @@ -86,6 +118,9 @@ class HttpConnectionManagerImplMixin : public ConnectionManagerConfig { absl::optional maxConnectionDuration() const override { return max_connection_duration_; } + bool http1SafeMaxConnectionDuration() const override { + return http1_safe_max_connection_duration_; + } std::chrono::milliseconds streamIdleTimeout() const override { return stream_idle_timeout_; } std::chrono::milliseconds requestTimeout() const override { return request_timeout_; } std::chrono::milliseconds requestHeadersTimeout() const override { @@ -248,6 +283,7 @@ class HttpConnectionManagerImplMixin : public ConnectionManagerConfig { uint64_t max_requests_per_connection_{}; absl::optional idle_timeout_; absl::optional max_connection_duration_; + bool http1_safe_max_connection_duration_{false}; std::chrono::milliseconds stream_idle_timeout_{}; std::chrono::milliseconds request_timeout_{}; std::chrono::milliseconds request_headers_timeout_{}; diff --git a/test/extensions/filters/http/ext_proc/filter_test.cc b/test/extensions/filters/http/ext_proc/filter_test.cc index 15c3023c90c7..6fd45bff4495 100644 --- a/test/extensions/filters/http/ext_proc/filter_test.cc +++ b/test/extensions/filters/http/ext_proc/filter_test.cc @@ -4261,7 +4261,7 @@ TEST_F(HttpFilter2Test, LastDecodeDataCallExceedsStreamBufferLimitWouldJustRaise envoy_grpc: cluster_name: "ext_proc_server" )EOF"); - HttpConnectionManagerImplMixin::setup(false, "fake-server"); + HttpConnectionManagerImplMixin::setup(Envoy::Http::SetupOpts().setServerName("fake-server")); HttpConnectionManagerImplMixin::initial_buffer_limit_ = 10; HttpConnectionManagerImplMixin::setUpBufferLimits(); @@ -4352,7 +4352,7 @@ TEST_F(HttpFilter2Test, LastEncodeDataCallExceedsStreamBufferLimitWouldJustRaise response_trailer_mode: "SKIP" )EOF"); - HttpConnectionManagerImplMixin::setup(false, "fake-server"); + HttpConnectionManagerImplMixin::setup(Envoy::Http::SetupOpts().setServerName("fake-server")); HttpConnectionManagerImplMixin::initial_buffer_limit_ = 10; HttpConnectionManagerImplMixin::setUpBufferLimits(); diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 261ddd3c430e..f45f177a5b2e 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -2858,6 +2858,69 @@ TEST_P(ProtocolIntegrationTest, TestDownstreamResetIdleTimeout) { EXPECT_THAT(waitForAccessLog(access_log_name_), Not(HasSubstr("DPE"))); } +// Test that with http1_safe_max_connection_duration set to true, drain_timeout is not used for +// http1 connections after max_connection_duration is reached. Instead, envoy waits for the next +// request, adds connection:close to the response headers, then closes the connection after the +// stream ends. +TEST_P(ProtocolIntegrationTest, Http1SafeConnDurationTimeout) { + config_helper_.setDownstreamMaxConnectionDuration(std::chrono::milliseconds(500)); + config_helper_.addConfigModifier( + [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { + hcm.mutable_drain_timeout()->set_nanos(1'000'000 /*=1ms*/); + hcm.set_http1_safe_max_connection_duration(true); + }); + initialize(); + + codec_client_ = makeRawHttpConnection(makeClientConnection(lookupPort("http")), absl::nullopt); + + auto response = codec_client_->makeRequestWithBody(default_request_headers_, 1024); + waitForNextUpstreamRequest(); + + upstream_request_->encodeHeaders(default_response_headers_, true); + ASSERT_TRUE(response->waitForEndStream()); + + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_TRUE(response->complete()); + test_server_->waitForCounterGe("cluster.cluster_0.upstream_cx_total", 1); + test_server_->waitForCounterGe("cluster.cluster_0.upstream_rq_200", 1); + + if (downstream_protocol_ != Http::CodecType::HTTP1) { + ASSERT_TRUE(codec_client_->waitForDisconnect(std::chrono::milliseconds(10000))); + test_server_->waitForCounterGe("http.config_test.downstream_cx_max_duration_reached", 1); + EXPECT_EQ(test_server_->gauge("http.config_test.downstream_cx_http1_soft_drain")->value(), 0); + // The rest of the test is only for http1. + return; + } + + // Wait until after the max connection duration + test_server_->waitForCounterGe("http.config_test.downstream_cx_max_duration_reached", 1); + test_server_->waitForGaugeGe("http.config_test.downstream_cx_http1_soft_drain", 1); + + // Envoy now waits for one more request/response over this connection before sending the + // connection:close header and closing the connection. No matter how long the request/response + // takes, envoy will not close the connection until it's able to send the connection:close header + // downstream in a response. + // + // Sleeping for longer than the drain phase duration just to show it is no longer relevant. + absl::SleepFor(absl::Seconds(1)); + + auto soft_drain_response = codec_client_->makeRequestWithBody(default_request_headers_, 1024); + waitForNextUpstreamRequest(); + + upstream_request_->encodeHeaders(default_response_headers_, true); + ASSERT_TRUE(soft_drain_response->waitForEndStream()); + // Envoy will close the connection after the response has been sent. + ASSERT_TRUE(codec_client_->waitForDisconnect(std::chrono::milliseconds(10000))); + + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_TRUE(soft_drain_response->complete()); + + // The client must have been notified that the connection will be closed. + EXPECT_EQ(soft_drain_response->headers().getConnectionValue(), + Http::Headers::get().ConnectionValues.Close); +} + // Test connection is closed after single request processed. TEST_P(ProtocolIntegrationTest, ConnDurationTimeoutBasic) { config_helper_.setDownstreamMaxConnectionDuration(std::chrono::milliseconds(500)); diff --git a/test/mocks/http/mocks.h b/test/mocks/http/mocks.h index a09d7f283a01..351db788e577 100644 --- a/test/mocks/http/mocks.h +++ b/test/mocks/http/mocks.h @@ -638,6 +638,7 @@ class MockConnectionManagerConfig : public ConnectionManagerConfig { MOCK_METHOD(absl::optional, idleTimeout, (), (const)); MOCK_METHOD(bool, isRoutable, (), (const)); MOCK_METHOD(absl::optional, maxConnectionDuration, (), (const)); + MOCK_METHOD(bool, http1SafeMaxConnectionDuration, (), (const)); MOCK_METHOD(absl::optional, maxStreamDuration, (), (const)); MOCK_METHOD(std::chrono::milliseconds, streamIdleTimeout, (), (const)); MOCK_METHOD(std::chrono::milliseconds, requestTimeout, (), (const)); From 603bc61fbbf0ee968da5357bb258221609dc48ba Mon Sep 17 00:00:00 2001 From: blake-snyder Date: Thu, 8 Aug 2024 10:12:43 -0700 Subject: [PATCH 072/130] ORCA: Added utility methods to parse ORCA response headers from backends. (#35422) Signed-off-by: blake-snyder --- source/common/orca/BUILD | 25 ++++++++++ source/common/orca/orca_parser.cc | 45 +++++++++++++++++ source/common/orca/orca_parser.h | 19 ++++++++ test/common/orca/BUILD | 26 ++++++++++ test/common/orca/orca_parser_test.cc | 72 ++++++++++++++++++++++++++++ 5 files changed, 187 insertions(+) create mode 100644 source/common/orca/BUILD create mode 100644 source/common/orca/orca_parser.cc create mode 100644 source/common/orca/orca_parser.h create mode 100644 test/common/orca/BUILD create mode 100644 test/common/orca/orca_parser_test.cc diff --git a/source/common/orca/BUILD b/source/common/orca/BUILD new file mode 100644 index 000000000000..9337ab05ecaf --- /dev/null +++ b/source/common/orca/BUILD @@ -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", + ], +) diff --git a/source/common/orca/orca_parser.cc b/source/common/orca/orca_parser.cc new file mode 100644 index 000000000000..73ba57da26d9 --- /dev/null +++ b/source/common/orca/orca_parser.cc @@ -0,0 +1,45 @@ +#include "source/common/orca/orca_parser.h" + +#include + +#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 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 diff --git a/source/common/orca/orca_parser.h b/source/common/orca/orca_parser.h new file mode 100644 index 000000000000..86fd23944017 --- /dev/null +++ b/source/common/orca/orca_parser.h @@ -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 +parseOrcaLoadReportHeaders(const Envoy::Http::HeaderMap& headers); +} // namespace Orca +} // namespace Envoy diff --git a/test/common/orca/BUILD b/test/common/orca/BUILD new file mode 100644 index 000000000000..9122593cf921 --- /dev/null +++ b/test/common/orca/BUILD @@ -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", + ], +) diff --git a/test/common/orca/orca_parser_test.cc b/test/common/orca/orca_parser_test.cc new file mode 100644 index 000000000000..84debabd85ed --- /dev/null +++ b/test/common/orca/orca_parser_test.cc @@ -0,0 +1,72 @@ +#include + +#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 From 65bcd93dbdd1517961c9148fecc8ad16a906755e Mon Sep 17 00:00:00 2001 From: danzh Date: Thu, 8 Aug 2024 17:05:16 -0400 Subject: [PATCH 073/130] quic: fix stream reset error stats (#35548) Commit Message: fix a stats error where outgoing QUIC reset error stats is mistakenly incremented both when sending RESET_STREAM frame and receiving RESET_STREAM frame in MaybeSendRstStreamFrame(). As a result, `.tx.quic_reset_stream_error_code_XXX` is counting `.rx.quic_reset_stream_error_code_XXX` into it. Additional Description: This PR also refactor the stats increment into QuicFilterManagerConnectionImpl shared between client and server sessions. Risk Level: low, stats only Testing: new unit tests Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A Signed-off-by: Dan Zhang Co-authored-by: Dan Zhang --- source/common/quic/BUILD | 1 + .../common/quic/envoy_quic_client_session.cc | 21 +++++-------- .../common/quic/envoy_quic_client_session.h | 4 --- .../common/quic/envoy_quic_client_stream.cc | 2 ++ .../common/quic/envoy_quic_server_session.cc | 23 +++++--------- .../common/quic/envoy_quic_server_session.h | 5 --- .../common/quic/envoy_quic_server_stream.cc | 2 ++ .../quic_filter_manager_connection_impl.cc | 9 +++++- .../quic_filter_manager_connection_impl.h | 9 +++++- .../quic/envoy_quic_server_session_test.cc | 31 +++++++++++++++++++ .../quic/envoy_quic_server_stream_test.cc | 5 ++- ...uic_filter_manager_connection_impl_test.cc | 13 +++++--- test/common/quic/test_utils.h | 6 ++-- 13 files changed, 84 insertions(+), 47 deletions(-) diff --git a/source/common/quic/BUILD b/source/common/quic/BUILD index 8c4cded47bff..ecb713acc45d 100644 --- a/source/common/quic/BUILD +++ b/source/common/quic/BUILD @@ -258,6 +258,7 @@ envoy_cc_library( ":envoy_quic_simulated_watermark_buffer_lib", ":quic_network_connection_lib", ":quic_ssl_connection_info_lib", + ":quic_stat_names_lib", ":send_buffer_monitor_lib", "//envoy/event:dispatcher_interface", "//envoy/network:connection_interface", diff --git a/source/common/quic/envoy_quic_client_session.cc b/source/common/quic/envoy_quic_client_session.cc index b6ac762f7b43..aed2ac6115d8 100644 --- a/source/common/quic/envoy_quic_client_session.cc +++ b/source/common/quic/envoy_quic_client_session.cc @@ -85,12 +85,12 @@ EnvoyQuicClientSession::EnvoyQuicClientSession( std::make_unique( dispatcher.timeSource(), connection->connectionSocket()->connectionInfoProviderSharedPtr(), - StreamInfo::FilterState::LifeSpan::Connection)), + StreamInfo::FilterState::LifeSpan::Connection), + quic_stat_names, scope), quic::QuicSpdyClientSession(config, supported_versions, connection.release(), server_id, crypto_config.get()), crypto_config_(crypto_config), crypto_stream_factory_(crypto_stream_factory), - quic_stat_names_(quic_stat_names), rtt_cache_(rtt_cache), scope_(scope), - transport_socket_options_(transport_socket_options), + rtt_cache_(rtt_cache), transport_socket_options_(transport_socket_options), transport_socket_factory_(makeOptRefFromPtr( dynamic_cast(transport_socket_factory.ptr()))) { streamInfo().setUpstreamInfo(std::make_shared()); @@ -137,7 +137,8 @@ void EnvoyQuicClientSession::OnConnectionClosed(const quic::QuicConnectionCloseF } } quic::QuicSpdyClientSession::OnConnectionClosed(frame, source); - quic_stat_names_.chargeQuicConnectionCloseStats(scope_, frame.quic_error_code, source, true); + quic_stat_names_.chargeQuicConnectionCloseStats(stats_scope_, frame.quic_error_code, source, + true); onConnectionCloseEvent(frame, source, version()); } @@ -163,18 +164,10 @@ void EnvoyQuicClientSession::OnHttp3GoAway(uint64_t stream_id) { } } -void EnvoyQuicClientSession::MaybeSendRstStreamFrame(quic::QuicStreamId id, - quic::QuicResetStreamError error, - quic::QuicStreamOffset bytes_written) { - QuicSpdyClientSession::MaybeSendRstStreamFrame(id, error, bytes_written); - quic_stat_names_.chargeQuicResetStreamErrorStats(scope_, error, /*from_self*/ true, - /*is_upstream*/ true); -} - void EnvoyQuicClientSession::OnRstStream(const quic::QuicRstStreamFrame& frame) { QuicSpdyClientSession::OnRstStream(frame); - quic_stat_names_.chargeQuicResetStreamErrorStats(scope_, frame.error(), - /*from_self*/ false, /*is_upstream*/ true); + incrementSentQuicResetStreamErrorStats(frame.error(), + /*from_self*/ false, /*is_upstream*/ true); } void EnvoyQuicClientSession::OnCanCreateNewOutgoingStream(bool unidirectional) { diff --git a/source/common/quic/envoy_quic_client_session.h b/source/common/quic/envoy_quic_client_session.h index 5e88f53e60d8..5ef804e3e8ea 100644 --- a/source/common/quic/envoy_quic_client_session.h +++ b/source/common/quic/envoy_quic_client_session.h @@ -59,8 +59,6 @@ class EnvoyQuicClientSession : public QuicFilterManagerConnectionImpl, void OnCanWrite() override; void OnHttp3GoAway(uint64_t stream_id) override; void OnTlsHandshakeComplete() override; - void MaybeSendRstStreamFrame(quic::QuicStreamId id, quic::QuicResetStreamError error, - quic::QuicStreamOffset bytes_written) override; void OnRstStream(const quic::QuicRstStreamFrame& frame) override; void OnNewEncryptionKeyAvailable(quic::EncryptionLevel level, std::unique_ptr encrypter) override; @@ -118,9 +116,7 @@ class EnvoyQuicClientSession : public QuicFilterManagerConnectionImpl, Http::ConnectionCallbacks* http_connection_callbacks_{nullptr}; std::shared_ptr crypto_config_; EnvoyQuicCryptoClientStreamFactoryInterface& crypto_stream_factory_; - QuicStatNames& quic_stat_names_; OptRef rtt_cache_; - Stats::Scope& scope_; bool disable_keepalive_{false}; Network::TransportSocketOptionsConstSharedPtr transport_socket_options_; OptRef transport_socket_factory_; diff --git a/source/common/quic/envoy_quic_client_stream.cc b/source/common/quic/envoy_quic_client_stream.cc index 3e329da66541..848817777945 100644 --- a/source/common/quic/envoy_quic_client_stream.cc +++ b/source/common/quic/envoy_quic_client_stream.cc @@ -369,6 +369,8 @@ void EnvoyQuicClientStream::OnStreamReset(const quic::QuicRstStreamFrame& frame) void EnvoyQuicClientStream::ResetWithError(quic::QuicResetStreamError error) { ENVOY_STREAM_LOG(debug, "sending reset code={}", *this, error.internal_code()); stats_.tx_reset_.inc(); + filterManagerConnection()->incrementSentQuicResetStreamErrorStats(error, /*from_self*/ true, + /*is_upstream*/ true); // Upper layers expect calling resetStream() to immediately raise reset callbacks. runResetCallbacks( quicRstErrorToEnvoyLocalResetReason(error.internal_code()), diff --git a/source/common/quic/envoy_quic_server_session.cc b/source/common/quic/envoy_quic_server_session.cc index fa418b4013db..6ff59b7c4825 100644 --- a/source/common/quic/envoy_quic_server_session.cc +++ b/source/common/quic/envoy_quic_server_session.cc @@ -43,11 +43,12 @@ EnvoyQuicServerSession::EnvoyQuicServerSession( EnvoyQuicConnectionDebugVisitorFactoryInterfaceOptRef debug_visitor_factory) : quic::QuicServerSessionBase(config, supported_versions, connection.get(), visitor, helper, crypto_config, compressed_certs_cache), - QuicFilterManagerConnectionImpl( - *connection, connection->connection_id(), dispatcher, send_buffer_limit, - std::make_shared(*this), std::move(stream_info)), - quic_connection_(std::move(connection)), quic_stat_names_(quic_stat_names), - listener_scope_(listener_scope), crypto_server_stream_factory_(crypto_server_stream_factory), + QuicFilterManagerConnectionImpl(*connection, connection->connection_id(), dispatcher, + send_buffer_limit, + std::make_shared(*this), + std::move(stream_info), quic_stat_names, listener_scope), + quic_connection_(std::move(connection)), + crypto_server_stream_factory_(crypto_server_stream_factory), connection_stats_(connection_stats) { #ifdef ENVOY_ENABLE_HTTP_DATAGRAMS http_datagram_support_ = quic::HttpDatagramSupport::kRfc; @@ -175,18 +176,10 @@ void EnvoyQuicServerSession::OnTlsHandshakeComplete() { raiseConnectionEvent(Network::ConnectionEvent::Connected); } -void EnvoyQuicServerSession::MaybeSendRstStreamFrame(quic::QuicStreamId id, - quic::QuicResetStreamError error, - quic::QuicStreamOffset bytes_written) { - QuicServerSessionBase::MaybeSendRstStreamFrame(id, error, bytes_written); - quic_stat_names_.chargeQuicResetStreamErrorStats(listener_scope_, error, /*from_self*/ true, - /*is_upstream*/ false); -} - void EnvoyQuicServerSession::OnRstStream(const quic::QuicRstStreamFrame& frame) { QuicServerSessionBase::OnRstStream(frame); - quic_stat_names_.chargeQuicResetStreamErrorStats(listener_scope_, frame.error(), - /*from_self*/ false, /*is_upstream*/ false); + incrementSentQuicResetStreamErrorStats(frame.error(), + /*from_self*/ false, /*is_upstream*/ false); } void EnvoyQuicServerSession::setHttp3Options( diff --git a/source/common/quic/envoy_quic_server_session.h b/source/common/quic/envoy_quic_server_session.h index 05ee14081129..62014e9d8854 100644 --- a/source/common/quic/envoy_quic_server_session.h +++ b/source/common/quic/envoy_quic_server_session.h @@ -82,8 +82,6 @@ class EnvoyQuicServerSession : public quic::QuicServerSessionBase, void Initialize() override; void OnCanWrite() override; void OnTlsHandshakeComplete() override; - void MaybeSendRstStreamFrame(quic::QuicStreamId id, quic::QuicResetStreamError error, - quic::QuicStreamOffset bytes_written) override; void OnRstStream(const quic::QuicRstStreamFrame& frame) override; void ProcessUdpPacket(const quic::QuicSocketAddress& self_address, const quic::QuicSocketAddress& peer_address, @@ -137,9 +135,6 @@ class EnvoyQuicServerSession : public quic::QuicServerSessionBase, envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction headers_with_underscores_action_; - QuicStatNames& quic_stat_names_; - Stats::Scope& listener_scope_; - EnvoyQuicCryptoServerStreamFactoryInterface& crypto_server_stream_factory_; absl::optional position_; QuicConnectionStats& connection_stats_; diff --git a/source/common/quic/envoy_quic_server_stream.cc b/source/common/quic/envoy_quic_server_stream.cc index 812025913c59..f3ca7a38a3e3 100644 --- a/source/common/quic/envoy_quic_server_stream.cc +++ b/source/common/quic/envoy_quic_server_stream.cc @@ -359,6 +359,8 @@ void EnvoyQuicServerStream::OnStreamReset(const quic::QuicRstStreamFrame& frame) void EnvoyQuicServerStream::ResetWithError(quic::QuicResetStreamError error) { ENVOY_STREAM_LOG(debug, "sending reset code={}", *this, error.internal_code()); stats_.tx_reset_.inc(); + filterManagerConnection()->incrementSentQuicResetStreamErrorStats(error, /*from_self*/ true, + /*is_upstream*/ false); if (!local_end_stream_) { // Upper layers expect calling resetStream() to immediately raise reset callbacks. runResetCallbacks( diff --git a/source/common/quic/quic_filter_manager_connection_impl.cc b/source/common/quic/quic_filter_manager_connection_impl.cc index 24d837c6116c..520cb822a51a 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.cc +++ b/source/common/quic/quic_filter_manager_connection_impl.cc @@ -13,11 +13,13 @@ QuicFilterManagerConnectionImpl::QuicFilterManagerConnectionImpl( QuicNetworkConnection& connection, const quic::QuicConnectionId& connection_id, Event::Dispatcher& dispatcher, uint32_t send_buffer_limit, std::shared_ptr&& info, - std::unique_ptr&& stream_info) + std::unique_ptr&& stream_info, QuicStatNames& quic_stat_names, + Stats::Scope& stats_scope) // Using this for purpose other than logging is not safe. Because QUIC connection id can be // 18 bytes, so there might be collision when it's hashed to 8 bytes. : Network::ConnectionImplBase(dispatcher, /*id=*/connection_id.Hash()), network_connection_(&connection), quic_ssl_info_(std::move(info)), + quic_stat_names_(quic_stat_names), stats_scope_(stats_scope), filter_manager_( std::make_unique(*this, *connection.connectionSocket())), stream_info_(std::move(stream_info)), @@ -271,5 +273,10 @@ void QuicFilterManagerConnectionImpl::maybeHandleCloseDuringInitialize() { } } +void QuicFilterManagerConnectionImpl::incrementSentQuicResetStreamErrorStats( + quic::QuicResetStreamError error, bool from_self, bool is_upstream) { + quic_stat_names_.chargeQuicResetStreamErrorStats(stats_scope_, error, from_self, is_upstream); +} + } // namespace Quic } // namespace Envoy diff --git a/source/common/quic/quic_filter_manager_connection_impl.h b/source/common/quic/quic_filter_manager_connection_impl.h index 7e366650a935..da591a31b86b 100644 --- a/source/common/quic/quic_filter_manager_connection_impl.h +++ b/source/common/quic/quic_filter_manager_connection_impl.h @@ -13,6 +13,7 @@ #include "source/common/quic/envoy_quic_simulated_watermark_buffer.h" #include "source/common/quic/quic_network_connection.h" #include "source/common/quic/quic_ssl_connection_info.h" +#include "source/common/quic/quic_stat_names.h" #include "source/common/quic/send_buffer_monitor.h" #include "source/common/stream_info/stream_info_impl.h" @@ -35,7 +36,8 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, const quic::QuicConnectionId& connection_id, Event::Dispatcher& dispatcher, uint32_t send_buffer_limit, std::shared_ptr&& info, - std::unique_ptr&& stream_info); + std::unique_ptr&& stream_info, + QuicStatNames& quic_stat_names, Stats::Scope& stats_scope); // Network::FilterManager // Overridden to delegate calls to filter_manager_. void addWriteFilter(Network::WriteFilterSharedPtr filter) override; @@ -171,6 +173,9 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, max_headers_count_ = max_headers_count; } + void incrementSentQuicResetStreamErrorStats(quic::QuicResetStreamError error, bool from_self, + bool is_upstream); + protected: // Propagate connection close to network_connection_callbacks_. void onConnectionCloseEvent(const quic::QuicConnectionCloseFrame& frame, @@ -197,6 +202,8 @@ class QuicFilterManagerConnectionImpl : public Network::ConnectionImplBase, OptRef http3_options_; bool initialized_{false}; std::shared_ptr quic_ssl_info_; + QuicStatNames& quic_stat_names_; + Stats::Scope& stats_scope_; private: friend class Envoy::TestPauseFilterForQuic; diff --git a/test/common/quic/envoy_quic_server_session_test.cc b/test/common/quic/envoy_quic_server_session_test.cc index 4fba3ac27b03..b670a20d8702 100644 --- a/test/common/quic/envoy_quic_server_session_test.cc +++ b/test/common/quic/envoy_quic_server_session_test.cc @@ -376,6 +376,10 @@ TEST_F(EnvoyQuicServerSessionTest, OnResetFrameIetfQuic) { listener_config_.listenerScope().store(), "http3.downstream.rx.quic_reset_stream_error_code_QUIC_ERROR_PROCESSING_STREAM") ->value()); + EXPECT_EQ(nullptr, + TestUtility::findCounter( + listener_config_.listenerScope().store(), + "http3.downstream.tx.quic_reset_stream_error_code_QUIC_ERROR_PROCESSING_STREAM")); EXPECT_CALL(http_connection_callbacks_, newStream(_, false)) .WillOnce(Invoke([&request_decoder, &stream_callbacks](Http::ResponseEncoder& encoder, @@ -403,6 +407,9 @@ TEST_F(EnvoyQuicServerSessionTest, OnResetFrameIetfQuic) { listener_config_.listenerScope().store(), "http3.downstream.rx.quic_reset_stream_error_code_QUIC_REFUSED_STREAM") ->value()); + EXPECT_EQ(nullptr, TestUtility::findCounter( + listener_config_.listenerScope().store(), + "http3.downstream.tx.quic_reset_stream_error_code_QUIC_REFUSED_STREAM")); EXPECT_CALL(http_connection_callbacks_, newStream(_, false)) .WillOnce(Invoke([&request_decoder, &stream_callbacks](Http::ResponseEncoder& encoder, @@ -424,6 +431,30 @@ TEST_F(EnvoyQuicServerSessionTest, OnResetFrameIetfQuic) { listener_config_.listenerScope().store(), "http3.downstream.rx.quic_reset_stream_error_code_QUIC_REFUSED_STREAM") ->value()); + EXPECT_EQ(nullptr, TestUtility::findCounter( + listener_config_.listenerScope().store(), + "http3.downstream.tx.quic_reset_stream_error_code_QUIC_REFUSED_STREAM")); +} + +TEST_F(EnvoyQuicServerSessionTest, ResetStream) { + installReadFilter(); + + Http::MockRequestDecoder request_decoder; + Http::MockStreamCallbacks stream_callbacks; + EXPECT_CALL(request_decoder, accessLogHandlers()); + auto stream1 = + dynamic_cast(createNewStream(request_decoder, stream_callbacks)); + EXPECT_CALL(stream_callbacks, onResetStream(Http::StreamResetReason::LocalReset, _)); + EXPECT_CALL(*quic_connection_, SendControlFrame(_)); + stream1->resetStream(Http::StreamResetReason::LocalReset); + EXPECT_EQ(1U, TestUtility::findCounter( + listener_config_.listenerScope().store(), + "http3.downstream.tx.quic_reset_stream_error_code_QUIC_STREAM_REQUEST_REJECTED") + ->value()); + EXPECT_EQ(nullptr, + TestUtility::findCounter( + listener_config_.listenerScope().store(), + "http3.downstream.rx.quic_reset_stream_error_code_QUIC_STREAM_REQUEST_REJECTED")); } TEST_F(EnvoyQuicServerSessionTest, ConnectionClose) { diff --git a/test/common/quic/envoy_quic_server_stream_test.cc b/test/common/quic/envoy_quic_server_stream_test.cc index f8f02758f95d..a1958f3afa2d 100644 --- a/test/common/quic/envoy_quic_server_stream_test.cc +++ b/test/common/quic/envoy_quic_server_stream_test.cc @@ -50,8 +50,10 @@ class EnvoyQuicServerStreamTest : public testing::Test { quic_connection_(connection_helper_, alarm_factory_, writer_, quic::ParsedQuicVersionVector{quic_version_}, *listener_config_.socket_, connection_id_generator_), + quic_stat_names_(listener_config_.listenerScope().symbolTable()), quic_session_(quic_config_, {quic_version_}, &quic_connection_, *dispatcher_, - quic_config_.GetInitialStreamFlowControlWindowToSend() * 2), + quic_config_.GetInitialStreamFlowControlWindowToSend() * 2, quic_stat_names_, + listener_config_.listenerScope()), stats_( {ALL_HTTP3_CODEC_STATS(POOL_COUNTER_PREFIX(listener_config_.listenerScope(), "http3."), POOL_GAUGE_PREFIX(listener_config_.listenerScope(), "http3."))}), @@ -222,6 +224,7 @@ class EnvoyQuicServerStreamTest : public testing::Test { quic::DeterministicConnectionIdGenerator connection_id_generator_{ quic::kQuicDefaultConnectionIdLength}; testing::NiceMock quic_connection_; + Envoy::Quic::QuicStatNames quic_stat_names_; MockEnvoyQuicSession quic_session_; quic::QuicStreamId stream_id_{kStreamId}; Http::Http3::CodecStats stats_; diff --git a/test/common/quic/quic_filter_manager_connection_impl_test.cc b/test/common/quic/quic_filter_manager_connection_impl_test.cc index 1217c6d69d46..a4ddd7600825 100644 --- a/test/common/quic/quic_filter_manager_connection_impl_test.cc +++ b/test/common/quic/quic_filter_manager_connection_impl_test.cc @@ -16,13 +16,15 @@ class TestQuicFilterManagerConnectionImpl : public QuicFilterManagerConnectionIm TestQuicFilterManagerConnectionImpl(QuicNetworkConnection& connection, const quic::QuicConnectionId& connection_id, Event::Dispatcher& dispatcher, uint32_t send_buffer_limit, - std::shared_ptr&& ssl_info) + std::shared_ptr&& ssl_info, + QuicStatNames& quic_stat_names, Stats::Scope& scope) : QuicFilterManagerConnectionImpl( connection, connection_id, dispatcher, send_buffer_limit, std::move(ssl_info), std::make_unique( dispatcher.timeSource(), connection.connectionSocket()->connectionInfoProviderSharedPtr(), - StreamInfo::FilterState::LifeSpan::Connection)) {} + StreamInfo::FilterState::LifeSpan::Connection), + quic_stat_names, scope) {} void dumpState(std::ostream& /*os*/, int /*indent_level = 0*/) const override {} absl::string_view requestedServerName() const override { return {}; } @@ -42,11 +44,12 @@ class QuicFilterManagerConnectionImplTest : public ::testing::Test { public: QuicFilterManagerConnectionImplTest() : socket_(std::make_unique>()), - connection_(std::move(socket_)), + quic_stat_names_(store_.symbolTable()), connection_(std::move(socket_)), quic_session_(new quic::test::MockQuicConnection(&helper_, &alarm_factory_, quic::Perspective::IS_SERVER)), ssl_info_(std::make_shared(quic_session_)), - impl_(connection_, connection_id_, dispatcher_, send_buffer_limit_, std::move(ssl_info_)) {} + impl_(connection_, connection_id_, dispatcher_, send_buffer_limit_, std::move(ssl_info_), + quic_stat_names_, *store_.rootScope()) {} protected: std::unique_ptr> socket_; @@ -55,6 +58,8 @@ class QuicFilterManagerConnectionImplTest : public ::testing::Test { uint32_t send_buffer_limit_ = 0; quic::test::MockQuicConnectionHelper helper_; quic::test::MockAlarmFactory alarm_factory_; + Stats::IsolatedStoreImpl store_; + QuicStatNames quic_stat_names_; QuicNetworkConnection connection_; quic::test::MockQuicSession quic_session_; std::shared_ptr ssl_info_; diff --git a/test/common/quic/test_utils.h b/test/common/quic/test_utils.h index 38f73504af8c..c89c33e70ea0 100644 --- a/test/common/quic/test_utils.h +++ b/test/common/quic/test_utils.h @@ -97,14 +97,16 @@ class MockEnvoyQuicSession : public quic::QuicSpdySession, public QuicFilterMana MockEnvoyQuicSession(const quic::QuicConfig& config, const quic::ParsedQuicVersionVector& supported_versions, EnvoyQuicServerConnection* connection, Event::Dispatcher& dispatcher, - uint32_t send_buffer_limit) + uint32_t send_buffer_limit, QuicStatNames& quic_stat_names, + Stats::Scope& scope) : quic::QuicSpdySession(connection, /*visitor=*/nullptr, config, supported_versions), QuicFilterManagerConnectionImpl( *connection, connection->connection_id(), dispatcher, send_buffer_limit, {nullptr}, std::make_unique( dispatcher.timeSource(), connection->connectionSocket()->connectionInfoProviderSharedPtr(), - StreamInfo::FilterState::LifeSpan::Connection)), + StreamInfo::FilterState::LifeSpan::Connection), + quic_stat_names, scope), crypto_stream_(std::make_unique(this)) {} void Initialize() override { From cda4f6171ecbd66f3c740f477ffa94543c00e689 Mon Sep 17 00:00:00 2001 From: Brian O'Rourke Date: Thu, 8 Aug 2024 18:03:40 -0700 Subject: [PATCH 074/130] filter-network-redis: add support for Redis publish command (#35632) Commit Message: Add support for Redis publish command Additional Description: "publish" is a simple command that does not require pubsub mode Risk Level: Low Testing: Covered by existing tests Docs Changes: Added command to docs Release Notes: Added to release notes Platform Specific Features: N/A Signed-off-by: Brian P O'Rourke --- changelogs/current.yaml | 3 +++ docs/root/intro/arch_overview/other_protocols/redis.rst | 1 + .../filters/network/common/redis/supported_commands.h | 6 +++--- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index e9f0c7bda101..f7abaa70c2d6 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -131,6 +131,9 @@ removed_config_or_runtime: Removed ``envoy.reloadable_features.normalize_host_for_preresolve_dfp_dns`` runtime flag and legacy code paths. new_features: +- area: redis + change: | + Added support for publish. - area: jwt_authn change: | Added missing implementation to jwt_authn matchers to allow glob pattern matching. diff --git a/docs/root/intro/arch_overview/other_protocols/redis.rst b/docs/root/intro/arch_overview/other_protocols/redis.rst index 72e3000c1944..6fb54f1a834b 100644 --- a/docs/root/intro/arch_overview/other_protocols/redis.rst +++ b/docs/root/intro/arch_overview/other_protocols/redis.rst @@ -200,6 +200,7 @@ For details on each command's usage see the official RPOP, List RPUSH, List RPUSHX, List + PUBLISH, Pubsub EVAL, Scripting EVALSHA, Scripting SADD, Set diff --git a/source/extensions/filters/network/common/redis/supported_commands.h b/source/extensions/filters/network/common/redis/supported_commands.h index 14a8a8686724..bbda434d72ab 100644 --- a/source/extensions/filters/network/common/redis/supported_commands.h +++ b/source/extensions/filters/network/common/redis/supported_commands.h @@ -28,9 +28,9 @@ struct SupportedCommands { "hincrbyfloat", "hkeys", "hlen", "hmget", "hmset", "hscan", "hset", "hsetnx", "hstrlen", "hvals", "incr", "incrby", "incrbyfloat", "lindex", "linsert", "llen", "lmove", "lpop", "lpush", "lpushx", "lrange", "lrem", "lset", "ltrim", "persist", "pexpire", "pexpireat", - "pfadd", "pfcount", "psetex", "pttl", "restore", "rpop", "rpush", "rpushx", "sadd", "scard", - "set", "setbit", "setex", "setnx", "setrange", "sismember", "smembers", "spop", - "srandmember", "srem", "sscan", "strlen", "ttl", "type", "watch", "xack", "xadd", + "pfadd", "pfcount", "psetex", "pttl", "publish", "restore", "rpop", "rpush", "rpushx", + "sadd", "scard", "set", "setbit", "setex", "setnx", "setrange", "sismember", "smembers", + "spop", "srandmember", "srem", "sscan", "strlen", "ttl", "type", "watch", "xack", "xadd", "xautoclaim", "xclaim", "xdel", "xlen", "xpending", "xrange", "xrevrange", "xtrim", "zadd", "zcard", "zcount", "zincrby", "zlexcount", "zpopmin", "zpopmax", "zrange", "zrangebylex", "zrangebyscore", "zrank", "zrem", "zremrangebylex", "zremrangebyrank", "zremrangebyscore", From 03561251fbc50e9d16b35e02ae6032e073c16430 Mon Sep 17 00:00:00 2001 From: Tony Allen Date: Thu, 8 Aug 2024 18:11:12 -0700 Subject: [PATCH 075/130] http_11_proxy: Allow for configuration via endpoint metadata (#35486) Previously the only way to configure the HTTP/1.1 proxy transport socket was by adding information to the streamInfo metadata via an intermediate filter. This patch adds the ability to configure proxy addresses using endpoint or locality metadata. The metadata key is `envoy.http11_proxy_transport_socket.proxy_address`. Configuration can be set in the metadata associated with `LocalityLbEndpoints`. The metadata associated with each individual endpoint overrides this value and the original method of configuration (filter state metadata) takes precedence above all. The format of the value must be a valid `config::core::v3::Address`. Risk Level: Low. Alpha feature. Testing: Unit test. Docs Changes: Done. Release Notes: Done. --------- Signed-off-by: Tony Allen --- .../v3/upstream_http_11_connect.proto | 33 +++-- changelogs/current.yaml | 3 + envoy/upstream/host_description.h | 5 + source/common/config/well_known_names.h | 3 + source/common/upstream/upstream_impl.cc | 68 +++++++-- source/common/upstream/upstream_impl.h | 5 +- .../extensions/clusters/common/logical_host.h | 3 + .../transport_sockets/http_11_proxy/BUILD | 1 + .../http_11_proxy/connect.cc | 36 ++++- .../transport_sockets/http_11_proxy/connect.h | 3 +- .../transport_sockets/http_11_proxy/BUILD | 1 + .../http_11_proxy/connect_test.cc | 131 ++++++++++++------ test/mocks/upstream/host.h | 2 + 13 files changed, 222 insertions(+), 72 deletions(-) diff --git a/api/envoy/extensions/transport_sockets/http_11_proxy/v3/upstream_http_11_connect.proto b/api/envoy/extensions/transport_sockets/http_11_proxy/v3/upstream_http_11_connect.proto index 99c2e451047b..76b6bc9849e4 100644 --- a/api/envoy/extensions/transport_sockets/http_11_proxy/v3/upstream_http_11_connect.proto +++ b/api/envoy/extensions/transport_sockets/http_11_proxy/v3/upstream_http_11_connect.proto @@ -16,20 +16,31 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Upstream HTTP/1.1 Proxy] // [#extension: envoy.transport_sockets.http_11_proxy] -// Configuration for HTTP/1.1 proxy transport sockets. -// This is intended for use in Envoy Mobile, though may eventually be extended -// for upstream Envoy use. -// If this transport socket is configured, and an intermediate filter adds the -// stream info necessary for proxying to the stream info (as the test filter -// does :repo:`here `) then +// The HTTP/1.1 proxy transport socket opens an upstream connection to a specified proxy address +// rather than the target host's address. If this transport socket is configured and proxy +// information is configured, then: // -// * Upstream connections will be directed to the specified proxy address rather -// than the host's address -// * Upstream TLS connections will have a raw HTTP/1.1 CONNECT header prefaced -// to the payload, and 200 response stripped (if less than 200 bytes) +// * Upstream connections to the proxy address will have a raw HTTP/1.1 CONNECT header prefaced to +// the payload, and 200 response stripped (if less than 200 bytes). // * Plaintext HTTP/1.1 connections will be sent with a fully qualified URL. // -// This transport socket is not compatible with HTTP/3, plaintext HTTP/2, or raw TCP. +// There are two primary ways to configure proxy information: +// +// * An intermediate filter adds the stream info necessary for proxying to the stream info (as the +// test filter does :repo:`here `). +// * Setting the "typed_filter_metadata" in :ref:`LbEndpoint.Metadata ` +// or :ref:`LocalityLbEndpoints.Metadata +// ` using the key +// "envoy.http11_proxy_transport_socket.proxy_address" and the proxy address in +// config::core::v3::Address format. +// +// Some important notes regarding this transport socket: +// +// * Configuration via stream info (as opposed to endpoint/locality metadata) will only proxy TLS +// connections to the proxy address on port 443. This is to maintain the original behavior of the +// transport socket when using this method of configuration. +// * The transport socket is not compatible with HTTP/3 or plaintext HTTP/2. +// message Http11ProxyUpstreamTransport { // The underlying transport socket being wrapped. config.core.v3.TransportSocket transport_socket = 1 [(validate.rules).message = {required: true}]; diff --git a/changelogs/current.yaml b/changelogs/current.yaml index f7abaa70c2d6..0a283171254a 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -155,6 +155,9 @@ new_features: change: | Added full feature absl::FormatTime() support to the DateFormatter. This allows the timepoint formatters (like ``%START_TIME%``) to use ``%E#S``, ``%E*S``, ``%E#f`` and ``%E*f`` to format the subsecond part of the timepoint. +- area: http_11_proxy + change: | + Added the option to configure the transport socket via locality or endpoint metadata. - area: sockets change: | Added socket ``type`` field for specifying a socket type to apply the socket option to under :ref:`SocketOption diff --git a/envoy/upstream/host_description.h b/envoy/upstream/host_description.h index 0a8d4815bbcd..069ddb5e46ed 100644 --- a/envoy/upstream/host_description.h +++ b/envoy/upstream/host_description.h @@ -204,6 +204,11 @@ class HostDescription { */ virtual const envoy::config::core::v3::Locality& locality() const PURE; + /** + * @return the metadata associated with the locality endpoints the host belongs to. + */ + virtual const MetadataConstSharedPtr localityMetadata() const PURE; + /** * @return the human readable name of the host's locality zone as a StatName. */ diff --git a/source/common/config/well_known_names.h b/source/common/config/well_known_names.h index 3e3130020ac3..565aa8bcfa27 100644 --- a/source/common/config/well_known_names.h +++ b/source/common/config/well_known_names.h @@ -34,6 +34,9 @@ class MetadataFilterValues { const std::string ENVOY_LB = "envoy.lb"; // Filter namespace for built-in transport socket match in cluster. const std::string ENVOY_TRANSPORT_SOCKET_MATCH = "envoy.transport_socket_match"; + // Proxy address configuration namespace for HTTP/1.1 proxy transport sockets. + const std::string ENVOY_HTTP11_PROXY_TRANSPORT_SOCKET_ADDR = + "envoy.http11_proxy_transport_socket.proxy_address"; }; using MetadataFilters = ConstSingleton; diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index 5d2af3d366a3..6c368861692d 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -42,6 +42,7 @@ #include "source/common/http/http2/codec_stats.h" #include "source/common/http/utility.h" #include "source/common/network/address_impl.h" +#include "source/common/network/filter_state_proxy_info.h" #include "source/common/network/happy_eyeballs_connection_impl.h" #include "source/common/network/resolver_impl.h" #include "source/common/network/socket_option_factory.h" @@ -535,6 +536,54 @@ Host::CreateConnectionData HostImplBase::createHealthCheckConnection( transport_socket_options, shared_from_this()); } +absl::optional HostImplBase::maybeGetProxyRedirectAddress( + const Network::TransportSocketOptionsConstSharedPtr transport_socket_options, + HostDescriptionConstSharedPtr host) { + if (transport_socket_options && transport_socket_options->http11ProxyInfo().has_value()) { + return transport_socket_options->http11ProxyInfo()->proxy_address; + } + + // See if host metadata contains a proxy address and only check locality metadata if host + // metadata did not have the relevant key. + for (const auto& metadata : {host->metadata(), host->localityMetadata()}) { + if (metadata == nullptr) { + continue; + } + + auto addr_it = metadata->typed_filter_metadata().find( + Config::MetadataFilters::get().ENVOY_HTTP11_PROXY_TRANSPORT_SOCKET_ADDR); + if (addr_it == metadata->typed_filter_metadata().end()) { + continue; + } + + // Parse an address from the metadata. + envoy::config::core::v3::Address proxy_addr; + auto status = MessageUtil::unpackTo(addr_it->second, proxy_addr); + if (!status.ok()) { + ENVOY_LOG_EVERY_POW_2( + error, "failed to parse proto from endpoint/locality metadata field {}, host={}", + Config::MetadataFilters::get().ENVOY_HTTP11_PROXY_TRANSPORT_SOCKET_ADDR, + host->hostname()); + return absl::nullopt; + } + + // Resolve the parsed address proto. + auto resolve_status = Network::Address::resolveProtoAddress(proxy_addr); + if (!resolve_status.ok()) { + ENVOY_LOG_EVERY_POW_2( + error, "failed to resolve address from endpoint/locality metadata field {}, host={}", + Config::MetadataFilters::get().ENVOY_HTTP11_PROXY_TRANSPORT_SOCKET_ADDR, + host->hostname()); + return absl::nullopt; + } + + // We successfully resolved, so return the instance ptr. + return resolve_status.value(); + } + + return absl::nullopt; +} + Host::CreateConnectionData HostImplBase::createConnection( Event::Dispatcher& dispatcher, const ClusterInfo& cluster, const Network::Address::InstanceConstSharedPtr& address, @@ -545,17 +594,20 @@ Host::CreateConnectionData HostImplBase::createConnection( HostDescriptionConstSharedPtr host) { auto source_address_selector = cluster.getUpstreamLocalAddressSelector(); + absl::optional proxy_address = + maybeGetProxyRedirectAddress(transport_socket_options, host); + Network::ClientConnectionPtr connection; - // If the transport socket options indicate the connection should be - // redirected to a proxy, create the TCP connection to the proxy's address not - // the host's address. - if (transport_socket_options && transport_socket_options->http11ProxyInfo().has_value()) { + // If the transport socket options or endpoint/locality metadata indicate the connection should + // be redirected to a proxy, create the TCP connection to the proxy's address not the host's + // address. + if (proxy_address.has_value()) { auto upstream_local_address = source_address_selector->getUpstreamLocalAddress(address, options); ENVOY_LOG(debug, "Connecting to configured HTTP/1.1 proxy at {}", - transport_socket_options->http11ProxyInfo()->proxy_address->asString()); + proxy_address.value()->asString()); connection = dispatcher.createClientConnection( - transport_socket_options->http11ProxyInfo()->proxy_address, upstream_local_address.address_, + proxy_address.value(), upstream_local_address.address_, socket_factory.createTransportSocket(transport_socket_options, host), upstream_local_address.socket_options_, transport_socket_options); } else if (address_list_or_null != nullptr && address_list_or_null->size() > 1) { @@ -1238,8 +1290,8 @@ ClusterInfoImpl::ClusterInfoImpl( optional_timeouts_.set(*idle_timeout); } - // Use default (10m) or configured `tcp_pool_idle_timeout`, unless it's set to 0, indicating that - // no timeout should be used. + // Use default (10m) or configured `tcp_pool_idle_timeout`, unless it's set to 0, indicating + // that no timeout should be used. absl::optional tcp_pool_idle_timeout(std::chrono::minutes(10)); if (tcp_protocol_options_ && tcp_protocol_options_->idleTimeout().has_value()) { tcp_pool_idle_timeout = tcp_protocol_options_->idleTimeout(); diff --git a/source/common/upstream/upstream_impl.h b/source/common/upstream/upstream_impl.h index c5867e4b5dec..70edb9c49d98 100644 --- a/source/common/upstream/upstream_impl.h +++ b/source/common/upstream/upstream_impl.h @@ -225,6 +225,7 @@ class HostDescriptionImplBase : virtual public HostDescription, const std::string& hostnameForHealthChecks() const override { return health_checks_hostname_; } const std::string& hostname() const override { return hostname_; } const envoy::config::core::v3::Locality& locality() const override { return locality_; } + const MetadataConstSharedPtr localityMetadata() const override { return locality_metadata_; } Stats::StatName localityZoneStatName() const override { return locality_zone_stat_name_.statName(); } @@ -430,6 +431,9 @@ class HostImplBase : public Host, const Network::ConnectionSocket::OptionsSharedPtr& options, Network::TransportSocketOptionsConstSharedPtr transport_socket_options, HostDescriptionConstSharedPtr host); + static absl::optional maybeGetProxyRedirectAddress( + const Network::TransportSocketOptionsConstSharedPtr transport_socket_options, + HostDescriptionConstSharedPtr host); private: // Helper function to check multiple health flags at once. @@ -722,7 +726,6 @@ class PrioritySetImpl : public PrioritySet { overprovisioning_factor); } -protected: virtual void runUpdateCallbacks(const HostVector& hosts_added, const HostVector& hosts_removed) { THROW_IF_NOT_OK(member_update_cb_helper_.runCallbacks(hosts_added, hosts_removed)); } diff --git a/source/extensions/clusters/common/logical_host.h b/source/extensions/clusters/common/logical_host.h index 3fe2164d18b5..3c13c909904c 100644 --- a/source/extensions/clusters/common/logical_host.h +++ b/source/extensions/clusters/common/logical_host.h @@ -77,6 +77,9 @@ class RealHostDescription : public HostDescription { // Upstream:HostDescription observers are delegated to logical_host_. bool canary() const override { return logical_host_->canary(); } MetadataConstSharedPtr metadata() const override { return logical_host_->metadata(); } + const MetadataConstSharedPtr localityMetadata() const override { + return logical_host_->localityMetadata(); + } Network::UpstreamTransportSocketFactory& transportSocketFactory() const override { return logical_host_->transportSocketFactory(); diff --git a/source/extensions/transport_sockets/http_11_proxy/BUILD b/source/extensions/transport_sockets/http_11_proxy/BUILD index f6a709461b7c..78df614fb285 100644 --- a/source/extensions/transport_sockets/http_11_proxy/BUILD +++ b/source/extensions/transport_sockets/http_11_proxy/BUILD @@ -36,6 +36,7 @@ envoy_cc_library( "//source/common/buffer:buffer_lib", "//source/common/common:scalar_to_byte_vector_lib", "//source/common/common:utility_lib", + "//source/common/config:well_known_names", "//source/common/http/http1:balsa_parser_lib", "//source/common/http/http1:legacy_parser_lib", "//source/common/network:address_lib", diff --git a/source/extensions/transport_sockets/http_11_proxy/connect.cc b/source/extensions/transport_sockets/http_11_proxy/connect.cc index d464734b33c3..5e1c10f7d5c7 100644 --- a/source/extensions/transport_sockets/http_11_proxy/connect.cc +++ b/source/extensions/transport_sockets/http_11_proxy/connect.cc @@ -7,6 +7,7 @@ #include "source/common/buffer/buffer_impl.h" #include "source/common/common/scalar_to_byte_vector.h" #include "source/common/common/utility.h" +#include "source/common/config/well_known_names.h" #include "source/common/network/address_impl.h" #include "source/common/runtime/runtime_features.h" @@ -29,12 +30,35 @@ bool UpstreamHttp11ConnectSocket::isValidConnectResponse(absl::string_view respo UpstreamHttp11ConnectSocket::UpstreamHttp11ConnectSocket( Network::TransportSocketPtr&& transport_socket, - Network::TransportSocketOptionsConstSharedPtr options) + Network::TransportSocketOptionsConstSharedPtr options, + std::shared_ptr host) : PassthroughSocket(std::move(transport_socket)), options_(options) { - if (options_ && options_->http11ProxyInfo() && transport_socket_->ssl()) { - header_buffer_.add( - absl::StrCat("CONNECT ", options_->http11ProxyInfo()->hostname, ":443 HTTP/1.1\r\n\r\n")); - need_to_strip_connect_response_ = true; + // If the filter state metadata has populated the relevant entries in the transport socket + // options, we want to maintain the original behavior of this transport socket. + if (options_ && options_->http11ProxyInfo()) { + if (transport_socket_->ssl()) { + header_buffer_.add( + absl::StrCat("CONNECT ", options_->http11ProxyInfo()->hostname, ":443 HTTP/1.1\r\n\r\n")); + need_to_strip_connect_response_ = true; + } + + return; + } + + // The absence of proxy info from the transport socket options means that we should use the host + // address of the provided HostDescription if it has the appropriate metadata set. + for (auto& metadata : {host->metadata(), host->localityMetadata()}) { + if (metadata == nullptr) { + continue; + } + + const bool has_proxy_addr = metadata->typed_filter_metadata().contains( + Config::MetadataFilters::get().ENVOY_HTTP11_PROXY_TRANSPORT_SOCKET_ADDR); + if (has_proxy_addr) { + header_buffer_.add( + absl::StrCat("CONNECT ", host->address()->asStringView(), " HTTP/1.1\r\n\r\n")); + need_to_strip_connect_response_ = true; + } } } @@ -139,7 +163,7 @@ Network::TransportSocketPtr UpstreamHttp11ConnectSocketFactory::createTransportS if (inner_socket == nullptr) { return nullptr; } - return std::make_unique(std::move(inner_socket), options); + return std::make_unique(std::move(inner_socket), options, host); } void UpstreamHttp11ConnectSocketFactory::hashKey( diff --git a/source/extensions/transport_sockets/http_11_proxy/connect.h b/source/extensions/transport_sockets/http_11_proxy/connect.h index 2e06915a11f1..d82459182503 100644 --- a/source/extensions/transport_sockets/http_11_proxy/connect.h +++ b/source/extensions/transport_sockets/http_11_proxy/connect.h @@ -28,7 +28,8 @@ class UpstreamHttp11ConnectSocket : public TransportSockets::PassthroughSocket, size_t& bytes_processed); UpstreamHttp11ConnectSocket(Network::TransportSocketPtr&& transport_socket, - Network::TransportSocketOptionsConstSharedPtr options); + Network::TransportSocketOptionsConstSharedPtr options, + std::shared_ptr host); void setTransportSocketCallbacks(Network::TransportSocketCallbacks& callbacks) override; Network::IoResult doWrite(Buffer::Instance& buffer, bool end_stream) override; diff --git a/test/extensions/transport_sockets/http_11_proxy/BUILD b/test/extensions/transport_sockets/http_11_proxy/BUILD index c9814c9adcb4..b0138dfeedee 100644 --- a/test/extensions/transport_sockets/http_11_proxy/BUILD +++ b/test/extensions/transport_sockets/http_11_proxy/BUILD @@ -22,6 +22,7 @@ envoy_extension_cc_test( "//test/mocks/network:network_mocks", "//test/mocks/network:transport_socket_mocks", "//test/test_common:test_runtime_lib", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], ) diff --git a/test/extensions/transport_sockets/http_11_proxy/connect_test.cc b/test/extensions/transport_sockets/http_11_proxy/connect_test.cc index fdfc43b8b35c..816a5db8a8d1 100644 --- a/test/extensions/transport_sockets/http_11_proxy/connect_test.cc +++ b/test/extensions/transport_sockets/http_11_proxy/connect_test.cc @@ -1,3 +1,5 @@ +#include "envoy/config/core/v3/address.pb.h" + #include "source/common/buffer/buffer_impl.h" #include "source/common/network/address_impl.h" #include "source/common/network/filter_state_proxy_info.h" @@ -36,33 +38,11 @@ namespace { class Http11ConnectTest : public testing::TestWithParam { public: Http11ConnectTest() = default; - void initialize(bool no_proxy_protocol = false) { - std::string address_string = - absl::StrCat(Network::Test::getLoopbackAddressUrlString(GetParam()), ":1234"); - Network::Address::InstanceConstSharedPtr address = - Network::Utility::parseInternetAddressAndPortNoThrow(address_string); - auto info = - std::make_unique("www.foo.com", address); - if (no_proxy_protocol) { - info.reset(); - } - options_ = std::make_shared( - "", std::vector{}, std::vector{}, std::vector{}, - absl::nullopt, nullptr, std::move(info)); + void initialize(bool no_proxy_protocol = false) { initializeInternal(no_proxy_protocol, false); } - setAddress(); - auto inner_socket = std::make_unique>(); - inner_socket_ = inner_socket.get(); - EXPECT_CALL(*inner_socket_, ssl()).Times(AnyNumber()).WillRepeatedly(Return(ssl_)); - EXPECT_CALL(Const(*inner_socket_), ssl()).Times(AnyNumber()).WillRepeatedly(Return(ssl_)); - - ON_CALL(transport_callbacks_, ioHandle()).WillByDefault(ReturnRef(io_handle_)); - connect_socket_ = - std::make_unique(std::move(inner_socket), options_); - connect_socket_->setTransportSocketCallbacks(transport_callbacks_); - connect_socket_->onConnected(); - } + // Initialize the test with the proxy address provided via endpoint metadata. + void initializeWithMetadataProxyAddr() { initializeInternal(false, true); } void setAddress() { std::string address_string = @@ -75,6 +55,27 @@ class Http11ConnectTest : public testing::TestWithParamdoWrite(msg, false); + // Only the connect will be written initially. All other writes should be + // buffered in the Network::Connection buffer until the connect has been + // processed. + EXPECT_EQ(connect_data_.length(), rc1.bytes_processed_); + Network::IoResult rc2 = connect_socket_->doWrite(msg, false); + EXPECT_EQ(0, rc2.bytes_processed_); + + EXPECT_CALL(*inner_socket_, onConnected()); + connect_socket_->onConnected(); + } + Network::TransportSocketOptionsConstSharedPtr options_; NiceMock* inner_socket_; NiceMock io_handle_; @@ -83,30 +84,70 @@ class Http11ConnectTest : public testing::TestWithParam>()}; -}; -// Test injects CONNECT only once -TEST_P(Http11ConnectTest, InjectsHeaderOnlyOnce) { - initialize(); +private: + void initializeInternal(bool no_proxy_protocol, bool use_metadata_proxy_addr) { + std::string address_string = + absl::StrCat(Network::Test::getLoopbackAddressUrlString(GetParam()), ":1234"); + Network::Address::InstanceConstSharedPtr address = + Network::Utility::parseInternetAddressAndPortNoThrow(address_string); - EXPECT_CALL(io_handle_, write(BufferStringEqual(connect_data_.toString()))) - .WillOnce(Invoke([&](Buffer::Instance& buffer) { - auto length = buffer.length(); - buffer.drain(length); - return Api::IoCallUint64Result(length, Api::IoError::none()); - })); - Buffer::OwnedImpl msg("initial data"); + const std::string proxy_info_hostname = "www.foo.com"; + auto host = std::make_shared>(); + std::unique_ptr info; + + if (!no_proxy_protocol) { + if (use_metadata_proxy_addr) { + // In the case of endpoint metadata configuring the proxy address, we expect the hostname + // used to be that of the host. + connect_data_ = Buffer::OwnedImpl{ + fmt::format("CONNECT {} HTTP/1.1\r\n\r\n", host->address()->asStringView())}; + + auto metadata = std::make_shared(); + const std::string metadata_key = + Config::MetadataFilters::get().ENVOY_HTTP11_PROXY_TRANSPORT_SOCKET_ADDR; + envoy::config::core::v3::Address addr_proto; + addr_proto.mutable_socket_address()->set_address(proxy_info_hostname); + addr_proto.mutable_socket_address()->set_port_value(1234); + ProtobufWkt::Any anypb; + anypb.PackFrom(addr_proto); + metadata->mutable_typed_filter_metadata()->emplace(std::make_pair(metadata_key, anypb)); + EXPECT_CALL(*host, metadata()).Times(AnyNumber()).WillRepeatedly(Return(metadata)); + } else { + info = std::make_unique( + proxy_info_hostname, address); + setAddress(); + } + } - Network::IoResult rc1 = connect_socket_->doWrite(msg, false); - // Only the connect will be written initially. All other writes should be - // buffered in the Network::Connection buffer until the connect has been - // processed. - EXPECT_EQ(connect_data_.length(), rc1.bytes_processed_); - Network::IoResult rc2 = connect_socket_->doWrite(msg, false); - EXPECT_EQ(0, rc2.bytes_processed_); + options_ = std::make_shared( + "", std::vector{}, std::vector{}, std::vector{}, + absl::nullopt, nullptr, std::move(info)); - EXPECT_CALL(*inner_socket_, onConnected()); - connect_socket_->onConnected(); + auto inner_socket = std::make_unique>(); + inner_socket_ = inner_socket.get(); + EXPECT_CALL(*inner_socket_, ssl()).Times(AnyNumber()).WillRepeatedly(Return(ssl_)); + EXPECT_CALL(Const(*inner_socket_), ssl()).Times(AnyNumber()).WillRepeatedly(Return(ssl_)); + + ON_CALL(transport_callbacks_, ioHandle()).WillByDefault(ReturnRef(io_handle_)); + + connect_socket_ = + std::make_unique(std::move(inner_socket), options_, host); + connect_socket_->setTransportSocketCallbacks(transport_callbacks_); + connect_socket_->onConnected(); + } +}; + +// Test injects CONNECT only once. Configured via transport socket options. +TEST_P(Http11ConnectTest, InjectsHeaderOnlyOnceTransportSocketOpts) { + initialize(); + injectHeaderOnceTest(); +} + +// Test injects CONNECT only once. Configured via endpoint metadata. +TEST_P(Http11ConnectTest, InjectsHeaderOnlyOnceEndpointMetadata) { + initializeWithMetadataProxyAddr(); + injectHeaderOnceTest(); } // Test the socket is a no-op if there's no header proto. diff --git a/test/mocks/upstream/host.h b/test/mocks/upstream/host.h index e67f0a5ea6a5..46460eaf9e8e 100644 --- a/test/mocks/upstream/host.h +++ b/test/mocks/upstream/host.h @@ -89,6 +89,7 @@ class MockHostDescription : public HostDescription { MOCK_METHOD(void, canary, (bool new_canary)); MOCK_METHOD(MetadataConstSharedPtr, metadata, (), (const)); MOCK_METHOD(void, metadata, (MetadataConstSharedPtr)); + MOCK_METHOD(const MetadataConstSharedPtr, localityMetadata, (), (const)); MOCK_METHOD(const ClusterInfo&, cluster, (), (const)); MOCK_METHOD(bool, canCreateConnection, (Upstream::ResourcePriority), (const)); MOCK_METHOD(Outlier::DetectorHostMonitor&, outlierDetector, (), (const)); @@ -165,6 +166,7 @@ class MockHostLight : public Host { MOCK_METHOD(bool, canary, (), (const)); MOCK_METHOD(void, canary, (bool new_canary)); MOCK_METHOD(MetadataConstSharedPtr, metadata, (), (const)); + MOCK_METHOD(const MetadataConstSharedPtr, localityMetadata, (), (const)); MOCK_METHOD(void, metadata, (MetadataConstSharedPtr)); MOCK_METHOD(const ClusterInfo&, cluster, (), (const)); MOCK_METHOD(bool, canCreateConnection, (Upstream::ResourcePriority), (const)); From c8d13707c97b2bcc8959451006b2df7ddab9e26c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 09:11:03 +0100 Subject: [PATCH 076/130] build(deps): bump aiohttp from 3.9.5 to 3.10.2 in /tools/base (#35644) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/base/requirements.txt | 158 ++++++++++++++++++------------------ 1 file changed, 81 insertions(+), 77 deletions(-) diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index d715dd3c0189..9ba7c763badc 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -88,83 +88,87 @@ aiofiles==24.1.0 \ --hash=sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c \ --hash=sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5 # via envoy-github-release -aiohttp==3.9.5 \ - --hash=sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8 \ - --hash=sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c \ - --hash=sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475 \ - --hash=sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed \ - --hash=sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf \ - --hash=sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372 \ - --hash=sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81 \ - --hash=sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f \ - --hash=sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1 \ - --hash=sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd \ - --hash=sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a \ - --hash=sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb \ - --hash=sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46 \ - --hash=sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de \ - --hash=sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78 \ - --hash=sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c \ - --hash=sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771 \ - --hash=sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb \ - --hash=sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430 \ - --hash=sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233 \ - --hash=sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156 \ - --hash=sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9 \ - --hash=sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59 \ - --hash=sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888 \ - --hash=sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c \ - --hash=sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c \ - --hash=sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da \ - --hash=sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424 \ - --hash=sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2 \ - --hash=sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb \ - --hash=sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8 \ - --hash=sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a \ - --hash=sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10 \ - --hash=sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0 \ - --hash=sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09 \ - --hash=sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031 \ - --hash=sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4 \ - --hash=sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3 \ - --hash=sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa \ - --hash=sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a \ - --hash=sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe \ - --hash=sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a \ - --hash=sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2 \ - --hash=sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1 \ - --hash=sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323 \ - --hash=sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b \ - --hash=sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b \ - --hash=sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106 \ - --hash=sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac \ - --hash=sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6 \ - --hash=sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832 \ - --hash=sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75 \ - --hash=sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6 \ - --hash=sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d \ - --hash=sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72 \ - --hash=sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db \ - --hash=sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a \ - --hash=sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da \ - --hash=sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678 \ - --hash=sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b \ - --hash=sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24 \ - --hash=sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed \ - --hash=sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f \ - --hash=sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e \ - --hash=sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58 \ - --hash=sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a \ - --hash=sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342 \ - --hash=sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558 \ - --hash=sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2 \ - --hash=sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551 \ - --hash=sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595 \ - --hash=sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee \ - --hash=sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11 \ - --hash=sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d \ - --hash=sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7 \ - --hash=sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f +aiohappyeyeballs==2.3.5 \ + --hash=sha256:4d6dea59215537dbc746e93e779caea8178c866856a721c9c660d7a5a7b8be03 \ + --hash=sha256:6fa48b9f1317254f122a07a131a86b71ca6946ca989ce6326fff54a99a920105 + # via aiohttp +aiohttp==3.10.2 \ + --hash=sha256:01c98041f90927c2cbd72c22a164bb816fa3010a047d264969cf82e1d4bcf8d1 \ + --hash=sha256:0df930015db36b460aa9badbf35eccbc383f00d52d4b6f3de2ccb57d064a6ade \ + --hash=sha256:1238fc979160bc03a92fff9ad021375ff1c8799c6aacb0d8ea1b357ea40932bb \ + --hash=sha256:14dc3fcb0d877911d775d511eb617a486a8c48afca0a887276e63db04d3ee920 \ + --hash=sha256:14eb6b17f6246959fb0b035d4f4ae52caa870c4edfb6170aad14c0de5bfbf478 \ + --hash=sha256:18186a80ec5a701816adbf1d779926e1069392cf18504528d6e52e14b5920525 \ + --hash=sha256:19073d57d0feb1865d12361e2a1f5a49cb764bf81a4024a3b608ab521568093a \ + --hash=sha256:1aa005f060aff7124cfadaa2493f00a4e28ed41b232add5869e129a2e395935a \ + --hash=sha256:2c474af073e1a6763e1c5522bbb2d85ff8318197e4c6c919b8d7886e16213345 \ + --hash=sha256:30a9d59da1543a6f1478c3436fd49ec59be3868bca561a33778b4391005e499d \ + --hash=sha256:341f8ece0276a828d95b70cd265d20e257f5132b46bf77d759d7f4e0443f2906 \ + --hash=sha256:352f3a4e5f11f3241a49b6a48bc5b935fabc35d1165fa0d87f3ca99c1fcca98b \ + --hash=sha256:377220a5efde6f9497c5b74649b8c261d3cce8a84cb661be2ed8099a2196400a \ + --hash=sha256:3988044d1635c7821dd44f0edfbe47e9875427464e59d548aece447f8c22800a \ + --hash=sha256:465e445ec348d4e4bd349edd8b22db75f025da9d7b6dc1369c48e7935b85581e \ + --hash=sha256:494a6f77560e02bd7d1ab579fdf8192390567fc96a603f21370f6e63690b7f3d \ + --hash=sha256:49904f38667c44c041a0b44c474b3ae36948d16a0398a8f8cd84e2bb3c42a069 \ + --hash=sha256:4d1f694b5d6e459352e5e925a42e05bac66655bfde44d81c59992463d2897014 \ + --hash=sha256:4ddb43d06ce786221c0dfd3c91b4892c318eaa36b903f7c4278e7e2fa0dd5102 \ + --hash=sha256:518dc3cb37365255708283d1c1c54485bbacccd84f0a0fb87ed8917ba45eda5b \ + --hash=sha256:53e8898adda402be03ff164b0878abe2d884e3ea03a4701e6ad55399d84b92dc \ + --hash=sha256:54ba10eb5a3481c28282eb6afb5f709aedf53cf9c3a31875ffbdc9fc719ffd67 \ + --hash=sha256:54e36c67e1a9273ecafab18d6693da0fb5ac48fd48417e4548ac24a918c20998 \ + --hash=sha256:562b1153ab7f766ee6b8b357ec777a302770ad017cf18505d34f1c088fccc448 \ + --hash=sha256:5a7ceb2a0d2280f23a02c64cd0afdc922079bb950400c3dd13a1ab2988428aac \ + --hash=sha256:655e583afc639bef06f3b2446972c1726007a21003cd0ef57116a123e44601bc \ + --hash=sha256:685c1508ec97b2cd3e120bfe309a4ff8e852e8a7460f1ef1de00c2c0ed01e33c \ + --hash=sha256:686c87782481fda5ee6ba572d912a5c26d9f98cc5c243ebd03f95222af3f1b0f \ + --hash=sha256:69d73f869cf29e8a373127fc378014e2b17bcfbe8d89134bc6fb06a2f67f3cb3 \ + --hash=sha256:6fe8503b1b917508cc68bf44dae28823ac05e9f091021e0c41f806ebbb23f92f \ + --hash=sha256:74c091a5ded6cb81785de2d7a8ab703731f26de910dbe0f3934eabef4ae417cc \ + --hash=sha256:7cc8f65f5b22304693de05a245b6736b14cb5bc9c8a03da6e2ae9ef15f8b458f \ + --hash=sha256:7dd9c7db94b4692b827ce51dcee597d61a0e4f4661162424faf65106775b40e7 \ + --hash=sha256:7de3ddb6f424af54535424082a1b5d1ae8caf8256ebd445be68c31c662354720 \ + --hash=sha256:7f98e70bbbf693086efe4b86d381efad8edac040b8ad02821453083d15ec315f \ + --hash=sha256:87fab7f948e407444c2f57088286e00e2ed0003ceaf3d8f8cc0f60544ba61d91 \ + --hash=sha256:8bd7be6ff6c162a60cb8fce65ee879a684fbb63d5466aba3fa5b9288eb04aefa \ + --hash=sha256:8da9449a575133828cc99985536552ea2dcd690e848f9d41b48d8853a149a959 \ + --hash=sha256:91b10208b222ddf655c3a3d5b727879d7163db12b634492df41a9182a76edaae \ + --hash=sha256:92f7f4a4dc9cdb5980973a74d43cdbb16286dacf8d1896b6c3023b8ba8436f8e \ + --hash=sha256:9360e3ffc7b23565600e729e8c639c3c50d5520e05fdf94aa2bd859eef12c407 \ + --hash=sha256:947847f07a8f81d7b39b2d0202fd73e61962ebe17ac2d8566f260679e467da7b \ + --hash=sha256:95213b3d79c7e387144e9cb7b9d2809092d6ff2c044cb59033aedc612f38fb6d \ + --hash=sha256:96e010736fc16d21125c7e2dc5c350cd43c528b85085c04bf73a77be328fe944 \ + --hash=sha256:99f81f9c1529fd8e03be4a7bd7df32d14b4f856e90ef6e9cbad3415dbfa9166c \ + --hash=sha256:9bb2834a6f11d65374ce97d366d6311a9155ef92c4f0cee543b2155d06dc921f \ + --hash=sha256:9dfc906d656e14004c5bc672399c1cccc10db38df2b62a13fb2b6e165a81c316 \ + --hash=sha256:9f6f0b252a009e98fe84028a4ec48396a948e7a65b8be06ccfc6ef68cf1f614d \ + --hash=sha256:9fd16b5e1a7bdd14668cd6bde60a2a29b49147a535c74f50d8177d11b38433a7 \ + --hash=sha256:a0fde16d284efcacbe15fb0c1013f0967b6c3e379649239d783868230bf1db42 \ + --hash=sha256:a1a50e59b720060c29e2951fd9f13c01e1ea9492e5a527b92cfe04dd64453c16 \ + --hash=sha256:a4be88807283bd96ae7b8e401abde4ca0bab597ba73b5e9a2d98f36d451e9aac \ + --hash=sha256:ad2274e707be37420d0b6c3d26a8115295fe9d8e6e530fa6a42487a8ca3ad052 \ + --hash=sha256:b2bfdda4971bd79201f59adbad24ec2728875237e1c83bba5221284dbbf57bda \ + --hash=sha256:b52a27a5c97275e254704e1049f4b96a81e67d6205f52fa37a4777d55b0e98ef \ + --hash=sha256:c01fbb87b5426381cd9418b3ddcf4fc107e296fa2d3446c18ce6c76642f340a3 \ + --hash=sha256:c836bf3c7512100219fe1123743fd8dd9a2b50dd7cfb0c3bb10d041309acab4b \ + --hash=sha256:c8e98e1845805f184d91fda6f9ab93d7c7b0dddf1c07e0255924bfdb151a8d05 \ + --hash=sha256:ca2f5abcb0a9a47e56bac173c01e9f6c6e7f27534d91451c5f22e6a35a5a2093 \ + --hash=sha256:cd33d9de8cfd006a0d0fe85f49b4183c57e91d18ffb7e9004ce855e81928f704 \ + --hash=sha256:d611d1a01c25277bcdea06879afbc11472e33ce842322496b211319aa95441bb \ + --hash=sha256:d9076810a5621236e29b2204e67a68e1fe317c8727ee4c9abbfbb1083b442c38 \ + --hash=sha256:d984db6d855de58e0fde1ef908d48fe9a634cadb3cf715962722b4da1c40619d \ + --hash=sha256:dafb4abb257c0ed56dc36f4e928a7341b34b1379bd87e5a15ce5d883c2c90574 \ + --hash=sha256:ddfd2dca3f11c365d6857a07e7d12985afc59798458a2fdb2ffa4a0332a3fd43 \ + --hash=sha256:df59f8486507c421c0620a2c3dce81fbf1d54018dc20ff4fecdb2c106d6e6abc \ + --hash=sha256:e00191d38156e09e8c81ef3d75c0d70d4f209b8381e71622165f22ef7da6f101 \ + --hash=sha256:e2f43d238eae4f0b04f58d4c0df4615697d4ca3e9f9b1963d49555a94f0f5a04 \ + --hash=sha256:e57e21e1167705f8482ca29cc5d02702208d8bf4aff58f766d94bcd6ead838cd \ + --hash=sha256:e8f515d6859e673940e08de3922b9c4a2249653b0ac181169313bd6e4b1978ac \ + --hash=sha256:eabe6bf4c199687592f5de4ccd383945f485779c7ffb62a9b9f1f8a3f9756df8 \ + --hash=sha256:ec6ad66ed660d46503243cbec7b2b3d8ddfa020f984209b3b8ef7d98ce69c3f2 \ + --hash=sha256:f81cd85a0e76ec7b8e2b6636fe02952d35befda4196b8c88f3cec5b4fb512839 \ + --hash=sha256:f9f49bdb94809ac56e09a310a62f33e5f22973d6fd351aac72a39cd551e98194 \ + --hash=sha256:fae962b62944eaebff4f4fddcf1a69de919e7b967136a318533d82d93c3c6bd1 \ + --hash=sha256:fc61f39b534c5d5903490478a0dd349df397d2284a939aa3cbaa2fb7a19b8397 # via # -r requirements.in # aio-api-github From 422e51142a74f1acab8f9d52bed3a612b9e25ace Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 09:11:13 +0100 Subject: [PATCH 077/130] build(deps): bump orjson from 3.10.6 to 3.10.7 in /tools/base (#35645) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/base/requirements.txt | 110 +++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 52 deletions(-) diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index 9ba7c763badc..c0d360fa9d91 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -954,58 +954,64 @@ oauth2client==4.1.3 \ # via # gcs-oauth2-boto-plugin # google-apitools -orjson==3.10.6 \ - --hash=sha256:03c95484d53ed8e479cade8628c9cea00fd9d67f5554764a1110e0d5aa2de96e \ - --hash=sha256:05ac3d3916023745aa3b3b388e91b9166be1ca02b7c7e41045da6d12985685f0 \ - --hash=sha256:0943e4c701196b23c240b3d10ed8ecd674f03089198cf503105b474a4f77f21f \ - --hash=sha256:1335d4ef59ab85cab66fe73fd7a4e881c298ee7f63ede918b7faa1b27cbe5212 \ - --hash=sha256:1c680b269d33ec444afe2bdc647c9eb73166fa47a16d9a75ee56a374f4a45f43 \ - --hash=sha256:227df19441372610b20e05bdb906e1742ec2ad7a66ac8350dcfd29a63014a83b \ - --hash=sha256:30b0a09a2014e621b1adf66a4f705f0809358350a757508ee80209b2d8dae219 \ - --hash=sha256:3722fddb821b6036fd2a3c814f6bd9b57a89dc6337b9924ecd614ebce3271394 \ - --hash=sha256:446dee5a491b5bc7d8f825d80d9637e7af43f86a331207b9c9610e2f93fee22a \ - --hash=sha256:450e39ab1f7694465060a0550b3f6d328d20297bf2e06aa947b97c21e5241fbd \ - --hash=sha256:49e3bc615652617d463069f91b867a4458114c5b104e13b7ae6872e5f79d0844 \ - --hash=sha256:4bbc6d0af24c1575edc79994c20e1b29e6fb3c6a570371306db0993ecf144dc5 \ - --hash=sha256:5410111d7b6681d4b0d65e0f58a13be588d01b473822483f77f513c7f93bd3b2 \ - --hash=sha256:55d43d3feb8f19d07e9f01e5b9be4f28801cf7c60d0fa0d279951b18fae1932b \ - --hash=sha256:57985ee7e91d6214c837936dc1608f40f330a6b88bb13f5a57ce5257807da143 \ - --hash=sha256:61272a5aec2b2661f4fa2b37c907ce9701e821b2c1285d5c3ab0207ebd358d38 \ - --hash=sha256:633a3b31d9d7c9f02d49c4ab4d0a86065c4a6f6adc297d63d272e043472acab5 \ - --hash=sha256:64c81456d2a050d380786413786b057983892db105516639cb5d3ee3c7fd5148 \ - --hash=sha256:66680eae4c4e7fc193d91cfc1353ad6d01b4801ae9b5314f17e11ba55e934183 \ - --hash=sha256:697a35a083c4f834807a6232b3e62c8b280f7a44ad0b759fd4dce748951e70db \ - --hash=sha256:6eeb13218c8cf34c61912e9df2de2853f1d009de0e46ea09ccdf3d757896af0a \ - --hash=sha256:7275664f84e027dcb1ad5200b8b18373e9c669b2a9ec33d410c40f5ccf4b257e \ - --hash=sha256:738dbe3ef909c4b019d69afc19caf6b5ed0e2f1c786b5d6215fbb7539246e4c6 \ - --hash=sha256:79b9b9e33bd4c517445a62b90ca0cc279b0f1f3970655c3df9e608bc3f91741a \ - --hash=sha256:874ce88264b7e655dde4aeaacdc8fd772a7962faadfb41abe63e2a4861abc3dc \ - --hash=sha256:95a0cce17f969fb5391762e5719575217bd10ac5a189d1979442ee54456393f3 \ - --hash=sha256:960db0e31c4e52fa0fc3ecbaea5b2d3b58f379e32a95ae6b0ebeaa25b93dfd34 \ - --hash=sha256:965a916373382674e323c957d560b953d81d7a8603fbeee26f7b8248638bd48b \ - --hash=sha256:9c1c4b53b24a4c06547ce43e5fee6ec4e0d8fe2d597f4647fc033fd205707365 \ - --hash=sha256:a2debd8ddce948a8c0938c8c93ade191d2f4ba4649a54302a7da905a81f00b56 \ - --hash=sha256:a6ea7afb5b30b2317e0bee03c8d34c8181bc5a36f2afd4d0952f378972c4efd5 \ - --hash=sha256:ac3045267e98fe749408eee1593a142e02357c5c99be0802185ef2170086a863 \ - --hash=sha256:b1ec490e10d2a77c345def52599311849fc063ae0e67cf4f84528073152bb2ba \ - --hash=sha256:b6f3d167d13a16ed263b52dbfedff52c962bfd3d270b46b7518365bcc2121eed \ - --hash=sha256:bb1f28a137337fdc18384079fa5726810681055b32b92253fa15ae5656e1dddb \ - --hash=sha256:bf2fbbce5fe7cd1aa177ea3eab2b8e6a6bc6e8592e4279ed3db2d62e57c0e1b2 \ - --hash=sha256:c27bc6a28ae95923350ab382c57113abd38f3928af3c80be6f2ba7eb8d8db0b0 \ - --hash=sha256:c2c116072a8533f2fec435fde4d134610f806bdac20188c7bd2081f3e9e0133f \ - --hash=sha256:caff75b425db5ef8e8f23af93c80f072f97b4fb3afd4af44482905c9f588da28 \ - --hash=sha256:d27456491ca79532d11e507cadca37fb8c9324a3976294f68fb1eff2dc6ced5a \ - --hash=sha256:d40f839dddf6a7d77114fe6b8a70218556408c71d4d6e29413bb5f150a692ff7 \ - --hash=sha256:df25d9271270ba2133cc88ee83c318372bdc0f2cd6f32e7a450809a111efc45c \ - --hash=sha256:e060748a04cccf1e0a6f2358dffea9c080b849a4a68c28b1b907f272b5127e9b \ - --hash=sha256:e54b63d0a7c6c54a5f5f726bc93a2078111ef060fec4ecbf34c5db800ca3b3a7 \ - --hash=sha256:ea2977b21f8d5d9b758bb3f344a75e55ca78e3ff85595d248eee813ae23ecdfb \ - --hash=sha256:eadc8fd310edb4bdbd333374f2c8fec6794bbbae99b592f448d8214a5e4050c0 \ - --hash=sha256:f215789fb1667cdc874c1b8af6a84dc939fd802bf293a8334fce185c79cd359b \ - --hash=sha256:f710f346e4c44a4e8bdf23daa974faede58f83334289df80bc9cd12fe82573c7 \ - --hash=sha256:f759503a97a6ace19e55461395ab0d618b5a117e8d0fbb20e70cfd68a47327f2 \ - --hash=sha256:fb0ee33124db6eaa517d00890fc1a55c3bfe1cf78ba4a8899d71a06f2d6ff5c7 \ - --hash=sha256:fd502f96bf5ea9a61cbc0b2b5900d0dd68aa0da197179042bdd2be67e51a1e4b +orjson==3.10.7 \ + --hash=sha256:084e537806b458911137f76097e53ce7bf5806dda33ddf6aaa66a028f8d43a23 \ + --hash=sha256:09b2d92fd95ad2402188cf51573acde57eb269eddabaa60f69ea0d733e789fe9 \ + --hash=sha256:0fa5886854673222618638c6df7718ea7fe2f3f2384c452c9ccedc70b4a510a5 \ + --hash=sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad \ + --hash=sha256:1193b2416cbad1a769f868b1749535d5da47626ac29445803dae7cc64b3f5c98 \ + --hash=sha256:144888c76f8520e39bfa121b31fd637e18d4cc2f115727865fdf9fa325b10412 \ + --hash=sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1 \ + --hash=sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864 \ + --hash=sha256:33cfb96c24034a878d83d1a9415799a73dc77480e6c40417e5dda0710d559ee6 \ + --hash=sha256:348bdd16b32556cf8d7257b17cf2bdb7ab7976af4af41ebe79f9796c218f7e91 \ + --hash=sha256:34a566f22c28222b08875b18b0dfbf8a947e69df21a9ed5c51a6bf91cfb944ac \ + --hash=sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c \ + --hash=sha256:430ee4d85841e1483d487e7b81401785a5dfd69db5de01314538f31f8fbf7ee1 \ + --hash=sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f \ + --hash=sha256:479fd0844ddc3ca77e0fd99644c7fe2de8e8be1efcd57705b5c92e5186e8a250 \ + --hash=sha256:480f455222cb7a1dea35c57a67578848537d2602b46c464472c995297117fa09 \ + --hash=sha256:4829cf2195838e3f93b70fd3b4292156fc5e097aac3739859ac0dcc722b27ac0 \ + --hash=sha256:4b6146e439af4c2472c56f8540d799a67a81226e11992008cb47e1267a9b3225 \ + --hash=sha256:4e6c3da13e5a57e4b3dca2de059f243ebec705857522f188f0180ae88badd354 \ + --hash=sha256:5b24a579123fa884f3a3caadaed7b75eb5715ee2b17ab5c66ac97d29b18fe57f \ + --hash=sha256:6b0dd04483499d1de9c8f6203f8975caf17a6000b9c0c54630cef02e44ee624e \ + --hash=sha256:6ea2b2258eff652c82652d5e0f02bd5e0463a6a52abb78e49ac288827aaa1469 \ + --hash=sha256:7122a99831f9e7fe977dc45784d3b2edc821c172d545e6420c375e5a935f5a1c \ + --hash=sha256:74f4544f5a6405b90da8ea724d15ac9c36da4d72a738c64685003337401f5c12 \ + --hash=sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3 \ + --hash=sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3 \ + --hash=sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149 \ + --hash=sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb \ + --hash=sha256:7db8539039698ddfb9a524b4dd19508256107568cdad24f3682d5773e60504a2 \ + --hash=sha256:8272527d08450ab16eb405f47e0f4ef0e5ff5981c3d82afe0efd25dcbef2bcd2 \ + --hash=sha256:82763b46053727a7168d29c772ed5c870fdae2f61aa8a25994c7984a19b1021f \ + --hash=sha256:8a9c9b168b3a19e37fe2778c0003359f07822c90fdff8f98d9d2a91b3144d8e0 \ + --hash=sha256:8de062de550f63185e4c1c54151bdddfc5625e37daf0aa1e75d2a1293e3b7d9a \ + --hash=sha256:974683d4618c0c7dbf4f69c95a979734bf183d0658611760017f6e70a145af58 \ + --hash=sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe \ + --hash=sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09 \ + --hash=sha256:a763bc0e58504cc803739e7df040685816145a6f3c8a589787084b54ebc9f16e \ + --hash=sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2 \ + --hash=sha256:ac7cf6222b29fbda9e3a472b41e6a5538b48f2c8f99261eecd60aafbdb60690c \ + --hash=sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313 \ + --hash=sha256:b58d3795dafa334fc8fd46f7c5dc013e6ad06fd5b9a4cc98cb1456e7d3558bd6 \ + --hash=sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93 \ + --hash=sha256:bf6ba8ebc8ef5792e2337fb0419f8009729335bb400ece005606336b7fd7bab7 \ + --hash=sha256:c31008598424dfbe52ce8c5b47e0752dca918a4fdc4a2a32004efd9fab41d866 \ + --hash=sha256:cb61938aec8b0ffb6eef484d480188a1777e67b05d58e41b435c74b9d84e0b9c \ + --hash=sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b \ + --hash=sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5 \ + --hash=sha256:d374d36726746c81a49f3ff8daa2898dccab6596864ebe43d50733275c629175 \ + --hash=sha256:de817e2f5fc75a9e7dd350c4b0f54617b280e26d1631811a43e7e968fa71e3e9 \ + --hash=sha256:e724cebe1fadc2b23c6f7415bad5ee6239e00a69f30ee423f319c6af70e2a5c0 \ + --hash=sha256:e72591bcfe7512353bd609875ab38050efe3d55e18934e2f18950c108334b4ff \ + --hash=sha256:e76be12658a6fa376fcd331b1ea4e58f5a06fd0220653450f0d415b8fd0fbe20 \ + --hash=sha256:eb8d384a24778abf29afb8e41d68fdd9a156cf6e5390c04cc07bbc24b89e98b5 \ + --hash=sha256:ed350d6978d28b92939bfeb1a0570c523f6170efc3f0a0ef1f1df287cd4f4960 \ + --hash=sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024 \ + --hash=sha256:f4db56635b58cd1a200b0a23744ff44206ee6aa428185e2b6c4a65b3197abdcd \ + --hash=sha256:fdf5197a21dd660cf19dfd2a3ce79574588f8f5e2dbf21bda9ee2d2b46924d84 # via # -r requirements.in # envoy-base-utils From a298895dd66a8df6ff834c5d646c38e0fc21e105 Mon Sep 17 00:00:00 2001 From: Nigel Brittain <108375408+nbaws@users.noreply.github.com> Date: Fri, 9 Aug 2024 23:42:45 +1000 Subject: [PATCH 078/130] aws: fix test flake when no IMDS can be found (#35633) Commit Message: aws: fix test flake when no IMDS can be found Additional Description: Lack of IMDS (169.254.169.254 address) can cause a race condition and crash during testing due to the shutdown of cluster manager. This scenario should not occur normally, as cluster manager will still exist and the lack of IMDS is handled gracefully. Signed-off-by: Nigel Brittain --- .../extensions/common/aws/metadata_fetcher.cc | 6 +++++ .../common/aws/metadata_fetcher_test.cc | 26 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/source/extensions/common/aws/metadata_fetcher.cc b/source/extensions/common/aws/metadata_fetcher.cc index f5dbe85eea55..aeaac64661d2 100644 --- a/source/extensions/common/aws/metadata_fetcher.cc +++ b/source/extensions/common/aws/metadata_fetcher.cc @@ -52,6 +52,12 @@ class MetadataFetcherImpl : public MetadataFetcher, ASSERT(!request_); complete_ = false; receiver_ = makeOptRef(receiver); + + // Stop processing if we are shutting down + if (cm_.isShutdown()) { + return; + } + const auto thread_local_cluster = cm_.getThreadLocalCluster(cluster_name_); if (thread_local_cluster == nullptr) { ENVOY_LOG(error, "{} AWS Metadata failed: [cluster = {}] not found", __func__, cluster_name_); diff --git a/test/extensions/common/aws/metadata_fetcher_test.cc b/test/extensions/common/aws/metadata_fetcher_test.cc index da4702211d00..32356aa883e1 100644 --- a/test/extensions/common/aws/metadata_fetcher_test.cc +++ b/test/extensions/common/aws/metadata_fetcher_test.cc @@ -77,6 +77,20 @@ MATCHER_P(OptionsHasRetryPolicy, policyMatcher, "") { class MetadataFetcherTest : public testing::Test { public: void setupFetcher() { + + mock_factory_ctx_.server_factory_context_.cluster_manager_.initializeThreadLocalClusters( + {"cluster_name"}); + fetcher_ = MetadataFetcher::create(mock_factory_ctx_.server_factory_context_.cluster_manager_, + "cluster_name"); + EXPECT_TRUE(fetcher_ != nullptr); + } + + void setupFetcherShutDown() { + ON_CALL(mock_factory_ctx_.server_factory_context_.cluster_manager_, getThreadLocalCluster(_)) + .WillByDefault(Return(nullptr)); + ON_CALL(mock_factory_ctx_.server_factory_context_.cluster_manager_, isShutdown()) + .WillByDefault(Return(true)); + mock_factory_ctx_.server_factory_context_.cluster_manager_.initializeThreadLocalClusters( {"cluster_name"}); fetcher_ = MetadataFetcher::create(mock_factory_ctx_.server_factory_context_.cluster_manager_, @@ -103,6 +117,18 @@ TEST_F(MetadataFetcherTest, TestGetSuccess) { fetcher_->fetch(message, parent_span_, receiver); } +TEST_F(MetadataFetcherTest, TestClusterShutdown) { + // Setup + setupFetcherShutDown(); + Http::RequestMessageImpl message; + MockMetadataReceiver receiver; + EXPECT_CALL(receiver, onMetadataSuccess(_)).Times(0); + EXPECT_CALL(receiver, onMetadataError(_)).Times(0); + + // Act + fetcher_->fetch(message, parent_span_, receiver); +} + TEST_F(MetadataFetcherTest, TestRequestMatchAndSpanPassedDown) { // Setup setupFetcher(); From 9516f7ca43466ae8fce4e0c13923e54333a79aa6 Mon Sep 17 00:00:00 2001 From: yanavlasov Date: Fri, 9 Aug 2024 15:47:14 -0400 Subject: [PATCH 079/130] Revert "external authz: Interpret error grpc codes of the external authz server as failure to fix failure_mode_allowed feature (#34951)" (#35637) Fixes #35610 Fixes commit #34951 Signed-off-by: Yan Avlasov --- .../filter/http/ext_authz/v2/ext_authz.proto | 5 +- .../filters/http/ext_authz/v3/ext_authz.proto | 5 +- changelogs/current.yaml | 6 -- source/common/runtime/runtime_features.cc | 2 - .../common/ext_authz/ext_authz_grpc_impl.cc | 12 +-- .../extensions/filters/common/ext_authz/BUILD | 1 - .../ext_authz/ext_authz_grpc_impl_test.cc | 91 +++++-------------- 7 files changed, 29 insertions(+), 93 deletions(-) diff --git a/api/envoy/config/filter/http/ext_authz/v2/ext_authz.proto b/api/envoy/config/filter/http/ext_authz/v2/ext_authz.proto index c55938fc3cbe..c79dd4a24bc9 100644 --- a/api/envoy/config/filter/http/ext_authz/v2/ext_authz.proto +++ b/api/envoy/config/filter/http/ext_authz/v2/ext_authz.proto @@ -40,12 +40,11 @@ message ExtAuthz { // // 1. When set to true, the filter will *accept* client request even if the communication with // the authorization service has failed, or if the authorization service has returned a HTTP 5xx - // error. In case with GRPC authorization service, only PermissionDenied (7) and Unauthenticated (16) - // status codes will *reject* client requests. And other GRPC statuses will *accept* client requests. + // error. // // 2. When set to false, ext-authz will *reject* client requests and return a *Forbidden* // response if the communication with the authorization service has failed, or if the - // authorization service has returned a HTTP 5xx error or any non-Ok GRPC status. + // authorization service has returned a HTTP 5xx error. // // Note that errors can be *always* tracked in the :ref:`stats // `. diff --git a/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto b/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto index 357dd5fc645a..3b6a67cc9074 100644 --- a/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto +++ b/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto @@ -57,12 +57,11 @@ message ExtAuthz { // // 1. When set to true, the filter will ``accept`` client request even if the communication with // the authorization service has failed, or if the authorization service has returned a HTTP 5xx - // error. In case with GRPC authorization service, only PermissionDenied (7) and Unauthenticated (16) - // status codes will ``reject`` client requests. And other GRPC statuses will ``accept`` client requests. + // error. // // 2. When set to false, ext-authz will ``reject`` client requests and return a ``Forbidden`` // response if the communication with the authorization service has failed, or if the - // authorization service has returned a HTTP 5xx error or any non-Ok GRPC status. + // authorization service has returned a HTTP 5xx error. // // Note that errors can be ``always`` tracked in the :ref:`stats // `. diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 0a283171254a..8f12340b7aa0 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -54,12 +54,6 @@ bug_fixes: - area: c-ares change: | Applying a C-ares patch to fix DNS resoultion by the Google gRPC library. -- area: ext_authz - change: | - Fixed fail-open behavior of the :ref:`failure_mode_allow config option - ` - when a grpc external authz server is used. - The behavior can be enabled by ``envoy_reloadable_features_process_ext_authz_grpc_error_codes_as_errors``. - area: websocket change: | Fixed a bug where the websocket upgrade filter would not take into account per-filter configs. diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 60669e8d41d4..bd21d36a8b6c 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -69,8 +69,6 @@ RUNTIME_GUARD(envoy_reloadable_features_no_extension_lookup_by_name); RUNTIME_GUARD(envoy_reloadable_features_no_timer_based_rate_limit_token_bucket); RUNTIME_GUARD(envoy_reloadable_features_original_dst_rely_on_idle_timeout); RUNTIME_GUARD(envoy_reloadable_features_prefer_ipv6_dns_on_macos); -// Fixes fail-open behaviour of failure_mode_allow for external authz grpc servers. -RUNTIME_GUARD(envoy_reloadable_features_process_ext_authz_grpc_error_codes_as_errors); RUNTIME_GUARD(envoy_reloadable_features_proxy_104); RUNTIME_GUARD(envoy_reloadable_features_proxy_status_mapping_more_core_response_flags); RUNTIME_GUARD(envoy_reloadable_features_quic_receive_ecn); diff --git a/source/extensions/filters/common/ext_authz/ext_authz_grpc_impl.cc b/source/extensions/filters/common/ext_authz/ext_authz_grpc_impl.cc index 22b72b803224..dab5d014d109 100644 --- a/source/extensions/filters/common/ext_authz/ext_authz_grpc_impl.cc +++ b/source/extensions/filters/common/ext_authz/ext_authz_grpc_impl.cc @@ -114,11 +114,7 @@ void GrpcClientImpl::onSuccess(std::unique_ptrok_response(); copyOkResponseMutations(authz_response, ok_response); } - } else if (response->status().code() == Grpc::Status::WellKnownGrpcStatus::PermissionDenied || - response->status().code() == Grpc::Status::WellKnownGrpcStatus::Unauthenticated || - !Runtime::runtimeFeatureEnabled( - "envoy.reloadable_features.process_ext_authz_grpc_error_codes_as_errors")) { - // The request was explicitly forbidden by the external authz server. + } else { span.setTag(TracingConstants::get().TraceStatus, TracingConstants::get().TraceUnauthz); authz_response->status = CheckStatus::Denied; @@ -133,12 +129,6 @@ void GrpcClientImpl::onSuccess(std::unique_ptrbody = response->denied_response().body(); } - } else { - // Unexpected response from external authz server is interpreted as failure - ENVOY_LOG(trace, "CheckRequest call failed with status: {}", - Grpc::Utility::grpcStatusToString(response->status().code())); - authz_response->status = CheckStatus::Error; - authz_response->status_code = Http::Code::Forbidden; } // OkHttpResponse.dynamic_metadata is deprecated. Until OkHttpResponse.dynamic_metadata is diff --git a/test/extensions/filters/common/ext_authz/BUILD b/test/extensions/filters/common/ext_authz/BUILD index 02e8da4a7e29..a9c84c57d34a 100644 --- a/test/extensions/filters/common/ext_authz/BUILD +++ b/test/extensions/filters/common/ext_authz/BUILD @@ -36,7 +36,6 @@ envoy_cc_test( "//source/extensions/filters/common/ext_authz:ext_authz_grpc_lib", "//test/extensions/filters/common/ext_authz:ext_authz_test_common", "//test/mocks/tracing:tracing_mocks", - "//test/test_common:test_runtime_lib", "@envoy_api//envoy/service/auth/v3:pkg_cc_proto", "@envoy_api//envoy/type/v3:pkg_cc_proto", ], diff --git a/test/extensions/filters/common/ext_authz/ext_authz_grpc_impl_test.cc b/test/extensions/filters/common/ext_authz/ext_authz_grpc_impl_test.cc index cec883ca7506..a7832ddeda2a 100644 --- a/test/extensions/filters/common/ext_authz/ext_authz_grpc_impl_test.cc +++ b/test/extensions/filters/common/ext_authz/ext_authz_grpc_impl_test.cc @@ -11,7 +11,6 @@ #include "test/mocks/grpc/mocks.h" #include "test/mocks/stream_info/mocks.h" #include "test/mocks/tracing/mocks.h" -#include "test/test_common/test_runtime.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -180,6 +179,30 @@ TEST_F(ExtAuthzGrpcClientTest, AuthorizationDenied) { client_->onSuccess(std::move(check_response), span_); } +// Test the client when a gRPC status code unknown is received from the authorization server. +TEST_F(ExtAuthzGrpcClientTest, AuthorizationDeniedGrpcUnknownStatus) { + initialize(); + + auto check_response = std::make_unique(); + auto status = check_response->mutable_status(); + status->set_code(Grpc::Status::WellKnownGrpcStatus::Unknown); + auto authz_response = Response{}; + authz_response.status = CheckStatus::Denied; + + envoy::service::auth::v3::CheckRequest request; + expectCallSend(request); + client_->check(request_callbacks_, request, Tracing::NullSpan::instance(), stream_info_); + + Http::TestRequestHeaderMapImpl headers; + client_->onCreateInitialMetadata(headers); + EXPECT_EQ(nullptr, headers.RequestId()); + EXPECT_CALL(span_, setTag(Eq("ext_authz_status"), Eq("ext_authz_unauthorized"))); + EXPECT_CALL(request_callbacks_, onComplete_(WhenDynamicCastTo( + AuthzResponseNoAttributes(authz_response)))); + + client_->onSuccess(std::move(check_response), span_); +} + // Test the client when a denied response with additional HTTP attributes is received. TEST_F(ExtAuthzGrpcClientTest, AuthorizationDeniedWithAllAttributes) { initialize(); @@ -414,72 +437,6 @@ TEST_F(ExtAuthzGrpcClientTest, AuthorizationOkWithAppendActions) { span_); } -// Test the client when an error code response is received. -TEST_F(ExtAuthzGrpcClientTest, AuthorizationReturnsErrorOnBadGrpcCode) { - initialize(); - - auto check_response = std::make_unique(); - auto status = check_response->mutable_status(); - - ProtobufWkt::Struct expected_dynamic_metadata; - auto* metadata_fields = expected_dynamic_metadata.mutable_fields(); - (*metadata_fields)["foo"] = ValueUtil::stringValue("ok"); - (*metadata_fields)["bar"] = ValueUtil::numberValue(1); - - // The expected dynamic metadata is set to the outer check response, hence regardless the - // check_response's http_response value (either OkHttpResponse or DeniedHttpResponse) the dynamic - // metadata is set to be equal to the check response's dynamic metadata. - check_response->mutable_dynamic_metadata()->MergeFrom(expected_dynamic_metadata); - - status->set_code(Grpc::Status::WellKnownGrpcStatus::Unavailable); - - // This is the expected authz response. - auto authz_response = Response{}; - authz_response.status = CheckStatus::Error; - - authz_response.dynamic_metadata = expected_dynamic_metadata; - - envoy::service::auth::v3::CheckRequest request; - expectCallSend(request); - client_->check(request_callbacks_, request, Tracing::NullSpan::instance(), stream_info_); - - Http::TestRequestHeaderMapImpl headers; - client_->onCreateInitialMetadata(headers); - - EXPECT_CALL(request_callbacks_, onComplete_(WhenDynamicCastTo( - AuthzResponseNoAttributes(authz_response)))); - client_->onSuccess(std::move(check_response), span_); -} - -// Test the client when an error code response is received and -// process_ext_authz_grpc_error_codes_as_errors is false. -TEST_F(ExtAuthzGrpcClientTest, AuthorizationReturnsDeniedOnUnknownGrpcCodeWhenFeatureFlagDisabled) { - TestScopedRuntime scoped_runtime; - scoped_runtime.mergeValues( - {{"envoy.reloadable_features.process_ext_authz_grpc_error_codes_as_errors", "false"}}); - - initialize(); - - auto check_response = std::make_unique(); - auto status = check_response->mutable_status(); - status->set_code(Grpc::Status::WellKnownGrpcStatus::Unknown); - auto authz_response = Response{}; - authz_response.status = CheckStatus::Denied; - - envoy::service::auth::v3::CheckRequest request; - expectCallSend(request); - client_->check(request_callbacks_, request, Tracing::NullSpan::instance(), stream_info_); - - Http::TestRequestHeaderMapImpl headers; - client_->onCreateInitialMetadata(headers); - EXPECT_EQ(nullptr, headers.RequestId()); - EXPECT_CALL(span_, setTag(Eq("ext_authz_status"), Eq("ext_authz_unauthorized"))); - EXPECT_CALL(request_callbacks_, onComplete_(WhenDynamicCastTo( - AuthzResponseNoAttributes(authz_response)))); - - client_->onSuccess(std::move(check_response), span_); -} - } // namespace ExtAuthz } // namespace Common } // namespace Filters From 62b628b6f76e9ac3bc2368f7d0cb14d43c208caf Mon Sep 17 00:00:00 2001 From: "Adi (Suissa) Peleg" Date: Fri, 9 Aug 2024 16:32:30 -0400 Subject: [PATCH 080/130] xDS-failover: attempt reconnection to primary after disconnected from failover (#35591) Prior to this PR, Envoy would stick to either the primary or failover xDS source, once it got a response from one. This PR changes the behavior by allowing Envoy to attempt reconnecting to the primary source, after being disconnected from the failover source. Risk Level: low - xDS-failover is disabled by default. Testing: Added unit and integration tests. Docs Changes: N/A - docs will be created in a subsequent PR. Release Notes: N/A - no release note yet as this is WIP. Platform Specific Features: N/A Signed-off-by: Adi Suissa-Peleg --- .../grpc/grpc_mux_failover.h | 223 ++++++------- .../grpc/grpc_mux_failover_test.cc | 69 +++- .../grpc/xds_failover_integration_test.cc | 307 +++++++++++++++--- 3 files changed, 423 insertions(+), 176 deletions(-) diff --git a/source/extensions/config_subscription/grpc/grpc_mux_failover.h b/source/extensions/config_subscription/grpc/grpc_mux_failover.h index 00876af5c555..745e1be9409f 100644 --- a/source/extensions/config_subscription/grpc/grpc_mux_failover.h +++ b/source/extensions/config_subscription/grpc/grpc_mux_failover.h @@ -30,19 +30,25 @@ namespace Config { * will be followed by the GrpcMuxFailover calling the GrpcStreamCallbacks on the GrpcMux * object that initialized it. * - * Note: this class is WIP and should be considered alpha! - * At the moment, the supported policy is as follows: + * To simplify the state-machine, Envoy can be in one of the mutually exclusive states: + * ConnectingToPrimary - attempting to connect to the primary source. + * ConnectedToPrimary - after receiving a response from the primary source. + * ConnectingToFailover - attempting to connect to the failover source. + * ConnectedToFailover - after receiving a response from the failover source. + * None - not attempting to connect or connected to any source (e.g., upon initialization). + * * The GrpcMuxFailover attempts to establish a connection to the primary source. Once a response is * received from the primary source it will be considered available, and the failover will not be * used. Any future reconnection attempts will be to the primary source only. * However, if no response is received from the primary source, and accessing the primary has * failed 2 times in a row, the GrpcMuxFailover will attempt to establish a connection to the - * failover source. If a response from the failover source is received, only the failover source - * will be used. - * If the failover source is unavailable, the GrpcMuxFailover will alternate between attempts - * to reconnect to the primary source and the failover source. - * In the future, this behavior may change, and the GrpcMuxFailover will always - * prefer the primary source, even if prior connection to the failover was successful. + * failover source. Envoy will keep alternating between the primary and failover sources attempting + * to connect to one of them. If a response from the failover source is received, it will be the + * source of configuration until the connection is closed. In case the failover connection is + * closed, Envoy will attempt to connect to the primary, before retrying to connect to the failover + * source. If the failover source is unavailable or a connection to it is closed, the + * GrpcMuxFailover will alternate between attempts to reconnect to the primary source and the + * failover source. * TODO(adisuissa): The number of consecutive failures is currently statically * defined, and may be converted to a config field in the future. */ @@ -64,8 +70,7 @@ class GrpcMuxFailover : public GrpcStreamInterface, Event::Dispatcher& dispatcher) : grpc_mux_callbacks_(grpc_mux_callbacks), primary_callbacks_(*this), primary_grpc_stream_(std::move(primary_stream_creator(&primary_callbacks_))), - connecting_to_primary_(false), connecting_to_failover_(false), - connected_to_(ConnectedState::None), ever_connected_to_(ConnectedState::None) { + connection_state_(ConnectionState::None), ever_connected_to_primary_(false) { ASSERT(primary_grpc_stream_ != nullptr); if (failover_stream_creator.has_value()) { ENVOY_LOG(warn, "Using xDS-Failover. Note that the implementation is currently considered " @@ -84,32 +89,34 @@ class GrpcMuxFailover : public GrpcStreamInterface, // Attempts to establish a new stream to the either the primary or failover source. void establishNewStream() override { // Attempt establishing a connection to the primary source. - // This method may be called multiple times, even if the primary stream is already - // established or in the process of being established. + // This method may be called multiple times, even if the primary/failover stream + // is already established or in the process of being established. if (complete_retry_timer_) { complete_retry_timer_->disableTimer(); } - // First check if Envoy ever connected to the primary/failover, and if so - // persist attempts to that source. - if (ever_connected_to_ == ConnectedState::Primary) { - ASSERT(!connecting_to_failover_); - ENVOY_LOG_MISC(trace, "Attempting to reconnect to the primary gRPC source, as a connection " - "to it was previously established."); - establishStreamToPrimaryIfNotConnected(); - return; - } else if (ever_connected_to_ == ConnectedState::Failover) { - ASSERT(!connecting_to_primary_); - ENVOY_LOG_MISC(trace, "Attempting to reconnect to the failover gRPC source, as a connection " - "to it was previously established."); - establishStreamToFailoverIfNotConnected(); + // If already connected to one of the source, return. + if (connection_state_ == ConnectionState::ConnectedToPrimary || + connection_state_ == ConnectionState::ConnectedToFailover) { + ENVOY_LOG_MISC(trace, + "Already connected to an xDS server, skipping establishNewStream() call"); return; } - // No prior connection was established, prefer the primary over the failover. - if (connecting_to_failover_) { - establishStreamToFailoverIfNotConnected(); + // connection_state_ is either None, ConnectingToPrimary or + // ConnectingToFailover. In the first 2 cases try to connect to the primary + // (preferring the primary in the case of None), and in the third case + // try to connect to the failover. + // Note that if a connection to the primary source was ever successful, the + // failover manager will keep setting connection_state_ to either None or + // ConnectingToPrimary, which ensures that only the primary stream will be + // established. + if (connection_state_ == ConnectionState::ConnectingToFailover) { + ASSERT(!ever_connected_to_primary_); + failover_grpc_stream_->establishNewStream(); } else { - // Either connecting to primary or connected to it, or neither. - establishStreamToPrimaryIfNotConnected(); + ASSERT(connection_state_ == ConnectionState::None || + connection_state_ == ConnectionState::ConnectingToPrimary); + connection_state_ = ConnectionState::ConnectingToPrimary; + primary_grpc_stream_->establishNewStream(); } } @@ -125,6 +132,7 @@ class GrpcMuxFailover : public GrpcStreamInterface, // Sends a message using the underlying stream. void sendMessage(const RequestType& request) override { if (connectingToOrConnectedToFailover()) { + ASSERT(!ever_connected_to_primary_); failover_grpc_stream_->sendMessage(request); return; } @@ -172,10 +180,9 @@ class GrpcMuxFailover : public GrpcStreamInterface, // Retries to connect again to the primary and then (possibly) to the // failover. Assumes that no connection has been made or is being attempted. void retryConnections() { - ASSERT(!connecting_to_primary_ && !connecting_to_failover_ && - (connected_to_ == ConnectedState::None)); + ASSERT(connection_state_ == ConnectionState::None); ENVOY_LOG(trace, "Expired timer, retrying to reconnect to the primary xDS server."); - connecting_to_primary_ = true; + connection_state_ = ConnectionState::ConnectingToPrimary; primary_grpc_stream_->establishNewStream(); } @@ -191,21 +198,19 @@ class GrpcMuxFailover : public GrpcStreamInterface, // considering the primary source as available. // Calling the onStreamEstablished() callback on the GrpcMux object will // trigger the GrpcMux to start sending requests. - ASSERT(parent_.connecting_to_primary_ && !parent_.connecting_to_failover_ && - (parent_.connected_to_ == ConnectedState::None)); + ASSERT(parent_.connection_state_ == ConnectionState::ConnectingToPrimary); parent_.grpc_mux_callbacks_.onStreamEstablished(); } void onEstablishmentFailure() override { // This will be called when the primary stream fails to establish a connection, or after the // connection was closed. - ASSERT(parent_.connectingToOrConnectedToPrimary() && - !parent_.connectingToOrConnectedToFailover()); + ASSERT(parent_.connectingToOrConnectedToPrimary()); // If there's no failover supported, this will just be a pass-through // callback. if (parent_.failover_grpc_stream_ != nullptr) { - if (parent_.connecting_to_primary_ && - (parent_.ever_connected_to_ != ConnectedState::Primary)) { + if (parent_.connection_state_ == ConnectionState::ConnectingToPrimary && + !parent_.ever_connected_to_primary_) { // If there are 2 consecutive failures to the primary, Envoy will try to connect to the // failover. primary_consecutive_failures_++; @@ -214,12 +219,14 @@ class GrpcMuxFailover : public GrpcStreamInterface, // Terminate the primary stream and establish a connection to the failover stream. ENVOY_LOG(debug, "Primary xDS stream failed to establish a connection at least 2 times " "in a row. Attempting to connect to the failover stream."); - parent_.connecting_to_primary_ = false; // This will close the stream and prevent the retry timer from // reconnecting to the primary source. + // TODO(adisuissa): need to ensure that when moving between primary and failover, + // the initial_resource_versions that are sent are empty. This will be + // done in a followup PR. parent_.primary_grpc_stream_->closeStream(); parent_.grpc_mux_callbacks_.onEstablishmentFailure(); - parent_.connecting_to_failover_ = true; + parent_.connection_state_ = ConnectionState::ConnectingToFailover; parent_.failover_grpc_stream_->establishNewStream(); return; } @@ -229,8 +236,7 @@ class GrpcMuxFailover : public GrpcStreamInterface, // later by the underlying grpc stream. ENVOY_LOG_MISC(trace, "Not trying to connect to failover. Will try again to reconnect to the " "primary (upon retry)."); - parent_.connecting_to_primary_ = true; - parent_.connected_to_ = ConnectedState::None; + parent_.connection_state_ = ConnectionState::ConnectingToPrimary; parent_.grpc_mux_callbacks_.onEstablishmentFailure(); } @@ -240,10 +246,9 @@ class GrpcMuxFailover : public GrpcStreamInterface, !parent_.connectingToOrConnectedToFailover()); // Received a response from the primary. The primary is now considered available (no failover // will be attempted). - parent_.ever_connected_to_ = ConnectedState::Primary; + parent_.ever_connected_to_primary_ = true; primary_consecutive_failures_ = 0; - parent_.connected_to_ = ConnectedState::Primary; - parent_.connecting_to_primary_ = false; + parent_.connection_state_ = ConnectionState::ConnectedToPrimary; parent_.grpc_mux_callbacks_.onDiscoveryResponse(std::move(message), control_plane_stats); } @@ -268,55 +273,45 @@ class GrpcMuxFailover : public GrpcStreamInterface, // the first response to be received before considering the failover available. // Calling the onStreamEstablished() callback on the GrpcMux object will // trigger the GrpcMux to start sending requests. - ASSERT(parent_.connecting_to_failover_ && !parent_.connecting_to_primary_ && - (parent_.connected_to_ == ConnectedState::None)); + ASSERT(parent_.connection_state_ == ConnectionState::ConnectingToFailover); + ASSERT(!parent_.ever_connected_to_primary_); parent_.grpc_mux_callbacks_.onStreamEstablished(); } void onEstablishmentFailure() override { // This will be called when the failover stream fails to establish a connection, or after the // connection was closed. - ASSERT(parent_.connectingToOrConnectedToFailover() && - !parent_.connectingToOrConnectedToPrimary()); - if (parent_.ever_connected_to_ != ConnectedState::Failover) { - ASSERT(parent_.connecting_to_failover_); - // If Envoy never established a connecting the failover, it will try to connect to the - // primary next. - ENVOY_LOG(debug, "Failover xDS stream failed to establish a connection. Attempting to " - "connect to the primary stream."); - parent_.connecting_to_failover_ = false; - // This will close the stream and prevent the retry timer from - // reconnecting to the failover source. - parent_.failover_grpc_stream_->closeStream(); - parent_.grpc_mux_callbacks_.onEstablishmentFailure(); - // Wait for a short period of time before retrying to reconnect to the - // primary, reducing strain on the network/servers in case of an issue. - // TODO(adisuissa): In the future, the reconnection attempts to the - // primary and failover sources will be decoupled, as each will use its - // own backoff timer, and this will not be needed. - parent_.complete_retry_timer_->enableTimer(std::chrono::milliseconds(500)); - return; - } - // Pass along the failure to the GrpcMux object. Retry will be triggered - // later by the underlying grpc stream. - ENVOY_LOG_MISC(trace, "Not trying to connect to primary. Will try again to reconnect to the " - "failover (upon retry)."); - parent_.connecting_to_failover_ = true; - parent_.connected_to_ = ConnectedState::None; + ASSERT(parent_.connectingToOrConnectedToFailover()); + // Either this was an intentional disconnection from the failover source, + // or unintentional. Either way, try to connect to the primary next. + ENVOY_LOG(debug, "Failover xDS stream diconnected (either after establishing a connection or " + "before). Attempting to connect to the primary stream."); + + // This will close the stream and prevent the retry timer from + // reconnecting to the failover source. + // TODO(adisuissa): need to ensure that when moving between primary and failover, + // the initial_resource_versions that are sent are empty. This will be + // done in a followup PR. + parent_.failover_grpc_stream_->closeStream(); parent_.grpc_mux_callbacks_.onEstablishmentFailure(); + // Setting the connection state to None, and when the retry timer will + // expire, Envoy will try to connect to the primary source. + parent_.connection_state_ = ConnectionState::None; + // Wait for a short period of time before retrying to reconnect to the + // primary, reducing strain on the network/servers in case of an issue. + // TODO(adisuissa): need to use the primary source's retry timer here, to wait + // for the next time to connect to the primary. This requires a refactor + // of the retry timer and moving it from the grpc_stream to here. + parent_.complete_retry_timer_->enableTimer(std::chrono::milliseconds(500)); } void onDiscoveryResponse(ResponseProtoPtr&& message, ControlPlaneStats& control_plane_stats) override { - ASSERT(parent_.connectingToOrConnectedToFailover() && - !parent_.connectingToOrConnectedToPrimary()); + ASSERT(parent_.connectingToOrConnectedToFailover()); + ASSERT(!parent_.ever_connected_to_primary_); // Received a response from the failover. The failover is now considered available (no going // back to the primary will be attempted). - // TODO(adisuissa): This will be modified in the future, when allowing the primary to always - // be preferred over the failover. - parent_.ever_connected_to_ = ConnectedState::Failover; - parent_.connected_to_ = ConnectedState::Failover; - parent_.connecting_to_failover_ = false; + parent_.connection_state_ = ConnectionState::ConnectedToFailover; parent_.grpc_mux_callbacks_.onDiscoveryResponse(std::move(message), control_plane_stats); } @@ -332,30 +327,14 @@ class GrpcMuxFailover : public GrpcStreamInterface, // Returns true iff the state is connecting to primary or connected to it. bool connectingToOrConnectedToPrimary() const { - return connecting_to_primary_ || (connected_to_ == ConnectedState::Primary); + return connection_state_ == ConnectionState::ConnectingToPrimary || + connection_state_ == ConnectionState::ConnectedToPrimary; } // Returns true iff the state is connecting to failover or connected to it. bool connectingToOrConnectedToFailover() const { - return connecting_to_failover_ || (connected_to_ == ConnectedState::Failover); - } - - // Establishes a new stream to the primary source if not connected to it. - void establishStreamToPrimaryIfNotConnected() { - if (connected_to_ != ConnectedState::Primary) { - ASSERT(connected_to_ == ConnectedState::None); - connecting_to_primary_ = true; - primary_grpc_stream_->establishNewStream(); - } - } - - // Establishes a new stream to the failover source if not connected to it. - void establishStreamToFailoverIfNotConnected() { - if (connected_to_ != ConnectedState::Failover) { - ASSERT(connected_to_ == ConnectedState::None); - connecting_to_failover_ = true; - failover_grpc_stream_->establishNewStream(); - } + return connection_state_ == ConnectionState::ConnectingToFailover || + connection_state_ == ConnectionState::ConnectedToFailover; } // The following method overrides are to allow GrpcMuxFailover to extend the @@ -389,31 +368,33 @@ class GrpcMuxFailover : public GrpcStreamInterface, // initialized when failover is supported. Event::TimerPtr complete_retry_timer_{nullptr}; - enum class ConnectedState { None, Primary, Failover }; + enum class ConnectionState { + None, + ConnectingToPrimary, + ConnectedToPrimary, + ConnectingToFailover, + ConnectedToFailover + }; // Flags to keep track of the state of connections to primary/failover. - // All initialized to false/None, as there is no connection process during - // initialization. - // The object starts with all the connecting flags set to false, and - // connected_to to None. Once a new stream is attempted, - // connecting_to_primary_ will become true, until a response will be received - // from the primary (connected_to_ will become Primary), or a failure - // to establish a connection to the primary occurs. In the latter case, if - // Envoy attempts to reconnect to the primary, connecting_to_primary_ will - // stay true, but if it attempts to connect to the failover, connecting_to_primary_ - // will be set to false, and connecting_to_failover_ will be true. - // The values of connecting_to_failover_ and connected_to_ set to Failover will be - // determined similar to the primary variants. + // The object starts with all the connecting_to and connected_to flags set + // to None. + // Once a new stream is attempted, connecting_to_ will become Primary, until + // a response will be received from the primary (connected_to_ will be set + // to Primary), or a failure to establish a connection to the primary occurs. + // In the latter case, if Envoy attempts to reconnect to the primary, + // connecting_to_ will stay Primary, but if it attempts to connect to the failover, + // connecting_to_ will be set to Failover. + // If Envoy successfully connects to the failover, connected_to_ will be set + // to Failover. // Note that while Envoy can only be connected to a single source (mutually // exclusive), it can attempt connecting to more than one source at a time. - bool connecting_to_primary_{false}; - bool connecting_to_failover_{false}; - ConnectedState connected_to_; + ConnectionState connection_state_; // A flag that keeps track of whether Envoy successfully connected to either the - // primary or failover source. Envoy successfully connected to a source once - // it receives a response from it. - ConnectedState ever_connected_to_; + // primary or failover source. Envoy is considered successfully connected to a source + // once it receives a response from it. + bool ever_connected_to_primary_{false}; }; } // namespace Config diff --git a/test/extensions/config_subscription/grpc/grpc_mux_failover_test.cc b/test/extensions/config_subscription/grpc/grpc_mux_failover_test.cc index 6e69db824701..383b13a750d6 100644 --- a/test/extensions/config_subscription/grpc/grpc_mux_failover_test.cc +++ b/test/extensions/config_subscription/grpc/grpc_mux_failover_test.cc @@ -398,25 +398,43 @@ TEST_F(GrpcMuxFailoverTest, PrimaryOnlyAttemptsAfterPrimaryAvailable) { grpc_mux_failover_->establishNewStream(); } -// Validate that after the failover is available (a response is received), all -// reconnect attempts will be to the failover. -TEST_F(GrpcMuxFailoverTest, FailoverOnlyAttemptsAfterFailoverAvailable) { +// Validate that after the failover is available (a response is received), Envoy +// will try to reconnect to the primary (and then failover), and keep +// alternating between the two. +TEST_F(GrpcMuxFailoverTest, AlternatingPrimaryAndFailoverAttemptsAfterFailoverAvailable) { connectToFailover(); - // Emulate 5 disconnects, and ensure the primary reconnection isn't attempted. + // Emulate a 5 times disconnects. for (int attempt = 0; attempt < 5; ++attempt) { - // Emulate a failover source failure that will not result in an attempt to - // connect to the primary. It should not close the failover stream (so - // the retry mechanism will kick in). - EXPECT_CALL(failover_stream_, closeStream()).Times(0); - EXPECT_CALL(grpc_mux_callbacks_, onEstablishmentFailure()); - EXPECT_CALL(primary_stream_, establishNewStream()).Times(0); - failover_callbacks_->onEstablishmentFailure(); + if (attempt % 2 == 0) { + // Emulate a failover source failure that will result in an attempt to + // connect to the primary. It should close the failover stream, and + // enable the retry timer. + EXPECT_CALL(failover_stream_, closeStream()); + EXPECT_CALL(grpc_mux_callbacks_, onEstablishmentFailure()); + EXPECT_CALL(failover_stream_, establishNewStream()).Times(0); + EXPECT_CALL(*timer_, enableTimer(_, _)); + failover_callbacks_->onEstablishmentFailure(); + // Emulate a timer tick, which should try to reconnect to the primary + // stream. + EXPECT_CALL(primary_stream_, establishNewStream()); + timer_cb_(); + } else { + // Emulate a primary source failure that will result in an attempt to + // connect to the failover. It should close the primary stream, and + // try to establish the failover stream. + EXPECT_CALL(primary_stream_, closeStream()); + EXPECT_CALL(grpc_mux_callbacks_, onEstablishmentFailure()); + EXPECT_CALL(primary_stream_, establishNewStream()).Times(0); + EXPECT_CALL(failover_stream_, establishNewStream()); + primary_callbacks_->onEstablishmentFailure(); + } } - // Emulate a call to establishNewStream(). - EXPECT_CALL(primary_stream_, establishNewStream()).Times(0); - EXPECT_CALL(failover_stream_, establishNewStream()); + // Last attempt ended with failing to establish a failover stream, + // emulate a successful primary stream. + EXPECT_CALL(failover_stream_, establishNewStream()).Times(0); + EXPECT_CALL(primary_stream_, establishNewStream()); grpc_mux_failover_->establishNewStream(); } @@ -603,6 +621,29 @@ TEST_F(GrpcMuxFailoverTest, OnWriteableConnectedToPrimaryInvoked) { primary_callbacks_->onWriteable(); } +// Validates that when connected to primary, a subsequent call to establishNewStream +// will not try to recreate the stream. +TEST_F(GrpcMuxFailoverTest, NoRecreateStreamWhenConnectedToPrimary) { + // Validate connected to primary. + { + connectToPrimary(); + EXPECT_CALL(primary_stream_, establishNewStream()).Times(0); + EXPECT_CALL(failover_stream_, establishNewStream()).Times(0); + grpc_mux_failover_->establishNewStream(); + } +} + +// Validates that when connected to failover, a subsequent call to establishNewStream +// will not try to recreate the stream. +TEST_F(GrpcMuxFailoverTest, NoRecreateStreamWhenConnectedToFailover) { + // Validate connected to failover. + { + connectToFailover(); + EXPECT_CALL(primary_stream_, establishNewStream()).Times(0); + EXPECT_CALL(failover_stream_, establishNewStream()).Times(0); + grpc_mux_failover_->establishNewStream(); + } +} } // namespace } // namespace Config } // namespace Envoy diff --git a/test/extensions/config_subscription/grpc/xds_failover_integration_test.cc b/test/extensions/config_subscription/grpc/xds_failover_integration_test.cc index d48a51b16980..93be6621f445 100644 --- a/test/extensions/config_subscription/grpc/xds_failover_integration_test.cc +++ b/test/extensions/config_subscription/grpc/xds_failover_integration_test.cc @@ -183,6 +183,14 @@ class XdsFailoverAdsIntegrationTest : public AdsDeltaSotwIntegrationSubStatePara RELEASE_ASSERT(result, result.message()); } + // Waits for a failover source connected and immediately disconnects. + void failoverConnectionFailure() { + AssertionResult result = xds_upstream_->waitForHttpConnection(*dispatcher_, xds_connection_); + RELEASE_ASSERT(result, result.message()); + result = xds_connection_->close(); + RELEASE_ASSERT(result, result.message()); + } + envoy::config::endpoint::v3::ClusterLoadAssignment buildClusterLoadAssignment(const std::string& name) { return ConfigHelper::buildClusterLoadAssignment( @@ -559,8 +567,8 @@ TEST_P(XdsFailoverAdsIntegrationTest, NoFailoverUseAfterPrimaryResponse) { xds_stream_.get())); } -// Validate that once failover answers, primary will not be used, even after disconnecting. -TEST_P(XdsFailoverAdsIntegrationTest, NoPrimaryUseAfterFailoverResponse) { +// Validate that once failover responds, and then disconnects, primary will be attempted. +TEST_P(XdsFailoverAdsIntegrationTest, PrimaryUseAfterFailoverResponseAndDisconnect) { // These tests are not executed with GoogleGrpc because they are flaky due to // the large timeout values for retries. SKIP_IF_GRPC_CLIENT(Grpc::ClientType::GoogleGrpc); @@ -579,9 +587,9 @@ TEST_P(XdsFailoverAdsIntegrationTest, NoPrimaryUseAfterFailoverResponse) { ASSERT_TRUE(xds_connection_->waitForDisconnect()); // The CDS request fails when the primary disconnects. After that fetch the config // dump to ensure that the retry timer kicks in. - waitForPrimaryXdsRetryTimer(); // Expect another connection attempt to the primary. Reject the stream (gRPC failure) immediately. // As this is a 2nd consecutive failure, it will trigger failover. + waitForPrimaryXdsRetryTimer(); primaryConnectionFailure(); ASSERT_TRUE(xds_connection_->waitForDisconnect()); @@ -601,68 +609,285 @@ TEST_P(XdsFailoverAdsIntegrationTest, NoPrimaryUseAfterFailoverResponse) { Grpc::Status::WellKnownGrpcStatus::Ok, "", failover_xds_stream_.get())); sendDiscoveryResponse( - CdsTypeUrl, {ConfigHelper::buildCluster("cluster_0")}, - {ConfigHelper::buildCluster("cluster_0")}, {}, "1", {}, failover_xds_stream_.get()); + CdsTypeUrl, {ConfigHelper::buildCluster("failover_cluster_0")}, + {ConfigHelper::buildCluster("failover_cluster_0")}, {}, "failover1", {}, + failover_xds_stream_.get()); + // Wait for an EDS request, and send its response. test_server_->waitForGaugeEq("cluster_manager.warming_clusters", 1); - test_server_->waitForGaugeEq("cluster.cluster_0.warming_state", 1); + test_server_->waitForGaugeEq("cluster.failover_cluster_0.warming_state", 1); + EXPECT_TRUE(compareDiscoveryRequest( + EdsTypeUrl, "", {"failover_cluster_0"}, {"failover_cluster_0"}, {}, false, + Grpc::Status::WellKnownGrpcStatus::Ok, "", failover_xds_stream_.get())); + sendDiscoveryResponse( + EdsTypeUrl, {buildClusterLoadAssignment("failover_cluster_0")}, + {buildClusterLoadAssignment("failover_cluster_0")}, {}, "failover1", {}, + failover_xds_stream_.get()); + test_server_->waitForGaugeEq("cluster_manager.warming_clusters", 0); + test_server_->waitForGaugeGe("cluster_manager.active_clusters", 2); + test_server_->waitForGaugeEq("cluster.failover_cluster_0.warming_state", 0); + EXPECT_TRUE(compareDiscoveryRequest(CdsTypeUrl, "failover1", {}, {}, {}, false, + Grpc::Status::WellKnownGrpcStatus::Ok, "", + failover_xds_stream_.get())); + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Listener, "", {}, {}, {}, false, + Grpc::Status::WellKnownGrpcStatus::Ok, "", + failover_xds_stream_.get())); - // Envoy has received a CDS response, it means the primary is available. - // Now disconnect the primary. + // Envoy has received CDS and EDS responses, it means the failover is available. + // Now disconnect the failover source. failover_xds_stream_->finishGrpcStream(Grpc::Status::Internal); - // CDS was successful, but EDS will fail. After that add a notification to the + // CDS and EDS were successful, but LDS will fail. After that add a notification to the // main thread to ensure that the retry timer kicks in. - test_server_->waitForCounterGe("cluster.cluster_0.update_failure", 1); + test_server_->waitForCounterGe("listener_manager.lds.update_failure", 1); absl::Notification notification; test_server_->server().dispatcher().post([&]() { notification.Notify(); }); notification.WaitForNotification(); timeSystem().advanceTimeWait(std::chrono::milliseconds(1000)); - // In this case (received a response), both EnvoyGrpc and GoogleGrpc keep the connection open. + // The failover disconnected, so the next step is trying to connect to the + // primary. First ensure that the failover isn't being attempted, and then let + // the connection to the primary succeed. + EXPECT_FALSE(failover_xds_upstream_->waitForHttpConnection(*dispatcher_, failover_xds_connection_, + std::chrono::seconds(5))); + EXPECT_TRUE(xds_upstream_->waitForHttpConnection(*dispatcher_, xds_connection_)); + + // Allow receiving config from the primary. + result = xds_connection_->waitForNewStream(*dispatcher_, xds_stream_); + xds_stream_->startGrpcStream(); + + // Ensure basic flow with primary works. Validate that the + // initial_resource_versions for delta-xDS is empty. + // TODO(adisuissa): ensure initial_resource_versions is empty, once this is supported. + EXPECT_TRUE(compareDiscoveryRequest(CdsTypeUrl, "", {}, {}, {}, true, + Grpc::Status::WellKnownGrpcStatus::Ok, "", + xds_stream_.get())); + EXPECT_TRUE( + compareDiscoveryRequest(EdsTypeUrl, "", {"failover_cluster_0"}, {"failover_cluster_0"}, {}, + false, Grpc::Status::WellKnownGrpcStatus::Ok, "", xds_stream_.get())); + EXPECT_TRUE(compareDiscoveryRequest(LdsTypeUrl, "", {}, {}, {}, false, + Grpc::Status::WellKnownGrpcStatus::Ok, "", + xds_stream_.get())); + sendDiscoveryResponse( + CdsTypeUrl, {ConfigHelper::buildCluster("primary_cluster_0")}, + {ConfigHelper::buildCluster("primary_cluster_0")}, {}, "primary1", {}, xds_stream_.get()); + test_server_->waitForGaugeEq("cluster_manager.warming_clusters", 1); + test_server_->waitForGaugeEq("cluster.primary_cluster_0.warming_state", 1); + + // Expect an updated failover EDS request. + EXPECT_TRUE(compareDiscoveryRequest(EdsTypeUrl, "", {"primary_cluster_0"}, {"primary_cluster_0"}, + {}, false, Grpc::Status::WellKnownGrpcStatus::Ok, "", + xds_stream_.get())); +} + +// Validates that if failover is used, and then disconnected, and the primary +// still doesn't respond, failover will be attempted with the correct +// initial_resource_versions. +TEST_P(XdsFailoverAdsIntegrationTest, FailoverUseAfterFailoverResponseAndDisconnect) { + // These tests are not executed with GoogleGrpc because they are flaky due to + // the large timeout values for retries. + SKIP_IF_GRPC_CLIENT(Grpc::ClientType::GoogleGrpc); +#ifdef ENVOY_ENABLE_UHV + // With UHV the finishGrpcStream() isn't detected as invalid frame because of + // no ":status" header, unless "envoy.reloadable_features.enable_universal_header_validator" + // is also enabled. + config_helper_.addRuntimeOverride("envoy.reloadable_features.enable_universal_header_validator", + "true"); +#endif + initialize(); + + // 2 consecutive primary failures. + // Expect a connection to the primary. Reject the connection immediately. + primaryConnectionFailure(); + ASSERT_TRUE(xds_connection_->waitForDisconnect()); + // The CDS request fails when the primary disconnects. After that fetch the config + // dump to ensure that the retry timer kicks in. + // Expect another connection attempt to the primary. Reject the stream (gRPC failure) immediately. + // As this is a 2nd consecutive failure, it will trigger failover. + waitForPrimaryXdsRetryTimer(); + primaryConnectionFailure(); + ASSERT_TRUE(xds_connection_->waitForDisconnect()); + + // The CDS request fails when the primary disconnects. + test_server_->waitForCounterGe("cluster_manager.cds.update_failure", 2); + + AssertionResult result = + failover_xds_upstream_->waitForHttpConnection(*dispatcher_, failover_xds_connection_); + RELEASE_ASSERT(result, result.message()); + // Failover is healthy, start the ADS gRPC stream. result = failover_xds_connection_->waitForNewStream(*dispatcher_, failover_xds_stream_); RELEASE_ASSERT(result, result.message()); - // Immediately fail the connection. - failover_xds_stream_->finishGrpcStream(Grpc::Status::Internal); - - // Ensure that Envoy still attempts to connect to the primary, - // and keep disconnecting a few times and validate that the failover - // connection isn't attempted. - for (int i = 1; i < 5; ++i) { - ASSERT_TRUE(failover_xds_connection_->waitForDisconnect()); - // Wait longer due to the fixed 5 seconds failover . - waitForPrimaryXdsRetryTimer(i, 5); - // EnvoyGrpc will disconnect if the gRPC stream is immediately closed (as - // done above). - result = failover_xds_upstream_->waitForHttpConnection(*dispatcher_, failover_xds_connection_); - RELEASE_ASSERT(result, result.message()); - result = failover_xds_connection_->waitForNewStream(*dispatcher_, failover_xds_stream_); - RELEASE_ASSERT(result, result.message()); - // Immediately fail the connection. - failover_xds_stream_->finishGrpcStream(Grpc::Status::Internal); - } + failover_xds_stream_->startGrpcStream(); - // When EnvoyGrpc is used, no new connection to the primary will be attempted. - EXPECT_FALSE( - xds_upstream_->waitForHttpConnection(*dispatcher_, xds_connection_, std::chrono::seconds(1))); + // Ensure basic flow with failover works. + EXPECT_TRUE(compareDiscoveryRequest(CdsTypeUrl, "", {}, {}, {}, true, + Grpc::Status::WellKnownGrpcStatus::Ok, "", + failover_xds_stream_.get())); + sendDiscoveryResponse( + CdsTypeUrl, {ConfigHelper::buildCluster("failover_cluster_0")}, + {ConfigHelper::buildCluster("failover_cluster_0")}, {}, "failover1", {}, + failover_xds_stream_.get()); + // Wait for an EDS request, and send its response. + test_server_->waitForGaugeEq("cluster_manager.warming_clusters", 1); + test_server_->waitForGaugeEq("cluster.failover_cluster_0.warming_state", 1); + EXPECT_TRUE(compareDiscoveryRequest( + EdsTypeUrl, "", {"failover_cluster_0"}, {"failover_cluster_0"}, {}, false, + Grpc::Status::WellKnownGrpcStatus::Ok, "", failover_xds_stream_.get())); + sendDiscoveryResponse( + EdsTypeUrl, {buildClusterLoadAssignment("failover_cluster_0")}, + {buildClusterLoadAssignment("failover_cluster_0")}, {}, "failover1", {}, + failover_xds_stream_.get()); + test_server_->waitForGaugeEq("cluster_manager.warming_clusters", 0); + test_server_->waitForGaugeGe("cluster_manager.active_clusters", 2); + test_server_->waitForGaugeEq("cluster.failover_cluster_0.warming_state", 0); + EXPECT_TRUE(compareDiscoveryRequest(CdsTypeUrl, "failover1", {}, {}, {}, false, + Grpc::Status::WellKnownGrpcStatus::Ok, "", + failover_xds_stream_.get())); + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Listener, "", {}, {}, {}, false, + Grpc::Status::WellKnownGrpcStatus::Ok, "", + failover_xds_stream_.get())); + // Envoy has received CDS and EDS responses, it means the failover is available. + // Now disconnect the failover. + failover_xds_stream_->finishGrpcStream(Grpc::Status::Internal); + EXPECT_TRUE(failover_xds_connection_->close()); ASSERT_TRUE(failover_xds_connection_->waitForDisconnect()); - // Wait longer due to the fixed 5 seconds failover . - waitForPrimaryXdsRetryTimer(5, 5); - // Allow a connection to the failover. - // Expect a connection to the failover when using EnvoyGrpc. - // In case GoogleGrpc is used the current connection will be reused (new stream). + // Wait longer due to the fixed 5 seconds failover. + waitForPrimaryXdsRetryTimer(3, 5); + + // The failover disconnected, so the next step is trying to connect to the + // primary source. Disconnect the primary source immediately. + primaryConnectionFailure(); + ASSERT_TRUE(xds_connection_->waitForDisconnect()); + + // Expect a connection to the failover source to be attempted. result = failover_xds_upstream_->waitForHttpConnection(*dispatcher_, failover_xds_connection_); RELEASE_ASSERT(result, result.message()); + + // Failover is healthy, start the ADS gRPC stream. result = failover_xds_connection_->waitForNewStream(*dispatcher_, failover_xds_stream_); + RELEASE_ASSERT(result, result.message()); failover_xds_stream_->startGrpcStream(); - // Validate that just the initial requests are sent to the primary. - EXPECT_TRUE(compareDiscoveryRequest(CdsTypeUrl, "1", {}, {}, {}, true, + // Ensure basic flow with primary works. Validate that the + // initial_resource_versions for delta-xDS is empty. + // TODO(adisuissa): ensure initial_resource_versions contains the correct versions. + EXPECT_TRUE(compareDiscoveryRequest(CdsTypeUrl, "", {}, {}, {}, true, Grpc::Status::WellKnownGrpcStatus::Ok, "", failover_xds_stream_.get())); - EXPECT_TRUE(compareDiscoveryRequest(EdsTypeUrl, "", {"cluster_0"}, {"cluster_0"}, {}, false, + EXPECT_TRUE(compareDiscoveryRequest( + EdsTypeUrl, "", {"failover_cluster_0"}, {"failover_cluster_0"}, {}, false, + Grpc::Status::WellKnownGrpcStatus::Ok, "", failover_xds_stream_.get())); + EXPECT_TRUE(compareDiscoveryRequest(LdsTypeUrl, "", {}, {}, {}, false, Grpc::Status::WellKnownGrpcStatus::Ok, "", failover_xds_stream_.get())); + sendDiscoveryResponse( + CdsTypeUrl, {ConfigHelper::buildCluster("failover_cluster_1")}, + {ConfigHelper::buildCluster("failover_cluster_1")}, {}, "failover2", {}, + failover_xds_stream_.get()); + test_server_->waitForGaugeEq("cluster_manager.warming_clusters", 1); + test_server_->waitForGaugeEq("cluster.failover_cluster_1.warming_state", 1); + + // Expect an updated failover EDS request. + EXPECT_TRUE(compareDiscoveryRequest( + EdsTypeUrl, "", {"failover_cluster_1"}, {"failover_cluster_1"}, {}, false, + Grpc::Status::WellKnownGrpcStatus::Ok, "", failover_xds_stream_.get())); +} + +// Validate that once failover responds, and then disconnects, Envoy +// will alternate between the primary and failover sources (multiple times) if +// both are not responding. +TEST_P(XdsFailoverAdsIntegrationTest, + PrimaryAndFailoverAttemptsAfterFailoverResponseAndDisconnect) { + // These tests are not executed with GoogleGrpc because they are flaky due to + // the large timeout values for retries. + SKIP_IF_GRPC_CLIENT(Grpc::ClientType::GoogleGrpc); +#ifdef ENVOY_ENABLE_UHV + // With UHV the finishGrpcStream() isn't detected as invalid frame because of + // no ":status" header, unless "envoy.reloadable_features.enable_universal_header_validator" + // is also enabled. + config_helper_.addRuntimeOverride("envoy.reloadable_features.enable_universal_header_validator", + "true"); +#endif + initialize(); + + // 2 consecutive primary failures. + // Expect a connection to the primary. Reject the connection immediately. + primaryConnectionFailure(); + ASSERT_TRUE(xds_connection_->waitForDisconnect()); + // The CDS request fails when the primary disconnects. After that fetch the config + // dump to ensure that the retry timer kicks in. + // Expect another connection attempt to the primary. Reject the stream (gRPC failure) immediately. + // As this is a 2nd consecutive failure, it will trigger failover. + waitForPrimaryXdsRetryTimer(); + primaryConnectionFailure(); + ASSERT_TRUE(xds_connection_->waitForDisconnect()); + + // The CDS request fails when the primary disconnects. + test_server_->waitForCounterGe("cluster_manager.cds.update_failure", 2); + + AssertionResult result = + failover_xds_upstream_->waitForHttpConnection(*dispatcher_, failover_xds_connection_); + RELEASE_ASSERT(result, result.message()); + // Failover is healthy, start the ADS gRPC stream. + result = failover_xds_connection_->waitForNewStream(*dispatcher_, failover_xds_stream_); + RELEASE_ASSERT(result, result.message()); + failover_xds_stream_->startGrpcStream(); + + // Ensure basic flow with failover works. + EXPECT_TRUE(compareDiscoveryRequest(CdsTypeUrl, "", {}, {}, {}, true, + Grpc::Status::WellKnownGrpcStatus::Ok, "", + failover_xds_stream_.get())); + sendDiscoveryResponse( + CdsTypeUrl, {ConfigHelper::buildCluster("failover_cluster_0")}, + {ConfigHelper::buildCluster("failover_cluster_0")}, {}, "failover1", {}, + failover_xds_stream_.get()); + // Wait for an EDS request, and send its response. + test_server_->waitForGaugeEq("cluster_manager.warming_clusters", 1); + test_server_->waitForGaugeEq("cluster.failover_cluster_0.warming_state", 1); + EXPECT_TRUE(compareDiscoveryRequest( + EdsTypeUrl, "", {"failover_cluster_0"}, {"failover_cluster_0"}, {}, false, + Grpc::Status::WellKnownGrpcStatus::Ok, "", failover_xds_stream_.get())); + sendDiscoveryResponse( + EdsTypeUrl, {buildClusterLoadAssignment("failover_cluster_0")}, + {buildClusterLoadAssignment("failover_cluster_0")}, {}, "failover1", {}, + failover_xds_stream_.get()); + test_server_->waitForGaugeEq("cluster_manager.warming_clusters", 0); + test_server_->waitForGaugeGe("cluster_manager.active_clusters", 2); + test_server_->waitForGaugeEq("cluster.failover_cluster_0.warming_state", 0); + EXPECT_TRUE(compareDiscoveryRequest(CdsTypeUrl, "failover1", {}, {}, {}, false, + Grpc::Status::WellKnownGrpcStatus::Ok, "", + failover_xds_stream_.get())); + EXPECT_TRUE(compareDiscoveryRequest(Config::TypeUrl::get().Listener, "", {}, {}, {}, false, + Grpc::Status::WellKnownGrpcStatus::Ok, "", + failover_xds_stream_.get())); + + // Envoy has received CDS and EDS responses, it means the failover is available. + // Now disconnect the failover source. + failover_xds_stream_->finishGrpcStream(Grpc::Status::Internal); + EXPECT_TRUE(failover_xds_connection_->close()); + ASSERT_TRUE(failover_xds_connection_->waitForDisconnect()); + + // Ensure Envoy alternates between primary and failover. + // Up to this step there were 3 CDS update failures. In each iteration of the + // next loop there are going to be 2 failures (one for primary and another for + // failover). + for (int i = 1; i < 3; ++i) { + // Wait longer due to the fixed 5 seconds failover . + waitForPrimaryXdsRetryTimer(2 * i + 1, 5); + + // The failover disconnected, so the next step is trying to connect to the + // primary source. Disconnect the primary source immediately. + primaryConnectionFailure(); + ASSERT_TRUE(xds_connection_->waitForDisconnect()); + + // Expect a connection to the failover source to be attempted. Disconnect + // immediately. + result = failover_xds_upstream_->waitForHttpConnection(*dispatcher_, failover_xds_connection_); + RELEASE_ASSERT(result, result.message()); + result = failover_xds_connection_->close(); + RELEASE_ASSERT(result, result.message()); + ASSERT_TRUE(failover_xds_connection_->waitForDisconnect()); + } } } // namespace Envoy From ee567154320b11f54f2ad0453e9d620d910df4f4 Mon Sep 17 00:00:00 2001 From: danzh Date: Fri, 9 Aug 2024 16:33:21 -0400 Subject: [PATCH 081/130] Update QUICHE from f4ed5e0c7 to 0d70743fc (#35649) Update QUICHE from f4ed5e0c7 to 0d70743fc https://github.com/google/quiche/compare/f4ed5e0c7..0d70743fc 2024-08-09 fayang No public description 2024-08-08 martinduke Refactor MoQ ChatClient to support end-to-end tests and use moq_chat.h. 2024-08-08 vasilvv Fix standalone QUICHE build. 2024-08-07 ricea Allocate memory less often in HttpHeaderBlock hash function 2024-08-07 martinduke Implement moq-chat server. 2024-08-07 birenroy Deprecates QUICHE reloadable flag http2_add_hpack_overhead_bytes2. 2024-08-07 bnc Update and simplify QUICHE platform documentation. 2024-08-07 bnc Remove obsolete comment. 2024-08-07 birenroy Adds a way to reject messages with `obs-text` in header field names. 2024-08-07 birenroy Reorders members of BalsaFrame, and uses a more compact representation. 2024-08-06 bnc Use kMetadataExtensionId directly in switch case. 2024-08-06 bnc Modernize constants in http_protocol.h. 2024-08-06 fayang Set chrome_value to true for gfe2_reloadable_flag_enable_h3_origin_frame. 2024-08-06 quiche-dev Enabling rolled out flags. 2024-08-01 wub Fix //third_party/quic/core:quic_dispatcher_test with Q046. 2024-08-01 quiche-dev Add EVMB to Envoy QUIC connection options. 2024-08-01 wub QUIC TlsClientHandshaker: Demote some INFO logs to DLOG. 2024-07-31 wub Replace QUIC CID when the first INITIAL packet of a connection is enqueued into the QuicBufferedPacketStore. 2024-07-31 martinduke Remove unused MoqtSession reference from MoqtOutgoingQueue. 2024-07-31 bnc Add CookieCrumbling argument to QpackEncoder constructor, and add QuicSpdySession::DisableCookieCrumbling() method. 2024-07-31 bnc Add ValueSplittingHeaderList option to disable cookie crumbling during QPACK encoding. 2024-07-30 martinduke Restore operation of chat_client_bin after draft-05 update. 2024-07-30 birenroy Updates //net/floo to refer to the new location of some HTTP/2 libraries. 2024-07-29 rch Add QUICHE support for the HTTP/3 ORIGIN frame. Risk Level: low Testing: existing tests Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A --------- Signed-off-by: Dan Zhang Co-authored-by: Dan Zhang --- bazel/external/quiche.BUILD | 2 +- bazel/repository_locations.bzl | 6 +++--- source/common/quic/envoy_quic_stream.cc | 4 ++-- test/common/quic/envoy_quic_h3_fuzz_helper.cc | 3 ++- test/common/quic/test_utils.h | 3 ++- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/bazel/external/quiche.BUILD b/bazel/external/quiche.BUILD index 0abec6540ec7..5f8bc42cfdb8 100644 --- a/bazel/external/quiche.BUILD +++ b/bazel/external/quiche.BUILD @@ -1373,7 +1373,6 @@ envoy_cc_library( name = "http2_core_headers_handler_interface_lib", hdrs = [ "quiche/http2/core/spdy_headers_handler_interface.h", - "quiche/spdy/core/spdy_headers_handler_interface.h", ], copts = quiche_copts, repository = "@envoy", @@ -3929,6 +3928,7 @@ envoy_quic_cc_library( ":quic_platform", ":quic_server_session_lib", ":quiche_common_callbacks", + ":quiche_common_intrusive_list_lib", ":quiche_common_text_utils_lib", ], ) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 23879aef24bc..04d980d76742 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -1200,12 +1200,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "QUICHE", project_desc = "QUICHE (QUIC, HTTP/2, Etc) is Google‘s implementation of QUIC and related protocols", project_url = "https://github.com/google/quiche", - version = "f4ed5e0c74485fb302367b833b8974373fed9e4c", - sha256 = "05e40b18e78b76a14bfa02eca1d6ebcf4c2ea0333c5db9fbe04287f912db2c20", + version = "0d70743fc056f254743082221eda54eba7431fa7", + sha256 = "b2c0ad26505d93416305618b5af10be35280a8d166c341f3e9795704914d1e12", urls = ["https://github.com/google/quiche/archive/{version}.tar.gz"], strip_prefix = "quiche-{version}", use_category = ["controlplane", "dataplane_core"], - release_date = "2024-07-26", + release_date = "2024-08-09", cpe = "N/A", license = "BSD-3-Clause", license_url = "https://github.com/google/quiche/blob/{version}/LICENSE", diff --git a/source/common/quic/envoy_quic_stream.cc b/source/common/quic/envoy_quic_stream.cc index 280c7ab91513..12d7a1b20f5b 100644 --- a/source/common/quic/envoy_quic_stream.cc +++ b/source/common/quic/envoy_quic_stream.cc @@ -123,8 +123,8 @@ std::unique_ptr dataFromString(const std::string& str) { void serializeMetadata(const Http::MetadataMapPtr& metadata, quic::QuicStreamId id, absl::InlinedVector& slices) { quic::NoopDecoderStreamErrorDelegate decoder_stream_error_delegate; - quic::QpackEncoder qpack_encoder(&decoder_stream_error_delegate, - quic::HuffmanEncoding::kDisabled); + quic::QpackEncoder qpack_encoder(&decoder_stream_error_delegate, quic::HuffmanEncoding::kDisabled, + quic::CookieCrumbling::kDisabled); spdy::Http2HeaderBlock header_block; for (const auto& [key, value] : *metadata) { diff --git a/test/common/quic/envoy_quic_h3_fuzz_helper.cc b/test/common/quic/envoy_quic_h3_fuzz_helper.cc index 30f4f69e66e1..a129446aaf31 100644 --- a/test/common/quic/envoy_quic_h3_fuzz_helper.cc +++ b/test/common/quic/envoy_quic_h3_fuzz_helper.cc @@ -27,7 +27,8 @@ class Delegate : public quic::QpackEncoder::DecoderStreamErrorDelegate { static std::string encodeHeaders(const spdy::Http2HeaderBlock& headers) { static Delegate delegate; - quic::QpackEncoder encoder(&delegate, quic::HuffmanEncoding::kEnabled); + quic::QpackEncoder encoder(&delegate, quic::HuffmanEncoding::kEnabled, + quic::CookieCrumbling::kEnabled); return encoder.EncodeHeaderList(0, headers, nullptr); } diff --git a/test/common/quic/test_utils.h b/test/common/quic/test_utils.h index c89c33e70ea0..cc94aff40c5a 100644 --- a/test/common/quic/test_utils.h +++ b/test/common/quic/test_utils.h @@ -277,7 +277,8 @@ std::string spdyHeaderToHttp3StreamPayload(const spdy::Http2HeaderBlock& header) quic::test::NoopQpackStreamSenderDelegate encoder_stream_sender_delegate; quic::NoopDecoderStreamErrorDelegate decoder_stream_error_delegate; auto qpack_encoder = std::make_unique(&decoder_stream_error_delegate, - quic::HuffmanEncoding::kEnabled); + quic::HuffmanEncoding::kEnabled, + quic::CookieCrumbling::kEnabled); qpack_encoder->set_qpack_stream_sender_delegate(&encoder_stream_sender_delegate); // QpackEncoder does not use the dynamic table by default, // therefore the value of |stream_id| does not matter. From 7ae5ca51350a195ae653fb1928c8855305c595a0 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Fri, 9 Aug 2024 15:50:10 -0700 Subject: [PATCH 082/130] Revert "implement per-route override for rbac stat prefixes (#35531)" (#35655) Revert "implement per-route override for rbac stat prefixes (#35531)" Causing //test/extensions/filters/network/rbac:integration_test to flakily time out. This reverts commit 751217c2bf6cdf0f48f3c7016ae8dfcb49097739. Signed-off-by: Ryan Hamilton Signed-off-by: Ryan Hamilton --- changelogs/current.yaml | 4 - .../filters/http/rbac/rbac_filter.cc | 39 +----- .../filters/http/rbac/rbac_filter.h | 25 ++-- .../filters/http/rbac/rbac_filter_test.cc | 128 ------------------ 4 files changed, 22 insertions(+), 174 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 8f12340b7aa0..86f5204385de 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -61,10 +61,6 @@ bug_fixes: change: | Add runtime guard for timeout error code 504 Gateway Timeout that is returned to downstream. If runtime flag ``envoy.reloadable_features.ext_proc_timeout_error`` is set to false, old error code 500 Internal Server Error will be returned. -- area: rbac - change: | - RBAC will now allow stat prefixes configured in per-route config to override the base config's - stat prefix. removed_config_or_runtime: # *Normally occurs at the end of the* :ref:`deprecation period ` diff --git a/source/extensions/filters/http/rbac/rbac_filter.cc b/source/extensions/filters/http/rbac/rbac_filter.cc index e55924cfc861..cefa97f010c4 100644 --- a/source/extensions/filters/http/rbac/rbac_filter.cc +++ b/source/extensions/filters/http/rbac/rbac_filter.cc @@ -1,4 +1,3 @@ -#include "rbac_filter.h" #include "source/extensions/filters/http/rbac/rbac_filter.h" #include "envoy/stats/scope.h" @@ -75,29 +74,6 @@ RoleBasedAccessControlFilterConfig::RoleBasedAccessControlFilterConfig( shadow_engine_(Filters::Common::RBAC::createShadowEngine( proto_config, context, validation_visitor, action_validation_visitor_)) {} -#define DEFINE_DYNAMIC_METADATA_STAT_KEY_GETTER(GETTER_NAME, PREFIX, ROUTE_LOCAL_PREFIX_OVERRIDE, \ - DYNAMIC_METADATA_KEY) \ - std::string RoleBasedAccessControlFilterConfig::GETTER_NAME( \ - const Http::StreamFilterCallbacks* callbacks) const { \ - const auto* route_local = Http::Utility::resolveMostSpecificPerFilterConfig< \ - RoleBasedAccessControlRouteSpecificFilterConfig>(callbacks); \ - std::string prefix = PREFIX; \ - if (route_local && !route_local->ROUTE_LOCAL_PREFIX_OVERRIDE().empty()) { \ - prefix = route_local->ROUTE_LOCAL_PREFIX_OVERRIDE(); \ - } \ - return prefix + \ - Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().DYNAMIC_METADATA_KEY; \ - } - -DEFINE_DYNAMIC_METADATA_STAT_KEY_GETTER(shadowEffectivePolicyIdField, shadow_rules_stat_prefix_, - shadowRulesStatPrefix, ShadowEffectivePolicyIdField) -DEFINE_DYNAMIC_METADATA_STAT_KEY_GETTER(shadowEngineResultField, shadow_rules_stat_prefix_, - shadowRulesStatPrefix, ShadowEngineResultField) -DEFINE_DYNAMIC_METADATA_STAT_KEY_GETTER(enforcedEffectivePolicyIdField, rules_stat_prefix_, - rulesStatPrefix, EnforcedEffectivePolicyIdField) -DEFINE_DYNAMIC_METADATA_STAT_KEY_GETTER(enforcedEngineResultField, rules_stat_prefix_, - rulesStatPrefix, EnforcedEngineResultField) - const Filters::Common::RBAC::RoleBasedAccessControlEngine* RoleBasedAccessControlFilterConfig::engine(const Http::StreamFilterCallbacks* callbacks, Filters::Common::RBAC::EnforcementMode mode) const { @@ -127,9 +103,7 @@ RoleBasedAccessControlRouteSpecificFilterConfig::RoleBasedAccessControlRouteSpec const envoy::extensions::filters::http::rbac::v3::RBACPerRoute& per_route_config, Server::Configuration::ServerFactoryContext& context, ProtobufMessage::ValidationVisitor& validation_visitor) - : rules_stat_prefix_(per_route_config.rbac().rules_stat_prefix()), - shadow_rules_stat_prefix_(per_route_config.rbac().shadow_rules_stat_prefix()), - per_rule_stats_(per_route_config.rbac().track_per_rule_stats()) { + : per_rule_stats_(per_route_config.rbac().track_per_rule_stats()) { // Moved from member initializer to ctor body to overcome clang false warning about memory // leak (clang-analyzer-cplusplus.NewDeleteLeaks,-warnings-as-errors). // Potentially https://lists.llvm.org/pipermail/llvm-bugs/2018-July/066769.html @@ -191,11 +165,10 @@ RoleBasedAccessControlFilter::decodeHeaders(Http::RequestHeaderMap& headers, boo } if (!effective_policy_id.empty()) { - *fields[config_->shadowEffectivePolicyIdField(callbacks_)].mutable_string_value() = - effective_policy_id; + *fields[config_->shadowEffectivePolicyIdField()].mutable_string_value() = effective_policy_id; } - *fields[config_->shadowEngineResultField(callbacks_)].mutable_string_value() = shadow_resp_code; + *fields[config_->shadowEngineResultField()].mutable_string_value() = shadow_resp_code; } const auto engine = config_->engine(callbacks_, Filters::Common::RBAC::EnforcementMode::Enforced); @@ -205,7 +178,7 @@ RoleBasedAccessControlFilter::decodeHeaders(Http::RequestHeaderMap& headers, boo callbacks_->streamInfo(), &effective_policy_id); const std::string log_policy_id = effective_policy_id.empty() ? "none" : effective_policy_id; if (!effective_policy_id.empty()) { - *fields[config_->enforcedEffectivePolicyIdField(callbacks_)].mutable_string_value() = + *fields[config_->enforcedEffectivePolicyIdField()].mutable_string_value() = effective_policy_id; } if (allowed) { @@ -215,7 +188,7 @@ RoleBasedAccessControlFilter::decodeHeaders(Http::RequestHeaderMap& headers, boo config_->stats().incPolicyAllowed(effective_policy_id); } - *fields[config_->enforcedEngineResultField(callbacks_)].mutable_string_value() = + *fields[config_->enforcedEngineResultField()].mutable_string_value() = Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().EngineResultAllowed; callbacks_->streamInfo().setDynamicMetadata("envoy.filters.http.rbac", metrics); @@ -230,7 +203,7 @@ RoleBasedAccessControlFilter::decodeHeaders(Http::RequestHeaderMap& headers, boo config_->stats().incPolicyDenied(effective_policy_id); } - *fields[config_->enforcedEngineResultField(callbacks_)].mutable_string_value() = + *fields[config_->enforcedEngineResultField()].mutable_string_value() = Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().EngineResultDenied; callbacks_->streamInfo().setDynamicMetadata("envoy.filters.http.rbac", metrics); diff --git a/source/extensions/filters/http/rbac/rbac_filter.h b/source/extensions/filters/http/rbac/rbac_filter.h index bb6748691fd2..9f021cb94e98 100644 --- a/source/extensions/filters/http/rbac/rbac_filter.h +++ b/source/extensions/filters/http/rbac/rbac_filter.h @@ -35,15 +35,10 @@ class RoleBasedAccessControlRouteSpecificFilterConfig : public Router::RouteSpec return mode == Filters::Common::RBAC::EnforcementMode::Enforced ? engine_.get() : shadow_engine_.get(); } - std::string rulesStatPrefix() const { return rules_stat_prefix_; } - - std::string shadowRulesStatPrefix() const { return shadow_rules_stat_prefix_; } bool perRuleStatsEnabled() const { return per_rule_stats_; } private: - const std::string rules_stat_prefix_; - const std::string shadow_rules_stat_prefix_; ActionValidationVisitor action_validation_visitor_; std::unique_ptr engine_; std::unique_ptr shadow_engine_; @@ -62,11 +57,23 @@ class RoleBasedAccessControlFilterConfig { ProtobufMessage::ValidationVisitor& validation_visitor); Filters::Common::RBAC::RoleBasedAccessControlFilterStats& stats() { return stats_; } - std::string shadowEffectivePolicyIdField(const Http::StreamFilterCallbacks* callbacks) const; - std::string shadowEngineResultField(const Http::StreamFilterCallbacks* callbacks) const; + std::string shadowEffectivePolicyIdField() const { + return shadow_rules_stat_prefix_ + + Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().ShadowEffectivePolicyIdField; + } + std::string shadowEngineResultField() const { + return shadow_rules_stat_prefix_ + + Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().ShadowEngineResultField; + } - std::string enforcedEffectivePolicyIdField(const Http::StreamFilterCallbacks* callbacks) const; - std::string enforcedEngineResultField(const Http::StreamFilterCallbacks* callbacks) const; + std::string enforcedEffectivePolicyIdField() const { + return rules_stat_prefix_ + Filters::Common::RBAC::DynamicMetadataKeysSingleton::get() + .EnforcedEffectivePolicyIdField; + } + std::string enforcedEngineResultField() const { + return rules_stat_prefix_ + + Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().EnforcedEngineResultField; + } const Filters::Common::RBAC::RoleBasedAccessControlEngine* engine(const Http::StreamFilterCallbacks* callbacks, diff --git a/test/extensions/filters/http/rbac/rbac_filter_test.cc b/test/extensions/filters/http/rbac/rbac_filter_test.cc index 3877ab4046bc..2d772457a176 100644 --- a/test/extensions/filters/http/rbac/rbac_filter_test.cc +++ b/test/extensions/filters/http/rbac/rbac_filter_test.cc @@ -576,134 +576,6 @@ TEST_F(RoleBasedAccessControlFilterTest, RouteLocalOverride) { checkAccessLogMetadata(LogResult::Undecided); } -TEST_F(RoleBasedAccessControlFilterTest, RouteLocalOverrideDynamicMetadataStats) { - setupPolicy(envoy::config::rbac::v3::RBAC::DENY, "original_rbac_rules_prefix_"); - - setDestinationPort(123); - setMetadata(); - - // Set up an allow route_config that overrides the deny policy. - envoy::extensions::filters::http::rbac::v3::RBACPerRoute route_config; - route_config.mutable_rbac()->mutable_rules()->set_action(envoy::config::rbac::v3::RBAC::ALLOW); - route_config.mutable_rbac()->mutable_shadow_rules()->set_action( - envoy::config::rbac::v3::RBAC::ALLOW); - route_config.mutable_rbac()->set_rules_stat_prefix("override_rules_stat_prefix_"); - route_config.mutable_rbac()->set_shadow_rules_stat_prefix("override_shadow_rules_stat_prefix_"); - - envoy::config::rbac::v3::Policy policy; - auto policy_rules = policy.add_permissions()->mutable_or_rules(); - policy_rules->add_rules()->set_destination_port(123); - policy.add_principals()->set_any(true); - - envoy::config::rbac::v3::Policy shadow_policy; - auto shadow_policy_rules = shadow_policy.add_permissions()->mutable_or_rules(); - shadow_policy_rules->add_rules()->set_destination_port(123); - shadow_policy.add_principals()->set_any(true); - - (*route_config.mutable_rbac()->mutable_rules()->mutable_policies())["foobar"] = policy; - (*route_config.mutable_rbac()->mutable_shadow_rules()->mutable_policies())["foobar"] = - shadow_policy; - NiceMock factory_context; - NiceMock engine{route_config.rbac().rules(), factory_context}; - NiceMock per_route_config_{route_config, - context_}; - - EXPECT_CALL(engine, handleAction(_, _, _, _)).WillRepeatedly(Return(true)); - EXPECT_CALL(per_route_config_, engine()).WillRepeatedly(ReturnRef(engine)); - - EXPECT_CALL(*callbacks_.route_, mostSpecificPerFilterConfig(_)) - .WillRepeatedly(Return(&per_route_config_)); - - // Filter iteration should continue since the route-specific policy is ALLOW - // and there are enforced and shadow rules. - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(headers_, true)); - ASSERT_TRUE(req_info_.dynamicMetadata().filter_metadata().contains("envoy.filters.http.rbac")); - auto filter_meta = req_info_.dynamicMetadata().filter_metadata().at("envoy.filters.http.rbac"); - - // We expect the route-specific rules and prefix to be used for the enforced - // engine and the shadow rules and prefix to be used for the shadow engine. - ASSERT_TRUE(filter_meta.fields().contains("override_rules_stat_prefix_enforced_engine_result")); - EXPECT_EQ( - "allowed", - filter_meta.fields().at("override_rules_stat_prefix_enforced_engine_result").string_value()); - ASSERT_TRUE( - filter_meta.fields().contains("override_rules_stat_prefix_enforced_effective_policy_id")); - EXPECT_EQ("foobar", filter_meta.fields() - .at("override_rules_stat_prefix_enforced_effective_policy_id") - .string_value()); - - ASSERT_TRUE( - filter_meta.fields().contains("override_shadow_rules_stat_prefix_shadow_engine_result")); - EXPECT_EQ("allowed", filter_meta.fields() - .at("override_shadow_rules_stat_prefix_shadow_engine_result") - .string_value()); - ASSERT_TRUE(filter_meta.fields().contains( - "override_shadow_rules_stat_prefix_shadow_effective_policy_id")); - EXPECT_EQ("foobar", filter_meta.fields() - .at("override_shadow_rules_stat_prefix_shadow_effective_policy_id") - .string_value()); -} - -TEST_F(RoleBasedAccessControlFilterTest, NoRouteLocalOverrideDynamicMetadataStatsEmpty) { - setupPolicy(envoy::config::rbac::v3::RBAC::DENY, "rules_stat_prefix_"); - - setDestinationPort(123); - setMetadata(); - - // Set up an allow route_config that overrides the deny policy. But do not set up stat prefixes. - envoy::extensions::filters::http::rbac::v3::RBACPerRoute route_config; - route_config.mutable_rbac()->mutable_rules()->set_action(envoy::config::rbac::v3::RBAC::ALLOW); - route_config.mutable_rbac()->mutable_shadow_rules()->set_action( - envoy::config::rbac::v3::RBAC::ALLOW); - - envoy::config::rbac::v3::Policy policy; - auto policy_rules = policy.add_permissions()->mutable_or_rules(); - policy_rules->add_rules()->set_destination_port(123); - policy.add_principals()->set_any(true); - - envoy::config::rbac::v3::Policy shadow_policy; - auto shadow_policy_rules = shadow_policy.add_permissions()->mutable_or_rules(); - shadow_policy_rules->add_rules()->set_destination_port(123); - shadow_policy.add_principals()->set_any(true); - - (*route_config.mutable_rbac()->mutable_rules()->mutable_policies())["foobar"] = policy; - (*route_config.mutable_rbac()->mutable_shadow_rules()->mutable_policies())["foobar"] = - shadow_policy; - NiceMock factory_context; - NiceMock engine{route_config.rbac().rules(), factory_context}; - NiceMock per_route_config_{route_config, - context_}; - - EXPECT_CALL(engine, handleAction(_, _, _, _)).WillRepeatedly(Return(true)); - EXPECT_CALL(per_route_config_, engine()).WillRepeatedly(ReturnRef(engine)); - - EXPECT_CALL(*callbacks_.route_, mostSpecificPerFilterConfig(_)) - .WillRepeatedly(Return(&per_route_config_)); - - // Filter iteration should continue since the route-specific policy is ALLOW and there are - // enforced and shadow rules. - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(headers_, true)); - ASSERT_TRUE(req_info_.dynamicMetadata().filter_metadata().contains("envoy.filters.http.rbac")); - auto filter_meta = req_info_.dynamicMetadata().filter_metadata().at("envoy.filters.http.rbac"); - - // We expect the base rules and prefix to be used since no route-specific stat was set up. - ASSERT_TRUE(filter_meta.fields().contains("rules_stat_prefix_enforced_engine_result")); - EXPECT_EQ("allowed", - filter_meta.fields().at("rules_stat_prefix_enforced_engine_result").string_value()); - ASSERT_TRUE(filter_meta.fields().contains("rules_stat_prefix_enforced_effective_policy_id")); - EXPECT_EQ( - "foobar", - filter_meta.fields().at("rules_stat_prefix_enforced_effective_policy_id").string_value()); - - ASSERT_TRUE(filter_meta.fields().contains("shadow_rules_prefix_shadow_engine_result")); - EXPECT_EQ("allowed", - filter_meta.fields().at("shadow_rules_prefix_shadow_engine_result").string_value()); - ASSERT_TRUE(filter_meta.fields().contains("shadow_rules_prefix_shadow_effective_policy_id")); - EXPECT_EQ( - "foobar", - filter_meta.fields().at("shadow_rules_prefix_shadow_effective_policy_id").string_value()); -} - TEST_F(RoleBasedAccessControlFilterTest, RouteLocalOverrideWithPerRuleStats) { setupPolicy(envoy::config::rbac::v3::RBAC::ALLOW); From a35253b28fb99b579956eaf49bd72359a13cd367 Mon Sep 17 00:00:00 2001 From: Fernando Cainelli Date: Sun, 11 Aug 2024 09:50:34 -0300 Subject: [PATCH 083/130] ext_proc: fix MSAN issue in tracer test filter (#35639) Commit Message: MSAN: fix issue in tracer test filter Additional Description: also fix other clang complains "clang: 'setOperation' overrides a member function but is not marked 'override'" Risk Level: Low Fixes https://github.com/envoyproxy/envoy/issues/35630 Signed-off-by: Fernando Cainelli --- .../http/ext_proc/tracer_test_filter.cc | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/test/extensions/filters/http/ext_proc/tracer_test_filter.cc b/test/extensions/filters/http/ext_proc/tracer_test_filter.cc index a76b91e911f8..3c9e5e1047dd 100644 --- a/test/extensions/filters/http/ext_proc/tracer_test_filter.cc +++ b/test/extensions/filters/http/ext_proc/tracer_test_filter.cc @@ -33,7 +33,7 @@ class Span : public Tracing::Span { Span(const std::string& operation_name, ExpectedSpansSharedPtr& expected_spans) : operation_name_(operation_name), expected_spans_(expected_spans){}; - ~Span() { + ~Span() override { EXPECT_TRUE(finished_) << fmt::format("span not finished in operation: {}", operation_name_); for (auto& expect_span : *expected_spans_) { if (expect_span.operation_name != operation_name_) { @@ -56,40 +56,42 @@ class Span : public Tracing::Span { } } - void setTag(absl::string_view name, absl::string_view value) { - tags_.insert_or_assign(name.data(), value.data()); + void setTag(absl::string_view name, absl::string_view value) override { + tags_.insert_or_assign(std::string(name), std::string(value)); } - void setOperation(absl::string_view operation_name) { operation_name_ = operation_name; } - void setSampled(bool do_sample) { sampled_ = do_sample; } - void injectContext(Tracing::TraceContext& trace_context, const Tracing::UpstreamContext&) { + void setOperation(absl::string_view operation_name) override { operation_name_ = operation_name; } + void setSampled(bool do_sample) override { sampled_ = do_sample; } + + void injectContext(Tracing::TraceContext& trace_context, + const Tracing::UpstreamContext&) override { std::string traceparent_header_value = "1"; traceParentHeader().setRefKey(trace_context, traceparent_header_value); context_injected_ = true; } - void setBaggage(absl::string_view, absl::string_view) { /* not implemented */ + void setBaggage(absl::string_view, absl::string_view) override { /* not implemented */ } - void log(SystemTime, const std::string&) { /* not implemented */ + void log(SystemTime, const std::string&) override { /* not implemented */ } - std::string getBaggage(absl::string_view) { + std::string getBaggage(absl::string_view) override { /* not implemented */ return EMPTY_STRING; }; - std::string getTraceId() const { + std::string getTraceId() const override { /* not implemented */ return EMPTY_STRING; }; - std::string getSpanId() const { + std::string getSpanId() const override { /* not implemented */ return EMPTY_STRING; }; Tracing::SpanPtr spawnChild(const Tracing::Config&, const std::string& operation_name, - SystemTime) { + SystemTime) override { return std::make_unique(operation_name, expected_spans_); } - void finishSpan() { finished_ = true; } + void finishSpan() override { finished_ = true; } private: std::string operation_name_; @@ -106,7 +108,7 @@ class Driver : public Tracing::Driver, Logger::Loggable { Driver(const test::integration::filters::TracerTestConfig& test_config, Server::Configuration::CommonFactoryContext&) : expected_spans_(std::make_shared>()) { - for (auto expected_span : test_config.expect_spans()) { + for (const auto& expected_span : test_config.expect_spans()) { ExpectedSpan span; span.operation_name = expected_span.operation_name(); span.sampled = expected_span.sampled(); @@ -123,7 +125,7 @@ class Driver : public Tracing::Driver, Logger::Loggable { return std::make_unique(operation_name, expected_spans_); }; - ~Driver() { + ~Driver() override { for (auto& span : *expected_spans_) { EXPECT_TRUE(span.tested) << fmt::format("missing span with operation '{}'", span.operation_name); From be8683911df78418409db8dce37ee8c17329fcb4 Mon Sep 17 00:00:00 2001 From: Loong Dai Date: Mon, 12 Aug 2024 16:57:18 +0800 Subject: [PATCH 084/130] Add example repo link (#35666) Signed-off-by: Loong --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 247eac04a471..278b6dd0248f 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ involved and how Envoy plays a role, read the CNCF * [Official documentation](https://www.envoyproxy.io/) * [FAQ](https://www.envoyproxy.io/docs/envoy/latest/faq/overview) * [Unofficial Chinese documentation](https://cloudnative.to/envoy/) +* [Example documentation](https://github.com/envoyproxy/examples/) * [Blog](https://medium.com/@mattklein123/envoy-threading-model-a8d44b922310) about the threading model * [Blog](https://medium.com/@mattklein123/envoy-hot-restart-1d16b14555b5) about hot restart * [Blog](https://medium.com/@mattklein123/envoy-stats-b65c7f363342) about stats architecture From 00fd272cd0a0bf5ec5f19045cf17b4e44e6801e2 Mon Sep 17 00:00:00 2001 From: "Vikas Choudhary (vikasc)" Date: Mon, 12 Aug 2024 18:45:44 +0530 Subject: [PATCH 085/130] Testing: tcp/generic/config_test with upstream http filters (#35536) Commit Message: fixes #35509 Signed-off-by: Vikas Choudhary --- test/extensions/upstreams/tcp/generic/BUILD | 1 + .../upstreams/tcp/generic/config_test.cc | 45 ++++++++++++++----- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/test/extensions/upstreams/tcp/generic/BUILD b/test/extensions/upstreams/tcp/generic/BUILD index 57cc2e3fcc9a..c42794c63609 100644 --- a/test/extensions/upstreams/tcp/generic/BUILD +++ b/test/extensions/upstreams/tcp/generic/BUILD @@ -17,6 +17,7 @@ envoy_cc_test( "//source/extensions/upstreams/tcp/generic:config", "//test/mocks/server:factory_context_mocks", "//test/mocks/upstream:upstream_mocks", + "//test/test_common:test_runtime_lib", "@envoy_api//envoy/extensions/filters/network/tcp_proxy/v3:pkg_cc_proto", ], ) diff --git a/test/extensions/upstreams/tcp/generic/config_test.cc b/test/extensions/upstreams/tcp/generic/config_test.cc index 08d2ff0ead46..d227607ba8fe 100644 --- a/test/extensions/upstreams/tcp/generic/config_test.cc +++ b/test/extensions/upstreams/tcp/generic/config_test.cc @@ -9,6 +9,7 @@ #include "test/mocks/tcp/mocks.h" #include "test/mocks/upstream/cluster_manager.h" #include "test/mocks/upstream/load_balancer_context.h" +#include "test/test_common/test_runtime.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -24,11 +25,20 @@ namespace Extensions { namespace Upstreams { namespace Tcp { namespace Generic { -class TcpConnPoolTest : public ::testing::Test { +class TcpConnPoolTest : public testing::TestWithParam { public: TcpConnPoolTest() { EXPECT_CALL(lb_context_, downstreamConnection()).WillRepeatedly(Return(&connection_)); } + + void SetUp() override { + scoped_runtime_.mergeValues({{"envoy.restart_features.upstream_http_filters_with_tcp_proxy", + GetParam() ? "true" : "false"}}); + } + + bool useUpstreamFilters() { return GetParam(); } + + TestScopedRuntime scoped_runtime_; NiceMock thread_local_cluster_; GenericConnPoolFactory factory_; NiceMock callbacks_; @@ -42,14 +52,17 @@ class TcpConnPoolTest : public ::testing::Test { NiceMock context_; }; -TEST_F(TcpConnPoolTest, TestNoTunnelingConfig) { +INSTANTIATE_TEST_SUITE_P(UpstreamHttpFiltersWithTunneling, TcpConnPoolTest, + ::testing::Values(true, false)); + +TEST_P(TcpConnPoolTest, TestNoTunnelingConfig) { EXPECT_CALL(thread_local_cluster_, tcpConnPool(_, _)).WillOnce(Return(absl::nullopt)); EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(), &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); } -TEST_F(TcpConnPoolTest, TestTunnelingDisabledByFilterState) { +TEST_P(TcpConnPoolTest, TestTunnelingDisabledByFilterState) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config_proto; tcp_proxy_.mutable_tunneling_config()->set_hostname("host"); const TcpProxy::TunnelingConfigHelperImpl config(scope_, tcp_proxy_, context_); @@ -65,7 +78,7 @@ TEST_F(TcpConnPoolTest, TestTunnelingDisabledByFilterState) { &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); } -TEST_F(TcpConnPoolTest, TestTunnelingNotDisabledIfFilterStateHasFalseValue) { +TEST_P(TcpConnPoolTest, TestTunnelingNotDisabledIfFilterStateHasFalseValue) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config_proto; tcp_proxy_.mutable_tunneling_config()->set_hostname("host"); const TcpProxy::TunnelingConfigHelperImpl config(scope_, tcp_proxy_, context_); @@ -75,37 +88,43 @@ TEST_F(TcpConnPoolTest, TestTunnelingNotDisabledIfFilterStateHasFalseValue) { std::make_shared(false), StreamInfo::FilterState::StateType::Mutable, StreamInfo::FilterState::LifeSpan::Connection); - EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); + if (!useUpstreamFilters()) { + EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); + } + EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(config), &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); } -TEST_F(TcpConnPoolTest, TestNoConnPool) { +TEST_P(TcpConnPoolTest, TestNoConnPool) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config_proto; tcp_proxy_.mutable_tunneling_config()->set_hostname("host"); const TcpProxy::TunnelingConfigHelperImpl config(scope_, tcp_proxy_, context_); - EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); + if (!useUpstreamFilters()) { + EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); + } EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(config), &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); } -TEST_F(TcpConnPoolTest, Http2Config) { +TEST_P(TcpConnPoolTest, Http2Config) { auto info = std::make_shared(); const std::string fake_cluster_name = "fake_cluster"; envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config_proto; tcp_proxy_.mutable_tunneling_config()->set_hostname("host"); const TcpProxy::TunnelingConfigHelperImpl config(scope_, tcp_proxy_, context_); - - EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); + if (!useUpstreamFilters()) { + EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); + } EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(config), &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); } -TEST_F(TcpConnPoolTest, Http3Config) { +TEST_P(TcpConnPoolTest, Http3Config) { auto info = std::make_shared(); const std::string fake_cluster_name = "fake_cluster"; EXPECT_CALL(*info, features()) @@ -115,7 +134,9 @@ TEST_F(TcpConnPoolTest, Http3Config) { envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy_TunnelingConfig config_proto; tcp_proxy_.mutable_tunneling_config()->set_hostname("host"); const TcpProxy::TunnelingConfigHelperImpl config(scope_, tcp_proxy_, context_); - EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); + if (!useUpstreamFilters()) { + EXPECT_CALL(thread_local_cluster_, httpConnPool(_, _, _)).WillOnce(Return(absl::nullopt)); + } EXPECT_EQ(nullptr, factory_.createGenericConnPool( thread_local_cluster_, TcpProxy::TunnelingConfigHelperOptConstRef(config), &lb_context_, callbacks_, decoder_callbacks_, downstream_stream_info_)); From a8293b730371bcf3a0f54003fa1841beda947502 Mon Sep 17 00:00:00 2001 From: "Antonio V. Leonti" <53806445+antoniovleonti@users.noreply.github.com> Date: Mon, 12 Aug 2024 10:07:24 -0400 Subject: [PATCH 086/130] test interaction between drain upon completion & onDrainTimeout (#35568) Commit Message: test interaction between drain upon completion & onDrainTimeout Additional Description: adds unit & integration tests that demonstrate it is safe to use max_requests_per_connection with max_connection_duration, idle_timeout, and max_stream_duration. (idle_timeout is not explicitly tested but triggers the same exact drain behavior as max_connection_duration.) Risk Level: none Testing: test-only PR --------- Signed-off-by: antoniovleonti --- test/common/http/conn_manager_impl_test_2.cc | 52 +++++++++++ test/integration/protocol_integration_test.cc | 89 +++++++++++++++++++ 2 files changed, 141 insertions(+) diff --git a/test/common/http/conn_manager_impl_test_2.cc b/test/common/http/conn_manager_impl_test_2.cc index fead362c3328..526a874ee330 100644 --- a/test/common/http/conn_manager_impl_test_2.cc +++ b/test/common/http/conn_manager_impl_test_2.cc @@ -479,6 +479,58 @@ TEST_F(HttpConnectionManagerImplTest, MaxRequests) { conn_manager_->onEvent(Network::ConnectionEvent::RemoteClose); } +// max_requests_per_connection is met first then the drain timer fires. Drain timer should be +// ignored. +TEST_F(HttpConnectionManagerImplTest, DrainConnectionUponCompletionVsOnDrainTimeoutHttp11) { + // Http1.1 is used for this test because it defaults to keeping the connection alive. + EXPECT_CALL(*codec_, protocol()).WillRepeatedly(Return(Protocol::Http11)); + max_requests_per_connection_ = 2; + max_connection_duration_ = std::chrono::milliseconds(10); + + Event::MockTimer* connection_duration_timer = setUpTimer(); + EXPECT_CALL(*connection_duration_timer, enableTimer(_, _)); + // Set up connection. + setup(false, ""); + + // Create a filter so we can encode responses. + MockStreamDecoderFilter* filter = new NiceMock(); + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillRepeatedly(Invoke([&](FilterChainManager& manager) -> bool { + auto factory = createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{filter}); + manager.applyFilterFactoryCb({}, factory); + return true; + })); + + startRequest(true); + // Encode response, connection will not be closed since we're using http 1.1. + filter->callbacks_->streamInfo().setResponseCodeDetails(""); + filter->callbacks_->encodeHeaders( + ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, true, "details"); + response_encoder_.stream_.codec_callbacks_->onCodecEncodeComplete(); + + // Now connection is established and codec is not nullptr. This should start the drain timer. + Event::MockTimer* drain_timer = setUpTimer(); + EXPECT_CALL(*drain_timer, enableTimer(_, _)); + connection_duration_timer->invokeCallback(); + EXPECT_EQ(1U, stats_.named_.downstream_cx_max_duration_reached_.value()); + + // Get a fresh mock filter. + filter = new NiceMock(); + // Send a second request. This will cause max_requests_per_connection limit to be reached. + // Connection drain state will be set to closing. + startRequest(true); + EXPECT_EQ(1U, stats_.named_.downstream_cx_max_requests_reached_.value()); + + drain_timer->invokeCallback(); + + // Send the last response. The drain timer having already fired should not be an issue. + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)); + filter->callbacks_->streamInfo().setResponseCodeDetails(""); + filter->callbacks_->encodeHeaders( + ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, true, "details"); + response_encoder_.stream_.codec_callbacks_->onCodecEncodeComplete(); +} + TEST_F(HttpConnectionManagerImplTest, ConnectionDuration) { max_connection_duration_ = (std::chrono::milliseconds(10)); Event::MockTimer* connection_duration_timer = setUpTimer(); diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index f45f177a5b2e..90edb9b80931 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -3102,6 +3102,95 @@ TEST_P(DownstreamProtocolIntegrationTest, MaxRequestsPerConnectionReached) { ASSERT_TRUE(codec_client_->waitForDisconnect()); } +// Test that onDrainTimeout allows current stream to finish before closing connection for http1. +TEST_P(DownstreamProtocolIntegrationTest, MaxRequestsPerConnectionVsMaxConnectionDuration) { + config_helper_.setDownstreamMaxRequestsPerConnection(2); + config_helper_.setDownstreamMaxConnectionDuration(std::chrono::milliseconds(500)); + config_helper_.addConfigModifier( + [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { hcm.mutable_drain_timeout()->set_nanos(500'000'000 /*=500ms*/); }); + + initialize(); + codec_client_ = makeHttpConnection(lookupPort("http")); + + sendRequestAndWaitForResponse(default_request_headers_, 0, default_response_headers_, 0); + EXPECT_EQ(test_server_->counter("http.config_test.downstream_cx_max_requests_reached")->value(), + 0); + + test_server_->waitForCounterGe("http.config_test.downstream_cx_max_duration_reached", 1); + // http1 is not closed at this point because envoy needs to send a response with the + // connection:close response header to be able to safely close the connection. For other protocols + // it's safe for envoy to just close the connection, so they do so. + if (downstream_protocol_ != Http::CodecType::HTTP1) { + ASSERT_TRUE(codec_client_->waitForDisconnect()); + EXPECT_TRUE(codec_client_->sawGoAway()); + // The rest of the test is only for http1. + return; + } + + // Sending second request. + auto response_2 = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + waitForNextUpstreamRequest(); + + // Before sending the response, sleep past the drain timer. Nothing should happen. + timeSystem().advanceTimeWait(Seconds(1)); + EXPECT_FALSE(codec_client_->sawGoAway()); + + upstream_request_->encodeHeaders(default_response_headers_, true); + ASSERT_TRUE(codec_client_->waitForDisconnect()); + + ASSERT_TRUE(response_2->waitForEndStream()); + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_TRUE(response_2->complete()); + EXPECT_EQ(test_server_->counter("http.config_test.downstream_cx_max_requests_reached")->value(), + 1); + + if (downstream_protocol_ == Http::CodecType::HTTP1) { + ASSERT_NE(nullptr, response_2->headers().Connection()); + EXPECT_EQ("close", response_2->headers().getConnectionValue()); + } else { + EXPECT_TRUE(codec_client_->sawGoAway()); + } + ASSERT_TRUE(codec_client_->waitForDisconnect()); +} + +// Test that max stream duration still works after max requests per connection is reached (i.e. the +// final response is still time bounded). Also, if if max_stream_duration is triggered, it should +// add the connection:close header if the downstream protocol is http1 and this will be the last +// response! +TEST_P(DownstreamProtocolIntegrationTest, MaxRequestsPerConnectionVsMaxStreamDuration) { + config_helper_.setDownstreamMaxRequestsPerConnection(2); + config_helper_.setDownstreamMaxStreamDuration(std::chrono::milliseconds(500)); + + initialize(); + codec_client_ = makeHttpConnection(lookupPort("http")); + + // Sending first request and waiting to complete the response. + sendRequestAndWaitForResponse(default_request_headers_, 0, default_response_headers_, 0); + EXPECT_EQ(test_server_->counter("http.config_test.downstream_cx_max_requests_reached")->value(), + 0); + + // Sending second request and waiting to complete the response. + auto response_2 = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + waitForNextUpstreamRequest(); + EXPECT_EQ(test_server_->counter("http.config_test.downstream_cx_max_requests_reached")->value(), + 1); + + // Don't send a response. HCM should sendLocalReply after max stream duration has elapsed. + test_server_->waitForCounterGe("http.config_test.downstream_rq_max_duration_reached", 1); + + if (downstream_protocol_ == Http::CodecType::HTTP1) { + ASSERT_TRUE(codec_client_->waitForDisconnect()); + ASSERT_TRUE(response_2->complete()); + // This will be the last request / response; envoy's going to close the connection after this + // stream ends. We should notify the client. + EXPECT_EQ("close", response_2->headers().getConnectionValue()); + } else { + ASSERT_TRUE(response_2->waitForEndStream()); + codec_client_->close(); + } +} + // Make sure that invalid authority headers get blocked at or before the HCM. TEST_P(DownstreamProtocolIntegrationTest, InvalidAuthority) { disable_client_header_validation_ = true; From aed9ef29b35cf9a942a2c9e14f785fbb31414fff Mon Sep 17 00:00:00 2001 From: "Adi (Suissa) Peleg" Date: Mon, 12 Aug 2024 13:24:21 -0400 Subject: [PATCH 087/130] test: update a broken test due to PRs race (#35671) Commit Message: test: update a broken test due to PRs race Additional Description: PR #35568 used an old `setup()` method call which was modified by PR #35209 that was merged just before it, causing a build failure. This PR update the test and fixes the build failure. Risk Level: low Testing: N/A Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A Signed-off-by: Adi Suissa-Peleg --- test/common/http/conn_manager_impl_test_2.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/common/http/conn_manager_impl_test_2.cc b/test/common/http/conn_manager_impl_test_2.cc index 526a874ee330..8a37e53703ca 100644 --- a/test/common/http/conn_manager_impl_test_2.cc +++ b/test/common/http/conn_manager_impl_test_2.cc @@ -490,7 +490,7 @@ TEST_F(HttpConnectionManagerImplTest, DrainConnectionUponCompletionVsOnDrainTime Event::MockTimer* connection_duration_timer = setUpTimer(); EXPECT_CALL(*connection_duration_timer, enableTimer(_, _)); // Set up connection. - setup(false, ""); + setup(); // Create a filter so we can encode responses. MockStreamDecoderFilter* filter = new NiceMock(); From 5b80c5d9a9e63bf274963862f43ab5b9b1e57f50 Mon Sep 17 00:00:00 2001 From: phlax Date: Mon, 12 Aug 2024 18:51:50 +0100 Subject: [PATCH 088/130] repo/ci: Shift deb verification -> github (#35579) Signed-off-by: Ryan Northey --- .azure-pipelines/stage/verify.yml | 84 --------- .azure-pipelines/stages.yml | 10 -- ...stage_publish.yml => _publish_publish.yml} | 0 .github/workflows/_publish_verify.yml | 166 ++++++++++++++++++ .github/workflows/_run.yml | 12 +- .github/workflows/_stage_verify.yml | 88 ---------- .github/workflows/envoy-publish.yml | 4 +- 7 files changed, 177 insertions(+), 187 deletions(-) delete mode 100644 .azure-pipelines/stage/verify.yml rename .github/workflows/{_stage_publish.yml => _publish_publish.yml} (100%) create mode 100644 .github/workflows/_publish_verify.yml delete mode 100644 .github/workflows/_stage_verify.yml diff --git a/.azure-pipelines/stage/verify.yml b/.azure-pipelines/stage/verify.yml deleted file mode 100644 index 20bff07dc7cc..000000000000 --- a/.azure-pipelines/stage/verify.yml +++ /dev/null @@ -1,84 +0,0 @@ -parameters: - -# Auth -- name: authGCP - type: string - default: "" - - -jobs: -- job: packages_x64 - displayName: Debs (x64) - condition: | - and(not(canceled()), - succeeded(), - ne(stageDependencies.env.repo.outputs['changed.mobileOnly'], 'true'), - ne(stageDependencies.env.repo.outputs['changed.docsOnly'], 'true'), - ne(stageDependencies.env.repo.outputs['changed.examplesOnly'], 'true')) - timeoutInMinutes: 120 - pool: envoy-x64-small - steps: - - task: DownloadBuildArtifacts@0 - inputs: - buildType: current - artifactName: "distribution" - itemPattern: "distribution/x64/packages.x64.tar.gz" - downloadType: single - targetPath: $(Build.StagingDirectory) - - template: ../ci.yml - parameters: - ciTarget: verify_distro - cacheName: verify_distro - publishTestResults: false - tmpfsDockerDisabled: true - env: - ENVOY_DOCKER_IN_DOCKER: 1 - -- job: packages_arm64 - displayName: Debs (arm64) - condition: | - and(not(canceled()), - succeeded(), - ne(stageDependencies.env.repo.outputs['changed.mobileOnly'], 'true'), - ne(stageDependencies.env.repo.outputs['changed.docsOnly'], 'true'), - ne(stageDependencies.env.repo.outputs['changed.examplesOnly'], 'true')) - timeoutInMinutes: 120 - pool: "envoy-arm-small" - steps: - - task: DownloadBuildArtifacts@0 - inputs: - buildType: current - artifactName: "distribution" - itemPattern: "distribution/arm64/packages.arm64.tar.gz" - downloadType: single - targetPath: $(Build.StagingDirectory) - - template: ../ci.yml - parameters: - managedAgent: false - ciTarget: verify_distro - cacheName: verify_distro - rbe: false - artifactSuffix: ".arm64" - publishTestResults: false - tmpfsDockerDisabled: true - env: - ENVOY_DOCKER_IN_DOCKER: 1 - -- job: verified - displayName: Verification complete - dependsOn: ["packages_x64", "packages_arm64"] - pool: - vmImage: $(agentUbuntu) - # This condition ensures that this (required) check passes if all of - # the preceding checks either pass or are skipped - # adapted from: - # https://learn.microsoft.com/en-us/azure/devops/pipelines/process/expressions?view=azure-devops#job-to-job-dependencies-within-one-stage - condition: | - and( - eq(variables['Build.Reason'], 'PullRequest'), - in(dependencies.packages_x64.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'), - in(dependencies.packages_arm64.result, 'Succeeded', 'SucceededWithIssues', 'Skipped')) - steps: - - checkout: none - - bash: | - echo "checks complete" diff --git a/.azure-pipelines/stages.yml b/.azure-pipelines/stages.yml index a0fc0c9cbc1b..72438bf0ab46 100644 --- a/.azure-pipelines/stages.yml +++ b/.azure-pipelines/stages.yml @@ -103,13 +103,3 @@ stages: runPackaging: variables['RUN_PACKAGING'] publishDockerhub: variables['PUBLISH_DOCKERHUB'] publishGithubRelease: variables['PUBLISH_GITHUB_RELEASE'] - -- stage: verify - displayName: Verify - dependsOn: ["env", "publish"] - variables: - RUN_DOCKER: $[stageDependencies.env.repo.outputs['run.docker']] - jobs: - - template: stage/verify.yml - parameters: - authGCP: $(GcpServiceAccountKey) diff --git a/.github/workflows/_stage_publish.yml b/.github/workflows/_publish_publish.yml similarity index 100% rename from .github/workflows/_stage_publish.yml rename to .github/workflows/_publish_publish.yml diff --git a/.github/workflows/_publish_verify.yml b/.github/workflows/_publish_verify.yml new file mode 100644 index 000000000000..075e4aad0440 --- /dev/null +++ b/.github/workflows/_publish_verify.yml @@ -0,0 +1,166 @@ +name: Verify + +permissions: + contents: read + +on: + workflow_call: + inputs: + request: + type: string + required: true + trusted: + type: boolean + required: true + +concurrency: + group: >- + ${{ github.actor != 'trigger-release-envoy[bot]' + && github.event.inputs.head_ref + || github.run_id + }}-${{ github.event.workflow.id }}-verify + cancel-in-progress: true + + +jobs: + verify-examples: + permissions: + contents: read + packages: read + name: ${{ matrix.name || matrix.target }} + uses: ./.github/workflows/_run.yml + with: + bazel-extra: ${{ matrix.bazel-extra || '--config=rbe-envoy-engflow' }} + cache-build-image: ${{ matrix.cache-build-image }} + cache-build-image-key-suffix: ${{ matrix.arch == 'arm64' && format('-{0}', matrix.arch) || '' }} + container-command: ${{ matrix.container-command }} + concurrency-suffix: -${{ matrix.arch || 'x64' }} + rbe: ${{ matrix.rbe }} + request: ${{ inputs.request }} + runs-on: ${{ matrix.runs-on || 'ubuntu-24.04' }} + steps-pre: ${{ matrix.steps-pre }} + source: ${{ matrix.source }} + target: ${{ matrix.target }} + trusted: ${{ inputs.trusted }} + strategy: + fail-fast: false + matrix: + include: + - name: examples + target: verify_examples + rbe: false + source: | + export NO_BUILD_SETUP=1 + steps-pre: | + - run: | + # Install expected host packages + export DEBIAN_FRONTEND=noninteractive + sudo apt-get -qq update -y + sudo apt-get -qq install -y --no-install-recommends expect gettext yq whois + shell: bash + - id: url + uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.35 + with: + options: -Rr + input: >- + ${{ inputs.trusted + && fromJSON(inputs.request).request.sha + || fromJSON(inputs.request).request.ref }} + filter: | + .[:7] as $sha + | if ${{ inputs.trusted }} then + "envoy-postsubmit" + else + "envoy-pr" + end + | . as $bucket + | "https://storage.googleapis.com/\($bucket)/\($sha)" + - uses: envoyproxy/toolshed/gh-actions/docker/fetch@actions-v0.2.35 + with: + url: %{{ steps.url.outputs.value }}/docker/envoy.tar + variant: dev + - uses: envoyproxy/toolshed/gh-actions/docker/fetch@actions-v0.2.35 + with: + url: %{{ steps.url.outputs.value }}/docker/envoy-contrib.tar + variant: contrib-dev + - uses: envoyproxy/toolshed/gh-actions/docker/fetch@actions-v0.2.35 + with: + url: %{{ steps.url.outputs.value }}/docker/envoy-google-vrp.tar + variant: google-vrp-dev + - run: docker images | grep envoy + shell: bash + + verify-distro: + permissions: + contents: read + packages: read + name: ${{ matrix.name || matrix.target }} + uses: ./.github/workflows/_run.yml + with: + bazel-extra: ${{ matrix.bazel-extra || '--config=rbe-envoy-engflow' }} + cache-build-image: ${{ fromJSON(inputs.request).request.build-image.default }} + cache-build-image-key-suffix: ${{ matrix.arch == 'arm64' && format('-{0}', matrix.arch) || '' }} + container-command: ./ci/run_envoy_docker.sh + concurrency-suffix: -${{ matrix.arch || 'x64' }} + rbe: ${{ matrix.rbe && matrix.rbe || false }} + request: ${{ inputs.request }} + runs-on: ${{ matrix.runs-on || 'ubuntu-24.04' }} + source: | + export NO_BUILD_SETUP=1 + export ENVOY_DOCKER_IN_DOCKER=1 + target: ${{ matrix.target }} + trusted: ${{ inputs.trusted }} + steps-pre: | + - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.30 + id: url + with: + options: -Rr + input: >- + ${{ inputs.trusted + && fromJSON(inputs.request).request.sha + || fromJSON(inputs.request).request.ref }} + filter: | + .[:7] as $sha + | if ${{ inputs.trusted }} then + "envoy-postsubmit" + else + "envoy-pr" + end + | . as $bucket + | "https://storage.googleapis.com/\($bucket)/\($sha)/release/release.signed.tar.zst" + - uses: envoyproxy/toolshed/gh-actions/fetch@actions-v0.2.30 + id: fetch + with: + url: %{{ steps.url.outputs.value }} + - run: | + echo ARCH=${{ matrix.arch || 'x64' }} >> $GITHUB_ENV + echo DEB_ARCH=${{ matrix.arch != 'arm64' && 'amd64' || 'arm64' }} >> $GITHUB_ENV + shell: bash + - run: | + TEMP_DIR=$(mktemp -d) + zstd --stdout -d %{{ steps.fetch.outputs.path }} | tar --warning=no-timestamp -xf - -C "${TEMP_DIR}" + mkdir ${TEMP_DIR}/debs + tar xf ${TEMP_DIR}/bin/debs.tar.gz -C ${TEMP_DIR}/debs + mkdir -p ${TEMP_DIR}/distribution/deb + cp -a ${TEMP_DIR}/debs/*_${DEB_ARCH}* ${TEMP_DIR}/distribution/deb + cp -a ${TEMP_DIR}/signing.key ${TEMP_DIR}/distribution + mkdir -p %{{ runner.temp }}/distribution/${ARCH} + tar czf %{{ runner.temp }}/distribution/${ARCH}/packages.${ARCH}.tar.gz -C ${TEMP_DIR}/distribution . + shell: bash + + strategy: + fail-fast: false + matrix: + include: + + - name: verify_distro_x64 + target: verify_distro + rbe: true + + - name: verify_distro_arm64 + target: verify_distro + arch: arm64 + bazel-extra: >- + --config=cache-envoy-engflow + --config=bes-envoy-engflow + runs-on: envoy-arm64-small diff --git a/.github/workflows/_run.yml b/.github/workflows/_run.yml index ce82d5ac8ad1..e65e87a4e2cb 100644 --- a/.github/workflows/_run.yml +++ b/.github/workflows/_run.yml @@ -21,11 +21,16 @@ on: default: 75 cache-build-image: type: string + cache-build-image-key-suffix: + type: string catch-errors: type: boolean default: false checkout-extra: type: string + concurrency-suffix: + type: string + default: container-command: type: string default: ./ci/run_envoy_docker.sh @@ -141,7 +146,7 @@ concurrency: ${{ github.actor != 'trigger-release-envoy[bot]' && github.head_ref || github.run_id - }}-${{ github.workflow }}-${{ inputs.target }} + }}-${{ github.workflow }}-${{ inputs.target }}${{ inputs.concurrency-suffix }} cancel-in-progress: true env: @@ -190,6 +195,7 @@ jobs: uses: envoyproxy/toolshed/gh-actions/docker/cache/restore@actions-v0.2.35 with: image_tag: ${{ inputs.cache-build-image }} + key-suffix: ${{ inputs.cache-build-image-key-suffix }} - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.35 id: appauth @@ -259,11 +265,11 @@ jobs: env: GITHUB_TOKEN: ${{ inputs.trusted && steps.appauth.outputs.token || github.token }} ENVOY_DOCKER_BUILD_DIR: ${{ runner.temp }} - ENVOY_RBE: ${{ inputs.rbe != 'false' && 1 || '' }} + ENVOY_RBE: ${{ inputs.rbe == true && 1 || '' }} RBE_KEY: ${{ secrets.rbe-key }} BAZEL_BUILD_EXTRA_OPTIONS: >- --config=remote-ci ${{ inputs.bazel-extra }} - ${{ inputs.rbe != 'false' && format('--jobs={0}', inputs.bazel-rbe-jobs) || '' }} + ${{ inputs.rbe == true && format('--jobs={0}', inputs.bazel-rbe-jobs) || '' }} BAZEL_FAKE_SCM_REVISION: ${{ github.event_name == 'pull_request' && 'e3b4a6e9570da15ac1caffdded17a8bebdc7dfc9' || '' }} CI_TARGET_BRANCH: ${{ fromJSON(inputs.request).request.target-branch }} diff --git a/.github/workflows/_stage_verify.yml b/.github/workflows/_stage_verify.yml deleted file mode 100644 index 3011bbc9bd15..000000000000 --- a/.github/workflows/_stage_verify.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: Verify - -permissions: - contents: read - -on: - workflow_call: - inputs: - request: - type: string - required: true - trusted: - type: boolean - required: true - -concurrency: - group: >- - ${{ github.actor != 'trigger-release-envoy[bot]' - && github.event.inputs.head_ref - || github.run_id - }}-${{ github.event.workflow.id }}-verify - cancel-in-progress: true - - -jobs: - verify: - permissions: - contents: read - packages: read - name: ${{ matrix.name || matrix.target }} - uses: ./.github/workflows/_run.yml - with: - cache-build-image: - container-command: - rbe: ${{ matrix.rbe }} - request: ${{ inputs.request }} - runs-on: ubuntu-24.04 - steps-pre: ${{ matrix.steps-pre }} - source: ${{ matrix.source }} - target: ${{ matrix.target }} - trusted: ${{ inputs.trusted }} - strategy: - fail-fast: false - matrix: - include: - - name: examples - target: verify_examples - source: | - export NO_BUILD_SETUP=1 - rbe: false - steps-pre: | - - id: url - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.35 - with: - options: -Rr - input: >- - ${{ inputs.trusted - && fromJSON(inputs.request).request.sha - || fromJSON(inputs.request).request.ref }} - filter: | - .[:7] as $sha - | if ${{ inputs.trusted }} then - "envoy-postsubmit" - else - "envoy-pr" - end - | . as $bucket - | "https://storage.googleapis.com/\($bucket)/\($sha)" - - uses: envoyproxy/toolshed/gh-actions/docker/fetch@actions-v0.2.35 - with: - url: %{{ steps.url.outputs.value }}/docker/envoy.tar - variant: dev - - uses: envoyproxy/toolshed/gh-actions/docker/fetch@actions-v0.2.35 - with: - url: %{{ steps.url.outputs.value }}/docker/envoy-contrib.tar - variant: contrib-dev - - uses: envoyproxy/toolshed/gh-actions/docker/fetch@actions-v0.2.35 - with: - url: %{{ steps.url.outputs.value }}/docker/envoy-google-vrp.tar - variant: google-vrp-dev - - run: docker images | grep envoy - shell: bash - - run: | - # Install expected host packages - export DEBIAN_FRONTEND=noninteractive - sudo apt-get -qq update -y - sudo apt-get -qq install -y --no-install-recommends expect gettext yq whois - shell: bash diff --git a/.github/workflows/envoy-publish.yml b/.github/workflows/envoy-publish.yml index ab7a7b896292..df33cd5221ba 100644 --- a/.github/workflows/envoy-publish.yml +++ b/.github/workflows/envoy-publish.yml @@ -62,7 +62,7 @@ jobs: if: ${{ fromJSON(needs.load.outputs.request).run.publish }} needs: - load - uses: ./.github/workflows/_stage_publish.yml + uses: ./.github/workflows/_publish_publish.yml name: Publish with: request: ${{ needs.load.outputs.request }} @@ -75,7 +75,7 @@ jobs: if: ${{ fromJSON(needs.load.outputs.request).run.verify }} needs: - load - uses: ./.github/workflows/_stage_verify.yml + uses: ./.github/workflows/_publish_verify.yml name: Verify with: request: ${{ needs.load.outputs.request }} From 53152fbb7b271263db91c70a0acb5413e8fcdcaa Mon Sep 17 00:00:00 2001 From: phlax Date: Tue, 13 Aug 2024 08:08:53 +0100 Subject: [PATCH 089/130] bazel/ci: Shift GCS upload script to bazel (#35667) Signed-off-by: Ryan Northey --- ci/do_ci.sh | 34 +++++++-- tools/gcs/BUILD | 16 +++++ .../gcs/upload.sh | 71 ++++++++++++------- 3 files changed, 91 insertions(+), 30 deletions(-) create mode 100644 tools/gcs/BUILD rename ci/upload_gcs_artifact.sh => tools/gcs/upload.sh (52%) diff --git a/ci/do_ci.sh b/ci/do_ci.sh index 8f3db2df7474..038a7c7e4c06 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -55,6 +55,8 @@ FETCH_PROTO_TARGETS=( @com_github_bufbuild_buf//:bin/buf //tools/proto_format/...) +GCS_REDIRECT_PATH="${SYSTEM_PULLREQUEST_PULLREQUESTNUMBER:-${BUILD_SOURCEBRANCHNAME}}" + retry () { local n wait iterations wait="${1}" @@ -481,7 +483,16 @@ case $CI_TARGET in else TARGET=coverage fi - "${ENVOY_SRCDIR}/ci/upload_gcs_artifact.sh" "/source/generated/${TARGET}" "$TARGET" + GCS_LOCATION=$( + bazel run //tools/gcs:upload \ + "${GCS_ARTIFACT_BUCKET}" \ + "${GCP_SERVICE_ACCOUNT_KEY_PATH}" \ + "/source/generated/${TARGET}" \ + "$TARGET" \ + "${GCS_REDIRECT_PATH}") + if [[ "${COVERAGE_FAILED}" -eq 1 ]]; then + echo "##vso[task.logissue type=error]Coverage failed, check artifact at: ${GCS_LOCATION}" + fi ;; debug) @@ -642,7 +653,12 @@ case $CI_TARGET in docker-upload) setup_clang_toolchain - "${ENVOY_SRCDIR}/ci/upload_gcs_artifact.sh" "${BUILD_DIR}/build_images" docker + bazel run //tools/gcs:upload \ + "${GCS_ARTIFACT_BUCKET}" \ + "${GCP_SERVICE_ACCOUNT_KEY_PATH}" \ + "${BUILD_DIR}/build_images" \ + "docker" \ + "${GCS_REDIRECT_PATH}" ;; dockerhub-publish) @@ -684,7 +700,12 @@ case $CI_TARGET in docs-upload) setup_clang_toolchain - "${ENVOY_SRCDIR}/ci/upload_gcs_artifact.sh" /source/generated/docs docs + bazel run //tools/gcs:upload \ + "${GCS_ARTIFACT_BUCKET}" \ + "${GCP_SERVICE_ACCOUNT_KEY_PATH}" \ + /source/generated/docs \ + docs \ + "${GCS_REDIRECT_PATH}" ;; fetch|fetch-*) @@ -907,7 +928,12 @@ case $CI_TARGET in setup_clang_toolchain bazel build "${BAZEL_BUILD_OPTIONS[@]}" //distribution:signed cp -a bazel-bin/distribution/release.signed.tar.zst "${BUILD_DIR}/envoy/" - "${ENVOY_SRCDIR}/ci/upload_gcs_artifact.sh" "${BUILD_DIR}/envoy" release + bazel run //tools/gcs:upload \ + "${GCS_ARTIFACT_BUCKET}" \ + "${GCP_SERVICE_ACCOUNT_KEY_PATH}" \ + "${BUILD_DIR}/envoy" \ + "release" \ + "${GCS_REDIRECT_PATH}" ;; sizeopt) diff --git a/tools/gcs/BUILD b/tools/gcs/BUILD new file mode 100644 index 000000000000..c42a57de4cfc --- /dev/null +++ b/tools/gcs/BUILD @@ -0,0 +1,16 @@ +load("@envoy_repo//:path.bzl", "PATH") +load("//bazel:envoy_build_system.bzl", "envoy_package") + +licenses(["notice"]) # Apache 2 + +envoy_package() + +sh_binary( + name = "upload", + srcs = ["upload.sh"], + data = ["//tools/gsutil"], + env = { + "ENVOY_SOURCE_DIR": PATH, + "GSUTIL": "$(location //tools/gsutil)", + }, +) diff --git a/ci/upload_gcs_artifact.sh b/tools/gcs/upload.sh similarity index 52% rename from ci/upload_gcs_artifact.sh rename to tools/gcs/upload.sh index f088f60299b8..f6e26516ce99 100755 --- a/ci/upload_gcs_artifact.sh +++ b/tools/gcs/upload.sh @@ -2,16 +2,43 @@ set -e -o pipefail +GCS_ARTIFACT_BUCKET="${1:-}" +GCP_SERVICE_ACCOUNT_KEY_PATH="${2:-}" +UPLOAD_DIRECTORY="${3:-}" +TARGET_SUFFIX="${4:-}" +REDIRECT_PATH="${5:-}" + +# +# $ bazel run //tools/gcs:upload BUCKETNAME KEY_PATH REDIRECT_PATH + + +if [[ -z "${GSUTIL}" ]]; then + echo "GSUTIL is not set, not uploading artifacts." + exit 1 +fi + +if [[ -z "${ENVOY_SOURCE_DIR}" ]]; then + echo "ENVOY_SOURCE_DIR is not set, not uploading artifacts." + exit 1 +fi + if [[ -z "${GCS_ARTIFACT_BUCKET}" ]]; then echo "Artifact bucket is not set, not uploading artifacts." exit 1 fi -read -ra BAZEL_STARTUP_OPTIONS <<< "${BAZEL_STARTUP_OPTION_LIST:-}" -read -ra BAZEL_BUILD_OPTIONS <<< "${BAZEL_BUILD_OPTION_LIST:-}" - if [[ ! -s "${GCP_SERVICE_ACCOUNT_KEY_PATH}" ]]; then - echo "GCP key is not set, not uploading artifacts." + echo "GCP key path is not set, not uploading artifacts." + exit 1 +fi + +if [[ -z "${UPLOAD_DIRECTORY}" ]]; then + echo "UPLOAD_DIRECTORY is not set, not uploading artifacts." + exit 1 +fi + +if [[ -z "${TARGET_SUFFIX}" ]]; then + echo "TARGET_SUFFIX is not set, not uploading artifacts." exit 1 fi @@ -20,13 +47,6 @@ cat < ~/.boto gs_service_key_file=${GCP_SERVICE_ACCOUNT_KEY_PATH} EOF -SOURCE_DIRECTORY="$1" -TARGET_SUFFIX="$2" - -if [ ! -d "${SOURCE_DIRECTORY}" ]; then - echo "ERROR: ${SOURCE_DIRECTORY} is not found." - exit 1 -fi # Upload to the last commit sha (first 7 chars) # the bucket is either `envoy-postsubmit` or `envoy-pr` @@ -40,32 +60,31 @@ fi # https://storage.googleapis.com/envoy-pr/28462/docs/index.html # -UPLOAD_PATH="$(git rev-parse HEAD | head -c7)" -REDIRECT_PATH="${SYSTEM_PULLREQUEST_PULLREQUESTNUMBER:-${BUILD_SOURCEBRANCHNAME}}" +UPLOAD_PATH="$(git -C "${ENVOY_SOURCE_DIR}" rev-parse HEAD | head -c7)" GCS_LOCATION="${GCS_ARTIFACT_BUCKET}/${UPLOAD_PATH}/${TARGET_SUFFIX}" echo "Uploading to gs://${GCS_LOCATION} ..." -bazel "${BAZEL_STARTUP_OPTIONS[@]}" run "${BAZEL_BUILD_OPTIONS[@]}" \ - //tools/gsutil \ - -- -mq rsync \ - -dr "${SOURCE_DIRECTORY}" \ +"${GSUTIL}" \ + -mq rsync \ + -dr "${UPLOAD_DIRECTORY}" \ "gs://${GCS_LOCATION}" +if [[ -z "${REDIRECT_PATH}" ]]; then + echo "Artifacts uploaded to: https://storage.googleapis.com/${GCS_LOCATION}" >&2 + exit 0 +fi + TMP_REDIRECT="/tmp/redirect/${REDIRECT_PATH}/${TARGET_SUFFIX}" mkdir -p "$TMP_REDIRECT" echo "" \ > "${TMP_REDIRECT}/index.html" GCS_REDIRECT="${GCS_ARTIFACT_BUCKET}/${REDIRECT_PATH}/${TARGET_SUFFIX}" -echo "Uploading redirect to gs://${GCS_REDIRECT} ..." -bazel "${BAZEL_STARTUP_OPTIONS[@]}" run "${BAZEL_BUILD_OPTIONS[@]}" \ - //tools/gsutil \ - -- -h "Cache-Control:no-cache,max-age=0" \ +echo "Uploading redirect to gs://${GCS_REDIRECT} ..." >&2 +"${GSUTIL}" \ + -h "Cache-Control:no-cache,max-age=0" \ -mq rsync \ -dr "${TMP_REDIRECT}" \ "gs://${GCS_REDIRECT}" -if [[ "${COVERAGE_FAILED}" -eq 1 ]]; then - echo "##vso[task.logissue type=error]Coverage failed, check artifact at: https://storage.googleapis.com/${GCS_LOCATION}/index.html" -fi - -echo "Artifacts uploaded to: https://storage.googleapis.com/${GCS_LOCATION}/index.html" +echo "Artifacts uploaded to: https://storage.googleapis.com/${GCS_REDIRECT}/index.html" >&2 +echo "https://storage.googleapis.com/${GCS_REDIRECT}/index.html" From f65fa886a58de3fb1edd6d59dab7ece2cfa28520 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 09:12:40 +0100 Subject: [PATCH 090/130] build(deps): bump aiohttp from 3.10.2 to 3.10.3 in /tools/base (#35664) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/base/requirements.txt | 154 ++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index c0d360fa9d91..a82817c6cb94 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -92,83 +92,83 @@ aiohappyeyeballs==2.3.5 \ --hash=sha256:4d6dea59215537dbc746e93e779caea8178c866856a721c9c660d7a5a7b8be03 \ --hash=sha256:6fa48b9f1317254f122a07a131a86b71ca6946ca989ce6326fff54a99a920105 # via aiohttp -aiohttp==3.10.2 \ - --hash=sha256:01c98041f90927c2cbd72c22a164bb816fa3010a047d264969cf82e1d4bcf8d1 \ - --hash=sha256:0df930015db36b460aa9badbf35eccbc383f00d52d4b6f3de2ccb57d064a6ade \ - --hash=sha256:1238fc979160bc03a92fff9ad021375ff1c8799c6aacb0d8ea1b357ea40932bb \ - --hash=sha256:14dc3fcb0d877911d775d511eb617a486a8c48afca0a887276e63db04d3ee920 \ - --hash=sha256:14eb6b17f6246959fb0b035d4f4ae52caa870c4edfb6170aad14c0de5bfbf478 \ - --hash=sha256:18186a80ec5a701816adbf1d779926e1069392cf18504528d6e52e14b5920525 \ - --hash=sha256:19073d57d0feb1865d12361e2a1f5a49cb764bf81a4024a3b608ab521568093a \ - --hash=sha256:1aa005f060aff7124cfadaa2493f00a4e28ed41b232add5869e129a2e395935a \ - --hash=sha256:2c474af073e1a6763e1c5522bbb2d85ff8318197e4c6c919b8d7886e16213345 \ - --hash=sha256:30a9d59da1543a6f1478c3436fd49ec59be3868bca561a33778b4391005e499d \ - --hash=sha256:341f8ece0276a828d95b70cd265d20e257f5132b46bf77d759d7f4e0443f2906 \ - --hash=sha256:352f3a4e5f11f3241a49b6a48bc5b935fabc35d1165fa0d87f3ca99c1fcca98b \ - --hash=sha256:377220a5efde6f9497c5b74649b8c261d3cce8a84cb661be2ed8099a2196400a \ - --hash=sha256:3988044d1635c7821dd44f0edfbe47e9875427464e59d548aece447f8c22800a \ - --hash=sha256:465e445ec348d4e4bd349edd8b22db75f025da9d7b6dc1369c48e7935b85581e \ - --hash=sha256:494a6f77560e02bd7d1ab579fdf8192390567fc96a603f21370f6e63690b7f3d \ - --hash=sha256:49904f38667c44c041a0b44c474b3ae36948d16a0398a8f8cd84e2bb3c42a069 \ - --hash=sha256:4d1f694b5d6e459352e5e925a42e05bac66655bfde44d81c59992463d2897014 \ - --hash=sha256:4ddb43d06ce786221c0dfd3c91b4892c318eaa36b903f7c4278e7e2fa0dd5102 \ - --hash=sha256:518dc3cb37365255708283d1c1c54485bbacccd84f0a0fb87ed8917ba45eda5b \ - --hash=sha256:53e8898adda402be03ff164b0878abe2d884e3ea03a4701e6ad55399d84b92dc \ - --hash=sha256:54ba10eb5a3481c28282eb6afb5f709aedf53cf9c3a31875ffbdc9fc719ffd67 \ - --hash=sha256:54e36c67e1a9273ecafab18d6693da0fb5ac48fd48417e4548ac24a918c20998 \ - --hash=sha256:562b1153ab7f766ee6b8b357ec777a302770ad017cf18505d34f1c088fccc448 \ - --hash=sha256:5a7ceb2a0d2280f23a02c64cd0afdc922079bb950400c3dd13a1ab2988428aac \ - --hash=sha256:655e583afc639bef06f3b2446972c1726007a21003cd0ef57116a123e44601bc \ - --hash=sha256:685c1508ec97b2cd3e120bfe309a4ff8e852e8a7460f1ef1de00c2c0ed01e33c \ - --hash=sha256:686c87782481fda5ee6ba572d912a5c26d9f98cc5c243ebd03f95222af3f1b0f \ - --hash=sha256:69d73f869cf29e8a373127fc378014e2b17bcfbe8d89134bc6fb06a2f67f3cb3 \ - --hash=sha256:6fe8503b1b917508cc68bf44dae28823ac05e9f091021e0c41f806ebbb23f92f \ - --hash=sha256:74c091a5ded6cb81785de2d7a8ab703731f26de910dbe0f3934eabef4ae417cc \ - --hash=sha256:7cc8f65f5b22304693de05a245b6736b14cb5bc9c8a03da6e2ae9ef15f8b458f \ - --hash=sha256:7dd9c7db94b4692b827ce51dcee597d61a0e4f4661162424faf65106775b40e7 \ - --hash=sha256:7de3ddb6f424af54535424082a1b5d1ae8caf8256ebd445be68c31c662354720 \ - --hash=sha256:7f98e70bbbf693086efe4b86d381efad8edac040b8ad02821453083d15ec315f \ - --hash=sha256:87fab7f948e407444c2f57088286e00e2ed0003ceaf3d8f8cc0f60544ba61d91 \ - --hash=sha256:8bd7be6ff6c162a60cb8fce65ee879a684fbb63d5466aba3fa5b9288eb04aefa \ - --hash=sha256:8da9449a575133828cc99985536552ea2dcd690e848f9d41b48d8853a149a959 \ - --hash=sha256:91b10208b222ddf655c3a3d5b727879d7163db12b634492df41a9182a76edaae \ - --hash=sha256:92f7f4a4dc9cdb5980973a74d43cdbb16286dacf8d1896b6c3023b8ba8436f8e \ - --hash=sha256:9360e3ffc7b23565600e729e8c639c3c50d5520e05fdf94aa2bd859eef12c407 \ - --hash=sha256:947847f07a8f81d7b39b2d0202fd73e61962ebe17ac2d8566f260679e467da7b \ - --hash=sha256:95213b3d79c7e387144e9cb7b9d2809092d6ff2c044cb59033aedc612f38fb6d \ - --hash=sha256:96e010736fc16d21125c7e2dc5c350cd43c528b85085c04bf73a77be328fe944 \ - --hash=sha256:99f81f9c1529fd8e03be4a7bd7df32d14b4f856e90ef6e9cbad3415dbfa9166c \ - --hash=sha256:9bb2834a6f11d65374ce97d366d6311a9155ef92c4f0cee543b2155d06dc921f \ - --hash=sha256:9dfc906d656e14004c5bc672399c1cccc10db38df2b62a13fb2b6e165a81c316 \ - --hash=sha256:9f6f0b252a009e98fe84028a4ec48396a948e7a65b8be06ccfc6ef68cf1f614d \ - --hash=sha256:9fd16b5e1a7bdd14668cd6bde60a2a29b49147a535c74f50d8177d11b38433a7 \ - --hash=sha256:a0fde16d284efcacbe15fb0c1013f0967b6c3e379649239d783868230bf1db42 \ - --hash=sha256:a1a50e59b720060c29e2951fd9f13c01e1ea9492e5a527b92cfe04dd64453c16 \ - --hash=sha256:a4be88807283bd96ae7b8e401abde4ca0bab597ba73b5e9a2d98f36d451e9aac \ - --hash=sha256:ad2274e707be37420d0b6c3d26a8115295fe9d8e6e530fa6a42487a8ca3ad052 \ - --hash=sha256:b2bfdda4971bd79201f59adbad24ec2728875237e1c83bba5221284dbbf57bda \ - --hash=sha256:b52a27a5c97275e254704e1049f4b96a81e67d6205f52fa37a4777d55b0e98ef \ - --hash=sha256:c01fbb87b5426381cd9418b3ddcf4fc107e296fa2d3446c18ce6c76642f340a3 \ - --hash=sha256:c836bf3c7512100219fe1123743fd8dd9a2b50dd7cfb0c3bb10d041309acab4b \ - --hash=sha256:c8e98e1845805f184d91fda6f9ab93d7c7b0dddf1c07e0255924bfdb151a8d05 \ - --hash=sha256:ca2f5abcb0a9a47e56bac173c01e9f6c6e7f27534d91451c5f22e6a35a5a2093 \ - --hash=sha256:cd33d9de8cfd006a0d0fe85f49b4183c57e91d18ffb7e9004ce855e81928f704 \ - --hash=sha256:d611d1a01c25277bcdea06879afbc11472e33ce842322496b211319aa95441bb \ - --hash=sha256:d9076810a5621236e29b2204e67a68e1fe317c8727ee4c9abbfbb1083b442c38 \ - --hash=sha256:d984db6d855de58e0fde1ef908d48fe9a634cadb3cf715962722b4da1c40619d \ - --hash=sha256:dafb4abb257c0ed56dc36f4e928a7341b34b1379bd87e5a15ce5d883c2c90574 \ - --hash=sha256:ddfd2dca3f11c365d6857a07e7d12985afc59798458a2fdb2ffa4a0332a3fd43 \ - --hash=sha256:df59f8486507c421c0620a2c3dce81fbf1d54018dc20ff4fecdb2c106d6e6abc \ - --hash=sha256:e00191d38156e09e8c81ef3d75c0d70d4f209b8381e71622165f22ef7da6f101 \ - --hash=sha256:e2f43d238eae4f0b04f58d4c0df4615697d4ca3e9f9b1963d49555a94f0f5a04 \ - --hash=sha256:e57e21e1167705f8482ca29cc5d02702208d8bf4aff58f766d94bcd6ead838cd \ - --hash=sha256:e8f515d6859e673940e08de3922b9c4a2249653b0ac181169313bd6e4b1978ac \ - --hash=sha256:eabe6bf4c199687592f5de4ccd383945f485779c7ffb62a9b9f1f8a3f9756df8 \ - --hash=sha256:ec6ad66ed660d46503243cbec7b2b3d8ddfa020f984209b3b8ef7d98ce69c3f2 \ - --hash=sha256:f81cd85a0e76ec7b8e2b6636fe02952d35befda4196b8c88f3cec5b4fb512839 \ - --hash=sha256:f9f49bdb94809ac56e09a310a62f33e5f22973d6fd351aac72a39cd551e98194 \ - --hash=sha256:fae962b62944eaebff4f4fddcf1a69de919e7b967136a318533d82d93c3c6bd1 \ - --hash=sha256:fc61f39b534c5d5903490478a0dd349df397d2284a939aa3cbaa2fb7a19b8397 +aiohttp==3.10.3 \ + --hash=sha256:05d66203a530209cbe40f102ebaac0b2214aba2a33c075d0bf825987c36f1f0b \ + --hash=sha256:08bd0754d257b2db27d6bab208c74601df6f21bfe4cb2ec7b258ba691aac64b3 \ + --hash=sha256:0974f3b5b0132edcec92c3306f858ad4356a63d26b18021d859c9927616ebf27 \ + --hash=sha256:09bc79275737d4dc066e0ae2951866bb36d9c6b460cb7564f111cc0427f14844 \ + --hash=sha256:123e5819bfe1b87204575515cf448ab3bf1489cdeb3b61012bde716cda5853e7 \ + --hash=sha256:13031e7ec1188274bad243255c328cc3019e36a5a907978501256000d57a7201 \ + --hash=sha256:166de65e2e4e63357cfa8417cf952a519ac42f1654cb2d43ed76899e2319b1ee \ + --hash=sha256:214277dcb07ab3875f17ee1c777d446dcce75bea85846849cc9d139ab8f5081f \ + --hash=sha256:21650e7032cc2d31fc23d353d7123e771354f2a3d5b05a5647fc30fea214e696 \ + --hash=sha256:24fade6dae446b183e2410a8628b80df9b7a42205c6bfc2eff783cbeedc224a2 \ + --hash=sha256:2a5d0ea8a6467b15d53b00c4e8ea8811e47c3cc1bdbc62b1aceb3076403d551f \ + --hash=sha256:2b0f670502100cdc567188c49415bebba947eb3edaa2028e1a50dd81bd13363f \ + --hash=sha256:2bbc55a964b8eecb341e492ae91c3bd0848324d313e1e71a27e3d96e6ee7e8e8 \ + --hash=sha256:32007fdcaab789689c2ecaaf4b71f8e37bf012a15cd02c0a9db8c4d0e7989fa8 \ + --hash=sha256:3461d9294941937f07bbbaa6227ba799bc71cc3b22c40222568dc1cca5118f68 \ + --hash=sha256:3731a73ddc26969d65f90471c635abd4e1546a25299b687e654ea6d2fc052394 \ + --hash=sha256:38d91b98b4320ffe66efa56cb0f614a05af53b675ce1b8607cdb2ac826a8d58e \ + --hash=sha256:3a9dcdccf50284b1b0dc72bc57e5bbd3cc9bf019060dfa0668f63241ccc16aa7 \ + --hash=sha256:434b3ab75833accd0b931d11874e206e816f6e6626fd69f643d6a8269cd9166a \ + --hash=sha256:43b09f38a67679e32d380fe512189ccb0b25e15afc79b23fbd5b5e48e4fc8fd9 \ + --hash=sha256:44bb159b55926b57812dca1b21c34528e800963ffe130d08b049b2d6b994ada7 \ + --hash=sha256:48665433bb59144aaf502c324694bec25867eb6630fcd831f7a893ca473fcde4 \ + --hash=sha256:50544fe498c81cb98912afabfc4e4d9d85e89f86238348e3712f7ca6a2f01dab \ + --hash=sha256:5337cc742a03f9e3213b097abff8781f79de7190bbfaa987bd2b7ceb5bb0bdec \ + --hash=sha256:56fb94bae2be58f68d000d046172d8b8e6b1b571eb02ceee5535e9633dcd559c \ + --hash=sha256:59c489661edbd863edb30a8bd69ecb044bd381d1818022bc698ba1b6f80e5dd1 \ + --hash=sha256:5ba2e838b5e6a8755ac8297275c9460e729dc1522b6454aee1766c6de6d56e5e \ + --hash=sha256:61ccb867b2f2f53df6598eb2a93329b5eee0b00646ee79ea67d68844747a418e \ + --hash=sha256:671efce3a4a0281060edf9a07a2f7e6230dca3a1cbc61d110eee7753d28405f7 \ + --hash=sha256:673bb6e3249dc8825df1105f6ef74e2eab779b7ff78e96c15cadb78b04a83752 \ + --hash=sha256:6ae9ae382d1c9617a91647575255ad55a48bfdde34cc2185dd558ce476bf16e9 \ + --hash=sha256:6c51ed03e19c885c8e91f574e4bbe7381793f56f93229731597e4a499ffef2a5 \ + --hash=sha256:6d881353264e6156f215b3cb778c9ac3184f5465c2ece5e6fce82e68946868ef \ + --hash=sha256:7084876352ba3833d5d214e02b32d794e3fd9cf21fdba99cff5acabeb90d9806 \ + --hash=sha256:70b4a4984a70a2322b70e088d654528129783ac1ebbf7dd76627b3bd22db2f17 \ + --hash=sha256:71bb1d97bfe7e6726267cea169fdf5df7658831bb68ec02c9c6b9f3511e108bb \ + --hash=sha256:7c126f532caf238031c19d169cfae3c6a59129452c990a6e84d6e7b198a001dc \ + --hash=sha256:7f9159ae530297f61a00116771e57516f89a3de6ba33f314402e41560872b50a \ + --hash=sha256:812121a201f0c02491a5db335a737b4113151926a79ae9ed1a9f41ea225c0e3f \ + --hash=sha256:8542c9e5bcb2bd3115acdf5adc41cda394e7360916197805e7e32b93d821ef93 \ + --hash=sha256:85466b5a695c2a7db13eb2c200af552d13e6a9313d7fa92e4ffe04a2c0ea74c1 \ + --hash=sha256:8d98c604c93403288591d7d6d7d6cc8a63459168f8846aeffd5b3a7f3b3e5e09 \ + --hash=sha256:8da6b48c20ce78f5721068f383e0e113dde034e868f1b2f5ee7cb1e95f91db57 \ + --hash=sha256:926e68438f05703e500b06fe7148ef3013dd6f276de65c68558fa9974eeb59ad \ + --hash=sha256:9743fa34a10a36ddd448bba8a3adc2a66a1c575c3c2940301bacd6cc896c6bf1 \ + --hash=sha256:a541414578ff47c0a9b0b8b77381ea86b0c8531ab37fc587572cb662ccd80b88 \ + --hash=sha256:ab3361159fd3dcd0e48bbe804006d5cfb074b382666e6c064112056eb234f1a9 \ + --hash=sha256:aed12a54d4e1ee647376fa541e1b7621505001f9f939debf51397b9329fd88b9 \ + --hash=sha256:af4dbec58e37f5afff4f91cdf235e8e4b0bd0127a2a4fd1040e2cad3369d2f06 \ + --hash=sha256:b031ce229114825f49cec4434fa844ccb5225e266c3e146cb4bdd025a6da52f1 \ + --hash=sha256:b22cae3c9dd55a6b4c48c63081d31c00fc11fa9db1a20c8a50ee38c1a29539d2 \ + --hash=sha256:b51aef59370baf7444de1572f7830f59ddbabd04e5292fa4218d02f085f8d299 \ + --hash=sha256:b69d832e5f5fa15b1b6b2c8eb6a9fd2c0ec1fd7729cb4322ed27771afc9fc2ac \ + --hash=sha256:b84857b66fa6510a163bb083c1199d1ee091a40163cfcbbd0642495fed096204 \ + --hash=sha256:b97dc9a17a59f350c0caa453a3cb35671a2ffa3a29a6ef3568b523b9113d84e5 \ + --hash=sha256:ba562736d3fbfe9241dad46c1a8994478d4a0e50796d80e29d50cabe8fbfcc3f \ + --hash=sha256:bac352fceed158620ce2d701ad39d4c1c76d114255a7c530e057e2b9f55bdf9f \ + --hash=sha256:baec1eb274f78b2de54471fc4c69ecbea4275965eab4b556ef7a7698dee18bf2 \ + --hash=sha256:bc8e9f15939dacb0e1f2d15f9c41b786051c10472c7a926f5771e99b49a5957f \ + --hash=sha256:bf75716377aad2c718cdf66451c5cf02042085d84522aec1f9246d3e4b8641a6 \ + --hash=sha256:c124b9206b1befe0491f48185fd30a0dd51b0f4e0e7e43ac1236066215aff272 \ + --hash=sha256:c9ed607dbbdd0d4d39b597e5bf6b0d40d844dfb0ac6a123ed79042ef08c1f87e \ + --hash=sha256:cc36cbdedf6f259371dbbbcaae5bb0e95b879bc501668ab6306af867577eb5db \ + --hash=sha256:cd788602e239ace64f257d1c9d39898ca65525583f0fbf0988bcba19418fe93f \ + --hash=sha256:d1100e68e70eb72eadba2b932b185ebf0f28fd2f0dbfe576cfa9d9894ef49752 \ + --hash=sha256:d35235a44ec38109b811c3600d15d8383297a8fab8e3dec6147477ec8636712a \ + --hash=sha256:d3e66d5b506832e56add66af88c288c1d5ba0c38b535a1a59e436b300b57b23e \ + --hash=sha256:d5548444ef60bf4c7b19ace21f032fa42d822e516a6940d36579f7bfa8513f9c \ + --hash=sha256:d5a9ec959b5381271c8ec9310aae1713b2aec29efa32e232e5ef7dcca0df0279 \ + --hash=sha256:d73b073a25a0bb8bf014345374fe2d0f63681ab5da4c22f9d2025ca3e3ea54fc \ + --hash=sha256:e021c4c778644e8cdc09487d65564265e6b149896a17d7c0f52e9a088cc44e1b \ + --hash=sha256:e1128c5d3a466279cb23c4aa32a0f6cb0e7d2961e74e9e421f90e74f75ec1edf \ + --hash=sha256:e8cc0564b286b625e673a2615ede60a1704d0cbbf1b24604e28c31ed37dc62aa \ + --hash=sha256:f25d6c4e82d7489be84f2b1c8212fafc021b3731abdb61a563c90e37cced3a21 \ + --hash=sha256:f817a54059a4cfbc385a7f51696359c642088710e731e8df80d0607193ed2b73 \ + --hash=sha256:fda91ad797e4914cca0afa8b6cccd5d2b3569ccc88731be202f6adce39503189 # via # -r requirements.in # aio-api-github From 5efb4644b04b0efec1ea3bd004c16db3958a3c90 Mon Sep 17 00:00:00 2001 From: phlax Date: Tue, 13 Aug 2024 11:32:06 +0100 Subject: [PATCH 091/130] ci/verify: Fix Docker cache loading (#35681) Signed-off-by: Ryan Northey --- .github/workflows/_cache.yml | 18 ++++++++++++++---- .github/workflows/_load.yml | 20 ++++++++++++++++++-- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/.github/workflows/_cache.yml b/.github/workflows/_cache.yml index a21194312df9..a30cf01df779 100644 --- a/.github/workflows/_cache.yml +++ b/.github/workflows/_cache.yml @@ -11,12 +11,21 @@ on: app-key: required: true inputs: + arch: + type: string + default: x64 + cache-suffix: + type: string + default: image-tag: type: string required: true request: type: string required: true + runs-on: + type: string + default: ubuntu-24.04 lock-repository: type: string default: envoyproxy/ci-mutex @@ -37,7 +46,7 @@ on: jobs: docker: - runs-on: ubuntu-22.04 + runs-on: ${{ inputs.runs-on || 'ubuntu-24.04' }} steps: - uses: envoyproxy/toolshed/gh-actions/appauth@actions-v0.2.35 id: appauth @@ -47,9 +56,10 @@ jobs: key: ${{ secrets.app-key }} - uses: envoyproxy/toolshed/gh-actions/docker/cache/prime@actions-v0.2.35 id: docker - name: Prime Docker cache (${{ inputs.image-tag }}) + name: Prime Docker cache (${{ inputs.image-tag }}${{ inputs.cache-suffix }}) with: image-tag: ${{ inputs.image-tag }} + key-suffix: ${{ inputs.cache-suffix }} lock-token: ${{ steps.appauth.outputs.token }} lock-repository: ${{ inputs.lock-repository }} - uses: envoyproxy/toolshed/gh-actions/jq@actions-v0.2.35 @@ -59,11 +69,11 @@ jobs: input-format: yaml input: | cached: ${{ steps.docker.outputs.cached }} - key: ${{ inputs.image-tag }} + key: ${{ inputs.image-tag }}${{ inputs.cache-suffix }} - uses: envoyproxy/toolshed/gh-actions/json/table@actions-v0.2.35 name: Summary with: json: ${{ steps.data.outputs.value }} output-path: GITHUB_STEP_SUMMARY title: >- - Cache (Docker x64) + Cache (Docker ${{ inputs.arch }}) diff --git a/.github/workflows/_load.yml b/.github/workflows/_load.yml index acbf553f4113..85991ba4baaa 100644 --- a/.github/workflows/_load.yml +++ b/.github/workflows/_load.yml @@ -157,9 +157,25 @@ jobs: secrets: app-id: ${{ secrets.lock-app-id }} app-key: ${{ secrets.lock-app-key }} - uses: ./.github/workflows/_cache.yml + name: ${{ matrix.name || matrix.target }} needs: request + uses: ./.github/workflows/_cache.yml if: ${{ inputs.cache-docker && ! fromJSON(needs.request.outputs.skip) }} with: - request: ${{ toJSON(needs.request.outputs) }} + arch: ${{ matrix.arch }} + cache-suffix: ${{ matrix.cache-suffix }} image-tag: ${{ fromJSON(needs.request.outputs.build-image).default }} + request: ${{ toJSON(needs.request.outputs) }} + runs-on: ${{ matrix.runs-on }} + strategy: + fail-fast: false + matrix: + include: + - target: docker-x64 + name: Docker (x64) + arch: x64 + - target: docker-arm64 + name: Docker (arm64) + arch: arm64 + cache-suffix: -arm64 + runs-on: envoy-arm64-small From acb0626d57c74a6b027ac0a38f0139914c10b614 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:37:52 +0000 Subject: [PATCH 092/130] build(deps): bump envoy-dependency-check from 0.1.12 to 0.1.13 in /tools/base (#35683) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/base/requirements.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index a82817c6cb94..4d99a19158dc 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -25,9 +25,9 @@ aio-api-bazel==0.0.2 \ --hash=sha256:56e36463d236e477b7e282f2d870185a0b978b50e2c3803c1ebf8b8ac4b18f5b \ --hash=sha256:d3f563b7698e874437d80538a89dd4d79bc37de2e850c846330ae456e3f21dcc # via -r requirements.in -aio-api-github==0.2.5 \ - --hash=sha256:301a357209831ac2bc0fb5c79f8b8795a5363da5cabc2229f10155bdb6d42f5d \ - --hash=sha256:3532d0892e875e8bb6b188c0beba4e8bac9d5147e249ce987bb2beef1e7b711e +aio-api-github==0.2.6 \ + --hash=sha256:71ca0e572a48eab09f3e54267b374fb3d53e246b83f6f23fe1f29f5560acdaed \ + --hash=sha256:be12d6bf612ce2abc85c695ce74547220636f96fe80d4e64cd2de8670db69c32 # via # -r requirements.in # envoy-base-utils @@ -515,9 +515,9 @@ envoy-code-check==0.5.13 \ --hash=sha256:58c31be3ba1a3273eec8a76d1dcfe1a3ae5eae4730ca9d70a85fec0d641846c4 \ --hash=sha256:6c568d477642abdf7b41a0b6a5bb21fd480d92e500c53120837a01d4436d8591 # via -r requirements.in -envoy-dependency-check==0.1.12 \ - --hash=sha256:4673cb4cf9c0e2c55b2a0e0b39df3b8df9993d6524c6edb9527d3c8fb1ec24e2 \ - --hash=sha256:7443e530a2a9155d1e114b8a99d9355bbbe73005b0c96ee653907912ae368f3c +envoy-dependency-check==0.1.13 \ + --hash=sha256:4337b9c4129ae723dc92f70733b167a8dde187368d873687c6c54732d6fb5e48 \ + --hash=sha256:795e885eccd072d7878dc8ce11fe9f84761f0e449603e583fdab5e9e17111af2 # via -r requirements.in envoy-distribution-distrotest==0.0.10 \ --hash=sha256:83e912c48da22eb3e514fc1142247d33eb7ed0d59e94eca2ffbd178a26fbf808 \ From cb0fa5e29a9413bcab44babb1c22afc133e8b9f3 Mon Sep 17 00:00:00 2001 From: yanavlasov Date: Tue, 13 Aug 2024 09:12:14 -0400 Subject: [PATCH 093/130] Align buffer for inotify_events to avoid SIGTRAP (#35631) Found in internal testing. Risk Level: Low Testing: Unit tests Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A --------- Signed-off-by: Yan Avlasov --- source/common/filesystem/inotify/watcher_impl.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/common/filesystem/inotify/watcher_impl.cc b/source/common/filesystem/inotify/watcher_impl.cc index 534cd67f1933..f335c2ad62d3 100644 --- a/source/common/filesystem/inotify/watcher_impl.cc +++ b/source/common/filesystem/inotify/watcher_impl.cc @@ -54,7 +54,10 @@ absl::Status WatcherImpl::addWatch(absl::string_view path, uint32_t events, OnCh absl::Status WatcherImpl::onInotifyEvent() { while (true) { - uint8_t buffer[sizeof(inotify_event) + NAME_MAX + 1]; + // The buffer needs to be suitably aligned to store the first inotify_event structure. + // If there are multiple events returned by the read call, the kernel is responsible for + // properly aligning subsequent inotify_event structures (per `man inotify`). + alignas(inotify_event) uint8_t buffer[sizeof(inotify_event) + NAME_MAX + 1]; ssize_t rc = read(inotify_fd_, &buffer, sizeof(buffer)); if (rc == -1 && errno == EAGAIN) { return absl::OkStatus(); From 495acf65bf542745c2593db7772c43a6048da4e7 Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 13 Aug 2024 09:19:04 -0400 Subject: [PATCH 094/130] Update QUICHE from 0d70743fc to 36723962e (#35669) https://github.com/google/quiche/compare/0d70743fc..36723962e ``` $ git log 0d70743fc..36723962e --date=short --no-merges --format="%ad %al %s" 2024-08-11 quiche-dev Upgrade to the latest protobuf edition 2024-08-09 rch Fix bug in QpackEncoder when cookie crumbling is disabled. Without this fix, encoded cookies will be truncated. 2024-08-09 fayang No public description 2024-08-09 quiche-dev Add a first implementation of the CustomProbeService interface to the bonnet. ``` Signed-off-by: Alyssa Wilk --- bazel/repository_locations.bzl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 04d980d76742..de3fc7cc2ddb 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -1200,12 +1200,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "QUICHE", project_desc = "QUICHE (QUIC, HTTP/2, Etc) is Google‘s implementation of QUIC and related protocols", project_url = "https://github.com/google/quiche", - version = "0d70743fc056f254743082221eda54eba7431fa7", - sha256 = "b2c0ad26505d93416305618b5af10be35280a8d166c341f3e9795704914d1e12", + version = "36723962ef5c9f3f9f42093ff9cbe057bc7a80c4", + sha256 = "8735afd08104215a8487cc9f2ffff1adc16e6168dc61c4e65127a3fb23d90c54", urls = ["https://github.com/google/quiche/archive/{version}.tar.gz"], strip_prefix = "quiche-{version}", use_category = ["controlplane", "dataplane_core"], - release_date = "2024-08-09", + release_date = "2024-08-11", cpe = "N/A", license = "BSD-3-Clause", license_url = "https://github.com/google/quiche/blob/{version}/LICENSE", From 3283bedf6700276477e681294b210ee3a01cde56 Mon Sep 17 00:00:00 2001 From: Tianyu <72890320+tyxia@users.noreply.github.com> Date: Tue, 13 Aug 2024 09:37:26 -0400 Subject: [PATCH 095/130] rlqs: Handle remote close (#35677) Commit Message: - Avoid periodical reports if stream has been remotely closed. - Clean up/refactor the code: remove redundant `stream_closed_` flag and use `stream_ == nullptr` as the indicator of stream close. --------- Signed-off-by: tyxia --- .../http/rate_limit_quota/client_impl.cc | 20 +++-- .../http/rate_limit_quota/client_impl.h | 2 - .../http/rate_limit_quota/integration_test.cc | 89 +++++++++++++++++++ 3 files changed, 100 insertions(+), 11 deletions(-) diff --git a/source/extensions/filters/http/rate_limit_quota/client_impl.cc b/source/extensions/filters/http/rate_limit_quota/client_impl.cc index b51e5725fda1..f454695b55bc 100644 --- a/source/extensions/filters/http/rate_limit_quota/client_impl.cc +++ b/source/extensions/filters/http/rate_limit_quota/client_impl.cc @@ -64,10 +64,14 @@ RateLimitQuotaUsageReports RateLimitClientImpl::buildReport(absl::optional bucket_id) { - ASSERT(stream_ != nullptr); - // Build the report and then send the report to RLQS server. - // `end_stream` should always be set to false as we don't want to close the stream locally. - stream_->sendMessage(buildReport(bucket_id), /*end_stream=*/false); + if (stream_ != nullptr) { + // Build the report and then send the report to RLQS server. + // `end_stream` should always be set to false as we don't want to close the stream locally. + stream_->sendMessage(buildReport(bucket_id), /*end_stream=*/false); + } else { + // Don't send any reports if stream has already been closed. + ENVOY_LOG(debug, "The stream has already been closed; no reports will be sent."); + } } void RateLimitClientImpl::onReceiveMessage(RateLimitQuotaResponsePtr&& response) { @@ -143,20 +147,18 @@ void RateLimitClientImpl::onReceiveMessage(RateLimitQuotaResponsePtr&& response) void RateLimitClientImpl::closeStream() { // Close the stream if it is in open state. - if (stream_ != nullptr && !stream_closed_) { + if (stream_ != nullptr) { ENVOY_LOG(debug, "Closing gRPC stream"); stream_->closeStream(); - stream_closed_ = true; stream_->resetStream(); + stream_ = nullptr; } } void RateLimitClientImpl::onRemoteClose(Grpc::Status::GrpcStatus status, const std::string& message) { - // TODO(tyxia) Revisit later, maybe add some logging. - stream_closed_ = true; ENVOY_LOG(debug, "gRPC stream closed remotely with status {}: {}", status, message); - closeStream(); + stream_ = nullptr; } absl::Status RateLimitClientImpl::startStream(const StreamInfo::StreamInfo& stream_info) { diff --git a/source/extensions/filters/http/rate_limit_quota/client_impl.h b/source/extensions/filters/http/rate_limit_quota/client_impl.h index 4999ef62f9ca..b471755d2420 100644 --- a/source/extensions/filters/http/rate_limit_quota/client_impl.h +++ b/source/extensions/filters/http/rate_limit_quota/client_impl.h @@ -58,8 +58,6 @@ class RateLimitClientImpl : public RateLimitClient, // Build the usage report (i.e., the request sent to RLQS server) from the buckets in quota bucket // cache. RateLimitQuotaUsageReports buildReport(absl::optional bucket_id); - - bool stream_closed_ = false; // Domain from filter configuration. The same domain name throughout the whole lifetime of client. std::string domain_name_; // Client is stored as the bare object since there is no ownership transfer involved. diff --git a/test/extensions/filters/http/rate_limit_quota/integration_test.cc b/test/extensions/filters/http/rate_limit_quota/integration_test.cc index 5af41c95c49b..ec4e45a89113 100644 --- a/test/extensions/filters/http/rate_limit_quota/integration_test.cc +++ b/test/extensions/filters/http/rate_limit_quota/integration_test.cc @@ -709,6 +709,95 @@ TEST_P(RateLimitQuotaIntegrationTest, BasicFlowPeriodicalReport) { } } +TEST_P(RateLimitQuotaIntegrationTest, BasicFlowPeriodicalReportWithStreamClosed) { + initializeConfig(); + HttpIntegrationTest::initialize(); + absl::flat_hash_map custom_headers = {{"environment", "staging"}, + {"group", "envoy"}}; + // Send downstream client request to upstream. + sendClientRequest(&custom_headers); + + ASSERT_TRUE(grpc_upstreams_[0]->waitForHttpConnection(*dispatcher_, rlqs_connection_)); + ASSERT_TRUE(rlqs_connection_->waitForNewStream(*dispatcher_, rlqs_stream_)); + // reports should be built in filter.cc + envoy::service::rate_limit_quota::v3::RateLimitQuotaUsageReports reports; + ASSERT_TRUE(rlqs_stream_->waitForGrpcMessage(*dispatcher_, reports)); + + // Verify the usage report content. + ASSERT_THAT(reports.bucket_quota_usages_size(), 1); + const auto& usage = reports.bucket_quota_usages(0); + // We only send single downstream client request and it is allowed. + EXPECT_EQ(usage.num_requests_allowed(), 1); + EXPECT_EQ(usage.num_requests_denied(), 0); + // It is first report so the time_elapsed is 0. + EXPECT_EQ(Protobuf::util::TimeUtil::DurationToSeconds(usage.time_elapsed()), 0); + + rlqs_stream_->startGrpcStream(); + + // Build the response. + envoy::service::rate_limit_quota::v3::RateLimitQuotaResponse rlqs_response; + absl::flat_hash_map custom_headers_cpy = custom_headers; + custom_headers_cpy.insert({"name", "prod"}); + auto* bucket_action = rlqs_response.add_bucket_action(); + + for (const auto& [key, value] : custom_headers_cpy) { + (*bucket_action->mutable_bucket_id()->mutable_bucket()).insert({key, value}); + } + rlqs_stream_->sendGrpcMessage(rlqs_response); + + // Handle the request received by upstream. + ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_)); + ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_)); + ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_)); + upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, false); + upstream_request_->encodeData(100, true); + + // Verify the response to downstream. + ASSERT_TRUE(response_->waitForEndStream()); + EXPECT_TRUE(response_->complete()); + EXPECT_EQ(response_->headers().getStatusValue(), "200"); + + // ValidMatcherConfig. + int report_interval_sec = 60; + // Trigger the report periodically. + for (int i = 0; i < 6; ++i) { + if (i == 2) { + // Close the stream. + rlqs_stream_->finishGrpcStream(Grpc::Status::Ok); + } + + // Advance the time by report_interval. + simTime().advanceTimeWait(std::chrono::milliseconds(report_interval_sec * 1000)); + + // Only perform rlqs server check and response before stream is remotely closed. + if (i < 2) { + // Checks that the rate limit server has received the periodical reports. + ASSERT_TRUE(rlqs_stream_->waitForGrpcMessage(*dispatcher_, reports)); + + // Verify the usage report content. + ASSERT_THAT(reports.bucket_quota_usages_size(), 1); + const auto& usage = reports.bucket_quota_usages(0); + // Report only represents the usage since last report. + // In the periodical report case here, the number of request allowed and denied is 0 since no + // new requests comes in. + EXPECT_EQ(usage.num_requests_allowed(), 0); + EXPECT_EQ(usage.num_requests_denied(), 0); + // time_elapsed equals to periodical reporting interval. + EXPECT_EQ(Protobuf::util::TimeUtil::DurationToSeconds(usage.time_elapsed()), + report_interval_sec); + + // Build the rlqs server response. + envoy::service::rate_limit_quota::v3::RateLimitQuotaResponse rlqs_response2; + auto* bucket_action2 = rlqs_response2.add_bucket_action(); + + for (const auto& [key, value] : custom_headers_cpy) { + (*bucket_action2->mutable_bucket_id()->mutable_bucket()).insert({key, value}); + } + rlqs_stream_->sendGrpcMessage(rlqs_response2); + } + } +} + // RLQS filter is operating in non-blocking mode now, this test could be flaky until the stats are // added to make the test behavior deterministic. (e.g., wait for stats in the test). // Disable the test for now. From 1cd67c5821125a246e2a4f13254f8d6c69068705 Mon Sep 17 00:00:00 2001 From: Xi Wu Date: Tue, 13 Aug 2024 07:25:25 -0700 Subject: [PATCH 096/130] GrpcFieldExtraction: Supports extracting fields of type `map` in addition to string (#35162) Commit Message: GrpcFieldExtraction: Supports extracting fields of type `map` in addition to string Additional Description: Risk Level: Low Testing: Unit test Docs Changes: Inline with the filter API proto. Release Notes: This change is backward compatible and no behavior change is expected for existing users. Platform Specific Features: --------- Signed-off-by: Xi Wu --- .../grpc_field_extraction/v3/config.proto | 9 ++++--- bazel/repository_locations.bzl | 6 ++--- changelogs/current.yaml | 3 +++ .../http/grpc_field_extraction/extractor.h | 4 +-- .../grpc_field_extraction/extractor_impl.cc | 15 ++++------- .../http/grpc_field_extraction/filter.cc | 5 +--- .../filter_config_test.cc | 5 ++++ .../http/grpc_field_extraction/filter_test.cc | 26 +++++++++++++++++++ test/proto/apikeys.proto | 2 ++ 9 files changed, 53 insertions(+), 22 deletions(-) diff --git a/api/envoy/extensions/filters/http/grpc_field_extraction/v3/config.proto b/api/envoy/extensions/filters/http/grpc_field_extraction/v3/config.proto index 3684f994d65f..aae62145731a 100644 --- a/api/envoy/extensions/filters/http/grpc_field_extraction/v3/config.proto +++ b/api/envoy/extensions/filters/http/grpc_field_extraction/v3/config.proto @@ -52,7 +52,9 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // // Here are config requirements // -// 1. the target field should be among the following primitive types: `string`, `uint32`, `uint64`, `int32`, `int64`, `sint32`, `sint64`, `fixed32`, `fixed64`, `sfixed32`, `sfixed64`, `float`, `double`. +// 1. the target field should be among the following primitive types: `string`, +// `uint32`, `uint64`, `int32`, `int64`, `sint32`, `sint64`, `fixed32`, +// `fixed64`, `sfixed32`, `sfixed64`, `float`, `double`, `map`. // // 2. the target field could be repeated. // @@ -61,9 +63,10 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // Output Format // ------------- // -// 1. the extracted field names/values will be wrapped in be ``field`` -> ``values``, which will be added in the dynamic ``metadata``. +// 1. the extracted field names/values will be wrapped in be ``field`` -> ``values``, which will be added in the dynamic ``metadata``. // -// 2. if the field value is empty, a empty ```` will be set. +// 2. if the field value is empty, an empty ```` will be set. // // Performance // ----------- diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index de3fc7cc2ddb..0ef1e9091d91 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -985,13 +985,13 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "proto-field-extraction", project_desc = "Library that supports the extraction from protobuf binary", project_url = "https://github.com/grpc-ecosystem/proto-field-extraction", - version = "2dfe27548e1f21a665f9068b97b2fc5beb678566", - sha256 = "ddbbd0dd07012339ac467f5fdac5c294e1efcdc93bb4b7152d468ddbfc9772f0", + version = "d5d39f0373e9b6691c32c85929838b1006bcb3fb", + sha256 = "cba864db90806515afa553aaa2fb3683df2859a7535e53a32cb9619da9cebc59", strip_prefix = "proto-field-extraction-{version}", urls = ["https://github.com/grpc-ecosystem/proto-field-extraction/archive/{version}.zip"], use_category = ["dataplane_ext"], extensions = ["envoy.filters.http.grpc_json_transcoder", "envoy.filters.http.grpc_field_extraction"], - release_date = "2023-07-18", + release_date = "2024-07-10", cpe = "N/A", license = "Apache-2.0", license_url = "https://github.com/grpc-ecosystem/proto-field-extraction/blob/{version}/LICENSE", diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 86f5204385de..26e77912e721 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -175,6 +175,9 @@ new_features: change: | Prefer using IPv6 address when addresses from both families are available. Can be reverted by setting ``envoy.reloadable_features.prefer_ipv6_dns_on_macos`` to false. +- area: grpc_field_extraction + change: | + Added ``map`` support: Target fields of type ``map`` can be extracted and added to dynamic metadata. - area: rbac change: | Added :ref:`delay_deny ` to support deny connection after diff --git a/source/extensions/filters/http/grpc_field_extraction/extractor.h b/source/extensions/filters/http/grpc_field_extraction/extractor.h index 4178e73fb090..d321b3651ec4 100644 --- a/source/extensions/filters/http/grpc_field_extraction/extractor.h +++ b/source/extensions/filters/http/grpc_field_extraction/extractor.h @@ -24,8 +24,8 @@ struct RequestField { // The request field path. absl::string_view path; - // The request field values. - std::vector values; + // The request field value. + ProtobufWkt::Value value; }; using ExtractionResult = std::vector; diff --git a/source/extensions/filters/http/grpc_field_extraction/extractor_impl.cc b/source/extensions/filters/http/grpc_field_extraction/extractor_impl.cc index adbaa44181bd..8470310aa09b 100644 --- a/source/extensions/filters/http/grpc_field_extraction/extractor_impl.cc +++ b/source/extensions/filters/http/grpc_field_extraction/extractor_impl.cc @@ -7,7 +7,6 @@ #include "source/common/common/logger.h" -#include "absl/strings/str_format.h" #include "proto_field_extraction/field_value_extractor/field_value_extractor_factory.h" #include "proto_field_extraction/field_value_extractor/field_value_extractor_interface.h" @@ -39,18 +38,14 @@ ExtractorImpl::processRequest(Protobuf::field_extraction::MessageData& message) ExtractionResult result; for (const auto& it : per_field_extractors_) { - auto extracted_values = it.second->Extract(message); - if (!extracted_values.ok()) { - return extracted_values.status(); + absl::StatusOr extracted_value = it.second->ExtractValue(message); + if (!extracted_value.ok()) { + return extracted_value.status(); } ENVOY_LOG_MISC(debug, "extracted the following resource values from the {} field: {}", it.first, - std::accumulate(extracted_values.value().begin(), extracted_values.value().end(), - std::string(), - [](const std::string& lhs, const std::string& rhs) { - return absl::StrFormat("%s, %s", lhs, rhs); - })); - result.push_back({it.first, std::move(extracted_values.value())}); + extracted_value->DebugString()); + result.push_back({it.first, std::move(*extracted_value)}); } return result; diff --git a/source/extensions/filters/http/grpc_field_extraction/filter.cc b/source/extensions/filters/http/grpc_field_extraction/filter.cc index fb66c448dffd..7ae1fb12f326 100644 --- a/source/extensions/filters/http/grpc_field_extraction/filter.cc +++ b/source/extensions/filters/http/grpc_field_extraction/filter.cc @@ -214,10 +214,7 @@ void Filter::handleExtractionResult(const ExtractionResult& result) { ProtobufWkt::Struct dest_metadata; for (const auto& req_field : result) { RELEASE_ASSERT(!req_field.path.empty(), "`req_field.path` shouldn't be empty"); - auto* list = (*dest_metadata.mutable_fields())[req_field.path].mutable_list_value(); - for (const auto& value : req_field.values) { - list->add_values()->set_string_value(value); - } + (*dest_metadata.mutable_fields())[req_field.path] = req_field.value; } if (dest_metadata.fields_size() > 0) { ENVOY_STREAM_LOG(debug, "injected dynamic metadata `{}` with `{}`", *decoder_callbacks_, diff --git a/test/extensions/filters/http/grpc_field_extraction/filter_config_test.cc b/test/extensions/filters/http/grpc_field_extraction/filter_config_test.cc index 55485685d229..e973387b1bd3 100644 --- a/test/extensions/filters/http/grpc_field_extraction/filter_config_test.cc +++ b/test/extensions/filters/http/grpc_field_extraction/filter_config_test.cc @@ -200,6 +200,11 @@ extractions_by_method: { value: { } } + request_field_extractions: { + key: "repeated_supported_types.map" + value: { + } + } } })pb"); *proto_config_.mutable_descriptor_set()->mutable_filename() = diff --git a/test/extensions/filters/http/grpc_field_extraction/filter_test.cc b/test/extensions/filters/http/grpc_field_extraction/filter_test.cc index 166b1b322dd5..b4cf646a959e 100644 --- a/test/extensions/filters/http/grpc_field_extraction/filter_test.cc +++ b/test/extensions/filters/http/grpc_field_extraction/filter_test.cc @@ -639,6 +639,11 @@ extractions_by_method: { value: { } } + request_field_extractions: { + key: "repeated_supported_types.map" + value: { + } + } } })pb"); TestRequestHeaderMapImpl req_headers = @@ -677,6 +682,8 @@ repeated_supported_types: { sfixed64: 1111 float: 1.212 double: 1.313 + map { key: "key1" value: "value1" } + map { key: "key2" value: "value2" } } )pb"); @@ -853,6 +860,25 @@ fields { } } } +} +fields { + key: "repeated_supported_types.map" + value { + list_value { + values { + struct_value { + fields { + key: "key1" + value { string_value: "value1" } + } + fields { + key: "key2" + value { string_value: "value2" } + } + } + } + } + } })pb"); })); EXPECT_EQ(Envoy::Http::FilterDataStatus::Continue, filter_->decodeData(*request_data, true)); diff --git a/test/proto/apikeys.proto b/test/proto/apikeys.proto index a38bbd6f65dc..d292f7ad7d42 100644 --- a/test/proto/apikeys.proto +++ b/test/proto/apikeys.proto @@ -94,6 +94,8 @@ message RepeatedSupportedTypes { repeated float float = 12; repeated double double = 13; + + map map = 14; } message UnsupportedTypes { From 1ba93259dba51d239be4026fa3b548a3454e8a9c Mon Sep 17 00:00:00 2001 From: alyssawilk Date: Tue, 13 Aug 2024 11:04:15 -0400 Subject: [PATCH 097/130] docs: updating cncf-envoy-distributors-announce@lists.cncf.io per audit (#35670) Signed-off-by: Alyssa Wilk --- SECURITY.md | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 5a5233601a5f..a76fc0cc8aa6 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -465,24 +465,20 @@ and security team to ensure they still qualify for inclusion on the list. | Organization | End User | Last Review | |:-------------:|:--------:|:-----------:| -| AWS | No | 06/21 | -| Cilium | No | 06/21 | -| Cloud Foundry | No | 06/21 | -| Datawire | No | 06/21 | -| F5 | No | 06/21 | -| Google | No | 06/21 | -| IBM | No | 06/21 | -| Istio | No | 06/21 | -| Microsoft | No | 2/21 | -| Red Hat | No | 06/21 | -| solo.io | No | 06/21 | -| Tetrate | No | 06/21 | -| VMware | No | 06/21 | -| Pinterest | Yes | 06/21 | -| Dropbox | Yes | 01/20 | -| Stripe | Yes | 01/20 | -| Square | Yes | 05/21 | -| Apple | Yes | 05/21 | -| Spotify | Yes | 06/21 | -| Netflix | Yes | 06/22 | +| AWS | No | 07/24 | +| Cilium | No | 07/24 | +| Cloud Foundry | No | 07/24 | +| F5 | No | 07/24 | +| Google | No | 07/24 | +| Istio | No | 07/24 | +| Microsoft | No | 07/24 | +| Red Hat | No | 07/24 | +| VMware | No | 07/24 | +| Tetrate | No | 07/24 | +| solo.io | No | 07/24 | +| Pinterest | Yes | 07/24 | +| Dropbox | Yes | 07/24 | +| Apple | Yes | 07/24 | +| Spotify | Yes | 02/21 | +| Netflix | Yes | 07/24 | | Slack | Yes | 07/24 | From 7e256bb30d43d3ed4cebf73a62c49a4b6d72d4a7 Mon Sep 17 00:00:00 2001 From: Tianyu <72890320+tyxia@users.noreply.github.com> Date: Tue, 13 Aug 2024 13:26:32 -0400 Subject: [PATCH 098/130] ext_proc: Update test (#35592) Commit Message: No need to check the bytes received for observability mode because it is a "send and go" read-only mode. Risk Level: Low Testing: yes Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A [Optional Runtime guard:] [Optional Fixes #Issue] [Optional Fixes commit #PR or SHA] [Optional Deprecated:] [Optional [API Considerations](https://github.com/envoyproxy/envoy/blob/main/api/review_checklist.md):] --------- Signed-off-by: tyxia --- .../http/ext_proc/ext_proc_integration_test.cc | 3 +++ .../filters/http/ext_proc/logging_test_filter.cc | 14 ++++++++++---- .../http/ext_proc/logging_test_filter.proto | 1 + 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/test/extensions/filters/http/ext_proc/ext_proc_integration_test.cc b/test/extensions/filters/http/ext_proc/ext_proc_integration_test.cc index d1ee77911444..93c9300e18f3 100644 --- a/test/extensions/filters/http/ext_proc/ext_proc_integration_test.cc +++ b/test/extensions/filters/http/ext_proc/ext_proc_integration_test.cc @@ -182,6 +182,9 @@ class ExtProcIntegrationTest : public HttpIntegrationTest, test::integration::filters::LoggingTestFilterConfig logging_filter_config; logging_filter_config.set_logging_id(ext_proc_filter_name); logging_filter_config.set_upstream_cluster_name(valid_grpc_cluster_name); + // No need to check the bytes received for observability mode because it is a + // "send and go" mode. + logging_filter_config.set_check_received_bytes(!proto_config_.observability_mode()); envoy::extensions::filters::network::http_connection_manager::v3::HttpFilter logging_filter; logging_filter.set_name("logging-test-filter"); logging_filter.mutable_typed_config()->PackFrom(logging_filter_config); diff --git a/test/extensions/filters/http/ext_proc/logging_test_filter.cc b/test/extensions/filters/http/ext_proc/logging_test_filter.cc index 30838fd74600..702bd61804d0 100644 --- a/test/extensions/filters/http/ext_proc/logging_test_filter.cc +++ b/test/extensions/filters/http/ext_proc/logging_test_filter.cc @@ -22,8 +22,10 @@ namespace ExternalProcessing { // A test filter that retrieve the logging info on encodeComplete. class LoggingTestFilter : public Http::PassThroughFilter { public: - LoggingTestFilter(const std::string& logging_id, const std::string& cluster_name) - : logging_id_(logging_id), expected_cluster_name_(cluster_name) {} + LoggingTestFilter(const std::string& logging_id, const std::string& cluster_name, + bool check_received_bytes) + : logging_id_(logging_id), expected_cluster_name_(cluster_name), + check_received_bytes_(check_received_bytes) {} void encodeComplete() override { ASSERT(decoder_callbacks_ != nullptr); const Envoy::StreamInfo::FilterStateSharedPtr& filter_state = @@ -32,7 +34,9 @@ class LoggingTestFilter : public Http::PassThroughFilter { filter_state->getDataReadOnly(logging_id_); if (ext_proc_logging_info != nullptr) { EXPECT_NE(ext_proc_logging_info->bytesSent(), 0); - EXPECT_NE(ext_proc_logging_info->bytesReceived(), 0); + if (check_received_bytes_) { + EXPECT_NE(ext_proc_logging_info->bytesReceived(), 0); + } ASSERT_TRUE(ext_proc_logging_info->upstreamHost() != nullptr); EXPECT_EQ(ext_proc_logging_info->upstreamHost()->cluster().name(), expected_cluster_name_); } @@ -41,6 +45,7 @@ class LoggingTestFilter : public Http::PassThroughFilter { private: std::string logging_id_; std::string expected_cluster_name_; + const bool check_received_bytes_; }; class LoggingTestFilterFactory : public Extensions::HttpFilters::Common::FactoryBase< @@ -53,7 +58,8 @@ class LoggingTestFilterFactory : public Extensions::HttpFilters::Common::Factory Server::Configuration::FactoryContext&) override { return [=](Http::FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamFilter(std::make_shared( - proto_config.logging_id(), proto_config.upstream_cluster_name())); + proto_config.logging_id(), proto_config.upstream_cluster_name(), + proto_config.check_received_bytes())); }; } }; diff --git a/test/extensions/filters/http/ext_proc/logging_test_filter.proto b/test/extensions/filters/http/ext_proc/logging_test_filter.proto index c6e5768b6860..293154343a33 100644 --- a/test/extensions/filters/http/ext_proc/logging_test_filter.proto +++ b/test/extensions/filters/http/ext_proc/logging_test_filter.proto @@ -5,4 +5,5 @@ package test.integration.filters; message LoggingTestFilterConfig { string logging_id = 1; string upstream_cluster_name = 2; + bool check_received_bytes = 3; } From 0290ac0c30860bde16d3818cada367a216cc8de3 Mon Sep 17 00:00:00 2001 From: William Dauchy Date: Tue, 13 Aug 2024 19:27:45 +0200 Subject: [PATCH 099/130] router: fix dynamic weight crash (#34793) fix up crash while updating weighted clusters, when the total weight is changing through runtime keys. Signed-off-by: William Dauchy --- source/common/router/config_impl.cc | 65 ++++++++++++++++++++------ source/common/router/config_impl.h | 11 +++-- test/common/router/config_impl_test.cc | 47 +++++++++++++++++-- 3 files changed, 101 insertions(+), 22 deletions(-) diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index a031d98faa38..d94b988dfea4 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -53,6 +53,7 @@ #include "source/extensions/path/match/uri_template/uri_template_match.h" #include "source/extensions/path/rewrite/uri_template/uri_template_rewrite.h" +#include "absl/container/inlined_vector.h" #include "absl/strings/match.h" namespace Envoy { @@ -671,9 +672,9 @@ RouteEntryImplBase::RouteEntryImplBase(const CommonVirtualHostSharedPtr& vhost, return; } - weighted_clusters_config_ = - std::make_unique(std::move(weighted_clusters), total_weight, - route.route().weighted_clusters().header_name()); + weighted_clusters_config_ = std::make_unique( + std::move(weighted_clusters), total_weight, route.route().weighted_clusters().header_name(), + route.route().weighted_clusters().runtime_key_prefix()); } else if (route.route().cluster_specifier_case() == envoy::config::route::v3::RouteAction::ClusterSpecifierCase:: @@ -1347,12 +1348,15 @@ RouteConstSharedPtr RouteEntryImplBase::clusterEntry(const Http::RequestHeaderMa return cluster_specifier_plugin_->route(shared_from_this(), headers); } } - return pickWeightedCluster(headers, random_value, true); + return pickWeightedCluster(headers, random_value); } +// Selects a cluster depending on weight parameters from configuration or from headers. +// This function takes into account the weights set through configuration or through +// runtime parameters. +// Returns selected cluster, or nullptr if weighted configuration is invalid. RouteConstSharedPtr RouteEntryImplBase::pickWeightedCluster(const Http::HeaderMap& headers, - const uint64_t random_value, - const bool ignore_overflow) const { + const uint64_t random_value) const { absl::optional random_value_from_header; // Retrieve the random value from the header if corresponding header name is specified. // weighted_clusters_config_ is known not to be nullptr here. If it were, pickWeightedCluster @@ -1380,23 +1384,55 @@ RouteConstSharedPtr RouteEntryImplBase::pickWeightedCluster(const Http::HeaderMa } } + auto runtime_key_prefix_configured = + (weighted_clusters_config_->runtime_key_prefix_.length() ? true : false); + uint32_t total_cluster_weight = weighted_clusters_config_->total_cluster_weight_; + absl::InlinedVector cluster_weights; + + // if runtime config is used, we need to recompute total_weight + if (runtime_key_prefix_configured) { + // Temporary storage to hold consistent cluster weights. Since cluster weight + // can be changed with runtime keys, we need a way to gather all the weight + // and aggregate the total without a change in between. + // The InlinedVector will be able to handle at least 4 cluster weights + // without allocation. For cases when more clusters are needed, it is + // reserved to ensure at most a single allocation. + cluster_weights.reserve(weighted_clusters_config_->weighted_clusters_.size()); + + total_cluster_weight = 0; + for (const WeightedClusterEntrySharedPtr& cluster : + weighted_clusters_config_->weighted_clusters_) { + auto cluster_weight = cluster->clusterWeight(); + cluster_weights.push_back(cluster_weight); + if (cluster_weight > std::numeric_limits::max() - total_cluster_weight) { + IS_ENVOY_BUG("Sum of weight cannot overflow 2^32"); + return nullptr; + } + total_cluster_weight += cluster_weight; + } + } + + if (total_cluster_weight == 0) { + IS_ENVOY_BUG("Sum of weight cannot be zero"); + return nullptr; + } const uint64_t selected_value = (random_value_from_header.has_value() ? random_value_from_header.value() : random_value) % - weighted_clusters_config_->total_cluster_weight_; + total_cluster_weight; uint64_t begin = 0; uint64_t end = 0; + auto cluster_weight = cluster_weights.begin(); // Find the right cluster to route to based on the interval in which // the selected value falls. The intervals are determined as // [0, cluster1_weight), [cluster1_weight, cluster1_weight+cluster2_weight),.. for (const WeightedClusterEntrySharedPtr& cluster : weighted_clusters_config_->weighted_clusters_) { - end = begin + cluster->clusterWeight(); - if (!ignore_overflow) { - // end > total_cluster_weight: This case can only occur with Runtimes, - // when the user specifies invalid weights such that - // sum(weights) > total_cluster_weight. - ASSERT(end <= weighted_clusters_config_->total_cluster_weight_); + + if (runtime_key_prefix_configured) { + end = begin + *cluster_weight++; + } else { + end = begin + cluster->clusterWeight(); } if (selected_value >= begin && selected_value < end) { @@ -1415,7 +1451,8 @@ RouteConstSharedPtr RouteEntryImplBase::pickWeightedCluster(const Http::HeaderMa begin = end; } - PANIC("unexpected"); + IS_ENVOY_BUG("unexpected"); + return nullptr; } absl::Status RouteEntryImplBase::validateClusters( diff --git a/source/common/router/config_impl.h b/source/common/router/config_impl.h index 2a6bfc723532..f9383d6bd22c 100644 --- a/source/common/router/config_impl.h +++ b/source/common/router/config_impl.h @@ -1089,13 +1089,16 @@ class RouteEntryImplBase : public RouteEntryAndRoute, struct WeightedClustersConfig { WeightedClustersConfig(const std::vector&& weighted_clusters, uint64_t total_cluster_weight, - const std::string& random_value_header_name) + const std::string& random_value_header_name, + const std::string& runtime_key_prefix) : weighted_clusters_(std::move(weighted_clusters)), total_cluster_weight_(total_cluster_weight), - random_value_header_name_(random_value_header_name) {} + random_value_header_name_(random_value_header_name), + runtime_key_prefix_(runtime_key_prefix) {} const std::vector weighted_clusters_; const uint64_t total_cluster_weight_; const std::string random_value_header_name_; + const std::string runtime_key_prefix_; }; protected: @@ -1207,8 +1210,8 @@ class RouteEntryImplBase : public RouteEntryAndRoute, const Http::HeaderMap& headers, const RouteEntryAndRoute* route_selector_override) const; - RouteConstSharedPtr pickWeightedCluster(const Http::HeaderMap& headers, uint64_t random_value, - bool ignore_overflow) const; + RouteConstSharedPtr pickWeightedCluster(const Http::HeaderMap& headers, + uint64_t random_value) const; // Default timeout is 15s if nothing is specified in the route config. static const uint64_t DEFAULT_ROUTE_TIMEOUT_MS = 15000; diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index b9679737b71b..8cc2795e4613 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -6305,21 +6305,60 @@ TEST_F(RouteMatcherTest, WeightedClusters) { { Http::TestRequestHeaderMapImpl headers = genHeaders("www3.lyft.com", "/foo", "GET"); EXPECT_CALL(runtime.snapshot_, featureEnabled("www3", 100, _)).WillRepeatedly(Return(true)); + // new total weight will be 140 EXPECT_CALL(runtime.snapshot_, getInteger("www3_weights.cluster1", 30)) .WillRepeatedly(Return(10)); - - // We return an invalid value here, one that is greater than 100 - // Expect any random value > 10 to always land in cluster2. EXPECT_CALL(runtime.snapshot_, getInteger("www3_weights.cluster2", 30)) .WillRepeatedly(Return(120)); EXPECT_CALL(runtime.snapshot_, getInteger("www3_weights.cluster3", 40)) .WillRepeatedly(Return(10)); - EXPECT_EQ("cluster1", config.route(headers, 1005)->routeEntry()->clusterName()); + // 1005 % total_weight == 25 + EXPECT_EQ("cluster2", config.route(headers, 1005)->routeEntry()->clusterName()); EXPECT_EQ("cluster2", config.route(headers, 82)->routeEntry()->clusterName()); EXPECT_EQ("cluster2", config.route(headers, 92)->routeEntry()->clusterName()); } + // Weighted Cluster with runtime values under total weight + // Makes sure new total weight is taken into account + // if total_weight is not recomputed, it will raise "unexpected" error + { + Http::TestRequestHeaderMapImpl headers = genHeaders("www3.lyft.com", "/foo", "GET"); + EXPECT_CALL(runtime.snapshot_, featureEnabled("www3", 100, _)).WillRepeatedly(Return(true)); + // new total weight will be 6 + EXPECT_CALL(runtime.snapshot_, getInteger("www3_weights.cluster1", 30)) + .WillRepeatedly(Return(1)); + EXPECT_CALL(runtime.snapshot_, getInteger("www3_weights.cluster2", 30)) + .WillRepeatedly(Return(2)); + EXPECT_CALL(runtime.snapshot_, getInteger("www3_weights.cluster3", 40)) + .WillRepeatedly(Return(3)); + + // 1005 % total_weight == 3 + EXPECT_EQ("cluster3", config.route(headers, 1005)->routeEntry()->clusterName()); + EXPECT_EQ("cluster3", config.route(headers, 82)->routeEntry()->clusterName()); + EXPECT_EQ("cluster2", config.route(headers, 92)->routeEntry()->clusterName()); + } + + // Total weight is set to zero + { + Http::TestRequestHeaderMapImpl headers = genHeaders("www3.lyft.com", "/foo", "GET"); + EXPECT_CALL(runtime.snapshot_, featureEnabled("www3", 100, _)).WillRepeatedly(Return(true)); + EXPECT_CALL(runtime.snapshot_, getInteger("www3_weights.cluster1", 30)) + .WillRepeatedly(Return(0)); + EXPECT_CALL(runtime.snapshot_, getInteger("www3_weights.cluster2", 30)) + .WillRepeatedly(Return(0)); + EXPECT_CALL(runtime.snapshot_, getInteger("www3_weights.cluster3", 40)) + .WillRepeatedly(Return(0)); + +#if defined(NDEBUG) + // sum of weight returns nullptr + EXPECT_EQ(nullptr, config.route(headers, 42)); +#else + // in debug mode, it aborts + EXPECT_DEATH(config.route(headers, 42), "Sum of weight cannot be zero"); +#endif + } + // Weighted Cluster with runtime values, total weight = 10000 { Http::TestRequestHeaderMapImpl headers = genHeaders("www4.lyft.com", "/foo", "GET"); From d7eff8f4f0dd52ff5ddba58d29974376544dc672 Mon Sep 17 00:00:00 2001 From: Raven Black Date: Tue, 13 Aug 2024 17:55:52 -0400 Subject: [PATCH 100/130] Fix file_system_buffer's adherence to continueEncoding API (#35674) Commit Message: Fix file_system_buffer's adherence to continueEncoding API Additional Description: According to the API, `continueEncoding` and `continueDecoding` should not be called after headers replied with anything other than `StopIteration`. This change makes it so if the stream is going to buffer, headers always return `StopIteration`. Risk Level: Some from leaving it as-is, some from changing it. Testing: Modified some tests. Integration tests still pass as before. Docs Changes: n/a Release Notes: n/a Platform Specific Features: n/a Signed-off-by: Raven Black --- .../filters/http/file_system_buffer/filter.cc | 18 ++++-------------- .../http/file_system_buffer/filter_test.cc | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/source/extensions/filters/http/file_system_buffer/filter.cc b/source/extensions/filters/http/file_system_buffer/filter.cc index c88198ad9173..a4e5e38f4295 100644 --- a/source/extensions/filters/http/file_system_buffer/filter.cc +++ b/source/extensions/filters/http/file_system_buffer/filter.cc @@ -67,13 +67,8 @@ Http::FilterHeadersStatus FileSystemBufferFilter::decodeHeaders(Http::RequestHea // the memory limit, once in our own buffer and once in the outgoing buffer. (Plus overflow // because the limit isn't hard.) request_callbacks_->setDecoderBufferLimit(request_state_.config_->memoryBufferBytesLimit()); - // If we're going to buffer everything, don't even start sending the data until we're ready. - if (request_state_.injecting_content_length_header_ || - request_state_.config_->behavior().alwaysFullyBuffer()) { - request_headers_ = &headers; - return Http::FilterHeadersStatus::StopIteration; - } - return Http::FilterHeadersStatus::Continue; + request_headers_ = &headers; + return Http::FilterHeadersStatus::StopIteration; } Http::FilterHeadersStatus FileSystemBufferFilter::encodeHeaders(Http::ResponseHeaderMap& headers, @@ -100,13 +95,8 @@ Http::FilterHeadersStatus FileSystemBufferFilter::encodeHeaders(Http::ResponseHe // the memory limit, once in our own buffer and once in the outgoing buffer. (Plus overflow // because the limit isn't hard.) response_callbacks_->setEncoderBufferLimit(response_state_.config_->memoryBufferBytesLimit()); - // If we're going to buffer everything, don't even start sending the data until we're ready. - if (response_state_.injecting_content_length_header_ || - response_state_.config_->behavior().alwaysFullyBuffer()) { - response_headers_ = &headers; - return Http::FilterHeadersStatus::StopIteration; - } - return Http::FilterHeadersStatus::Continue; + response_headers_ = &headers; + return Http::FilterHeadersStatus::StopIteration; } void FileSystemBufferFilter::onAboveWriteBufferHighWatermark() { diff --git a/test/extensions/filters/http/file_system_buffer/filter_test.cc b/test/extensions/filters/http/file_system_buffer/filter_test.cc index c781c8461953..846b912ebfef 100644 --- a/test/extensions/filters/http/file_system_buffer/filter_test.cc +++ b/test/extensions/filters/http/file_system_buffer/filter_test.cc @@ -270,7 +270,8 @@ TEST_F(FileSystemBufferFilterTest, DoesNotBufferRequestWhenContentLengthPresent) inject_content_length_if_necessary: {} )"); request_headers_.setContentLength(6); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->decodeHeaders(request_headers_, false)); Buffer::OwnedImpl data1("hello"); Buffer::OwnedImpl data2(" banana"); EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, filter_->decodeData(data1, false)); @@ -290,7 +291,8 @@ TEST_F(FileSystemBufferFilterTest, DoesNotBufferResponseWhenContentLengthPresent inject_content_length_if_necessary: {} )"); response_headers_.setContentLength(6); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->encodeHeaders(response_headers_, false)); Buffer::OwnedImpl data1("hello"); Buffer::OwnedImpl data2(" banana"); EXPECT_EQ(Http::FilterDataStatus::StopIterationNoBuffer, filter_->encodeData(data1, false)); @@ -312,7 +314,8 @@ constexpr int outerWatermarkAfterIntercepting() { TEST_F(FileSystemBufferFilterTest, BuffersResponseWhenDownstreamIsSlow) { createFilterFromYaml(minimal_config); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->encodeHeaders(response_headers_, false)); Buffer::OwnedImpl data1("hello"); Buffer::OwnedImpl data2(" banana"); sendResponseHighWatermark(); @@ -340,7 +343,8 @@ TEST_F(FileSystemBufferFilterTest, response: memory_buffer_bytes_limit: 6 )"); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->encodeHeaders(response_headers_, false)); Buffer::OwnedImpl data1("hello "); Buffer::OwnedImpl data2("wor"); Buffer::OwnedImpl data3("ld"); @@ -441,7 +445,8 @@ TEST_F(FileSystemBufferFilterTest, ResponseSlowsWhenTotalBufferSizeExceededAndSt memory_buffer_bytes_limit: 10 storage_buffer_bytes_limit: 0 )"); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->encodeHeaders(response_headers_, false)); sendResponseHighWatermark(); Buffer::OwnedImpl data1("hello "); Buffer::OwnedImpl data2("wor"); @@ -475,7 +480,8 @@ TEST_F(FileSystemBufferFilterTest, ResponseSlowsWhenDiskWriteTakesTooLongAndWate memory_buffer_bytes_limit: 10 storage_buffer_queue_high_watermark_bytes: 10 )"); - EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers_, false)); + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->encodeHeaders(response_headers_, false)); // Stream recipient says stop sending right away. sendResponseHighWatermark(); Buffer::OwnedImpl data1("hello worm"); From 4e0af00e54be34f69ed83b91e11fdd999b9d5602 Mon Sep 17 00:00:00 2001 From: Leonardo Sarra <8293810+leosarra@users.noreply.github.com> Date: Wed, 14 Aug 2024 00:20:27 +0200 Subject: [PATCH 101/130] Account for route-level host manipulation in auto-sni/auto-san-validation (#35566) Auto-SNI will now account for manipulation performed at route level when setting the SNI value. Before this changes it always relied on the original host value ignoring all host header mutations that takes place at route-level. Fixes #35330 Signed-off-by: Leonardo Sarra --- changelogs/current.yaml | 5 + envoy/router/router.h | 17 ++ source/common/http/null_route_impl.h | 3 + source/common/router/config_impl.cc | 48 +++--- source/common/router/config_impl.h | 4 + source/common/router/delegating_route_impl.cc | 5 + source/common/router/delegating_route_impl.h | 1 + source/common/router/router.cc | 6 +- source/common/runtime/runtime_features.cc | 1 + .../http/router/auto_sni_integration_test.cc | 147 +++++++++++++++++- test/mocks/router/mocks.cc | 4 + test/mocks/router/mocks.h | 2 + 12 files changed, 222 insertions(+), 21 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 26e77912e721..cc0d194f30c3 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -42,6 +42,11 @@ minor_behavior_changes: change: | When Lua script executes httpCall, backpressure is exercised when receiving body from downstream client. This behavior can be reverted by setting the runtime guard ``envoy.reloadable_features.lua_flow_control_while_http_call`` to false. +- area: sni + change: | + When computing SNI and SAN value for the auto-sni and auto-san verification feature, + route host manipulations are now taken into account. This behavior can be reverted + by setting the runtime guard ``envoy_reloadable_features_use_route_host_mutation_for_auto_sni_san`` to false. bug_fixes: # *Changes expected to improve the state of the world and are unlikely to have negative effects* diff --git a/envoy/router/router.h b/envoy/router/router.h index 0200131c27aa..48ed169bcd40 100644 --- a/envoy/router/router.h +++ b/envoy/router/router.h @@ -900,6 +900,23 @@ class RouteEntry : public ResponseEntry { */ virtual const std::string& clusterName() const PURE; + /** + * Returns the final host value for the request, taking into account route-level mutations. + * + * The value returned is computed with the following logic in order: + * + * 1. If a host rewrite is configured for the route, it returns that value. + * 2. If a host rewrite header is specified, it attempts to use the value from that header. + * 3. If a host rewrite path regex is configured, it applies the regex to the request path and + * returns the result. + * 4. If none of the above apply, it returns the original host value from the request headers. + * + * @param headers The constant reference to the request headers. + * @note This function will not attempt to restore the port in the host value. If port information + * is required, it should be handled separately. + */ + virtual const std::string getRequestHostValue(const Http::RequestHeaderMap& headers) const PURE; + /** * Returns the HTTP status code to use when configured cluster is not found. * @return Http::Code to use when configured cluster is not found. diff --git a/source/common/http/null_route_impl.h b/source/common/http/null_route_impl.h index 6afaebf2e940..672dcc207d9a 100644 --- a/source/common/http/null_route_impl.h +++ b/source/common/http/null_route_impl.h @@ -103,6 +103,9 @@ struct RouteEntryImpl : public Router::RouteEntry { // Router::RouteEntry const std::string& clusterName() const override { return cluster_name_; } + const std::string getRequestHostValue(const Http::RequestHeaderMap& headers) const override { + return std::string(headers.getHostValue()); + } const Router::RouteStatsContextOptRef routeStatsContext() const override { return Router::RouteStatsContextOptRef(); } diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index d94b988dfea4..0d4b12862985 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -906,6 +906,32 @@ bool RouteEntryImplBase::matchRoute(const Http::RequestHeaderMap& headers, const std::string& RouteEntryImplBase::clusterName() const { return cluster_name_; } +const std::string +RouteEntryImplBase::getRequestHostValue(const Http::RequestHeaderMap& headers) const { + if (!host_rewrite_.empty()) { + return host_rewrite_; + } + + if (auto_host_rewrite_header_) { + const auto& header = headers.get(*auto_host_rewrite_header_); + if (!header.empty()) { + const absl::string_view header_value = header[0]->value().getStringView(); + if (!header_value.empty()) { + return std::string(header_value); + } + } + } + + if (host_rewrite_path_regex_) { + absl::string_view path = headers.getPathValue(); + return host_rewrite_path_regex_->replaceAll(Http::PathUtil::removeQueryAndFragment(path), + host_rewrite_path_regex_substitution_); + } + + // Fallback to original host value + return std::string(headers.getHostValue()); +} + void RouteEntryImplBase::finalizeRequestHeaders(Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo& stream_info, bool insert_envoy_original_path) const { @@ -926,25 +952,9 @@ void RouteEntryImplBase::finalizeRequestHeaders(Http::RequestHeaderMap& headers, } } - if (!host_rewrite_.empty()) { - Http::Utility::updateAuthority(headers, host_rewrite_, append_xfh_); - } else if (auto_host_rewrite_header_) { - const auto header = headers.get(*auto_host_rewrite_header_); - if (!header.empty()) { - // This is an implicitly untrusted header, so per the API documentation only the first - // value is used. - const absl::string_view header_value = header[0]->value().getStringView(); - if (!header_value.empty()) { - Http::Utility::updateAuthority(headers, header_value, append_xfh_); - } - } - } else if (host_rewrite_path_regex_ != nullptr) { - const std::string path(headers.getPathValue()); - absl::string_view just_path(Http::PathUtil::removeQueryAndFragment(path)); - Http::Utility::updateAuthority( - headers, - host_rewrite_path_regex_->replaceAll(just_path, host_rewrite_path_regex_substitution_), - append_xfh_); + auto final_host_value = getRequestHostValue(headers); + if (final_host_value != headers.getHostValue()) { + Http::Utility::updateAuthority(headers, final_host_value, append_xfh_); } // Handle path rewrite diff --git a/source/common/router/config_impl.h b/source/common/router/config_impl.h index f9383d6bd22c..36b0e26dc6f4 100644 --- a/source/common/router/config_impl.h +++ b/source/common/router/config_impl.h @@ -682,6 +682,7 @@ class RouteEntryImplBase : public RouteEntryAndRoute, // Router::RouteEntry const std::string& clusterName() const override; + const std::string getRequestHostValue(const Http::RequestHeaderMap& headers) const override; const RouteStatsContextOptRef routeStatsContext() const override { if (route_stats_context_ != nullptr) { return *route_stats_context_; @@ -851,6 +852,9 @@ class RouteEntryImplBase : public RouteEntryAndRoute, // Router::RouteEntry const std::string& clusterName() const override { return cluster_name_; } + const std::string getRequestHostValue(const Http::RequestHeaderMap& headers) const override { + return parent_->getRequestHostValue(headers); + } Http::Code clusterNotFoundResponseCode() const override { return parent_->clusterNotFoundResponseCode(); } diff --git a/source/common/router/delegating_route_impl.cc b/source/common/router/delegating_route_impl.cc index 4b7afc9fee77..b8d5d903dc6a 100644 --- a/source/common/router/delegating_route_impl.cc +++ b/source/common/router/delegating_route_impl.cc @@ -32,6 +32,11 @@ const std::string& DelegatingRouteEntry::clusterName() const { return base_route_->routeEntry()->clusterName(); } +const std::string +DelegatingRouteEntry::getRequestHostValue(const Http::RequestHeaderMap& headers) const { + return base_route_->routeEntry()->getRequestHostValue(headers); +} + Http::Code DelegatingRouteEntry::clusterNotFoundResponseCode() const { return base_route_->routeEntry()->clusterNotFoundResponseCode(); } diff --git a/source/common/router/delegating_route_impl.h b/source/common/router/delegating_route_impl.h index 91d0b99f86c1..36deb173c7f9 100644 --- a/source/common/router/delegating_route_impl.h +++ b/source/common/router/delegating_route_impl.h @@ -74,6 +74,7 @@ class DelegatingRouteEntry : public Router::RouteEntry { // Router::RouteEntry const std::string& clusterName() const override; + const std::string getRequestHostValue(const Http::RequestHeaderMap& headers) const override; Http::Code clusterNotFoundResponseCode() const override; const CorsPolicy* corsPolicy() const override; absl::optional diff --git a/source/common/router/router.cc b/source/common/router/router.cc index 1a940b5db2a8..02930fe93365 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -580,7 +580,11 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, (upstream_http_protocol_options.value().auto_sni() || upstream_http_protocol_options.value().auto_san_validation())) { // Default the header to Host/Authority header. - absl::string_view header_value = headers.getHostValue(); + std::string header_value = route_entry_->getRequestHostValue(headers); + if (!Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.use_route_host_mutation_for_auto_sni_san")) { + header_value = std::string(headers.getHostValue()); + } // Check whether `override_auto_sni_header` is specified. const auto override_auto_sni_header = diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index bd21d36a8b6c..2037d0487b19 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -90,6 +90,7 @@ RUNTIME_GUARD(envoy_reloadable_features_udp_socket_apply_aggregated_read_limit); RUNTIME_GUARD(envoy_reloadable_features_uhv_allow_malformed_url_encoding); RUNTIME_GUARD(envoy_reloadable_features_upstream_remote_address_use_connection); RUNTIME_GUARD(envoy_reloadable_features_use_http3_header_normalisation); +RUNTIME_GUARD(envoy_reloadable_features_use_route_host_mutation_for_auto_sni_san); RUNTIME_GUARD(envoy_reloadable_features_use_typed_metadata_in_proxy_protocol_listener); RUNTIME_GUARD(envoy_reloadable_features_validate_connect); RUNTIME_GUARD(envoy_reloadable_features_validate_grpc_header_before_log_grpc_status); diff --git a/test/extensions/filters/http/router/auto_sni_integration_test.cc b/test/extensions/filters/http/router/auto_sni_integration_test.cc index 325c64af38af..38d84bdb7949 100644 --- a/test/extensions/filters/http/router/auto_sni_integration_test.cc +++ b/test/extensions/filters/http/router/auto_sni_integration_test.cc @@ -9,6 +9,7 @@ #include "source/common/tls/server_ssl_socket.h" #include "test/integration/http_integration.h" +#include "test/test_common/utility.h" namespace Envoy { namespace { @@ -18,7 +19,8 @@ class AutoSniIntegrationTest : public testing::TestWithParamset_name("envoy.transport_sockets.tls"); cluster_config.mutable_transport_socket()->mutable_typed_config()->PackFrom(tls_context); }); + if (route_config != nullptr) { + config_helper_.addConfigModifier( + [route_config](envoy::extensions::filters::network::http_connection_manager::v3:: + HttpConnectionManager& hcm) { + auto* new_route_config = hcm.mutable_route_config(); + new_route_config->CopyFrom(*route_config); + }); + } HttpIntegrationTest::initialize(); } @@ -90,6 +100,141 @@ TEST_P(AutoSniIntegrationTest, BasicAutoSniTest) { EXPECT_STREQ("localhost", SSL_get_servername(ssl_socket->ssl(), TLSEXT_NAMETYPE_host_name)); } +TEST_P(AutoSniIntegrationTest, AutoSniTestWithHostRewrite) { + const std::string yaml = R"EOF( +virtual_hosts: +- name: local_service + domains: ["*"] + routes: + - match: + prefix: "/" + name: "foo" + route: + cluster: cluster_0 + host_rewrite_literal: foo + )EOF"; + envoy::config::route::v3::RouteConfiguration route_config; + TestUtility::loadFromYaml(yaml, route_config); + setup("", &route_config); + + codec_client_ = makeHttpConnection(lookupPort("http")); + const auto response_ = sendRequestAndWaitForResponse( + Http::TestRequestHeaderMapImpl{ + {":method", "GET"}, {":path", "/"}, {":scheme", "http"}, {":authority", "localhost"}}, + 0, default_response_headers_, 0); + + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_TRUE(response_->complete()); + + const Extensions::TransportSockets::Tls::SslHandshakerImpl* ssl_socket = + dynamic_cast( + fake_upstream_connection_->connection().ssl().get()); + EXPECT_STREQ("foo", SSL_get_servername(ssl_socket->ssl(), TLSEXT_NAMETYPE_host_name)); +} + +TEST_P(AutoSniIntegrationTest, AutoSniTestWithHostRewriteLegacy) { + const std::string yaml = R"EOF( +virtual_hosts: +- name: local_service + domains: ["*"] + routes: + - match: + prefix: "/" + name: "foo" + route: + cluster: cluster_0 + host_rewrite_literal: foo + )EOF"; + envoy::config::route::v3::RouteConfiguration route_config; + TestUtility::loadFromYaml(yaml, route_config); + config_helper_.addRuntimeOverride( + "envoy.reloadable_features.use_route_host_mutation_for_auto_sni_san", "false"); + setup("", &route_config); + codec_client_ = makeHttpConnection(lookupPort("http")); + const auto response_ = sendRequestAndWaitForResponse( + Http::TestRequestHeaderMapImpl{ + {":method", "GET"}, {":path", "/"}, {":scheme", "http"}, {":authority", "localhost"}}, + 0, default_response_headers_, 0); + + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_TRUE(response_->complete()); + + const Extensions::TransportSockets::Tls::SslHandshakerImpl* ssl_socket = + dynamic_cast( + fake_upstream_connection_->connection().ssl().get()); + EXPECT_STRNE("foo", SSL_get_servername(ssl_socket->ssl(), TLSEXT_NAMETYPE_host_name)); +} + +TEST_P(AutoSniIntegrationTest, AutoSniTestWithHostRewriteRegex) { + const std::string yaml = R"EOF( +virtual_hosts: +- name: local_service + domains: ["*"] + routes: + - match: + prefix: "/" + name: "foo" + route: + cluster: cluster_0 + host_rewrite_path_regex: + pattern: + regex: ".*" + substitution: "foo" + )EOF"; + envoy::config::route::v3::RouteConfiguration route_config; + TestUtility::loadFromYaml(yaml, route_config); + setup("", &route_config); + + codec_client_ = makeHttpConnection(lookupPort("http")); + const auto response_ = sendRequestAndWaitForResponse( + Http::TestRequestHeaderMapImpl{ + {":method", "GET"}, {":path", "/"}, {":scheme", "http"}, {":authority", "localhost"}}, + 0, default_response_headers_, 0); + + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_TRUE(response_->complete()); + + const Extensions::TransportSockets::Tls::SslHandshakerImpl* ssl_socket = + dynamic_cast( + fake_upstream_connection_->connection().ssl().get()); + EXPECT_STREQ("foo", SSL_get_servername(ssl_socket->ssl(), TLSEXT_NAMETYPE_host_name)); +} + +TEST_P(AutoSniIntegrationTest, AutoSniTestWithHostRewriteHeader) { + const std::string yaml = R"EOF( +virtual_hosts: +- name: local_service + domains: ["*"] + routes: + - match: + prefix: "/" + name: "foo" + route: + cluster: cluster_0 + host_rewrite_header: bar + )EOF"; + envoy::config::route::v3::RouteConfiguration route_config; + TestUtility::loadFromYaml(yaml, route_config); + setup("", &route_config); + + codec_client_ = makeHttpConnection(lookupPort("http")); + const auto response_ = + sendRequestAndWaitForResponse(Http::TestRequestHeaderMapImpl{{":method", "GET"}, + {":path", "/"}, + {":scheme", "http"}, + {":authority", "localhost"}, + {"bar", "foo"}}, + 0, default_response_headers_, 0); + + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_TRUE(response_->complete()); + + const Extensions::TransportSockets::Tls::SslHandshakerImpl* ssl_socket = + dynamic_cast( + fake_upstream_connection_->connection().ssl().get()); + EXPECT_STREQ("foo", SSL_get_servername(ssl_socket->ssl(), TLSEXT_NAMETYPE_host_name)); +} + TEST_P(AutoSniIntegrationTest, AutoSniWithAltHeaderNameTest) { setup("x-host"); codec_client_ = makeHttpConnection(lookupPort("http")); diff --git a/test/mocks/router/mocks.cc b/test/mocks/router/mocks.cc index c321bfe1574f..8a3ae1c7b6bf 100644 --- a/test/mocks/router/mocks.cc +++ b/test/mocks/router/mocks.cc @@ -99,6 +99,10 @@ MockPathMatchCriterion::~MockPathMatchCriterion() = default; MockRouteEntry::MockRouteEntry() { ON_CALL(*this, clusterName()).WillByDefault(ReturnRef(cluster_name_)); + ON_CALL(*this, getRequestHostValue(_)) + .WillByDefault([](const Http::RequestHeaderMap& headers) -> std::string { + return std::string(headers.getHostValue()); + }); ON_CALL(*this, opaqueConfig()).WillByDefault(ReturnRef(opaque_config_)); ON_CALL(*this, rateLimitPolicy()).WillByDefault(ReturnRef(rate_limit_policy_)); ON_CALL(*this, retryPolicy()).WillByDefault(ReturnRef(retry_policy_)); diff --git a/test/mocks/router/mocks.h b/test/mocks/router/mocks.h index ca84f36654f4..1fcadf99f811 100644 --- a/test/mocks/router/mocks.h +++ b/test/mocks/router/mocks.h @@ -412,6 +412,8 @@ class MockRouteEntry : public RouteEntry { // Router::Config MOCK_METHOD(const std::string&, clusterName, (), (const)); + MOCK_METHOD(const std::string, getRequestHostValue, (const Http::RequestHeaderMap& headers), + (const)); MOCK_METHOD(Http::Code, clusterNotFoundResponseCode, (), (const)); MOCK_METHOD(void, finalizeRequestHeaders, (Http::RequestHeaderMap & headers, const StreamInfo::StreamInfo& stream_info, From a5993d0eb8f99707949a0457e3aed1d4ae2e1a39 Mon Sep 17 00:00:00 2001 From: Raven Black Date: Tue, 13 Aug 2024 19:51:48 -0400 Subject: [PATCH 102/130] Fix and simplify Status payloads (#35672) Commit Message: Fix Status payloads Additional Description: Fix the mis-sized status payload (which causes asan errors in gcc builds on status.ToString()). Testing: Passes existing tests. Docs Changes: n/a Release Notes: n/a Platform Specific Features: n/a --------- Signed-off-by: Raven Black --- source/common/http/status.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/common/http/status.cc b/source/common/http/status.cc index 967d8a6ae640..86117912c212 100644 --- a/source/common/http/status.cc +++ b/source/common/http/status.cc @@ -45,7 +45,7 @@ struct PrematureResponsePayload : public EnvoyStatusPayload { template void storePayload(absl::Status& status, const T& payload) { const T* allocated = new T(payload); const absl::string_view sv = - absl::string_view(reinterpret_cast(allocated), sizeof(allocated)); + absl::string_view(reinterpret_cast(allocated), sizeof(T)); absl::Cord cord = absl::MakeCordFromExternal(sv, [allocated]() { delete allocated; }); cord.Flatten(); // Flatten ahead of time for easier access later. status.SetPayload(EnvoyPayloadUrl, std::move(cord)); From 30e0508cc6910acbc8fc277babdbe2f23d4926e9 Mon Sep 17 00:00:00 2001 From: code Date: Wed, 14 Aug 2024 11:51:53 +0800 Subject: [PATCH 103/130] http: remove unnecessary filter canonical name (#35661) Commit Message: http: remove unnecessary filter canonical name Additional Description: This the `filter_name` field is unnecessary after #35331. Risk Level: low. Testing: n/a. Docs Changes: n/a. Release Notes: n/a. --------- Signed-off-by: wbpcode --- envoy/http/filter_factory.h | 5 +-- source/common/filter/config_discovery_impl.h | 21 +++------- source/common/http/filter_chain_helper.cc | 4 +- source/common/http/filter_chain_helper.h | 11 +++-- .../filters/http/composite/action.h | 13 +++--- .../network/http_connection_manager/config.h | 2 +- .../filter/config_discovery_impl_test.cc | 4 +- test/common/http/filter_chain_helper_test.cc | 12 ++---- test/common/http/filter_manager_test.cc | 40 +++++++++---------- 9 files changed, 45 insertions(+), 67 deletions(-) diff --git a/envoy/http/filter_factory.h b/envoy/http/filter_factory.h index 76f1162fcbd1..fdad6281d845 100644 --- a/envoy/http/filter_factory.h +++ b/envoy/http/filter_factory.h @@ -25,15 +25,12 @@ using FilterFactoryCb = std::function class HttpDynamicFilterConfigProviderImpl - : public DynamicFilterConfigProviderImpl { + : public DynamicFilterConfigProviderImpl { public: HttpDynamicFilterConfigProviderImpl( FilterConfigSubscriptionSharedPtr& subscription, @@ -219,14 +213,11 @@ class HttpDynamicFilterConfigProviderImpl } private: - absl::StatusOr + absl::StatusOr instantiateFilterFactory(const Protobuf::Message& message) const override { auto* factory = Registry::FactoryRegistry::getFactoryByType( message.GetTypeName()); - absl::StatusOr error_or_factory = - factory->createFilterFactoryFromProto(message, getStatPrefix(), factory_context_); - RETURN_IF_NOT_OK_REF(error_or_factory.status()); - return NamedHttpFilterFactoryCb{factory->name(), error_or_factory.value()}; + return factory->createFilterFactoryFromProto(message, getStatPrefix(), factory_context_); } Server::Configuration::ServerFactoryContext& server_context_; @@ -651,7 +642,7 @@ class FilterConfigProviderManagerImpl : public FilterConfigProviderManagerImplBa // HTTP filter class HttpFilterConfigProviderManagerImpl : public FilterConfigProviderManagerImpl< - Server::Configuration::NamedHttpFilterConfigFactory, NamedHttpFilterFactoryCb, + Server::Configuration::NamedHttpFilterConfigFactory, HttpFilterFactoryCb, Server::Configuration::FactoryContext, HttpDynamicFilterConfigProviderImpl< Server::Configuration::FactoryContext, @@ -679,7 +670,7 @@ class HttpFilterConfigProviderManagerImpl // HTTP filter class UpstreamHttpFilterConfigProviderManagerImpl : public FilterConfigProviderManagerImpl< - Server::Configuration::UpstreamHttpFilterConfigFactory, NamedHttpFilterFactoryCb, + Server::Configuration::UpstreamHttpFilterConfigFactory, HttpFilterFactoryCb, Server::Configuration::UpstreamFactoryContext, HttpDynamicFilterConfigProviderImpl< Server::Configuration::UpstreamFactoryContext, diff --git a/source/common/http/filter_chain_helper.cc b/source/common/http/filter_chain_helper.cc index 4963dd6e3ae0..05ad8023d440 100644 --- a/source/common/http/filter_chain_helper.cc +++ b/source/common/http/filter_chain_helper.cc @@ -27,9 +27,7 @@ void FilterChainUtility::createFilterChainForFactories( auto config = filter_config_provider.provider->config(); if (config.has_value()) { - Filter::NamedHttpFilterFactoryCb& factory_cb = config.value().get(); - manager.applyFilterFactoryCb({filter_config_provider.provider->name(), factory_cb.name}, - factory_cb.factory_cb); + manager.applyFilterFactoryCb({filter_config_provider.provider->name()}, config.ref()); continue; } diff --git a/source/common/http/filter_chain_helper.h b/source/common/http/filter_chain_helper.h index 7b8df370b0d6..24d0cda93c87 100644 --- a/source/common/http/filter_chain_helper.h +++ b/source/common/http/filter_chain_helper.h @@ -16,10 +16,10 @@ namespace Envoy { namespace Http { using DownstreamFilterConfigProviderManager = - Filter::FilterConfigProviderManager; using UpstreamFilterConfigProviderManager = - Filter::FilterConfigProviderManager; // Allows graceful handling of missing configuration for ECDS. @@ -42,7 +42,7 @@ static Http::FilterFactoryCb MissingConfigFilterFactory = class FilterChainUtility : Logger::Loggable { public: struct FilterFactoryProvider { - Filter::FilterConfigProviderPtr provider; + Filter::FilterConfigProviderPtr provider; // If true, this filter is disabled by default and must be explicitly enabled by // route configuration. bool disabled{}; @@ -70,7 +70,7 @@ class FilterChainHelper : Logger::Loggable { public: using FilterFactoriesList = FilterChainUtility::FilterFactoriesList; using FilterConfigProviderManager = - Filter::FilterConfigProviderManager; + Filter::FilterConfigProviderManager; FilterChainHelper(FilterConfigProviderManager& filter_config_provider_manager, Server::Configuration::ServerFactoryContext& server_context, @@ -146,14 +146,13 @@ class FilterChainHelper : Logger::Loggable { if (!callback_or_error.status().ok()) { return callback_or_error.status(); } - Http::FilterFactoryCb callback = callback_or_error.value(); dependency_manager.registerFilter(factory->name(), *factory->dependencies()); const bool is_terminal = factory->isTerminalFilterByProto(*message, server_context_); RETURN_IF_NOT_OK(Config::Utility::validateTerminalFilters(proto_config.name(), factory->name(), filter_chain_type, is_terminal, last_filter_in_current_config)); auto filter_config_provider = filter_config_provider_manager_.createStaticFilterConfigProvider( - {factory->name(), callback}, proto_config.name()); + callback_or_error.value(), proto_config.name()); #ifdef ENVOY_ENABLE_YAML ENVOY_LOG(debug, " name: {}", filter_config_provider->name()); ENVOY_LOG(debug, " config: {}", diff --git a/source/extensions/filters/http/composite/action.h b/source/extensions/filters/http/composite/action.h index dd06a0a924b9..834b757a573f 100644 --- a/source/extensions/filters/http/composite/action.h +++ b/source/extensions/filters/http/composite/action.h @@ -11,8 +11,8 @@ namespace Extensions { namespace HttpFilters { namespace Composite { -using HttpExtensionConfigProviderSharedPtr = std::shared_ptr< - Config::DynamicExtensionConfigProvider>; +using HttpExtensionConfigProviderSharedPtr = + std::shared_ptr>; class ExecuteFilterAction : public Matcher::ActionBase< @@ -80,14 +80,11 @@ class ExecuteFilterActionFactory return nullptr; } - auto config_value = provider->config(); - if (config_value.has_value()) { - auto factory_cb = config_value.value().get().factory_cb; - return std::make_unique(factory_cb, n); + if (auto config_value = provider->config(); config_value.has_value()) { + return std::make_unique(config_value.ref(), n); } // There is no dynamic config available. Apply missing config filter. - auto factory_cb = Envoy::Http::MissingConfigFilterFactory; - return std::make_unique(factory_cb, n); + return std::make_unique(Envoy::Http::MissingConfigFilterFactory, n); }; } diff --git a/source/extensions/filters/network/http_connection_manager/config.h b/source/extensions/filters/network/http_connection_manager/config.h index c7a25b431d1d..29130a2f1793 100644 --- a/source/extensions/filters/network/http_connection_manager/config.h +++ b/source/extensions/filters/network/http_connection_manager/config.h @@ -44,7 +44,7 @@ namespace NetworkFilters { namespace HttpConnectionManager { using FilterConfigProviderManager = - Filter::FilterConfigProviderManager; /** diff --git a/test/common/filter/config_discovery_impl_test.cc b/test/common/filter/config_discovery_impl_test.cc index cf8e30503fce..cb580db0ffde 100644 --- a/test/common/filter/config_discovery_impl_test.cc +++ b/test/common/filter/config_discovery_impl_test.cc @@ -275,7 +275,7 @@ class FilterConfigDiscoveryImplTest : public FilterConfigDiscoveryTestBase { // HTTP filter test class HttpFilterConfigDiscoveryImplTest : public FilterConfigDiscoveryImplTest< - NamedHttpFilterFactoryCb, Server::Configuration::FactoryContext, + HttpFilterFactoryCb, Server::Configuration::FactoryContext, HttpFilterConfigProviderManagerImpl, TestHttpFilterFactory, Server::Configuration::NamedHttpFilterConfigFactory, Server::Configuration::MockFactoryContext> { @@ -292,7 +292,7 @@ class HttpFilterConfigDiscoveryImplTest // Upstream HTTP filter test class HttpUpstreamFilterConfigDiscoveryImplTest : public FilterConfigDiscoveryImplTest< - NamedHttpFilterFactoryCb, Server::Configuration::UpstreamFactoryContext, + HttpFilterFactoryCb, Server::Configuration::UpstreamFactoryContext, UpstreamHttpFilterConfigProviderManagerImpl, TestHttpFilterFactory, Server::Configuration::UpstreamHttpFilterConfigFactory, Server::Configuration::MockUpstreamFactoryContext> { diff --git a/test/common/http/filter_chain_helper_test.cc b/test/common/http/filter_chain_helper_test.cc index 75483f21cd68..dd8c36985d58 100644 --- a/test/common/http/filter_chain_helper_test.cc +++ b/test/common/http/filter_chain_helper_test.cc @@ -28,10 +28,8 @@ TEST(FilterChainUtilityTest, CreateFilterChainForFactoriesWithRouteDisabled) { for (const auto& name : {"filter_0", "filter_1", "filter_2"}) { auto provider = - std::make_unique>( - Filter::NamedHttpFilterFactoryCb{"filter_type_name", - [](FilterChainFactoryCallbacks&) {}}, - name); + std::make_unique>( + [](FilterChainFactoryCallbacks&) {}, name); filter_factories.push_back({std::move(provider), false}); } @@ -61,10 +59,8 @@ TEST(FilterChainUtilityTest, CreateFilterChainForFactoriesWithRouteDisabledAndDe for (const auto& name : {"filter_0", "filter_1", "filter_2"}) { auto provider = - std::make_unique>( - Filter::NamedHttpFilterFactoryCb{"filter_type_name", - [](FilterChainFactoryCallbacks&) {}}, - name); + std::make_unique>( + [](FilterChainFactoryCallbacks&) {}, name); filter_factories.push_back({std::move(provider), true}); } diff --git a/test/common/http/filter_manager_test.cc b/test/common/http/filter_manager_test.cc index c360ed655c6a..d310d73873db 100644 --- a/test/common/http/filter_manager_test.cc +++ b/test/common/http/filter_manager_test.cc @@ -168,7 +168,7 @@ TEST_F(FilterManagerTest, SendLocalReplyDuringDecodingGrpcClassiciation) { EXPECT_CALL(filter_factory_, createFilterChain(_)) .WillRepeatedly(Invoke([&](FilterChainManager& manager) -> bool { auto factory = createDecoderFilterFactoryCb(filter); - manager.applyFilterFactoryCb({"configName1", "filterName1"}, factory); + manager.applyFilterFactoryCb({"configName1"}, factory); return true; })); @@ -222,10 +222,10 @@ TEST_F(FilterManagerTest, SendLocalReplyDuringEncodingGrpcClassiciation) { EXPECT_CALL(filter_factory_, createFilterChain(_)) .WillRepeatedly(Invoke([&](FilterChainManager& manager) -> bool { auto decoder_factory = createDecoderFilterFactoryCb(decoder_filter); - manager.applyFilterFactoryCb({"configName1", "filterName1"}, decoder_factory); + manager.applyFilterFactoryCb({"configName1"}, decoder_factory); auto stream_factory = createStreamFilterFactoryCb(encoder_filter); - manager.applyFilterFactoryCb({"configName2", "filterName2"}, stream_factory); + manager.applyFilterFactoryCb({"configName2"}, stream_factory); return true; })); @@ -272,11 +272,11 @@ TEST_F(FilterManagerTest, OnLocalReply) { EXPECT_CALL(filter_factory_, createFilterChain(_)) .WillRepeatedly(Invoke([&](FilterChainManager& manager) -> bool { auto decoder_factory = createDecoderFilterFactoryCb(decoder_filter); - manager.applyFilterFactoryCb({"configName1", "filterName1"}, decoder_factory); + manager.applyFilterFactoryCb({"configName1"}, decoder_factory); auto stream_factory = createStreamFilterFactoryCb(stream_filter); - manager.applyFilterFactoryCb({"configName2", "filterName2"}, stream_factory); + manager.applyFilterFactoryCb({"configName2"}, stream_factory); auto encoder_factory = createEncoderFilterFactoryCb(encoder_filter); - manager.applyFilterFactoryCb({"configName3", "filterName3"}, encoder_factory); + manager.applyFilterFactoryCb({"configName3"}, encoder_factory); return true; })); @@ -335,11 +335,11 @@ TEST_F(FilterManagerTest, MultipleOnLocalReply) { EXPECT_CALL(filter_factory_, createFilterChain(_)) .WillRepeatedly(Invoke([&](FilterChainManager& manager) -> bool { auto decoder_factory = createDecoderFilterFactoryCb(decoder_filter); - manager.applyFilterFactoryCb({"configName1", "filterName1"}, decoder_factory); + manager.applyFilterFactoryCb({"configName1"}, decoder_factory); auto stream_factory = createStreamFilterFactoryCb(stream_filter); - manager.applyFilterFactoryCb({"configName2", "filterName2"}, stream_factory); + manager.applyFilterFactoryCb({"configName2"}, stream_factory); auto encoder_factory = createEncoderFilterFactoryCb(encoder_filter); - manager.applyFilterFactoryCb({"configName3", "filterName3"}, encoder_factory); + manager.applyFilterFactoryCb({"configName3"}, encoder_factory); return true; })); @@ -437,7 +437,7 @@ TEST_F(FilterManagerTest, GetRouteLevelFilterConfig) { EXPECT_CALL(filter_factory_, createFilterChain(_)) .WillRepeatedly(Invoke([&](FilterChainManager& manager) -> bool { auto decoder_factory = createDecoderFilterFactoryCb(decoder_filter); - manager.applyFilterFactoryCb({"custom-name", "filter-name"}, decoder_factory); + manager.applyFilterFactoryCb({"custom-name"}, decoder_factory); return true; })); filter_manager_->createFilterChain(); @@ -491,7 +491,7 @@ TEST_F(FilterManagerTest, GetRouteLevelFilterConfigForNullRoute) { EXPECT_CALL(filter_factory_, createFilterChain(_)) .WillRepeatedly(Invoke([&](FilterChainManager& manager) -> bool { auto decoder_factory = createDecoderFilterFactoryCb(decoder_filter); - manager.applyFilterFactoryCb({"custom-name", "filter-name"}, decoder_factory); + manager.applyFilterFactoryCb({"custom-name"}, decoder_factory); return true; })); filter_manager_->createFilterChain(); @@ -523,9 +523,9 @@ TEST_F(FilterManagerTest, MetadataContinueAll) { EXPECT_CALL(filter_factory_, createFilterChain(_)) .WillRepeatedly(Invoke([&](FilterChainManager& manager) -> bool { auto decoder_factory = createStreamFilterFactoryCb(filter_1); - manager.applyFilterFactoryCb({"configName1", "filterName1"}, decoder_factory); + manager.applyFilterFactoryCb({"configName1"}, decoder_factory); decoder_factory = createStreamFilterFactoryCb(filter_2); - manager.applyFilterFactoryCb({"configName2", "filterName2"}, decoder_factory); + manager.applyFilterFactoryCb({"configName2"}, decoder_factory); return true; })); filter_manager_->createFilterChain(); @@ -595,9 +595,9 @@ TEST_F(FilterManagerTest, DecodeMetadataSendsLocalReply) { EXPECT_CALL(filter_factory_, createFilterChain(_)) .WillRepeatedly(Invoke([&](FilterChainManager& manager) -> bool { auto factory = createStreamFilterFactoryCb(filter_1); - manager.applyFilterFactoryCb({"configName1", "filterName1"}, factory); + manager.applyFilterFactoryCb({"configName1"}, factory); factory = createStreamFilterFactoryCb(filter_2); - manager.applyFilterFactoryCb({"configName2", "filterName2"}, factory); + manager.applyFilterFactoryCb({"configName2"}, factory); return true; })); filter_manager_->createFilterChain(); @@ -642,9 +642,9 @@ TEST_F(FilterManagerTest, MetadataContinueAllFollowedByHeadersLocalReply) { EXPECT_CALL(filter_factory_, createFilterChain(_)) .WillRepeatedly(Invoke([&](FilterChainManager& manager) -> bool { auto decoder_factory = createStreamFilterFactoryCb(filter_1); - manager.applyFilterFactoryCb({"configName1", "filterName1"}, decoder_factory); + manager.applyFilterFactoryCb({"configName1"}, decoder_factory); decoder_factory = createStreamFilterFactoryCb(filter_2); - manager.applyFilterFactoryCb({"configName2", "filterName2"}, decoder_factory); + manager.applyFilterFactoryCb({"configName2"}, decoder_factory); return true; })); filter_manager_->createFilterChain(); @@ -682,9 +682,9 @@ TEST_F(FilterManagerTest, EncodeMetadataSendsLocalReply) { EXPECT_CALL(filter_factory_, createFilterChain(_)) .WillRepeatedly(Invoke([&](FilterChainManager& manager) -> bool { auto factory = createStreamFilterFactoryCb(filter_1); - manager.applyFilterFactoryCb({"configName1", "filterName1"}, factory); + manager.applyFilterFactoryCb({"configName1"}, factory); factory = createStreamFilterFactoryCb(filter_2); - manager.applyFilterFactoryCb({"configName2", "filterName2"}, factory); + manager.applyFilterFactoryCb({"configName2"}, factory); return true; })); filter_manager_->createFilterChain(); @@ -721,7 +721,7 @@ TEST_F(FilterManagerTest, IdleTimerResets) { EXPECT_CALL(filter_factory_, createFilterChain(_)) .WillRepeatedly(Invoke([&](FilterChainManager& manager) -> bool { auto factory = createStreamFilterFactoryCb(filter_1); - manager.applyFilterFactoryCb({"configName1", "filterName1"}, factory); + manager.applyFilterFactoryCb({"configName1"}, factory); return true; })); filter_manager_->createFilterChain(); From 3299053020b423b2e16917bb264f7eb5285f72ef Mon Sep 17 00:00:00 2001 From: phlax Date: Wed, 14 Aug 2024 04:53:00 +0100 Subject: [PATCH 104/130] deps: Bump `com_github_curl` -> 8.9.1 (#35688) Fix #35686 and resolve related CVE ```console CVE-2024-7264 (com_github_curl@8.4.0) CVSS v3 score: 6.5 Severity: MEDIUM Published date: 2024-07-31 Last modified date: 2024-08-12 Description: libcurl's ASN1 parser code has the `GTime2str()` function, used for parsing an ASN.1 Generalized Time field. If given an syntactically incorrect field, the parser might end up using -1 for the length of the *time fraction*, leading to a `strlen()` getting performed on a pointer to a heap buffer area that is not (purposely) null terminated. This flaw most likely leads to a crash, but can also lead to heap contents getting returned to the application when [CURLINFO_CERTINFO](https://curl.se/libcurl/c/CURLINFO_CERTINFO.html) is used. Affected CPEs: - cpe:2.3:a:haxx:libcurl:* ``` Signed-off-by: Ryan Northey --- bazel/foreign_cc/curl.patch | 33 +++++++++++++++++---------------- bazel/repository_locations.bzl | 6 +++--- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/bazel/foreign_cc/curl.patch b/bazel/foreign_cc/curl.patch index 31d4b9b91c6c..708eb98d9dce 100644 --- a/bazel/foreign_cc/curl.patch +++ b/bazel/foreign_cc/curl.patch @@ -3,12 +3,12 @@ #Date: Tue Dec 22 15:31:03 2020 -0500 # # cmake: Add an option to disable libidn2 -# +# # New option USE_LIBIDN2 defaults to ON for libidn2 detection. Prior to # this change libidn2 detection could not be turned off in cmake builds. -# +# # Reported-by: William A Rowe Jr -# +# # Fixes https://github.com/curl/curl/issues/6361 # Closes #xxxx # @@ -17,47 +17,48 @@ #Date: Wed Oct 7 14:32:49 2020 -0500 # # Correct fragile windows assumptions -# +# # - Locking CMake to 3.16 breaks all features and corrections applied to # CMake 3.17 and later, including the correction of the poorly designed # and now abandoned Windows CRT election policy CMP0091 (see final para # of the policy description here: # https://cmake.org/cmake/help/v3.18/policy/CMP0091.html). Locking to # rev 3.16 from ensures a more difficult transition to CMake-current -# +# # - Windows curl builds previously only adjusted the Release and Debug # builds, and combined with CMP0091 to break other flavors. Update any # /MD* flags with /MT* present in the base and four alternate build # flavors, without introducing conflicting flag values or introducing # a CRT election where one is not present -# +# # - Windows clang-cl builds of curl static libs are broken when using # link-lld.exe because curl appended the dynamic run time flags to the # static library lib.exe options. While these were ignored/no-op on # Windows link.exe, they cause link-lld from LLVM/clang-cl compile # toolchain to fail to parse the library command. -# +# # Summary exists in this bazel-specific bug report; # https://github.com/bazelbuild/rules_foreign_cc/issues/426 diff --git a/CMakeLists.txt b/CMakeLists.txt -index ed60f07bc..0d2088cb9 100644 +index 580cc4357..13df6ca54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -62,6 +62,11 @@ - +@@ -44,6 +44,12 @@ + # variable is NOT DEFINED, the symbol detection will be performed. + cmake_minimum_required(VERSION 3.7...3.16 FATAL_ERROR) - ++ +# Revert CMake bug triggered by curl's defined max CMake policy version, see https://gitlab.kitware.com/cmake/cmake/-/issues/21288 +if(POLICY CMP0091) + cmake_policy(SET CMP0091 OLD) +endif() + + message(STATUS "Using CMake version ${CMAKE_VERSION}") + set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") - include(Utilities) - include(Macros) -@@ -306,9 +311,12 @@ if(ENABLE_MANUAL) +@@ -328,9 +334,12 @@ if(ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS) endif() - + if(CURL_STATIC_CRT) - set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT") @@ -69,5 +70,5 @@ index ed60f07bc..0d2088cb9 100644 + endif() + endforeach() endif() - + # Disable warnings on Borland to avoid changing 3rd party code. diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 0ef1e9091d91..eece5de7840b 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -1150,8 +1150,8 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "curl", project_desc = "Library for transferring data with URLs", project_url = "https://curl.haxx.se", - version = "8.4.0", - sha256 = "816e41809c043ff285e8c0f06a75a1fa250211bbfb2dc0a037eeef39f1a9e427", + version = "8.9.1", + sha256 = "291124a007ee5111997825940b3876b3048f7d31e73e9caa681b80fe48b2dcd5", strip_prefix = "curl-{version}", urls = ["https://github.com/curl/curl/releases/download/curl-{underscore_version}/curl-{version}.tar.gz"], use_category = ["dataplane_ext", "observability_ext"], @@ -1161,7 +1161,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( "envoy.grpc_credentials.aws_iam", "envoy.tracers.opencensus", ], - release_date = "2023-10-11", + release_date = "2024-07-31", cpe = "cpe:2.3:a:haxx:libcurl:*", license = "curl", license_url = "https://github.com/curl/curl/blob/curl-{underscore_version}/COPYING", From 5e0552582cc90fb881eef35af8bdf5942b0ad012 Mon Sep 17 00:00:00 2001 From: Divya Chakarwarti <138142449+dchakarwarti@users.noreply.github.com> Date: Wed, 14 Aug 2024 19:10:18 +0530 Subject: [PATCH 105/130] Add logging util lib for Proto Message Logging (PML) http filter (#34955) --------- Signed-off-by: dchakarwarti@google.com --- CODEOWNERS | 2 + bazel/repositories.bzl | 1 + bazel/repository_locations.bzl | 21 +- .../proto_message_logging/logging_util/BUILD | 41 + .../logging_util/logging_util.cc | 419 ++++++++ .../logging_util/logging_util.h | 130 +++ .../logging_util/proto_scrubber_interface.h | 54 + .../proto_message_logging/logging_util/BUILD | 34 + .../logging_util/logging_util_test.cc | 961 ++++++++++++++++++ .../logging_util/logging_util_test.yaml | 23 + test/proto/BUILD | 15 + test/proto/logging.proto | 111 ++ tools/spelling/spelling_dictionary.txt | 8 + 13 files changed, 1817 insertions(+), 3 deletions(-) create mode 100644 source/extensions/filters/http/proto_message_logging/logging_util/BUILD create mode 100644 source/extensions/filters/http/proto_message_logging/logging_util/logging_util.cc create mode 100644 source/extensions/filters/http/proto_message_logging/logging_util/logging_util.h create mode 100644 source/extensions/filters/http/proto_message_logging/logging_util/proto_scrubber_interface.h create mode 100644 test/extensions/filters/http/proto_message_logging/logging_util/BUILD create mode 100644 test/extensions/filters/http/proto_message_logging/logging_util/logging_util_test.cc create mode 100644 test/extensions/filters/http/proto_message_logging/logging_util/logging_util_test.yaml create mode 100644 test/proto/logging.proto diff --git a/CODEOWNERS b/CODEOWNERS index d8f7be277fc2..0f41ef52037c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -35,6 +35,8 @@ extensions/filters/common/original_src @klarose @mattklein123 /*/extensions/filters/http/jwt_authn @taoxuy @lizan @tyxia @yanavlasov # grpc_field_extraction http filter extension /*/extensions/filters/http/grpc_field_extraction @taoxuy @nareddyt @yanavlasov +# proto_message_logging http filter extension +/*/extensions/filters/http/proto_message_logging @dchakarwarti @taoxuy @yanavlasov # grpc_http1_reverse_bridge http filter extension /*/extensions/filters/http/grpc_http1_reverse_bridge @zuercher @mattklein123 # alts transport socket extension diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index c7ac92f05e98..f9c13aaabd8f 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -230,6 +230,7 @@ def _cc_deps(): patches = ["@envoy//bazel:com_google_protoconverter.patch"], ) external_http_archive("com_google_protofieldextraction") + external_http_archive("com_google_protoprocessinglib") external_http_archive("ocp") native.bind( name = "path_matcher", diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index eece5de7840b..3e8fa43b10b8 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -970,13 +970,13 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "proto-converter", project_desc = "Library that supports the conversion between protobuf binary and json", project_url = "https://github.com/grpc-ecosystem/proto-converter", - version = "d77ff301f48bf2e7a0f8935315e847c1a8e00017", - sha256 = "6081836fa3838ebb1aa15089a5c3e20f877a0244c7a39b92a2000efb40408dcb", + version = "1db76535b86b80aa97489a1edcc7009e18b67ab7", + sha256 = "9555d9cf7bd541ea5fdb67d7d6b72ea44da77df3e27b960b4155dc0c6b81d476", strip_prefix = "proto-converter-{version}", urls = ["https://github.com/grpc-ecosystem/proto-converter/archive/{version}.zip"], use_category = ["dataplane_ext"], extensions = ["envoy.filters.http.grpc_json_transcoder", "envoy.filters.http.grpc_field_extraction"], - release_date = "2023-06-07", + release_date = "2024-06-25", cpe = "N/A", license = "Apache-2.0", license_url = "https://github.com/grpc-ecosystem/proto-converter/blob/{version}/LICENSE", @@ -996,6 +996,21 @@ REPOSITORY_LOCATIONS_SPEC = dict( license = "Apache-2.0", license_url = "https://github.com/grpc-ecosystem/proto-field-extraction/blob/{version}/LICENSE", ), + com_google_protoprocessinglib = dict( + project_name = "proto_processing_lib", + project_desc = "Library that provides utility functionality for proto field scrubbing", + project_url = "https://github.com/grpc-ecosystem/proto_processing_lib", + version = "43bd06909a4ccfa5788dc8bf850e40102166e63f", + sha256 = "8f99bd6f568c6fddcba1ed51290eaa5556a0ad374524ba6f532c6c828a73214b", + strip_prefix = "proto_processing_lib-{version}", + urls = ["https://github.com/grpc-ecosystem/proto_processing_lib/archive/{version}.zip"], + use_category = ["dataplane_ext"], + extensions = ["envoy.filters.http.grpc_json_transcoder", "envoy.filters.http.grpc_field_extraction"], + release_date = "2024-08-06", + cpe = "N/A", + license = "Apache-2.0", + license_url = "https://github.com/grpc-ecosystem/proto_processing_lib/blob/{version}/LICENSE", + ), ocp = dict( project_name = "ocp", project_desc = "Libraries used in gRPC field extraction library", diff --git a/source/extensions/filters/http/proto_message_logging/logging_util/BUILD b/source/extensions/filters/http/proto_message_logging/logging_util/BUILD new file mode 100644 index 000000000000..6396b1a4d572 --- /dev/null +++ b/source/extensions/filters/http/proto_message_logging/logging_util/BUILD @@ -0,0 +1,41 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_library( + name = "proto_scrubber_interface", + hdrs = [ + "proto_scrubber_interface.h", + ], + deps = [ + "//source/common/protobuf", + "//source/common/protobuf:cc_wkt_protos", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_protofieldextraction//:all_libs", + ], +) + +envoy_cc_library( + name = "logging_util", + srcs = [ + "logging_util.cc", + ], + hdrs = [ + "logging_util.h", + ], + deps = [ + ":proto_scrubber_interface", + "//source/common/common:regex_lib", + "//source/common/protobuf", + "//source/common/protobuf:cc_wkt_protos", + "@com_google_protoconverter//:all", + "@com_google_protofieldextraction//:all_libs", + "@com_google_protoprocessinglib//proto_processing_lib/proto_scrubber", + ], +) diff --git a/source/extensions/filters/http/proto_message_logging/logging_util/logging_util.cc b/source/extensions/filters/http/proto_message_logging/logging_util/logging_util.cc new file mode 100644 index 000000000000..55abc9b7e8c2 --- /dev/null +++ b/source/extensions/filters/http/proto_message_logging/logging_util/logging_util.cc @@ -0,0 +1,419 @@ +#include "source/extensions/filters/http/proto_message_logging/logging_util/logging_util.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/common/protobuf/protobuf.h" + +#include "absl/container/flat_hash_map.h" +#include "absl/log/check.h" +#include "absl/log/log.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/match.h" +#include "absl/strings/str_replace.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "absl/strings/substitute.h" +#include "absl/types/span.h" +#include "proto_field_extraction/field_extractor/field_extractor.h" +#include "proto_field_extraction/message_data/message_data.h" +#include "proto_processing_lib/proto_scrubber/proto_scrubber.h" +#include "src/google/protobuf/util/converter/error_listener.h" +#include "src/google/protobuf/util/converter/json_objectwriter.h" +#include "src/google/protobuf/util/converter/json_stream_parser.h" +#include "src/google/protobuf/util/converter/object_source.h" +#include "src/google/protobuf/util/converter/object_writer.h" +#include "src/google/protobuf/util/converter/protostream_objectsource.h" +#include "src/google/protobuf/util/converter/protostream_objectwriter.h" +#include "src/google/protobuf/util/converter/type_info.h" +#include "src/google/protobuf/util/converter/utility.h" +#include "src/google/protobuf/util/field_mask_util.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace ProtoMessageLogging { + +namespace { + +using ::Envoy::Protobuf::Field; +using ::Envoy::Protobuf::Map; +using ::Envoy::Protobuf::Type; +using ::Envoy::Protobuf::field_extraction::FieldExtractor; +using ::Envoy::Protobuf::internal::WireFormatLite; +using ::Envoy::Protobuf::io::CodedInputStream; +using ::Envoy::Protobuf::io::CordOutputStream; +using ::Envoy::Protobuf::util::JsonParseOptions; +using ::Envoy::Protobuf::util::TypeResolver; +using ::Envoy::Protobuf::util::converter::GetFullTypeWithUrl; +using ::Envoy::Protobuf::util::converter::JsonObjectWriter; +using ::Envoy::Protobuf::util::converter::ProtoStreamObjectSource; +using ::Envoy::ProtobufWkt::Struct; +using ::Envoy::ProtobufWkt::Value; +using ::proto_processing_lib::proto_scrubber::ProtoScrubber; + +std::string kLocationRegionExtractorPattern = R"((?:^|/)(?:locations|regions)/([^/]+))"; + +} // namespace + +// Returns true if the given Struct only contains a "@type" field. +bool IsEmptyStruct(const Struct& message_struct) { + return message_struct.fields_size() == 1 && + message_struct.fields().cbegin()->first == kTypeProperty; +} + +bool IsLabelName(absl::string_view value) { + return absl::StartsWith(value, "{") && absl::EndsWith(value, "}"); +} + +// Monitored resource label names are captured within curly brackets ("{", "}"). +// The format is verified by the service config validator, so to extract label +// name, we just remove the brackets. +std::string GetLabelName(absl::string_view value) { + return absl::StrReplaceAll(value, {{"{", ""}, {"}", ""}}); +} + +// Singleton mapping of string to AuditDirective. +const absl::flat_hash_map& StringToDirectiveMap() { + static auto* string_to_directive_map = new absl::flat_hash_map({ + {kAuditRedact, AuditDirective::AUDIT_REDACT}, + {kAudit, AuditDirective::AUDIT}, + }); + return *string_to_directive_map; +} + +absl::optional AuditDirectiveFromString(absl::string_view directive) { + if (StringToDirectiveMap().contains(directive)) { + return StringToDirectiveMap().at(directive); + } + return std::nullopt; +} + +// Returns a mapping of monitored resource label keys to their values. +void GetMonitoredResourceLabels(absl::string_view label_extractor, + absl::string_view resource_string, + Map* labels) { + // The monitored resource label extractor is formatted as + // "project/*/bucket/{bucket}/object/{object}", where label names are + // surrounded by {}. + std::vector pattern_split = + absl::StrSplit(label_extractor, '/', absl::SkipEmpty()); + std::vector resource_split = + absl::StrSplit(resource_string, '/', absl::SkipEmpty()); + // Note that pattern_split and resource_split sizes can vary, since it's + // possible for certain APIs, IE. List APIs, some resource labels may + // naturally be missing. + // + // Iterate over both patternSplit and resourceSplit at the same time, stopping + // when the index exceeds either ranges. + int min_size = std::min(pattern_split.size(), resource_split.size()); + for (int index = 0; index < min_size; ++index) { + if (IsLabelName(pattern_split[index])) { + (*labels)[GetLabelName(pattern_split[index])] = std::string(resource_split[index]); + } + } +} + +WireFormatLite::WireType GetWireType(const Field& field_desc) { + static WireFormatLite::WireType field_kind_to_wire_type[] = { + static_cast(-1), // TYPE_UNKNOWN + WireFormatLite::WIRETYPE_FIXED64, // TYPE_DOUBLE + WireFormatLite::WIRETYPE_FIXED32, // TYPE_FLOAT + WireFormatLite::WIRETYPE_VARINT, // TYPE_INT64 + WireFormatLite::WIRETYPE_VARINT, // TYPE_UINT64 + WireFormatLite::WIRETYPE_VARINT, // TYPE_INT32 + WireFormatLite::WIRETYPE_FIXED64, // TYPE_FIXED64 + WireFormatLite::WIRETYPE_FIXED32, // TYPE_FIXED32 + WireFormatLite::WIRETYPE_VARINT, // TYPE_BOOL + WireFormatLite::WIRETYPE_LENGTH_DELIMITED, // TYPE_STRING + WireFormatLite::WIRETYPE_START_GROUP, // TYPE_GROUP + WireFormatLite::WIRETYPE_LENGTH_DELIMITED, // TYPE_MESSAGE + WireFormatLite::WIRETYPE_LENGTH_DELIMITED, // TYPE_BYTES + WireFormatLite::WIRETYPE_VARINT, // TYPE_UINT32 + WireFormatLite::WIRETYPE_VARINT, // TYPE_ENUM + WireFormatLite::WIRETYPE_FIXED32, // TYPE_SFIXED32 + WireFormatLite::WIRETYPE_FIXED64, // TYPE_SFIXED64 + WireFormatLite::WIRETYPE_VARINT, // TYPE_SINT32 + WireFormatLite::WIRETYPE_VARINT, // TYPE_SINT64 + }; + return field_kind_to_wire_type[field_desc.kind()]; +} + +absl::StatusOr +ExtractRepeatedFieldSizeHelper(const FieldExtractor& field_extractor, const std::string& path, + const Protobuf::field_extraction::MessageData& message) { + if (path.empty()) { + return absl::InvalidArgumentError("Field mask path cannot be empty."); + } + + auto extract_func = [](const Type& /*enclosing_type*/, const Field* field, + CodedInputStream* input_stream) -> absl::StatusOr { + if (field->cardinality() != Field::CARDINALITY_REPEATED) { + return absl::InvalidArgumentError( + absl::Substitute("Field '$0' is not a repeated or map field.", field->name())); + } + + // repeated field or map field. + uint32_t count = 0, tag = 0; + if (field->packed()) { + const WireFormatLite::WireType field_wire_type = GetWireType(*field); + + while ((tag = input_stream->ReadTag()) != 0) { + if (field->number() != WireFormatLite::GetTagFieldNumber(tag)) { + WireFormatLite::SkipField(input_stream, tag); + } else { + DCHECK_EQ(WireFormatLite::WIRETYPE_LENGTH_DELIMITED, WireFormatLite::GetTagWireType(tag)); + + uint32_t length; + input_stream->ReadVarint32(&length); + if (field->kind() == Field::TYPE_BOOL) { + count += length / WireFormatLite::kBoolSize; + input_stream->Skip(length); + } else if (field_wire_type == WireFormatLite::WIRETYPE_FIXED32) { + count += length / WireFormatLite::kFixed32Size; + input_stream->Skip(length); + } else if (field_wire_type == WireFormatLite::WIRETYPE_FIXED64) { + count += length / WireFormatLite::kFixed64Size; + input_stream->Skip(length); + } else { + CodedInputStream::Limit limit = input_stream->PushLimit(length); + uint64_t varint = 0; + while (input_stream->ReadVarint64(&varint)) { + ++count; + } + input_stream->PopLimit(limit); + } + } + } + } else { // not packed. + while ((tag = input_stream->ReadTag()) != 0) { + if (field->number() == WireFormatLite::GetTagFieldNumber(tag)) { + ++count; + } + WireFormatLite::SkipField(input_stream, tag); + } + } + return count; + }; + + Protobuf::field_extraction::MessageData& msg( + const_cast(message)); + + return field_extractor.ExtractFieldInfo(path, msg.CreateCodedInputStreamWrapper()->Get(), + extract_func); +} + +int64_t ExtractRepeatedFieldSize(const Type& type, + std::function type_finder, + const Protobuf::FieldMask* field_mask, + const Protobuf::field_extraction::MessageData& message) { + int64_t num_response_items = -1LL; + if (field_mask == nullptr || field_mask->paths_size() < 1) { + return num_response_items; + } + + // AUDIT_SIZE directive should only be applied to one field. Tools + // framework validation should check this case. + DCHECK_EQ(1, field_mask->paths_size()); + + FieldExtractor field_extractor(&type, std::move(type_finder)); + absl::StatusOr status_or_size = + ExtractRepeatedFieldSizeHelper(field_extractor, field_mask->paths(0), message); + if (!status_or_size.ok()) { + LOG(WARNING) << "Failed to extract repeated field size of '" << field_mask->paths(0) + << "' from proto '" << type.name() << "': " << status_or_size.status(); + } else { + num_response_items = *status_or_size; + } + return num_response_items; +} + +absl::string_view ExtractLocationIdFromResourceName(absl::string_view resource_name) { + absl::string_view location; + RE2::PartialMatch(resource_name, kLocationRegionExtractorPattern, &location); + return location; +} + +// Recursively redacts the path_pieces in the enclosing proto_struct. +void RedactPath(std::vector::const_iterator path_begin, + std::vector::const_iterator path_end, Struct* proto_struct) { + if (path_begin == path_end) { + proto_struct->Clear(); + return; + } + + const std::string& field = *path_begin; + path_begin++; + + auto* struct_fields = proto_struct->mutable_fields(); + // Return if any piece of the path wasn't populated. + auto field_it = struct_fields->find(field); + if (field_it == struct_fields->end()) { + return; + } + + // Handle repeated field. We allow redacting repeated leaf and non-leaf + // message type fields. + auto& field_value = field_it->second; + if (field_value.has_list_value()) { + auto* repeated_values = field_value.mutable_list_value()->mutable_values(); + for (int i = 0; i < repeated_values->size(); ++i) { + Value* value = repeated_values->Mutable(i); + CHECK(value->has_struct_value()) << "Cannot redact non-message-type field " << field; + RedactPath(path_begin, path_end, value->mutable_struct_value()); + } + return; + } + + // Fail if trying to redact non-message-type field. + CHECK(field_value.has_struct_value()) << "Cannot redact non-message-type field " << field; + RedactPath(path_begin, path_end, field_value.mutable_struct_value()); +} + +void RedactPaths(absl::Span paths_to_redact, Struct* proto_struct) { + for (const std::string& path : paths_to_redact) { + std::vector path_pieces = absl::StrSplit(path, '.', absl::SkipEmpty()); + CHECK(path_pieces.size() < kMaxRedactedPathDepth) + << "Attempting to redact path with depth >= " << kMaxRedactedPathDepth << ": " << path; + RedactPath(path_pieces.begin(), path_pieces.end(), proto_struct); + } +} + +// Finds the last value of the non-repeated string field after the first value. +// Returns an empty string if there is only one string field. Returns an error +// if the resource is malformed in case that the search goes forever. +absl::StatusOr FindSingularLastValue(const Field* field, + CodedInputStream* input_stream) { + std::string resource; + int position = input_stream->CurrentPosition(); + while (FieldExtractor::SearchField(*field, input_stream)) { + if (input_stream->CurrentPosition() == position) { + return absl::InvalidArgumentError( + "The request message is malformed with endless values for a single field."); + } + position = input_stream->CurrentPosition(); + if (field->kind() == Field::TYPE_STRING) { + WireFormatLite::ReadString(input_stream, &resource); + } + } + return resource; +} + +// Non-repeated fields can be repeat in a wire-format, in that case use the last +// value. +// +// Quote from the go/proto-encoding: +// "Normally, an encoded message would never have more than one instance of a +// non-repeated field. However, parsers are expected to handle the case in which +// they do." +absl::StatusOr SingularFieldUseLastValue(const std::string first_value, + const Field* field, + CodedInputStream* input_stream) { + ASSIGN_OR_RETURN(std::string last_value, FindSingularLastValue(field, input_stream)); + if (last_value.empty()) + return first_value; + return last_value; +} + +absl::StatusOr ExtractStringFieldValue( + const Type& type, std::function type_finder, + const std::string& path, const Protobuf::field_extraction::MessageData& message) { + if (path.empty()) { + return absl::InvalidArgumentError("Field mask path cannot be empty."); + } + + auto extract_func = [](const Type& /*enclosing_type*/, const Field* field, + CodedInputStream* input_stream) -> absl::StatusOr { + if (field->kind() != Field::TYPE_STRING) { + return absl::InvalidArgumentError( + absl::Substitute("Field '$0' is not a singular string field.", field->name())); + } else if (field->cardinality() == Field::CARDINALITY_REPEATED) { + return absl::InvalidArgumentError( + absl::Substitute("Field '$0' is a repeated string field, only singular " + "string field is accepted.", + field->name())); + } else { // singular string field + std::string result; + if (FieldExtractor::SearchField(*field, input_stream)) { + uint32_t length; + input_stream->ReadVarint32(&length); + input_stream->ReadString(&result, length); + } + + ASSIGN_OR_RETURN(result, SingularFieldUseLastValue(result, field, input_stream)); + + return result; + } + }; + + FieldExtractor field_extractor(&type, std::move(type_finder)); + return field_extractor.ExtractFieldInfo( + path, message.CreateCodedInputStreamWrapper()->Get(), extract_func); +} + +absl::Status RedactStructRecursively(std::vector::const_iterator path_pieces_begin, + std::vector::const_iterator path_pieces_end, + Struct* message_struct) { + if (message_struct == nullptr) { + return absl::InvalidArgumentError("message_struct cannot be nullptr."); + } + if (path_pieces_begin == path_pieces_end) { + return absl::OkStatus(); + } + + const std::string& current_piece = *path_pieces_begin; + if (current_piece.empty()) { + return absl::InvalidArgumentError("path piece cannot be empty."); + } + + auto* fields = message_struct->mutable_fields(); + auto iter = fields->find(current_piece); + if (iter == fields->end()) { + // Add empty struct. + (*fields)[current_piece].mutable_struct_value(); + } else if (!iter->second.has_struct_value()) { + return absl::InvalidArgumentError("message_struct cannot be nullptr."); + } + return RedactStructRecursively(++path_pieces_begin, path_pieces_end, + (*fields)[current_piece].mutable_struct_value()); +} + +absl::StatusOr +IsMessageFieldPathPresent(const Protobuf::Type& type, + std::function type_finder, + const std::string& path, + const Protobuf::field_extraction::MessageData& message) { + if (path.empty()) { + return absl::InvalidArgumentError("Field path cannot be empty."); + } + + auto extract_func = [](const Type& /*enclosing_type*/, const Field* field, + CodedInputStream* input_stream) -> absl::StatusOr { + if (field->kind() != Field::TYPE_MESSAGE) { + return absl::InvalidArgumentError( + absl::Substitute("Field '$0' is not a message type field.", field->name())); + } else if (field->cardinality() == Field::CARDINALITY_REPEATED) { + return absl::InvalidArgumentError( + absl::Substitute("Field '$0' is not a singular field.", field->name())); + } else { // singular message field + return FieldExtractor::SearchField(*field, input_stream); + } + }; + + FieldExtractor field_extractor(&type, std::move(type_finder)); + Protobuf::field_extraction::MessageData& msg( + const_cast(message)); + return field_extractor.ExtractFieldInfo(path, msg.CreateCodedInputStreamWrapper()->Get(), + extract_func); +} +} // namespace ProtoMessageLogging +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/proto_message_logging/logging_util/logging_util.h b/source/extensions/filters/http/proto_message_logging/logging_util/logging_util.h new file mode 100644 index 000000000000..d826a3c55cd2 --- /dev/null +++ b/source/extensions/filters/http/proto_message_logging/logging_util/logging_util.h @@ -0,0 +1,130 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "source/common/protobuf/protobuf.h" +#include "source/extensions/filters/http/proto_message_logging/logging_util/proto_scrubber_interface.h" + +#include "absl/base/attributes.h" +#include "absl/container/flat_hash_map.h" +#include "absl/log/check.h" +#include "absl/log/log.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" +#include "absl/types/span.h" +#include "google/api/monitored_resource.pb.h" +#include "proto_field_extraction/field_extractor/field_extractor.h" +#include "proto_field_extraction/message_data/message_data.h" +#include "proto_processing_lib/proto_scrubber/proto_scrubber.h" +#include "re2/re2.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace ProtoMessageLogging { + +// The type property value that will be included into the converted Struct. +constexpr char kTypeProperty[] = "@type"; + +ABSL_CONST_INIT const char* const kTypeServiceBaseUrl = "type.googleapis.com"; + +constexpr char kAuditRedact[] = "AUDIT_REDACT"; + +constexpr char kAudit[] = "AUDIT"; + +constexpr int kMaxRedactedPathDepth = 10; + +constexpr int kProtoTranslationMaxRecursionDepth = 64; + +ABSL_CONST_INIT const char* const kStructTypeUrl = "type.googleapis.com/google.protobuf.Struct"; + +// class proto_processing_lib::proto_scrubber::ProtoScrubber; + +bool IsEmptyStruct(const ProtobufWkt::Struct& message_struct); + +bool IsLabelName(absl::string_view value); + +// Monitored resource label names are captured within curly brackets ("{", "}"). +// The format is verified by the service config validator, so to extract label +// name, we just remove the brackets. +std::string GetLabelName(absl::string_view value); + +// Singleton mapping of string to AuditDirective. +const absl::flat_hash_map& StringToDirectiveMap(); + +absl::optional AuditDirectiveFromString(absl::string_view directive); + +// Returns a mapping of monitored resource label keys to their values. +void GetMonitoredResourceLabels(absl::string_view label_extractor, + absl::string_view resource_string, + Protobuf::Map* labels); + +// Returns the size of the repeated field represented by given field mask from +// given raw message. +// +// In case of error or nullptr/empty field_mask, it returns a negative value and +// logs the error. +int64_t +ExtractRepeatedFieldSize(const Protobuf::Type& type, + std::function type_finder, + const Protobuf::FieldMask* field_mask, + const Protobuf::field_extraction::MessageData& message); + +// Extract the size of the repeated field represented by given field mask +// path from given proto message. `path` must represent a repeated field. +absl::StatusOr +ExtractRepeatedFieldSizeHelper(const Protobuf::field_extraction::FieldExtractor& field_extractor, + const std::string& path, + const Protobuf::field_extraction::MessageData& message); + +absl::string_view ExtractLocationIdFromResourceName(absl::string_view resource_name); + +// Recursively redacts the path_pieces in the enclosing proto_struct. +void RedactPath(std::vector::const_iterator path_begin, + std::vector::const_iterator path_end, + ProtobufWkt::Struct* proto_struct); + +void RedactPaths(absl::Span paths_to_redact, ProtobufWkt::Struct* proto_struct); + +// Finds the last value of the non-repeated string field after the first +// value. Returns an empty string if there is only one string field. Returns +// an error if the resource is malformed in case that the search goes forever. +absl::StatusOr +FindSingularLastValue(const Protobuf::Field* field, + Envoy::Protobuf::io::CodedInputStream* input_stream); + +// Non-repeated fields can be repeat in a wire-format, in that case use the +// last value. +// +// "Normally, an encoded message would never have more than one instance of a +// non-repeated field. However, parsers are expected to handle the case in +// which they do." +absl::StatusOr +SingularFieldUseLastValue(const std::string first_value, const Protobuf::Field* field, + Envoy::Protobuf::io::CodedInputStream* input_stream); + +absl::StatusOr +ExtractStringFieldValue(const Protobuf::Type& type, + std::function type_finder, + const std::string& path, + const Protobuf::field_extraction::MessageData& message); + +absl::Status RedactStructRecursively(std::vector::const_iterator path_pieces_begin, + std::vector::const_iterator path_pieces_end, + ProtobufWkt::Struct* message_struct); + +absl::StatusOr +IsMessageFieldPathPresent(const Protobuf::Type& type, + std::function type_finder, + const std::string& path, + const Protobuf::field_extraction::MessageData& message); + +} // namespace ProtoMessageLogging +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/proto_message_logging/logging_util/proto_scrubber_interface.h b/source/extensions/filters/http/proto_message_logging/logging_util/proto_scrubber_interface.h new file mode 100644 index 000000000000..cb890634c1bf --- /dev/null +++ b/source/extensions/filters/http/proto_message_logging/logging_util/proto_scrubber_interface.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include + +#include "source/common/protobuf/protobuf.h" + +#include "absl/container/flat_hash_map.h" +#include "proto_field_extraction/message_data/message_data.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace ProtoMessageLogging { + +// All valid field auditing directives for Cloud Audit Logging. +enum class AuditDirective { + AUDIT_REDACT, + AUDIT, +}; + +using FieldPathToScrubType = absl::flat_hash_map>; + +// Metadata that can be captured during message scrubbing. +struct AuditMetadata { + absl::optional num_response_items; + absl::optional target_resource; + absl::optional target_resource_callback; + absl::optional resource_location; + ProtobufWkt::Struct scrubbed_message; +}; + +// A proto-scrubbing interface for audit logging that converts a source message +// to a proto Struct. +class ProtoScrubberInterface { +public: + // Scrubs the message for auditing, then populates and returns AuditMetadata + // that contains the scrubbed message and other audit metadata obtained during + // scrubbing. + virtual AuditMetadata + ScrubMessage(const Protobuf::field_extraction::MessageData& message) const = 0; + + // Returns the message type this scrubber will handle, without the + // type url prefix "type.googleapis.com". + virtual const std::string& MessageType() const = 0; + + virtual ~ProtoScrubberInterface() = default; +}; + +} // namespace ProtoMessageLogging +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/filters/http/proto_message_logging/logging_util/BUILD b/test/extensions/filters/http/proto_message_logging/logging_util/BUILD new file mode 100644 index 000000000000..d23ca6e27937 --- /dev/null +++ b/test/extensions/filters/http/proto_message_logging/logging_util/BUILD @@ -0,0 +1,34 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_test", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_test( + name = "logging_util_test", + srcs = ["logging_util_test.cc"], + data = [ + "//test/proto:logging.proto", + "//test/proto:logging_proto_descriptor", + ], + deps = [ + "//source/extensions/filters/http/proto_message_logging/logging_util", + "//test/proto:logging_proto_cc_proto", + "//test/test_common:environment_lib", + "//test/test_common:status_utility_lib", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + "@com_google_protobuf//:protobuf", + "@com_google_protoconverter//:all", + "@com_google_protofieldextraction//:all_libs", + "@com_google_protofieldextraction//proto_field_extraction/test_utils:utils", + "@com_google_protoprocessinglib//proto_processing_lib/proto_scrubber:cloud_audit_log_field_checker", + "@ocp//ocpdiag/core/testing:status_matchers", + ], +) diff --git a/test/extensions/filters/http/proto_message_logging/logging_util/logging_util_test.cc b/test/extensions/filters/http/proto_message_logging/logging_util/logging_util_test.cc new file mode 100644 index 000000000000..c315fb2f2a8e --- /dev/null +++ b/test/extensions/filters/http/proto_message_logging/logging_util/logging_util_test.cc @@ -0,0 +1,961 @@ +#include +#include +#include +#include +#include + +#include "source/extensions/filters/http/proto_message_logging/logging_util/logging_util.h" + +#include "test/proto/logging.pb.h" +#include "test/test_common/environment.h" +#include "test/test_common/logging.h" +#include "test/test_common/status_utility.h" +#include "test/test_common/utility.h" + +#include "absl/log/check.h" +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "ocpdiag/core/compat/status_macros.h" +#include "ocpdiag/core/testing/status_matchers.h" +#include "proto_field_extraction/message_data/cord_message_data.h" +#include "proto_field_extraction/test_utils/utils.h" +#include "proto_processing_lib/proto_scrubber/cloud_audit_log_field_checker.h" +#include "proto_processing_lib/proto_scrubber/proto_scrubber.h" +#include "proto_processing_lib/proto_scrubber/proto_scrubber_enums.h" +#include "proto_processing_lib/proto_scrubber/utility.h" +#include "src/google/protobuf/util/converter/type_info.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace ProtoMessageLogging { +namespace { + +using ::Envoy::Protobuf::Field; +using ::Envoy::Protobuf::FieldMask; +using ::Envoy::Protobuf::Type; +using ::Envoy::Protobuf::field_extraction::CordMessageData; +using ::Envoy::Protobuf::field_extraction::testing::TypeHelper; +using ::Envoy::Protobuf::io::CodedInputStream; +using ::Envoy::ProtobufWkt::Struct; +using ::Envoy::StatusHelpers::IsOkAndHolds; +using ::Envoy::StatusHelpers::StatusIs; +using ::logging::TestRequest; +using ::logging::TestResponse; +using ::proto_processing_lib::proto_scrubber::CloudAuditLogFieldChecker; +using ::proto_processing_lib::proto_scrubber::ProtoScrubber; +using ::proto_processing_lib::proto_scrubber::ScrubberContext; +using ::testing::ValuesIn; + +// The type property value that will be included into the converted Struct. +constexpr char kTypeProperty[] = "@type"; + +const char kTestRequest[] = R"pb( + id: 123445 + bucket { + name: "test-bucket" + ratio: 0.8 + objects: "test-object-1" + objects: "test-object-2" + objects: "test-object-3" + } + proto2_message { + repeated_strings: [ "repeated-string-0", "repeated-string-1" ] + repeated_enum: [ PROTO2_ALPHA, PROTO2_BETA, PROTO2_ALPHA, PROTO2_GAMMA ] + repeated_double: [ 0.42 ] + repeated_float: [ 0.42, 1.98 ] + repeated_int64: [ 12, -5, 20 ] + repeated_uint64: [ 12, 190, 20, 800 ] + repeated_int32: [ 1, -5, 4, 100, -780 ] + repeated_fixed64: [ 1, 2, 89, 3, 56, 49 ] + repeated_fixed32: [ 1, 2, 89, 3, 56, 49, 90 ] + repeated_bool: [ True, False, False, True, True ] + repeated_uint32: [ 1, 2, 89, 3, 56, 49, 90, 0 ] + repeated_sfixed64: [ 1, 2, -89, 3, 56, -49 ] + repeated_sfixed32: [ 1, 2, -89, 3, 56, -49, 90 ] + repeated_sint32: [ 12, -5, 0, -7, 180 ] + repeated_sint64: [ 12, -5, 0, -9, 90, 39028 ] + } + repeated_strings: [ "repeated-string-0" ] + repeated_enum: [ ALPHA, BETA, TEST_ENUM_UNSPECIFIED, GAMMA ] + repeated_double: [ 0.42 ] + repeated_float: [ 0.42, 1.98 ] + repeated_int64: [ 12, -5, 20 ] + repeated_uint64: [ 12, 190, 20, 800 ] + repeated_int32: [ 1, -5, 4, 100, -780 ] + repeated_fixed64: [ 1, 2, 89, 3, 56, 49 ] + repeated_fixed32: [ 1, 2, 89, 3, 56, 49, 90 ] + repeated_bool: [ True, False, False, True, True ] + repeated_uint32: [ 1, 2, 89, 3, 56, 49, 90, 0 ] + repeated_sfixed64: [ 1, 2, -89, 3, 56, -49 ] + repeated_sfixed32: [ 1, 2, -89, 3, 56, -49, 90 ] + repeated_sint32: [ 12, -5, 0, -7, 180 ] + repeated_sint64: [ 12, -5, 0, -9, 90, 39028 ] +)pb"; + +const char kTestResponse[] = R"pb( + buckets { + name: "test-bucket-0" + ratio: 0.8 + objects: "test-object-01" + objects: "test-object-02" + objects: "test-object-03" + } + buckets { + name: "test-bucket-1" + ratio: 0.9 + objects: "test-object-11" + objects: "test-object-12" + objects: "test-object-13" + } + sub_buckets { + key: "test-bucket-2" + value { name: "test-bucket-2" ratio: 0.5 objects: "test-object-21" } + } + sub_buckets { + key: "test-bucket-3" + value { name: "test-bucket-3" ratio: 0.2 objects: "test-object-31" } + } + bucket_present { name: "bucket_present" } + sub_message { bucket_present { name: "bucket_present" } } +)pb"; + +class AuditLoggingUtilTest : public ::testing::Test { +protected: + AuditLoggingUtilTest() = default; + const Protobuf::Type* FindType(const std::string& type_url) { + absl::StatusOr result = type_helper_->ResolveTypeUrl(type_url); + if (!result.ok()) { + return nullptr; + } + return result.value(); + } + + void SetUp() override { + const std::string descriptor_path = + TestEnvironment::runfilesPath("test/proto/logging.descriptor"); + absl::StatusOr> status = TypeHelper::Create(descriptor_path); + type_helper_ = std::move(status.value()); + + type_finder_ = std::bind_front(&AuditLoggingUtilTest::FindType, this); + + if (!Protobuf::TextFormat::ParseFromString(kTestRequest, &test_request_proto_)) { + LOG(ERROR) << "Failed to parse textproto: " << kTestRequest; + } + test_request_raw_proto_ = CordMessageData(test_request_proto_.SerializeAsCord()); + request_type_ = type_finder_("type.googleapis.com/" + "logging.TestRequest"); + + if (!Protobuf::TextFormat::ParseFromString(kTestResponse, &test_response_proto_)) { + LOG(ERROR) << "Failed to parse textproto: " << kTestResponse; + } + test_response_raw_proto_ = CordMessageData(test_response_proto_.SerializeAsCord()); + response_type_ = type_finder_("type.googleapis.com/" + "logging.TestResponse"); + + labels_.clear(); + } + + FieldMask* GetFieldMaskWith(const std::string& path) { + field_mask_.clear_paths(); + field_mask_.add_paths(path); + return &field_mask_; + } + + // Helper function to create a Struct with nested fields + Struct CreateNestedStruct(const std::vector& path, const std::string& final_value) { + Struct root; + Struct* current = &root; + for (const auto& piece : path) { + (*current->mutable_fields())[piece].mutable_struct_value(); + current = (*current->mutable_fields())[piece].mutable_struct_value(); + } + (*current->mutable_fields())[final_value].mutable_string_value(); + return root; + } + + // A TypeHelper for testing. + std::unique_ptr type_helper_ = nullptr; + + std::function type_finder_; + + TestRequest test_request_proto_; + Protobuf::field_extraction::CordMessageData test_request_raw_proto_; + const Type* request_type_; + + TestResponse test_response_proto_; + Protobuf::field_extraction::CordMessageData test_response_raw_proto_; + const Type* response_type_; + + FieldMask field_mask_; + + Envoy::Protobuf::Map labels_; +}; + +TEST_F(AuditLoggingUtilTest, IsEmptyStruct_EmptyStruct) { + ProtobufWkt::Struct message_struct; + message_struct.mutable_fields()->insert({kTypeProperty, ProtobufWkt::Value()}); + EXPECT_TRUE(IsEmptyStruct(message_struct)); +} + +TEST_F(AuditLoggingUtilTest, IsEmptyStruct_NonEmptyStruct) { + ProtobufWkt::Struct message_struct; + message_struct.mutable_fields()->insert({kTypeProperty, ProtobufWkt::Value()}); + message_struct.mutable_fields()->insert({"another_field", ProtobufWkt::Value()}); + EXPECT_FALSE(IsEmptyStruct(message_struct)); +} + +TEST_F(AuditLoggingUtilTest, IsLabelName_ValidLabel) { EXPECT_TRUE(IsLabelName("{label}")); } + +TEST_F(AuditLoggingUtilTest, IsLabelName_EmptyString) { EXPECT_FALSE(IsLabelName("")); } + +TEST_F(AuditLoggingUtilTest, GetLabelName_RemovesCurlyBraces) { + EXPECT_EQ(GetLabelName("{test}"), "test"); +} + +TEST_F(AuditLoggingUtilTest, GetLabelName_NoCurlyBraces) { + EXPECT_EQ(GetLabelName("test"), "test"); +} + +TEST_F(AuditLoggingUtilTest, GetLabelName_EmptyString) { EXPECT_EQ(GetLabelName(""), ""); } + +TEST_F(AuditLoggingUtilTest, GetMonitoredResourceLabels_BasicExtraction) { + GetMonitoredResourceLabels("project/*/bucket/{bucket}/object/{object}", + "project/myproject/bucket/mybucket/object/myobject", &labels_); + + EXPECT_EQ(labels_.size(), 2); + EXPECT_EQ(labels_["bucket"], "mybucket"); + EXPECT_EQ(labels_["object"], "myobject"); +} + +TEST_F(AuditLoggingUtilTest, StringToDirectiveMap_CorrectMapping) { + const auto& map = StringToDirectiveMap(); + + EXPECT_EQ(map.size(), 2); + EXPECT_EQ(map.at(kAuditRedact), AuditDirective::AUDIT_REDACT); + EXPECT_EQ(map.at(kAudit), AuditDirective::AUDIT); +} + +TEST_F(AuditLoggingUtilTest, AuditDirectiveFromString_ValidDirective) { + auto directive = AuditDirectiveFromString(kAuditRedact); + ASSERT_TRUE(directive.has_value()); + EXPECT_EQ(directive.value(), AuditDirective::AUDIT_REDACT); + + directive = AuditDirectiveFromString(kAudit); + ASSERT_TRUE(directive.has_value()); + EXPECT_EQ(directive.value(), AuditDirective::AUDIT); +} + +TEST_F(AuditLoggingUtilTest, AuditDirectiveFromString_InvalidDirective) { + auto directive = AuditDirectiveFromString("invalid_directive"); + EXPECT_FALSE(directive.has_value()); +} + +TEST_F(AuditLoggingUtilTest, GetMonitoredResourceLabels_MissingLabelsInResource) { + GetMonitoredResourceLabels("project/*/bucket/{bucket}/object/{object}", + "project/myproject/bucket/mybucket", &labels_); + + EXPECT_EQ(labels_.size(), 1); + EXPECT_EQ(labels_["bucket"], "mybucket"); + EXPECT_EQ(labels_.find("object"), labels_.end()); +} + +TEST_F(AuditLoggingUtilTest, GetMonitoredResourceLabels_ExtraSegmentsInResource) { + GetMonitoredResourceLabels("project/*/bucket/{bucket}/object/{object}", + "project/myproject/bucket/mybucket/object/myobject/extra", &labels_); + + EXPECT_EQ(labels_.size(), 2); + EXPECT_EQ(labels_["bucket"], "mybucket"); + EXPECT_EQ(labels_["object"], "myobject"); +} + +TEST_F(AuditLoggingUtilTest, GetMonitoredResourceLabels_WithNoLabels) { + GetMonitoredResourceLabels("project/*/bucket/*/object/*", + "project/myproject/bucket/mybucket/object/myobject", &labels_); + + EXPECT_EQ(labels_.size(), 0); +} + +TEST_F(AuditLoggingUtilTest, GetMonitoredResourceLabels_EmptyLabelExtractor) { + GetMonitoredResourceLabels("", "project/myproject/bucket/mybucket/object/myobject", &labels_); + + EXPECT_EQ(labels_.size(), 0); +} + +TEST_F(AuditLoggingUtilTest, RedactStructRecursively_EmptyPath) { + Struct message_struct = CreateNestedStruct({"level1", "level2"}, "value"); + EXPECT_TRUE(RedactStructRecursively({}, {}, &message_struct).ok()); +} + +TEST_F(AuditLoggingUtilTest, RedactStructRecursively_InvalidPath) { + Struct message_struct = CreateNestedStruct({"level1", "level2"}, "value"); + std::vector path_pieces = {"invalid", "path_end"}; + EXPECT_TRUE( + RedactStructRecursively(path_pieces.cbegin(), path_pieces.cend(), &message_struct).ok()); + + // Verify that the field "level2" has been replaced with an empty Struct + const auto& level1_field = message_struct.fields().at("level1"); + EXPECT_TRUE(level1_field.has_struct_value()); + const auto& level2_field = level1_field.struct_value().fields().at("level2"); + EXPECT_TRUE(level2_field.has_struct_value()); +} + +TEST_F(AuditLoggingUtilTest, RedactStructRecursively_ValidPath) { + Struct message_struct = CreateNestedStruct({"level1", "level2"}, "value"); + std::vector path_pieces = {"level1", "level2"}; + EXPECT_TRUE( + RedactStructRecursively(path_pieces.cbegin(), path_pieces.cend(), &message_struct).ok()); + + // Verify that the field "level2" has been replaced with an empty Struct + const auto& level1_field = message_struct.fields().at("level1"); + EXPECT_TRUE(level1_field.has_struct_value()); + const auto& level2_field = level1_field.struct_value().fields().at("level2"); + EXPECT_TRUE(level2_field.has_struct_value()); +} + +TEST_F(AuditLoggingUtilTest, RedactStructRecursively_MissingIntermediateField) { + Struct message_struct = CreateNestedStruct({"level1"}, "value"); + std::vector path_pieces = {"level1", "level2"}; + EXPECT_TRUE( + RedactStructRecursively(path_pieces.cbegin(), path_pieces.cend(), &message_struct).ok()); + + // Verify that "level2" field is created as an empty Struct + const auto& level1_field = message_struct.fields().at("level1"); + EXPECT_TRUE(level1_field.has_struct_value()); + const auto& level2_field = level1_field.struct_value().fields().at("level2"); + EXPECT_TRUE(level2_field.has_struct_value()); +} + +TEST_F(AuditLoggingUtilTest, RedactStructRecursively_EmptyPathPiece) { + Struct message_struct = CreateNestedStruct({"level1", "level2"}, "value"); + std::vector path_pieces = {"level1", ""}; + EXPECT_EQ(RedactStructRecursively(path_pieces.cbegin(), path_pieces.cend(), &message_struct), + absl::InvalidArgumentError("path piece cannot be empty.")); +} + +TEST_F(AuditLoggingUtilTest, RedactStructRecursively_NullptrMessageStruct) { + std::vector path_pieces = {"level1"}; + EXPECT_EQ(RedactStructRecursively(path_pieces.cbegin(), path_pieces.cend(), nullptr), + absl::InvalidArgumentError("message_struct cannot be nullptr.")); +} + +TEST_F(AuditLoggingUtilTest, IsMessageFieldPathPresent_EmptyPath) { + EXPECT_EQ( + IsMessageFieldPathPresent(*request_type_, type_finder_, "", test_request_raw_proto_).status(), + absl::InvalidArgumentError("Field path cannot be empty.")); +} + +TEST_F(AuditLoggingUtilTest, IsMessageFieldPathPresent_EmptyType) { + Type empty_type; + EXPECT_EQ( + IsMessageFieldPathPresent(empty_type, type_finder_, "id", test_request_raw_proto_).status(), + absl::InvalidArgumentError("Cannot find field 'id' in '' message.")); +} + +TEST_F(AuditLoggingUtilTest, IsMessageFieldPathPresent_InvalidPath) { + EXPECT_EQ(IsMessageFieldPathPresent(*request_type_, type_finder_, "invalid_path", + test_request_raw_proto_) + .status(), + absl::InvalidArgumentError( + "Cannot find field 'invalid_path' in 'logging.TestRequest' message.")); +} + +TEST_F(AuditLoggingUtilTest, IsMessageFieldPathPresent_ValidPath) { + EXPECT_TRUE( + IsMessageFieldPathPresent(*request_type_, type_finder_, "bucket", test_request_raw_proto_) + .status() + .ok()); +} + +TEST_F(AuditLoggingUtilTest, IsMessageFieldPathPresent_NotMessageField) { + EXPECT_EQ(IsMessageFieldPathPresent(*request_type_, type_finder_, "repeated_strings", + test_request_raw_proto_) + .status(), + absl::InvalidArgumentError("Field 'repeated_strings' is not a message type field.")); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_bytes) { + EXPECT_EQ(3, + ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("bucket.objects"), test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, FindSingularLastValue_SingleStringField) { + Field field; + field.set_name("id"); + field.set_number(1); + field.set_kind(Field::TYPE_INT64); + + std::string data = "123445"; + auto result = FindSingularLastValue( + &field, &test_request_raw_proto_.CreateCodedInputStreamWrapper()->Get()); + ASSERT_TRUE(result.ok()); + EXPECT_EQ(result.value(), ""); +} + +TEST_F(AuditLoggingUtilTest, FindSingularLastValue_RepeatedStringField) { + Field field; + field.set_name("repeated_strings"); + field.set_number(3); + field.set_kind(Field::TYPE_STRING); + field.set_cardinality(Field::CARDINALITY_REPEATED); + + std::string data = "repeated-string-0"; + auto result = FindSingularLastValue( + &field, &test_request_raw_proto_.CreateCodedInputStreamWrapper()->Get()); + EXPECT_TRUE(result.ok()); + EXPECT_EQ(result.value(), "repeated-string-0"); +} + +TEST_F(AuditLoggingUtilTest, FindSingularLastValue_RepeatedMessageField) { + Field field; + field.set_name("bucket"); + field.set_number(2); + field.set_kind(Field::TYPE_STRING); + + std::string data = "test-bucket"; + auto result = FindSingularLastValue( + &field, &test_request_raw_proto_.CreateCodedInputStreamWrapper()->Get()); + ASSERT_TRUE(result.ok()); + EXPECT_EQ( + result.value(), + "\n\vtest-bucket\x15\xCD\xCCL?\x1A\rtest-object-1\x1A\rtest-object-2\x1A\rtest-object-3"); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_string) { + EXPECT_EQ(1, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("repeated_strings"), + test_request_raw_proto_)); + EXPECT_EQ(2, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("proto2_message.repeated_strings"), + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_message) { + EXPECT_EQ(2, ExtractRepeatedFieldSize(*response_type_, type_finder_, GetFieldMaskWith("buckets"), + test_response_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_map) { + EXPECT_EQ(2, ExtractRepeatedFieldSize(*response_type_, type_finder_, + GetFieldMaskWith("sub_buckets"), test_response_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_enum) { + EXPECT_EQ(4, + ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("repeated_enum"), test_request_raw_proto_)); + EXPECT_EQ(4, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("proto2_message.repeated_enum"), + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_double) { + EXPECT_EQ(1, + ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("repeated_double"), test_request_raw_proto_)); + EXPECT_EQ(1, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("proto2_message.repeated_double"), + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_float) { + EXPECT_EQ(2, + ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("repeated_float"), test_request_raw_proto_)); + EXPECT_EQ(2, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("proto2_message.repeated_float"), + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_int64) { + EXPECT_EQ(3, + ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("repeated_int64"), test_request_raw_proto_)); + EXPECT_EQ(3, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("proto2_message.repeated_int64"), + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_uint64) { + EXPECT_EQ(4, + ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("repeated_uint64"), test_request_raw_proto_)); + EXPECT_EQ(4, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("proto2_message.repeated_uint64"), + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_int32) { + EXPECT_EQ(5, + ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("repeated_int32"), test_request_raw_proto_)); + EXPECT_EQ(5, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("proto2_message.repeated_int32"), + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_fixed64) { + EXPECT_EQ(6, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("repeated_fixed64"), + test_request_raw_proto_)); + EXPECT_EQ(6, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("proto2_message.repeated_fixed64"), + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_fixed32) { + EXPECT_EQ(7, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("repeated_fixed32"), + test_request_raw_proto_)); + EXPECT_EQ(7, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("proto2_message.repeated_fixed32"), + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_bool) { + EXPECT_EQ(5, + ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("repeated_bool"), test_request_raw_proto_)); + + EXPECT_EQ(5, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("proto2_message.repeated_bool"), + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_uint32) { + EXPECT_EQ(8, + ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("repeated_uint32"), test_request_raw_proto_)); + EXPECT_EQ(8, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("proto2_message.repeated_uint32"), + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_sfixed64) { + EXPECT_EQ(6, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("repeated_sfixed64"), + test_request_raw_proto_)); + EXPECT_EQ(6, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("proto2_message.repeated_sfixed64"), + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_sfixed32) { + EXPECT_EQ(7, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("repeated_sfixed32"), + test_request_raw_proto_)); + EXPECT_EQ(7, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("proto2_message.repeated_sfixed32"), + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_sint32) { + EXPECT_EQ(5, + ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("repeated_sint32"), test_request_raw_proto_)); + EXPECT_EQ(5, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("proto2_message.repeated_sint32"), + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_sint64) { + EXPECT_EQ(6, + ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("repeated_sint64"), test_request_raw_proto_)); + EXPECT_EQ(6, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("proto2_message.repeated_sint64"), + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_OK_DefaultValue) { + test_request_proto_.clear_repeated_strings(); + test_request_raw_proto_ = CordMessageData(test_request_proto_.SerializeAsCord()); + EXPECT_EQ(0, ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("repeated_strings"), + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_Error_EmptyPath) { + EXPECT_LT(ExtractRepeatedFieldSize(*request_type_, type_finder_, GetFieldMaskWith(""), + test_request_raw_proto_), + 0); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_Error_NonRepeatedField) { + EXPECT_LT(ExtractRepeatedFieldSize(*request_type_, type_finder_, GetFieldMaskWith("bucket.ratio"), + test_request_raw_proto_), + 0); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_Error_UnknownField) { + EXPECT_LT(ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("bucket.unknown"), test_request_raw_proto_), + 0); + EXPECT_LT(ExtractRepeatedFieldSize(*request_type_, type_finder_, GetFieldMaskWith("unknown"), + test_request_raw_proto_), + 0); + EXPECT_LT(ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("unknown1.unknown2"), + test_request_raw_proto_), + 0); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_Error_NonLeafPrimitiveTypeField) { + EXPECT_LT(ExtractRepeatedFieldSize(*request_type_, type_finder_, + GetFieldMaskWith("bucket.name.unknown"), + test_request_raw_proto_), + 0); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_Error_NonLeafMapField) { + EXPECT_LT(ExtractRepeatedFieldSize(*response_type_, type_finder_, + GetFieldMaskWith("sub_buckets.objects"), + test_response_raw_proto_), + 0); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_Error_NonLeafRepeatedField) { + EXPECT_LT(ExtractRepeatedFieldSize(*response_type_, type_finder_, + GetFieldMaskWith("buckets.name"), test_response_raw_proto_), + 0); + EXPECT_LT(ExtractRepeatedFieldSize(*response_type_, type_finder_, + GetFieldMaskWith("buckets.objects"), test_response_raw_proto_), + 0); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_Error_NullptrFieldMask) { + EXPECT_GT( + 0, ExtractRepeatedFieldSize(*request_type_, type_finder_, nullptr, test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractRepeatedFieldSize_EmptyFieldMask) { + FieldMask field_mask; + EXPECT_GT(0, ExtractRepeatedFieldSize(*request_type_, type_finder_, &field_mask_, + test_request_raw_proto_)); +} + +TEST_F(AuditLoggingUtilTest, ExtractStringFieldValue_OK) { + EXPECT_THAT( + ExtractStringFieldValue(*request_type_, type_finder_, "bucket.name", test_request_raw_proto_), + IsOkAndHolds("test-bucket")); +} + +TEST_F(AuditLoggingUtilTest, ExtractStringFieldValue_OK_DefaultValue) { + test_request_proto_.mutable_bucket()->clear_name(); + test_request_raw_proto_ = CordMessageData(test_request_proto_.SerializeAsCord()); + EXPECT_THAT( + ExtractStringFieldValue(*request_type_, type_finder_, "bucket.name", test_request_raw_proto_), + IsOkAndHolds("")); + + test_request_proto_.clear_bucket(); + test_request_raw_proto_ = CordMessageData(test_request_proto_.SerializeAsCord()); + EXPECT_THAT( + ExtractStringFieldValue(*request_type_, type_finder_, "bucket.name", test_request_raw_proto_), + IsOkAndHolds("")); +} + +TEST_F(AuditLoggingUtilTest, ExtractStringFieldValue_Error_EmptyPath) { + EXPECT_THAT(ExtractStringFieldValue(*request_type_, type_finder_, "", test_request_raw_proto_), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AuditLoggingUtilTest, ExtractStringFieldValue_Error_UnknownField) { + EXPECT_THAT(ExtractStringFieldValue(*request_type_, type_finder_, "bucket.unknown", + test_request_raw_proto_), + StatusIs(absl::StatusCode::kInvalidArgument)); + + EXPECT_THAT( + ExtractStringFieldValue(*request_type_, type_finder_, "unknown", test_request_raw_proto_), + StatusIs(absl::StatusCode::kInvalidArgument)); + + EXPECT_THAT(ExtractStringFieldValue(*request_type_, type_finder_, "unknown1.unknown2", + test_request_raw_proto_), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AuditLoggingUtilTest, ExtractStringFieldValue_Error_RepeatedStringLeafNode) { + EXPECT_THAT(ExtractStringFieldValue(*request_type_, type_finder_, "repeated_strings", + test_request_raw_proto_), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AuditLoggingUtilTest, ExtractStringFieldValue_Error_NonStringLeafNode) { + EXPECT_THAT(ExtractStringFieldValue(*request_type_, type_finder_, "bucket.ratio", + test_request_raw_proto_), + StatusIs(absl::StatusCode::kInvalidArgument)); + + EXPECT_THAT(ExtractStringFieldValue(*response_type_, type_finder_, "sub_buckets", + test_response_raw_proto_), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AuditLoggingUtilTest, ExtractStringFieldValue_Error_InvalidTypeFinder) { + auto invalid_type_finder = [](absl::string_view /*type_url*/) { return nullptr; }; + + EXPECT_THAT(ExtractStringFieldValue(*request_type_, invalid_type_finder, "bucket.ratio", + test_request_raw_proto_), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(StructUtilTest, RedactPaths_Basic) { + std::string proto_struct_string = "fields {" + " key: \"nested\"" + " value {" + " struct_value {" + " fields {" + " key: \"deeper_nest\"" + " value {" + " struct_value: {" + " fields {" + " key: \"nice_field\"" + " value {" + " string_value: \"nice value\"" + " }" + " }" + " }" + " }" + " }" + " }" + " }" + "}"; + Struct proto_struct; + CHECK(Protobuf::TextFormat::ParseFromString(proto_struct_string, &proto_struct)); + + std::vector paths_to_redact = {"nested.deeper_nest"}; + RedactPaths(paths_to_redact, &proto_struct); + + std::string expected_struct_string = "fields {" + " key: \"nested\"" + " value {" + " struct_value {" + " fields {" + " key: \"deeper_nest\"" + " value {" + " struct_value: {}" + " }" + " }" + " }" + " }" + "}"; + Struct expected_struct; + CHECK(Protobuf::TextFormat::ParseFromString(expected_struct_string, &expected_struct)); + EXPECT_TRUE(Protobuf::util::MessageDifferencer::Equals(expected_struct, proto_struct)); +} + +TEST(StructUtilTest, RedactPaths_HandlesOneOfFields) { + std::string proto_struct_string = "fields {" + " key: \"nested_value\"" + " value {" + " struct_value {" + " fields {" + " key: \"uint32_field\"" + " value {" + " number_value: 123" + " }" + " }" + " }" + " }" + "}"; + Struct proto_struct; + CHECK(Protobuf::TextFormat::ParseFromString(proto_struct_string, &proto_struct)); + + std::vector paths_to_redact = {"nested_value"}; + RedactPaths(paths_to_redact, &proto_struct); + + std::string expected_struct_string = "fields {" + " key: \"nested_value\"" + " value {" + " struct_value {}" + " }" + "}"; + Struct expected_struct; + CHECK(Protobuf::TextFormat::ParseFromString(expected_struct_string, &expected_struct)); + EXPECT_TRUE(Protobuf::util::MessageDifferencer::Equals(expected_struct, proto_struct)); +} + +TEST(StructUtilTest, RedactPaths_AllowsRepeatedLeafMessageType) { + std::string proto_struct_string = "fields {" + " key: \"repeated_message_field\"" + " value {" + " list_value {" + " values {" + " struct_value {" + " fields {" + " key: \"uint32_field\"" + " value {" + " number_value: 123" + " }" + " }" + " }" + " }" + " values {" + " struct_value {" + " fields {" + " key: \"uint32_field\"" + " value {" + " number_value: 456" + " }" + " }" + " }" + " }" + " }" + " }" + "}"; + Struct proto_struct; + CHECK(Protobuf::TextFormat::ParseFromString(proto_struct_string, &proto_struct)); + + std::vector paths_to_redact = {"repeated_message_field"}; + RedactPaths(paths_to_redact, &proto_struct); + + std::string expected_struct_string = "fields {" + " key: \"repeated_message_field\"" + " value {" + " list_value {" + " values {" + " struct_value {}" + " }" + " values {" + " struct_value {}" + " }" + " }" + " }" + "}"; + Struct expected_struct; + CHECK(Protobuf::TextFormat::ParseFromString(expected_struct_string, &expected_struct)); + EXPECT_TRUE(Protobuf::util::MessageDifferencer::Equals(expected_struct, proto_struct)); +} + +TEST(StructUtilTest, RedactPaths_AllowsRepeatedNonLeafMessageType) { + std::string proto_struct_string = "fields {" + " key: \"repeated_message_field\"" + " value {" + " list_value {" + " values {" + " struct_value {" + " fields {" + " key: \"uint32_field\"" + " value {" + " number_value: 123" + " }" + " }" + " fields {" + " key: \"deeper_nest\"" + " value {" + " struct_value {" + " fields {" + " key: \"nice_field\"" + " value: {" + " string_value: \"nice value\"" + " }" + " }" + " }" + " }" + " }" + " }" + " }" + " values {" + " struct_value {" + " fields {" + " key: \"uint32_field\"" + " value {" + " number_value: 456" + " }" + " }" + " }" + " }" + " }" + " }" + "}"; + Struct proto_struct; + CHECK(Protobuf::TextFormat::ParseFromString(proto_struct_string, &proto_struct)); + + std::vector paths_to_redact = {"repeated_message_field.deeper_nest"}; + RedactPaths(paths_to_redact, &proto_struct); + + std::string expected_struct_string = "fields {" + " key: \"repeated_message_field\"" + " value {" + " list_value {" + " values {" + " struct_value {" + " fields {" + " key: \"uint32_field\"" + " value {" + " number_value: 123" + " }" + " }" + " fields {" + " key: \"deeper_nest\"" + " value {" + " struct_value {}" + " }" + " }" + " }" + " }" + " values {" + " struct_value {" + " fields {" + " key: \"uint32_field\"" + " value {" + " number_value: 456" + " }" + " }" + " }" + " }" + " }" + " }" + "}"; + Struct expected_struct; + CHECK(Protobuf::TextFormat::ParseFromString(expected_struct_string, &expected_struct)); + EXPECT_TRUE(Protobuf::util::MessageDifferencer::Equals(expected_struct, proto_struct)); +} + +struct ResourceNameTestCase { + std::string test_name; + std::string resource_name; + std::string expected_location; +}; + +class ExtractLocationIdFromResourceNameTest + : public ::testing::TestWithParam {}; + +TEST_P(ExtractLocationIdFromResourceNameTest, Test) { + // Empty resource name - should return empty string. + const ResourceNameTestCase& params = GetParam(); + EXPECT_EQ(ExtractLocationIdFromResourceName(params.resource_name), params.expected_location); +} + +INSTANTIATE_TEST_SUITE_P( + ExtractLocationIdFromResourceNameTests, ExtractLocationIdFromResourceNameTest, + ValuesIn({ + {"EmptyResource", "", ""}, + {"NoLocation", "projects/123/buckets/abc", ""}, + {"LocationSubstring", "my_locations/123/frw1/fw1", ""}, + {"MissingLocationValue", "projects/123/buckets/abc/locations/", ""}, + {"EmptyLocationValue", "projects/123/locations//buckets/abc", ""}, + {"LocationInMiddle", "projects/123/locations/456/buckets/abc", "456"}, + {"LocationAtEnd", "projects/123/locations/456", "456"}, + {"LocationAtStartQualified", "//locations/123/", "123"}, + {"LocationOnly", "locations/123", "123"}, + {"LocationAtStartWithMore", "locations/123/frwl/fw1", "123"}, + {"RegionSubstring", "my_regions/123/frw1/fw1", ""}, + {"MissingRegionValue", "projects/123/buckets/abc/regions/", ""}, + {"EmptyRegionValue", "projects/123/regions//buckets/abc", ""}, + {"RegionInMiddle", "projects/123/regions/456/buckets/abc", "456"}, + {"RegionAtEnd", "projects/123/regions/456", "456"}, + {"RegionAtStartQualified", "//regions/123/", "123"}, + {"RegionOnly", "regions/123", "123"}, + {"RegionAtStartWithMore", "regions/123/frwl/fw1", "123"}, + }), + [](const ::testing::TestParamInfo& info) { + return info.param.test_name; + }); + +} // namespace +} // namespace ProtoMessageLogging +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/filters/http/proto_message_logging/logging_util/logging_util_test.yaml b/test/extensions/filters/http/proto_message_logging/logging_util/logging_util_test.yaml new file mode 100644 index 000000000000..f8c0fac6402b --- /dev/null +++ b/test/extensions/filters/http/proto_message_logging/logging_util/logging_util_test.yaml @@ -0,0 +1,23 @@ +type: google.api.Service +config_version: 3 +name: audit_logging_util_test.googleapis.com +title: Audit Logging Util Test API +apis: +- name: proto_processing_lib.proto_scrubber.testing.AuditLoggingUtilTestService + +documentation: + overview: | + (== suppress_warning quota-presence ==) + (== suppress_warning usage_manager-presence ==) + (== suppress_warning http-rest ==) + (== suppress_warning versioning-http-version-prefix ==) + (== suppress_warning documentation-presence ==) + (== suppress_warning legacy-contacts ==) + (== suppress_warning versioning-config ==) + summary: + Audit Logging Util Test + +backend: + rules: + - selector: '*' + address: diff --git a/test/proto/BUILD b/test/proto/BUILD index b3d1a6ffca59..81de53e0a69c 100644 --- a/test/proto/BUILD +++ b/test/proto/BUILD @@ -65,6 +65,21 @@ envoy_proto_descriptor( ], ) +envoy_proto_library( + name = "logging_proto", + srcs = [ + "logging.proto", + ], +) + +envoy_proto_descriptor( + name = "logging_proto_descriptor", + srcs = [ + "logging.proto", + ], + out = "logging.descriptor", +) + envoy_proto_library( name = "sensitive_proto", srcs = [":sensitive.proto"], diff --git a/test/proto/logging.proto b/test/proto/logging.proto new file mode 100644 index 000000000000..15d56aea9126 --- /dev/null +++ b/test/proto/logging.proto @@ -0,0 +1,111 @@ +syntax = "proto3"; + +package logging; + +message TestBucket { + string name = 1; + float ratio = 2; + repeated bytes objects = 3; +} + +enum TestEnum { + TEST_ENUM_UNSPECIFIED = 0; + ALPHA = 1; + BETA = 2; + GAMMA = 3; +} + +enum TestProto2Enum { + PROTO2_ALPHA = 0; + PROTO2_BETA = 1; + PROTO2_GAMMA = 2; +} + +message TestProto2Message { + repeated string repeated_strings = 3; + + repeated TestProto2Enum repeated_enum = 17 [packed = true]; + + repeated double repeated_double = 4; + + repeated float repeated_float = 5 [packed = true]; + + repeated int64 repeated_int64 = 6; + + repeated uint64 repeated_uint64 = 7 [packed = true]; + + repeated int32 repeated_int32 = 8; + + repeated fixed64 repeated_fixed64 = 9 [packed = true]; + + repeated fixed32 repeated_fixed32 = 10; + + repeated bool repeated_bool = 11 [packed = true]; + + repeated uint32 repeated_uint32 = 12; + + repeated sfixed64 repeated_sfixed64 = 13 [packed = true]; + + repeated sfixed32 repeated_sfixed32 = 14; + + repeated sint32 repeated_sint32 = 15 [packed = true]; + + repeated sint64 repeated_sint64 = 16; +} + +message TestRequest { + int64 id = 1; + + TestBucket bucket = 2; + + TestProto2Message proto2_message = 18; + + repeated string repeated_strings = 3; + + repeated TestEnum repeated_enum = 17; + + repeated double repeated_double = 4; + + repeated float repeated_float = 5; + + repeated int64 repeated_int64 = 6; + + repeated uint64 repeated_uint64 = 7; + + repeated int32 repeated_int32 = 8; + + repeated fixed64 repeated_fixed64 = 9; + + repeated fixed32 repeated_fixed32 = 10; + + repeated bool repeated_bool = 11; + + repeated uint32 repeated_uint32 = 12; + + repeated sfixed64 repeated_sfixed64 = 13; + + repeated sfixed32 repeated_sfixed32 = 14; + + repeated sint32 repeated_sint32 = 15; + + repeated sint64 repeated_sint64 = 16; +} + +message TestResponse { + repeated TestBucket buckets = 1; + + map sub_buckets = 3; + + TestBucket bucket_present = 4; + TestBucket bucket_absent = 5; + message SubMessage { + TestBucket bucket_present = 1; + TestBucket bucket_absent = 2; + } + SubMessage sub_message = 6; +} + +service AuditLoggingUtilTestService { + // AuditTest Method. + rpc AuditTest(TestRequest) returns (TestResponse); +} diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index 04d4f7e0f3a3..c0c4b3c28739 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -27,6 +27,7 @@ BACKTRACE BEL BBR BIDIRECTIONAL +CCL ECN ECS EKS @@ -38,6 +39,8 @@ BPF Bdecoded Bencoded CIO +cbegin +cend deadcode DFP Dynatrace @@ -326,6 +329,9 @@ middlewildcard monostate mpd mutators +mybucket +myobject +myproject na oghttp OID @@ -1235,6 +1241,7 @@ rollout roundtrip rpcs rq +rtest rtrim rtt ruleset @@ -1469,6 +1476,7 @@ virtualize virtualized vptr vtable +vtest vtt wakeup wakeups From 6f2d549725e831e8e3e5cf3656eb15e5cdf2289f Mon Sep 17 00:00:00 2001 From: Namrata Bhave Date: Wed, 14 Aug 2024 19:49:27 +0530 Subject: [PATCH 106/130] Add s390x CI build status (#35708) Signed-off-by: namrata-ibm --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 278b6dd0248f..f2f383d5f001 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ involved and how Envoy plays a role, read the CNCF [![Azure Pipelines](https://dev.azure.com/cncf/envoy/_apis/build/status/11?branchName=main)](https://dev.azure.com/cncf/envoy/_build/latest?definitionId=11&branchName=main) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/envoy.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:envoy) [![Jenkins](https://powerci.osuosl.org/buildStatus/icon?job=build-envoy-static-master&subject=ppc64le%20build)](https://powerci.osuosl.org/job/build-envoy-static-master/) +[![Jenkins](https://ibmz-ci.osuosl.org/buildStatus/icon?job=Envoy_IBMZ_CI&subject=s390x%20build)](https://ibmz-ci.osuosl.org/job/Envoy_IBMZ_CI/) ## Documentation From 69ee8cf7b5f5d6843d2f20c0149d3f2e55ea9e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E6=B3=BD=E8=BD=A9?= Date: Wed, 14 Aug 2024 22:43:42 +0800 Subject: [PATCH 107/130] golang: allow accessing {request,response}/{headers,trailer} in the OnLog phase (#34810) Signed-off-by: spacewander --- changelogs/current.yaml | 4 ++ contrib/golang/common/dso/dso.cc | 7 +- contrib/golang/common/dso/dso.h | 13 +++- contrib/golang/common/dso/libgolang.h | 7 +- contrib/golang/common/dso/test/mocks.h | 5 +- .../common/dso/test/test_data/simple.go | 4 +- contrib/golang/common/go/api/api.h | 8 +++ contrib/golang/common/go/api/filter.go | 12 ++-- .../filters/http/source/go/pkg/http/shim.go | 71 +++++++++++++++++-- .../filters/http/source/golang_filter.cc | 64 ++++++++++++++--- .../filters/http/source/golang_filter.h | 8 +-- .../filters/http/source/processor_state.h | 2 +- .../http/test/golang_filter_fuzz_test.cc | 6 +- .../http/test/golang_integration_test.cc | 20 ++++-- .../http/test/test_data/access_log/filter.go | 50 ++++++++++++- .../http/test/test_data/basic/filter.go | 2 +- .../http/test/test_data/property/filter.go | 2 +- 17 files changed, 235 insertions(+), 50 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index cc0d194f30c3..5c5e2dc62796 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -6,6 +6,10 @@ behavior_changes: change: | Removed support for (long deprecated) opentracing. See `issue 27401 `_ for details. +- area: golang + change: | + Change ``OnLogDownstreamStart``, ``OnLogDownstreamPeriodic`` and ``OnLog`` methods so that user can get the request/response's + headers and trailers when producing access log. - area: http change: | Added HTTP1-safe option for :ref:`max_connection_duration diff --git a/contrib/golang/common/dso/dso.cc b/contrib/golang/common/dso/dso.cc index ce7336453831..699ebf4b8579 100644 --- a/contrib/golang/common/dso/dso.cc +++ b/contrib/golang/common/dso/dso.cc @@ -95,9 +95,12 @@ GoUint64 HttpFilterDsoImpl::envoyGoFilterOnHttpData(processState* p0, GoUint64 p return envoy_go_filter_on_http_data_(p0, p1, p2, p3); } -void HttpFilterDsoImpl::envoyGoFilterOnHttpLog(httpRequest* p0, int p1) { +void HttpFilterDsoImpl::envoyGoFilterOnHttpLog(httpRequest* p0, int p1, processState* p2, + processState* p3, GoUint64 p4, GoUint64 p5, + GoUint64 p6, GoUint64 p7, GoUint64 p8, GoUint64 p9, + GoUint64 p10, GoUint64 p11) { ASSERT(envoy_go_filter_on_http_log_ != nullptr); - envoy_go_filter_on_http_log_(p0, GoUint64(p1)); + envoy_go_filter_on_http_log_(p0, GoUint64(p1), p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); } void HttpFilterDsoImpl::envoyGoFilterOnHttpDestroy(httpRequest* p0, int p1) { diff --git a/contrib/golang/common/dso/dso.h b/contrib/golang/common/dso/dso.h index 78c7c0d768ef..293072a2daa9 100644 --- a/contrib/golang/common/dso/dso.h +++ b/contrib/golang/common/dso/dso.h @@ -43,7 +43,9 @@ class HttpFilterDso : public Dso { GoUint64 p3) PURE; virtual GoUint64 envoyGoFilterOnHttpData(processState* p0, GoUint64 p1, GoUint64 p2, GoUint64 p3) PURE; - virtual void envoyGoFilterOnHttpLog(httpRequest* p0, int p1) PURE; + virtual void envoyGoFilterOnHttpLog(httpRequest* p0, int p1, processState* p2, processState* p3, + GoUint64 p4, GoUint64 p5, GoUint64 p6, GoUint64 p7, + GoUint64 p8, GoUint64 p9, GoUint64 p10, GoUint64 p11) PURE; virtual void envoyGoFilterOnHttpDestroy(httpRequest* p0, int p1) PURE; virtual void envoyGoRequestSemaDec(httpRequest* p0) PURE; }; @@ -61,7 +63,9 @@ class HttpFilterDsoImpl : public HttpFilterDso { GoUint64 p3) override; GoUint64 envoyGoFilterOnHttpData(processState* p0, GoUint64 p1, GoUint64 p2, GoUint64 p3) override; - void envoyGoFilterOnHttpLog(httpRequest* p0, int p1) override; + void envoyGoFilterOnHttpLog(httpRequest* p0, int p1, processState* p2, processState* p3, + GoUint64 p4, GoUint64 p5, GoUint64 p6, GoUint64 p7, GoUint64 p8, + GoUint64 p9, GoUint64 p10, GoUint64 p11) override; void envoyGoFilterOnHttpDestroy(httpRequest* p0, int p1) override; void envoyGoRequestSemaDec(httpRequest* p0) override; void cleanup() override; @@ -75,7 +79,10 @@ class HttpFilterDsoImpl : public HttpFilterDso { GoUint64 p3) = {nullptr}; GoUint64 (*envoy_go_filter_on_http_data_)(processState* p0, GoUint64 p1, GoUint64 p2, GoUint64 p3) = {nullptr}; - void (*envoy_go_filter_on_http_log_)(httpRequest* p0, GoUint64 p1) = {nullptr}; + void (*envoy_go_filter_on_http_log_)(httpRequest* p0, int p1, processState* p2, processState* p3, + GoUint64 p4, GoUint64 p5, GoUint64 p6, GoUint64 p7, + GoUint64 p8, GoUint64 p9, GoUint64 p10, + GoUint64 p11) = {nullptr}; void (*envoy_go_filter_on_http_destroy_)(httpRequest* p0, GoUint64 p1) = {nullptr}; void (*envoy_go_filter_go_request_sema_dec_)(httpRequest* p0) = {nullptr}; void (*envoy_go_filter_cleanup_)() = {nullptr}; diff --git a/contrib/golang/common/dso/libgolang.h b/contrib/golang/common/dso/libgolang.h index b477b5b55e6b..695ff00761f3 100644 --- a/contrib/golang/common/dso/libgolang.h +++ b/contrib/golang/common/dso/libgolang.h @@ -120,7 +120,12 @@ extern GoUint64 envoyGoFilterOnHttpData(processState* s, GoUint64 end_stream, Go // go:linkname envoyGoFilterOnHttpLog // github.com/envoyproxy/envoy/contrib/golang/filters/http/source/go/pkg/http.envoyGoFilterOnHttpLog -extern void envoyGoFilterOnHttpLog(httpRequest* r, GoUint64 type); +extern void envoyGoFilterOnHttpLog(httpRequest* r, GoUint64 type, processState* decoding_state, + processState* encoding_state, GoUint64 req_header_num, + GoUint64 req_header_bytes, GoUint64 req_trailer_num, + GoUint64 req_trailer_bytes, GoUint64 resp_header_num, + GoUint64 resp_header_bytes, GoUint64 resp_trailer_num, + GoUint64 resp_trailer_bytes); // go:linkname envoyGoFilterOnHttpDestroy // github.com/envoyproxy/envoy/contrib/golang/filters/http/source/go/pkg/http.envoyGoFilterOnHttpDestroy diff --git a/contrib/golang/common/dso/test/mocks.h b/contrib/golang/common/dso/test/mocks.h index 973a096bfb10..082251738863 100644 --- a/contrib/golang/common/dso/test/mocks.h +++ b/contrib/golang/common/dso/test/mocks.h @@ -21,7 +21,10 @@ class MockHttpFilterDsoImpl : public HttpFilterDso { (processState * p0, GoUint64 p1, GoUint64 p2, GoUint64 p3)); MOCK_METHOD(GoUint64, envoyGoFilterOnHttpData, (processState * p0, GoUint64 p1, GoUint64 p2, GoUint64 p3)); - MOCK_METHOD(void, envoyGoFilterOnHttpLog, (httpRequest * p0, int p1)); + MOCK_METHOD(void, envoyGoFilterOnHttpLog, + (httpRequest * p0, int p1, processState* p2, processState* p3, GoUint64 p4, + GoUint64 p5, GoUint64 p6, GoUint64 p7, GoUint64 p8, GoUint64 p9, GoUint64 p10, + GoUint64 p11)); MOCK_METHOD(void, envoyGoFilterOnHttpDestroy, (httpRequest * p0, int p1)); MOCK_METHOD(void, envoyGoRequestSemaDec, (httpRequest * p0)); MOCK_METHOD(void, envoyGoFilterCleanUp, ()); diff --git a/contrib/golang/common/dso/test/test_data/simple.go b/contrib/golang/common/dso/test/test_data/simple.go index a0ce2419bb2f..019d075b9958 100644 --- a/contrib/golang/common/dso/test/test_data/simple.go +++ b/contrib/golang/common/dso/test/test_data/simple.go @@ -57,7 +57,9 @@ func envoyGoFilterOnHttpData(s *C.processState, endStream, buffer, length uint64 } //export envoyGoFilterOnHttpLog -func envoyGoFilterOnHttpLog(r *C.httpRequest, logType uint64) { +func envoyGoFilterOnHttpLog(r *C.httpRequest, logType uint64, decodingState *C.processState, encodingState *C.processState, + reqHeaderNum, reqHeaderBytes, reqTrailerNum, reqTrailerBytes, + respHeaderNum, respHeaderBytes, respTrailerNum, respTrailerBytes uint64) { } //export envoyGoFilterOnHttpDestroy diff --git a/contrib/golang/common/go/api/api.h b/contrib/golang/common/go/api/api.h index e6825f04e9fe..fb99623b7721 100644 --- a/contrib/golang/common/go/api/api.h +++ b/contrib/golang/common/go/api/api.h @@ -3,7 +3,13 @@ // NOLINT(namespace-envoy) #ifdef __cplusplus +#include + +#define _Atomic(X) std::atomic + extern "C" { +#else +#include // NOLINT(modernize-deprecated-headers) #endif #include // NOLINT(modernize-deprecated-headers) @@ -27,6 +33,8 @@ typedef struct httpRequest { // NOLINT(modernize-use-using) // The ID of the worker that is processing this request, this enables the go filter to dedicate // memory to each worker and not require locks uint32_t worker_id; + // This flag will be read & written by different threads, so it need to be atomic + _Atomic(int) is_golang_processing_log; } httpRequest; typedef struct { // NOLINT(modernize-use-using) diff --git a/contrib/golang/common/go/api/filter.go b/contrib/golang/common/go/api/filter.go index f33e1e90ae71..ade8732ae475 100644 --- a/contrib/golang/common/go/api/filter.go +++ b/contrib/golang/common/go/api/filter.go @@ -81,22 +81,22 @@ type StreamFilter interface { StreamEncoderFilter // log - OnLog() - OnLogDownstreamStart() - OnLogDownstreamPeriodic() + OnLog(RequestHeaderMap, RequestTrailerMap, ResponseHeaderMap, ResponseTrailerMap) + OnLogDownstreamStart(RequestHeaderMap) + OnLogDownstreamPeriodic(RequestHeaderMap, RequestTrailerMap, ResponseHeaderMap, ResponseTrailerMap) // destroy filter OnDestroy(DestroyReason) // TODO add more for stream complete } -func (*PassThroughStreamFilter) OnLog() { +func (*PassThroughStreamFilter) OnLog(RequestHeaderMap, RequestTrailerMap, ResponseHeaderMap, ResponseTrailerMap) { } -func (*PassThroughStreamFilter) OnLogDownstreamStart() { +func (*PassThroughStreamFilter) OnLogDownstreamStart(RequestHeaderMap) { } -func (*PassThroughStreamFilter) OnLogDownstreamPeriodic() { +func (*PassThroughStreamFilter) OnLogDownstreamPeriodic(RequestHeaderMap, RequestTrailerMap, ResponseHeaderMap, ResponseTrailerMap) { } func (*PassThroughStreamFilter) OnDestroy(DestroyReason) { diff --git a/contrib/golang/filters/http/source/go/pkg/http/shim.go b/contrib/golang/filters/http/source/go/pkg/http/shim.go index c904ee5f98a2..4ae55bf75c27 100644 --- a/contrib/golang/filters/http/source/go/pkg/http/shim.go +++ b/contrib/golang/filters/http/source/go/pkg/http/shim.go @@ -266,7 +266,13 @@ func envoyGoFilterOnHttpData(s *C.processState, endStream, buffer, length uint64 } //export envoyGoFilterOnHttpLog -func envoyGoFilterOnHttpLog(r *C.httpRequest, logType uint64) { +func envoyGoFilterOnHttpLog(r *C.httpRequest, logType uint64, + decodingStateWrapper *C.processState, encodingStateWrapper *C.processState, + reqHeaderNum, reqHeaderBytes, reqTrailerNum, reqTrailerBytes, + respHeaderNum, respHeaderBytes, respTrailerNum, respTrailerBytes uint64) { + + decodingState := getOrCreateState(decodingStateWrapper) + encodingState := getOrCreateState(encodingStateWrapper) req := getRequest(r) if req == nil { req = createRequest(r) @@ -276,14 +282,67 @@ func envoyGoFilterOnHttpLog(r *C.httpRequest, logType uint64) { v := api.AccessLogType(logType) + // Request headers must exist because the HTTP filter won't be run if the headers are + // not sent yet. + // TODO: make the headers/trailers read-only + reqHeader := &requestHeaderMapImpl{ + requestOrResponseHeaderMapImpl{ + headerMapImpl{ + state: decodingState, + headerNum: reqHeaderNum, + headerBytes: reqHeaderBytes, + }, + }, + } + + var reqTrailer api.RequestTrailerMap + if reqTrailerNum != 0 { + reqTrailer = &requestTrailerMapImpl{ + requestOrResponseTrailerMapImpl{ + headerMapImpl{ + state: decodingState, + headerNum: reqTrailerNum, + headerBytes: reqTrailerBytes, + }, + }, + } + } + + var respHeader api.ResponseHeaderMap + if respHeaderNum != 0 { + respHeader = &responseHeaderMapImpl{ + requestOrResponseHeaderMapImpl{ + headerMapImpl{ + state: encodingState, + headerNum: respHeaderNum, + headerBytes: respHeaderBytes, + }, + }, + } + } + + var respTrailer api.ResponseTrailerMap + if respTrailerNum != 0 { + respTrailer = &responseTrailerMapImpl{ + requestOrResponseTrailerMapImpl{ + headerMapImpl{ + state: encodingState, + headerNum: respTrailerNum, + headerBytes: respTrailerBytes, + }, + }, + } + } + f := req.httpFilter + switch v { - case api.AccessLogDownstreamStart: - f.OnLogDownstreamStart() - case api.AccessLogDownstreamPeriodic: - f.OnLogDownstreamPeriodic() case api.AccessLogDownstreamEnd: - f.OnLog() + f.OnLog(reqHeader, reqTrailer, respHeader, respTrailer) + case api.AccessLogDownstreamPeriodic: + f.OnLogDownstreamPeriodic(reqHeader, reqTrailer, respHeader, respTrailer) + case api.AccessLogDownstreamStart: + f.OnLogDownstreamStart(reqHeader) default: api.LogErrorf("access log type %d is not supported yet", logType) } diff --git a/contrib/golang/filters/http/source/golang_filter.cc b/contrib/golang/filters/http/source/golang_filter.cc index 486d16da589d..067e1daa5d41 100644 --- a/contrib/golang/filters/http/source/golang_filter.cc +++ b/contrib/golang/filters/http/source/golang_filter.cc @@ -77,6 +77,8 @@ Http::FilterTrailersStatus Filter::decodeTrailers(Http::RequestTrailerMap& trail ProcessorState& state = decoding_state_; ENVOY_LOG(debug, "golang filter decodeTrailers, decoding state: {}", state.stateStr()); + request_trailers_ = &trailers; + bool done = doTrailer(state, trailers); return done ? Http::FilterTrailersStatus::Continue : Http::FilterTrailersStatus::StopIteration; @@ -91,10 +93,10 @@ Http::FilterHeadersStatus Filter::encodeHeaders(Http::ResponseHeaderMap& headers activation_response_headers_ = dynamic_cast(&headers); // NP: may enter encodeHeaders in any state, - // since other filters or filtermanager could call encodeHeaders or sendLocalReply in any time. - // eg. filtermanager may invoke sendLocalReply, when scheme is invalid, - // with "Sending local reply with details // http1.invalid_scheme" details. - // This means DecodeXXX & EncodeXXX may run concurrently in Golang side. + // since other filters or filtermanager could call encodeHeaders or sendLocalReply in any + // time. eg. filtermanager may invoke sendLocalReply, when scheme is invalid, with "Sending + // local reply with details // http1.invalid_scheme" details. This means DecodeXXX & EncodeXXX + // may run concurrently in Golang side. bool done = doHeaders(encoding_state_, headers, end_stream); @@ -159,6 +161,18 @@ void Filter::onDestroy() { // access_log is executed before the log of the stream filter void Filter::log(const Formatter::HttpFormatterContext& log_context, const StreamInfo::StreamInfo&) { + uint64_t req_header_num = 0; + uint64_t req_header_bytes = 0; + uint64_t req_trailer_num = 0; + uint64_t req_trailer_bytes = 0; + uint64_t resp_header_num = 0; + uint64_t resp_header_bytes = 0; + uint64_t resp_trailer_num = 0; + uint64_t resp_trailer_bytes = 0; + + auto decoding_state = dynamic_cast(&decoding_state_); + auto encoding_state = dynamic_cast(&encoding_state_); + // `log` may be called multiple times with different log type switch (log_context.accessLogType()) { case Envoy::AccessLog::AccessLogType::DownstreamStart: @@ -166,14 +180,42 @@ void Filter::log(const Formatter::HttpFormatterContext& log_context, case Envoy::AccessLog::AccessLogType::DownstreamEnd: // log called by AccessLogDownstreamStart will happen before doHeaders if (initRequest()) { - request_headers_ = static_cast( - const_cast(&log_context.requestHeaders())); + request_headers_ = const_cast(&log_context.requestHeaders()); + } + + if (request_headers_ != nullptr) { + req_header_num = request_headers_->size(); + req_header_bytes = request_headers_->byteSize(); + decoding_state_.headers = request_headers_; + } + + if (request_trailers_ != nullptr) { + req_trailer_num = request_trailers_->size(); + req_trailer_bytes = request_trailers_->byteSize(); + decoding_state_.trailers = request_trailers_; + } + + activation_response_headers_ = &log_context.responseHeaders(); + if (activation_response_headers_ != nullptr) { + resp_header_num = activation_response_headers_->size(); + resp_header_bytes = activation_response_headers_->byteSize(); + encoding_state_.headers = const_cast(activation_response_headers_); + } + + activation_response_trailers_ = &log_context.responseTrailers(); + if (activation_response_trailers_ != nullptr) { + resp_trailer_num = activation_response_trailers_->size(); + resp_trailer_bytes = activation_response_trailers_->byteSize(); + encoding_state_.trailers = + const_cast(activation_response_trailers_); } - // This only run in the work thread, it's safe even without lock. - is_golang_processing_log_ = true; - dynamic_lib_->envoyGoFilterOnHttpLog(req_, int(log_context.accessLogType())); - is_golang_processing_log_ = false; + req_->is_golang_processing_log = 1; + dynamic_lib_->envoyGoFilterOnHttpLog(req_, int(log_context.accessLogType()), decoding_state, + encoding_state, req_header_num, req_header_bytes, + req_trailer_num, req_trailer_bytes, resp_header_num, + resp_header_bytes, resp_trailer_num, resp_trailer_bytes); + req_->is_golang_processing_log = 0; break; default: // skip calling with unsupported log types @@ -1127,7 +1169,7 @@ CAPIStatus Filter::getStringProperty(absl::string_view path, uint64_t* value_dat } // to access the headers_ and its friends we need to hold the lock - activation_request_headers_ = dynamic_cast(request_headers_); + activation_request_headers_ = request_headers_; if (isThreadSafe()) { return getStringPropertyCommon(path, value_data, value_len); diff --git a/contrib/golang/filters/http/source/golang_filter.h b/contrib/golang/filters/http/source/golang_filter.h index f3a9a461c7e6..763c94934e80 100644 --- a/contrib/golang/filters/http/source/golang_filter.h +++ b/contrib/golang/filters/http/source/golang_filter.h @@ -293,8 +293,7 @@ class Filter : public Http::StreamFilter, GoInt32* rc); bool isProcessingInGo() { - return is_golang_processing_log_ || decoding_state_.isProcessingInGo() || - encoding_state_.isProcessingInGo(); + return decoding_state_.isProcessingInGo() || encoding_state_.isProcessingInGo(); } void deferredDeleteRequest(HttpRequestInternal* req); @@ -346,7 +345,8 @@ class Filter : public Http::StreamFilter, // save temp values for fetching request attributes in the later phase, // like getting request size - Http::RequestOrResponseHeaderMap* request_headers_{nullptr}; + Http::RequestHeaderMap* request_headers_{nullptr}; + Http::RequestTrailerMap* request_trailers_{nullptr}; HttpRequestInternal* req_{nullptr}; @@ -360,8 +360,6 @@ class Filter : public Http::StreamFilter, // back from go). Thread::MutexBasicLockable mutex_{}; bool has_destroyed_ ABSL_GUARDED_BY(mutex_){false}; - - bool is_golang_processing_log_{false}; }; struct httpConfigInternal : httpConfig { diff --git a/contrib/golang/filters/http/source/processor_state.h b/contrib/golang/filters/http/source/processor_state.h index d537d68a327c..9c030040561e 100644 --- a/contrib/golang/filters/http/source/processor_state.h +++ b/contrib/golang/filters/http/source/processor_state.h @@ -95,7 +95,7 @@ class ProcessorState : public processState, public Logger::Loggableis_golang_processing_log; } bool isProcessingHeader() { return filterState() == FilterState::ProcessingHeader; } diff --git a/contrib/golang/filters/http/test/golang_filter_fuzz_test.cc b/contrib/golang/filters/http/test/golang_filter_fuzz_test.cc index c34b4cc1accb..cc324d410482 100644 --- a/contrib/golang/filters/http/test/golang_filter_fuzz_test.cc +++ b/contrib/golang/filters/http/test/golang_filter_fuzz_test.cc @@ -54,8 +54,10 @@ DEFINE_PROTO_FUZZER(const envoy::extensions::filters::http::golang::GolangFilter .WillByDefault(Return(static_cast(GolangStatus::Continue))); ON_CALL(*dso_lib.get(), envoyGoFilterOnHttpData(_, _, _, _)) .WillByDefault(Return(static_cast(GolangStatus::Continue))); - ON_CALL(*dso_lib.get(), envoyGoFilterOnHttpLog(_, _)) - .WillByDefault(Invoke([&](httpRequest*, int) -> void {})); + ON_CALL(*dso_lib.get(), envoyGoFilterOnHttpLog(_, _, _, _, _, _, _, _, _, _, _, _)) + .WillByDefault( + Invoke([&](httpRequest*, int, processState*, processState*, GoUint64, GoUint64, GoUint64, + GoUint64, GoUint64, GoUint64, GoUint64, GoUint64) -> void {})); ON_CALL(*dso_lib.get(), envoyGoFilterOnHttpDestroy(_, _)) .WillByDefault(Invoke([&](httpRequest* p0, int) -> void { // delete the filter->req_, make LeakSanitizer happy. diff --git a/contrib/golang/filters/http/test/golang_integration_test.cc b/contrib/golang/filters/http/test/golang_integration_test.cc index cddf317e9368..a1af5a0e40bb 100644 --- a/contrib/golang/filters/http/test/golang_integration_test.cc +++ b/contrib/golang/filters/http/test/golang_integration_test.cc @@ -893,16 +893,19 @@ TEST_P(GolangIntegrationTest, AccessLog) { auto path = "/test"; codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http"))); Http::TestRequestHeaderMapImpl request_headers{ - {":method", "POST"}, - {":path", path}, - {":scheme", "http"}, - {":authority", "test.com"}, + {":method", "POST"}, {":path", path}, {":scheme", "http"}, + {":authority", "test.com"}, {"Referer", "r"}, }; auto encoder_decoder = codec_client_->startRequest(request_headers); Http::RequestEncoder& request_encoder = encoder_decoder.first; auto response = std::move(encoder_decoder.second); - codec_client_->sendData(request_encoder, "helloworld", true); + codec_client_->sendData(request_encoder, "helloworld", false); + + Http::TestRequestTrailerMapImpl request_trailers{ + {"x-trailer", "foo"}, + }; + codec_client_->sendTrailers(request_encoder, request_trailers); waitForNextUpstreamRequest(); @@ -913,7 +916,10 @@ TEST_P(GolangIntegrationTest, AccessLog) { Buffer::OwnedImpl response_data1("good"); upstream_request_->encodeData(response_data1, false); Buffer::OwnedImpl response_data2("bye"); - upstream_request_->encodeData(response_data2, true); + upstream_request_->encodeData(response_data2, false); + + Http::TestResponseTrailerMapImpl response_trailers{{"x-trailer", "bar"}}; + upstream_request_->encodeTrailers(response_trailers); ASSERT_TRUE(response->waitForEndStream()); codec_client_->close(); @@ -927,6 +933,8 @@ TEST_P(GolangIntegrationTest, AccessLog) { EXPECT_EQ("206", getHeader(upstream_request_->headers(), "respCode")); EXPECT_EQ("7", getHeader(upstream_request_->headers(), "respSize")); EXPECT_EQ("true", getHeader(upstream_request_->headers(), "canRunAsyncly")); + EXPECT_EQ("foo", getHeader(upstream_request_->headers(), "x-req-trailer")); + EXPECT_EQ("bar", getHeader(upstream_request_->headers(), "x-resp-trailer")); cleanup(); } diff --git a/contrib/golang/filters/http/test/test_data/access_log/filter.go b/contrib/golang/filters/http/test/test_data/access_log/filter.go index 8b7e8233f630..5713b36d1082 100644 --- a/contrib/golang/filters/http/test/test_data/access_log/filter.go +++ b/contrib/golang/filters/http/test/test_data/access_log/filter.go @@ -22,6 +22,9 @@ var ( canRunAsynclyForDownstreamPeriodic bool referers = []string{} + + xReqTrailer string + xRespTrailer string ) type filter struct { @@ -61,18 +64,26 @@ func (f *filter) DecodeHeaders(header api.RequestHeaderMap, endStream bool) api. // the counter will be 0 when this request is ended counter = -1 + header.Set("x-req-trailer", xReqTrailer) + header.Set("x-resp-trailer", xRespTrailer) } return api.Continue } -func (f *filter) OnLogDownstreamStart() { +func (f *filter) OnLogDownstreamStart(reqHeader api.RequestHeaderMap) { referer, err := f.callbacks.GetProperty("request.referer") if err != nil { api.LogErrorf("err: %s", err) return } + refererFromHdr, _ := reqHeader.Get("referer") + if referer != refererFromHdr { + api.LogErrorf("referer from property: %s, referer from header: %s", referer, refererFromHdr) + return + } + referers = append(referers, referer) wg.Add(1) @@ -83,13 +94,19 @@ func (f *filter) OnLogDownstreamStart() { }() } -func (f *filter) OnLogDownstreamPeriodic() { +func (f *filter) OnLogDownstreamPeriodic(reqHeader api.RequestHeaderMap, reqTrailer api.RequestTrailerMap, respHeader api.ResponseHeaderMap, respTrailer api.ResponseTrailerMap) { referer, err := f.callbacks.GetProperty("request.referer") if err != nil { api.LogErrorf("err: %s", err) return } + refererFromHdr, _ := reqHeader.Get("referer") + if referer != refererFromHdr { + api.LogErrorf("referer from property: %s, referer from header: %s", referer, refererFromHdr) + return + } + referers = append(referers, referer) wg.Add(1) @@ -100,13 +117,40 @@ func (f *filter) OnLogDownstreamPeriodic() { }() } -func (f *filter) OnLog() { +func (f *filter) OnLog(reqHeader api.RequestHeaderMap, reqTrailer api.RequestTrailerMap, respHeader api.ResponseHeaderMap, respTrailer api.ResponseTrailerMap) { + referer, err := f.callbacks.GetProperty("request.referer") + if err != nil { + api.LogErrorf("err: %s", err) + return + } + + refererFromHdr, _ := reqHeader.Get("referer") + if referer != refererFromHdr { + api.LogErrorf("referer from property: %s, referer from header: %s", referer, refererFromHdr) + return + } + + if reqTrailer != nil { + xReqTrailer, _ = reqTrailer.Get("x-trailer") + } + code, ok := f.callbacks.StreamInfo().ResponseCode() if !ok { return } respCode = strconv.Itoa(int(code)) api.LogCritical(respCode) + + status, _ := respHeader.Get(":status") + if status != respCode { + api.LogErrorf("status from StreamInfo: %s, status from header: %s", respCode, status) + return + } + + if respTrailer != nil { + xRespTrailer, _ = respTrailer.Get("x-trailer") + } + size, err := f.callbacks.GetProperty("response.size") if err != nil { api.LogErrorf("err: %s", err) diff --git a/contrib/golang/filters/http/test/test_data/basic/filter.go b/contrib/golang/filters/http/test/test_data/basic/filter.go index 680980979cef..ca1c8dadf0da 100644 --- a/contrib/golang/filters/http/test/test_data/basic/filter.go +++ b/contrib/golang/filters/http/test/test_data/basic/filter.go @@ -546,7 +546,7 @@ func (f *filter) EncodeTrailers(trailers api.ResponseTrailerMap) api.StatusType } } -func (f *filter) OnLog() { +func (f *filter) OnLog(reqHeader api.RequestHeaderMap, reqTrailer api.RequestTrailerMap, respHeader api.ResponseHeaderMap, respTrailer api.ResponseTrailerMap) { api.LogError("call log in OnLog") } diff --git a/contrib/golang/filters/http/test/test_data/property/filter.go b/contrib/golang/filters/http/test/test_data/property/filter.go index 9f77dd5382f9..e61df65f2392 100644 --- a/contrib/golang/filters/http/test/test_data/property/filter.go +++ b/contrib/golang/filters/http/test/test_data/property/filter.go @@ -129,7 +129,7 @@ func (f *filter) EncodeData(buffer api.BufferInstance, endStream bool) api.Statu return api.Continue } -func (f *filter) OnLog() { +func (f *filter) OnLog(reqHeader api.RequestHeaderMap, reqTrailer api.RequestTrailerMap, respHeader api.ResponseHeaderMap, respTrailer api.ResponseTrailerMap) { f.assertProperty("response.size", "7") // "goodbye" // panic if any condition is not met From ad805fa30ff37eef5133713b876449de06f638c2 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Wed, 14 Aug 2024 12:47:41 -0500 Subject: [PATCH 108/130] mobile: Remove unused test file (#35709) Risk Level: low (unused file) Testing: n/a Docs Changes: n/a Release Notes: n/a Platform Specific Features: n/a Signed-off-by: Fredy Wijaya --- mobile/test/kotlin/integration/BUILD | 12 ----- .../test/kotlin/integration/TestUtilities.kt | 52 ------------------- 2 files changed, 64 deletions(-) delete mode 100644 mobile/test/kotlin/integration/TestUtilities.kt diff --git a/mobile/test/kotlin/integration/BUILD b/mobile/test/kotlin/integration/BUILD index 4cb12437f196..9472c9806dab 100644 --- a/mobile/test/kotlin/integration/BUILD +++ b/mobile/test/kotlin/integration/BUILD @@ -1,6 +1,5 @@ load("@envoy//bazel:envoy_build_system.bzl", "envoy_mobile_package") load("@envoy_mobile//bazel:envoy_mobile_android_test.bzl", "envoy_mobile_android_test") -load("@io_bazel_rules_kotlin//kotlin:android.bzl", "kt_android_library") licenses(["notice"]) # Apache 2 @@ -372,14 +371,3 @@ envoy_mobile_android_test( "//library/kotlin/io/envoyproxy/envoymobile:envoy_lib", ], ) - -kt_android_library( - name = "test_utilities", - srcs = [ - "TestUtilities.kt", - ], - deps = [ - "//library/kotlin/io/envoyproxy/envoymobile:envoy_interfaces_lib", - "@maven//:junit_junit", - ], -) diff --git a/mobile/test/kotlin/integration/TestUtilities.kt b/mobile/test/kotlin/integration/TestUtilities.kt deleted file mode 100644 index acb78ab2870e..000000000000 --- a/mobile/test/kotlin/integration/TestUtilities.kt +++ /dev/null @@ -1,52 +0,0 @@ -package test.kotlin.integration - -import io.envoyproxy.envoymobile.Engine -import java.time.Instant -import java.util.concurrent.TimeUnit -import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds -import kotlin.time.DurationUnit -import org.junit.Assert.fail - -/** Gets the stats in the form of `Map`. */ -fun Engine.getStats(): Map { - // `dumpStats()` produces the following format: - // key1: value1 - // key2: value2 - // key3: value3 - // ... - val lines = dumpStats().split("\n") - return lines - .mapNotNull { - val keyValue = it.split(": ") - if (keyValue.size == 2) { - Pair(keyValue[0], keyValue[1]) - } else { - null - } - } - .toMap() -} - -/** - * Waits for 5 seconds (default) until the stat of the given [name] is greater than equal the - * specified [expectedValue]. - * - * @throws java.lang.AssertionError throw when the the operation timed out waiting for the stat - * value to be greater than the [expectedValue]. - */ -fun Engine.waitForStatGe( - name: String, - expectedValue: Long, - timeout: Duration = 5.seconds, -) { - val waitTime = Instant.now().plusMillis(timeout.toLong(DurationUnit.MILLISECONDS)) - var currentValue = getStats()[name] - while (currentValue == null || currentValue.toLong() < expectedValue) { - if (Instant.now() > waitTime) { - fail("Timed out waiting for $name to be greater than equal $expectedValue") - } - TimeUnit.MILLISECONDS.sleep(10) - currentValue = getStats()[name] - } -} From bf270142edd04d91d747864d85b4c0e731668b23 Mon Sep 17 00:00:00 2001 From: ohadvano <49730675+ohadvano@users.noreply.github.com> Date: Thu, 15 Aug 2024 01:29:12 +0300 Subject: [PATCH 109/130] udp_session_filters: move and rename interfaces (#35665) Additional Description: Step 1 in support of ECDS with UDP session filters. This PR moves UDP session filter interfaces to core code, later to be consumed by new ConfigProviderManager. Includes required renaming to interfaces and objects Risk Level: low Testing: unit test Docs Changes: none Release Notes: none Platform Specific Features: none --------- Signed-off-by: Ohad Vano --- envoy/network/filter.h | 211 ++++++++++++++++ envoy/server/filter_config.h | 23 ++ source/extensions/filters/udp/udp_proxy/BUILD | 3 +- .../filters/udp/udp_proxy/config.cc | 6 +- .../extensions/filters/udp/udp_proxy/config.h | 12 +- .../udp/udp_proxy/session_filters/BUILD | 27 +-- .../dynamic_forward_proxy/BUILD | 1 - .../dynamic_forward_proxy/config.cc | 2 +- .../dynamic_forward_proxy/config.h | 1 + .../dynamic_forward_proxy/proxy_filter.h | 5 +- .../udp_proxy/session_filters/factory_base.h | 7 +- .../udp/udp_proxy/session_filters/filter.h | 226 ------------------ .../udp_proxy/session_filters/filter_config.h | 43 ---- .../session_filters/http_capsule/BUILD | 1 - .../session_filters/http_capsule/config.cc | 2 +- .../session_filters/http_capsule/config.h | 1 + .../http_capsule/http_capsule.h | 9 +- .../session_filters/pass_through_filter.h | 10 +- .../filters/udp/udp_proxy/udp_proxy_filter.h | 17 +- test/extensions/filters/udp/udp_proxy/BUILD | 3 - test/extensions/filters/udp/udp_proxy/mocks.h | 5 +- .../udp/udp_proxy/session_filters/BUILD | 2 - .../udp_proxy/session_filters/buffer_filter.h | 9 +- .../session_filters/drainer_filter.h | 19 +- .../dynamic_forward_proxy/BUILD | 2 - .../dynamic_forward_proxy/dfp_setter.h | 7 +- .../udp/udp_proxy/udp_proxy_filter_test.cc | 6 + 27 files changed, 322 insertions(+), 338 deletions(-) delete mode 100644 source/extensions/filters/udp/udp_proxy/session_filters/filter.h delete mode 100644 source/extensions/filters/udp/udp_proxy/session_filters/filter_config.h diff --git a/envoy/network/filter.h b/envoy/network/filter.h index dba97018b9ff..cf536e8ee06f 100644 --- a/envoy/network/filter.h +++ b/envoy/network/filter.h @@ -647,6 +647,217 @@ class UdpListenerFilterManager { using UdpListenerFilterFactoryCb = std::function; +/** + * Common interface for UdpSessionReadFilterCallbacks and UdpSessionWriteFilterCallbacks. + */ +class UdpSessionFilterCallbacks { +public: + virtual ~UdpSessionFilterCallbacks() = default; + + /** + * @return uint64_t the ID of the originating UDP session. + */ + virtual uint64_t sessionId() const PURE; + + /** + * @return StreamInfo for logging purposes. + */ + virtual StreamInfo::StreamInfo& streamInfo() PURE; + + /** + * Allows a filter to inject a datagram to successive filters in the session filter chain. + * The injected datagram will be iterated as a regular received datagram, and may also be + * stopped by further filters. This can be used, for example, to continue processing previously + * buffered datagrams by a filter after an asynchronous operation ended. + */ + virtual void injectDatagramToFilterChain(Network::UdpRecvData& data) PURE; +}; + +class UdpSessionReadFilterCallbacks : public UdpSessionFilterCallbacks { +public: + ~UdpSessionReadFilterCallbacks() override = default; + + /** + * If a read filter stopped filter iteration, continueFilterChain() can be called to continue the + * filter chain. It will have onNewSession() called if it was not previously called. + * @return false if the session is removed and no longer valid, otherwise returns true. + */ + virtual bool continueFilterChain() PURE; +}; + +class UdpSessionWriteFilterCallbacks : public UdpSessionFilterCallbacks {}; + +class UdpSessionFilterBase { +public: + virtual ~UdpSessionFilterBase() = default; + + /** + * This routine is called before the access log handlers' final log() is called. Filters can use + * this callback to enrich the data passed in to the log handlers. + */ + void onSessionComplete() { + if (!on_session_complete_already_called_) { + onSessionCompleteInternal(); + on_session_complete_already_called_ = true; + } + } + +protected: + /** + * This routine is called by onSessionComplete to enrich the data passed in to the log handlers. + */ + virtual void onSessionCompleteInternal() { ASSERT(!on_session_complete_already_called_); } + +private: + bool on_session_complete_already_called_{false}; +}; + +/** + * Return codes for read filter invocations. + */ +enum class UdpSessionReadFilterStatus { + // Continue to further session filters. + Continue, + // Stop executing further session filters. + StopIteration, +}; + +/** + * Session read filter interface. + */ +class UdpSessionReadFilter : public virtual UdpSessionFilterBase { +public: + ~UdpSessionReadFilter() override = default; + + /** + * Called when a new UDP session is first established. Filters should do one time long term + * processing that needs to be done when a session is established. Filter chain iteration + * can be stopped if needed. + * @return status used by the filter manager to manage further filter iteration. + */ + virtual UdpSessionReadFilterStatus onNewSession() PURE; + + /** + * Called when UDP datagram is read and matches the session that manages the filter. + * @param data supplies the read data which may be modified. + * @return status used by the filter manager to manage further filter iteration. + */ + virtual UdpSessionReadFilterStatus onData(Network::UdpRecvData& data) PURE; + + /** + * Initializes the read filter callbacks used to interact with the filter manager. It will be + * called by the filter manager a single time when the filter is first registered. + * + * IMPORTANT: No outbound networking or complex processing should be done in this function. + * That should be done in the context of onNewSession() if needed. + * + * @param callbacks supplies the callbacks. + */ + virtual void initializeReadFilterCallbacks(UdpSessionReadFilterCallbacks& callbacks) PURE; +}; + +using UdpSessionReadFilterSharedPtr = std::shared_ptr; + +/** + * Return codes for write filter invocations. + */ +enum class UdpSessionWriteFilterStatus { + // Continue to further session filters. + Continue, + // Stop executing further session filters. + StopIteration, +}; + +/** + * Session write filter interface. + */ +class UdpSessionWriteFilter : public virtual UdpSessionFilterBase { +public: + ~UdpSessionWriteFilter() override = default; + + /** + * Called when data is to be written on the UDP session. + * @param data supplies the buffer to be written which may be modified. + * @return status used by the filter manager to manage further filter iteration. + */ + virtual UdpSessionWriteFilterStatus onWrite(Network::UdpRecvData& data) PURE; + + /** + * Initializes the write filter callbacks used to interact with the filter manager. It will be + * called by the filter manager a single time when the filter is first registered. + * + * IMPORTANT: No outbound networking or complex processing should be done in this function. + * That should be done in the context of ReadFilter::onNewSession() if needed. + * + * @param callbacks supplies the callbacks. + */ + virtual void initializeWriteFilterCallbacks(UdpSessionWriteFilterCallbacks& callbacks) PURE; +}; + +using UdpSessionWriteFilterSharedPtr = std::shared_ptr; + +/** + * A combination read and write filter. This allows a single filter instance to cover + * both the read and write paths. + */ +class UdpSessionFilter : public virtual UdpSessionReadFilter, + public virtual UdpSessionWriteFilter {}; +using UdpSessionFilterSharedPtr = std::shared_ptr; + +/** + * These callbacks are provided by the UDP session manager to the factory so that the factory + * can * build the filter chain in an application specific way. + */ +class UdpSessionFilterChainFactoryCallbacks { +public: + virtual ~UdpSessionFilterChainFactoryCallbacks() = default; + + /** + * Add a read filter that is used when reading UDP session data. + * @param filter supplies the filter to add. + */ + virtual void addReadFilter(UdpSessionReadFilterSharedPtr filter) PURE; + + /** + * Add a write filter that is used when writing UDP session data. + * @param filter supplies the filter to add. + */ + virtual void addWriteFilter(UdpSessionWriteFilterSharedPtr filter) PURE; + + /** + * Add a bidirectional filter that is used when reading and writing UDP session data. + * @param filter supplies the filter to add. + */ + virtual void addFilter(UdpSessionFilterSharedPtr filter) PURE; +}; + +/** + * This function is used to wrap the creation of a UDP session filter chain for new sessions as they + * come in. Filter factories create the function at configuration initialization time, and then + * they are used at runtime. + * @param callbacks supplies the callbacks for the stream to install filters to. Typically the + * function will install a single filter, but it's technically possibly to install more than one + * if desired. + */ +using UdpSessionFilterFactoryCb = + std::function; + +/** + * A UdpSessionFilterChainFactory is used by a UDP session manager to create a UDP session filter + * chain when a new session is created. + */ +class UdpSessionFilterChainFactory { +public: + virtual ~UdpSessionFilterChainFactory() = default; + + /** + * Called when a new UDP session is created. + * @param callbacks supplies the "sink" that is used for actually creating the filter chain. @see + * UdpSessionFilterChainFactoryCallbacks. + */ + virtual void createFilterChain(UdpSessionFilterChainFactoryCallbacks& callbacks) const PURE; +}; + /** * Creates a chain of network filters for a new connection. */ diff --git a/envoy/server/filter_config.h b/envoy/server/filter_config.h index 099101409d91..3b09178513c1 100644 --- a/envoy/server/filter_config.h +++ b/envoy/server/filter_config.h @@ -76,6 +76,29 @@ class NamedUdpListenerFilterConfigFactory : public ListenerFilterConfigFactoryBa std::string category() const override { return "envoy.filters.udp_listener"; } }; +/** + * Implemented by each UDP session filter and registered via Registry::registerFactory or the + * convenience class RegisterFactory. + */ +class NamedUdpSessionFilterConfigFactory : public Envoy::Config::TypedFactory { +public: + ~NamedUdpSessionFilterConfigFactory() override = default; + + /** + * Create a particular UDP session filter factory implementation. If the implementation is + * unable to produce a factory with the provided parameters, it should throw an EnvoyException + * in the case of general error. The returned callback should always be initialized. + * @param config supplies the configuration for the filter + * @param context supplies the filter's context. + * @return UdpSessionFilterFactoryCb the factory creation function. + */ + virtual Network::UdpSessionFilterFactoryCb + createFilterFactoryFromProto(const Protobuf::Message& config, + Server::Configuration::FactoryContext& context) PURE; + + std::string category() const override { return "envoy.filters.udp.session"; } +}; + /** * Implemented by each QUIC listener filter and registered via Registry::registerFactory() * or the convenience class RegisterFactory. diff --git a/source/extensions/filters/udp/udp_proxy/BUILD b/source/extensions/filters/udp/udp_proxy/BUILD index ba70f7ca39fe..dbc00fe8af00 100644 --- a/source/extensions/filters/udp/udp_proxy/BUILD +++ b/source/extensions/filters/udp/udp_proxy/BUILD @@ -48,8 +48,6 @@ envoy_cc_library( "//source/common/stream_info:stream_info_lib", "//source/common/upstream:load_balancer_context_base_lib", "//source/extensions/filters/udp/udp_proxy/router:router_lib", - "//source/extensions/filters/udp/udp_proxy/session_filters:filter_config_interface", - "//source/extensions/filters/udp/udp_proxy/session_filters:filter_interface", "@envoy_api//envoy/config/accesslog/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/udp/udp_proxy/v3:pkg_cc_proto", ], @@ -64,6 +62,7 @@ envoy_cc_extension( ":udp_proxy_filter_lib", "//envoy/registry", "//envoy/server:filter_config_interface", + "//source/common/filter:config_discovery_lib", "//source/common/formatter:substitution_format_string_lib", "@envoy_api//envoy/extensions/filters/udp/udp_proxy/v3:pkg_cc_proto", ], diff --git a/source/extensions/filters/udp/udp_proxy/config.cc b/source/extensions/filters/udp/udp_proxy/config.cc index 58dec2bb53a0..1fee7deedc54 100644 --- a/source/extensions/filters/udp/udp_proxy/config.cc +++ b/source/extensions/filters/udp/udp_proxy/config.cc @@ -145,10 +145,12 @@ UdpProxyFilterConfigImpl::UdpProxyFilterConfigImpl( MessageUtil::getJsonStringFromMessageOrError( static_cast(filter.typed_config()), true)); - auto& factory = Config::Utility::getAndCheckFactory(filter); + auto& factory = Config::Utility::getAndCheckFactory< + Server::Configuration::NamedUdpSessionFilterConfigFactory>(filter); ProtobufTypes::MessagePtr message = Envoy::Config::Utility::translateToFactoryConfig( filter, context.messageValidationVisitor(), factory); - FilterFactoryCb callback = factory.createFilterFactoryFromProto(*message, context); + Network::UdpSessionFilterFactoryCb callback = + factory.createFilterFactoryFromProto(*message, context); filter_factories_.push_back(callback); } } diff --git a/source/extensions/filters/udp/udp_proxy/config.h b/source/extensions/filters/udp/udp_proxy/config.h index 29793bf6d2a4..3ecc10711c6d 100644 --- a/source/extensions/filters/udp/udp_proxy/config.h +++ b/source/extensions/filters/udp/udp_proxy/config.h @@ -107,7 +107,7 @@ class TunnelingConfigImpl : public UdpTunnelingConfig { }; class UdpProxyFilterConfigImpl : public UdpProxyFilterConfig, - public FilterChainFactory, + public UdpSessionFilterChainFactory, Logger::Loggable { public: UdpProxyFilterConfigImpl( @@ -138,7 +138,7 @@ class UdpProxyFilterConfigImpl : public UdpProxyFilterConfig, const std::vector& proxyAccessLogs() const override { return proxy_access_logs_; } - const FilterChainFactory& sessionFilterFactory() const override { return *this; }; + const UdpSessionFilterChainFactory& sessionFilterFactory() const override { return *this; }; bool hasSessionFilters() const override { return !filter_factories_.empty(); } const UdpTunnelingConfigPtr& tunnelingConfig() const override { return tunneling_config_; }; bool flushAccessLogOnTunnelConnected() const override { @@ -149,9 +149,9 @@ class UdpProxyFilterConfigImpl : public UdpProxyFilterConfig, } Random::RandomGenerator& randomGenerator() const override { return random_generator_; } - // FilterChainFactory - void createFilterChain(FilterChainFactoryCallbacks& callbacks) const override { - for (const FilterFactoryCb& factory : filter_factories_) { + // UdpSessionFilterChainFactory + void createFilterChain(Network::UdpSessionFilterChainFactoryCallbacks& callbacks) const override { + for (const Network::UdpSessionFilterFactoryCb& factory : filter_factories_) { factory(callbacks); } }; @@ -178,7 +178,7 @@ class UdpProxyFilterConfigImpl : public UdpProxyFilterConfig, std::vector session_access_logs_; std::vector proxy_access_logs_; UdpTunnelingConfigPtr tunneling_config_; - std::list filter_factories_; + std::list filter_factories_; Random::RandomGenerator& random_generator_; }; diff --git a/source/extensions/filters/udp/udp_proxy/session_filters/BUILD b/source/extensions/filters/udp/udp_proxy/session_filters/BUILD index 4cbdb01a5796..205b33895743 100644 --- a/source/extensions/filters/udp/udp_proxy/session_filters/BUILD +++ b/source/extensions/filters/udp/udp_proxy/session_filters/BUILD @@ -8,42 +8,17 @@ licenses(["notice"]) # Apache 2 envoy_extension_package() -envoy_cc_library( - name = "filter_config_interface", - hdrs = ["filter_config.h"], - deps = [ - ":filter_interface", - "//envoy/config:typed_config_interface", - "//envoy/server:filter_config_interface", - "//source/common/common:macros", - "//source/common/protobuf:cc_wkt_protos", - ], -) - envoy_cc_library( name = "factory_base_lib", hdrs = ["factory_base.h"], visibility = ["//visibility:public"], deps = [ - ":filter_config_interface", + "//envoy/server:filter_config_interface", "//source/common/protobuf:utility_lib", ], ) -envoy_cc_library( - name = "filter_interface", - hdrs = ["filter.h"], - visibility = ["//visibility:public"], - deps = [ - "//envoy/network:listener_interface", - "//envoy/stream_info:stream_info_interface", - ], -) - envoy_cc_library( name = "pass_through_filter_lib", hdrs = ["pass_through_filter.h"], - deps = [ - ":filter_interface", - ], ) diff --git a/source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/BUILD b/source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/BUILD index 2ddede80431e..79c4bfadb04c 100644 --- a/source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/BUILD +++ b/source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/BUILD @@ -19,7 +19,6 @@ envoy_cc_library( "//source/common/http:header_utility_lib", "//source/common/stream_info:uint32_accessor_lib", "//source/extensions/common/dynamic_forward_proxy:dns_cache_interface", - "//source/extensions/filters/udp/udp_proxy/session_filters:filter_interface", "@envoy_api//envoy/extensions/filters/udp/udp_proxy/session/dynamic_forward_proxy/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/config.cc b/source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/config.cc index 0a5062b4a2a0..e01357ba8c33 100644 --- a/source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/config.cc +++ b/source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/config.cc @@ -24,7 +24,7 @@ FilterFactoryCb DynamicForwardProxyNetworkFilterConfigFactory::createFilterFacto ProxyFilterConfigSharedPtr filter_config( std::make_shared(proto_config, cache_manager_factory, context)); - return [filter_config](FilterChainFactoryCallbacks& callbacks) -> void { + return [filter_config](Network::UdpSessionFilterChainFactoryCallbacks& callbacks) -> void { callbacks.addReadFilter(std::make_shared(filter_config)); }; } diff --git a/source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/config.h b/source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/config.h index 11d98c0eee97..ca2aad02f0ed 100644 --- a/source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/config.h +++ b/source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/config.h @@ -14,6 +14,7 @@ namespace DynamicForwardProxy { using FilterConfig = envoy::extensions::filters::udp::udp_proxy::session::dynamic_forward_proxy::v3::FilterConfig; +using FilterFactoryCb = Network::UdpSessionFilterFactoryCb; /** * Config registration for the dynamic_forward_proxy filter. @see diff --git a/source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/proxy_filter.h b/source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/proxy_filter.h index 64516fb9ba5a..b9c4a26e687e 100644 --- a/source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/proxy_filter.h +++ b/source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/proxy_filter.h @@ -7,7 +7,6 @@ #include "source/common/common/logger.h" #include "source/common/http/header_utility.h" #include "source/extensions/common/dynamic_forward_proxy/dns_cache.h" -#include "source/extensions/filters/udp/udp_proxy/session_filters/filter.h" namespace Envoy { namespace Extensions { @@ -63,6 +62,10 @@ using ProxyFilterConfigSharedPtr = std::shared_ptr; using BufferedDatagramPtr = std::unique_ptr; using LoadDnsCacheEntryStatus = Common::DynamicForwardProxy::DnsCache::LoadDnsCacheEntryStatus; +using ReadFilter = Network::UdpSessionReadFilter; +using ReadFilterStatus = Network::UdpSessionReadFilterStatus; +using ReadFilterCallbacks = Network::UdpSessionReadFilterCallbacks; + class ProxyFilter : public ReadFilter, public Extensions::Common::DynamicForwardProxy::DnsCache::LoadDnsCacheEntryCallbacks, diff --git a/source/extensions/filters/udp/udp_proxy/session_filters/factory_base.h b/source/extensions/filters/udp/udp_proxy/session_filters/factory_base.h index 9cbbc12d05bd..32d1bd457a7c 100644 --- a/source/extensions/filters/udp/udp_proxy/session_filters/factory_base.h +++ b/source/extensions/filters/udp/udp_proxy/session_filters/factory_base.h @@ -1,7 +1,8 @@ #pragma once +#include "envoy/server/filter_config.h" + #include "source/common/protobuf/utility.h" -#include "source/extensions/filters/udp/udp_proxy/session_filters/filter_config.h" namespace Envoy { namespace Extensions { @@ -9,6 +10,10 @@ namespace UdpFilters { namespace UdpProxy { namespace SessionFilters { +using FilterFactoryCb = Network::UdpSessionFilterFactoryCb; +using NamedUdpSessionFilterConfigFactory = + Server::Configuration::NamedUdpSessionFilterConfigFactory; + template class FactoryBase : public NamedUdpSessionFilterConfigFactory { public: FilterFactoryCb diff --git a/source/extensions/filters/udp/udp_proxy/session_filters/filter.h b/source/extensions/filters/udp/udp_proxy/session_filters/filter.h deleted file mode 100644 index 8cedad217b15..000000000000 --- a/source/extensions/filters/udp/udp_proxy/session_filters/filter.h +++ /dev/null @@ -1,226 +0,0 @@ -#pragma once - -#include "envoy/buffer/buffer.h" -#include "envoy/network/listener.h" -#include "envoy/stream_info/stream_info.h" - -namespace Envoy { -namespace Extensions { -namespace UdpFilters { -namespace UdpProxy { -namespace SessionFilters { - -/** - * Common interface for ReadFilterCallbacks and WriteFilterCallbacks. - */ -class FilterCallbacks { -public: - virtual ~FilterCallbacks() = default; - - /** - * @return uint64_t the ID of the originating UDP session. - */ - virtual uint64_t sessionId() const PURE; - - /** - * @return StreamInfo for logging purposes. - */ - virtual StreamInfo::StreamInfo& streamInfo() PURE; - - /** - * Allows a filter to inject a datagram to successive filters in the session filter chain. - * The injected datagram will be iterated as a regular received datagram, and may also be - * stopped by further filters. This can be used, for example, to continue processing previously - * buffered datagrams by a filter after an asynchronous operation ended. - */ - virtual void injectDatagramToFilterChain(Network::UdpRecvData& data) PURE; -}; - -class ReadFilterCallbacks : public FilterCallbacks { -public: - ~ReadFilterCallbacks() override = default; - - /** - * If a read filter stopped filter iteration, continueFilterChain() can be called to continue the - * filter chain. It will have onNewSession() called if it was not previously called. - * @return false if the session is removed and no longer valid, otherwise returns true. - */ - virtual bool continueFilterChain() PURE; -}; - -class WriteFilterCallbacks : public FilterCallbacks {}; - -/** - * Return codes for read filter invocations. - */ -enum class ReadFilterStatus { - // Continue to further session filters. - Continue, - // Stop executing further session filters. - StopIteration, -}; - -class FilterBase { -public: - virtual ~FilterBase() = default; - - /** - * This routine is called before the access log handlers' final log() is called. Filters can use - * this callback to enrich the data passed in to the log handlers. - */ - void onSessionComplete() { - if (!on_session_complete_already_called_) { - onSessionCompleteInternal(); - on_session_complete_already_called_ = true; - } - } - -protected: - /** - * This routine is called by onSessionComplete to enrich the data passed in to the log handlers. - */ - virtual void onSessionCompleteInternal() { ASSERT(!on_session_complete_already_called_); } - -private: - bool on_session_complete_already_called_{false}; -}; - -/** - * Session read filter interface. - */ -class ReadFilter : public virtual FilterBase { -public: - ~ReadFilter() override = default; - - /** - * Called when a new UDP session is first established. Filters should do one time long term - * processing that needs to be done when a session is established. Filter chain iteration - * can be stopped if needed. - * @return status used by the filter manager to manage further filter iteration. - */ - virtual ReadFilterStatus onNewSession() PURE; - - /** - * Called when UDP datagram is read and matches the session that manages the filter. - * @param data supplies the read data which may be modified. - * @return status used by the filter manager to manage further filter iteration. - */ - virtual ReadFilterStatus onData(Network::UdpRecvData& data) PURE; - - /** - * Initializes the read filter callbacks used to interact with the filter manager. It will be - * called by the filter manager a single time when the filter is first registered. - * - * IMPORTANT: No outbound networking or complex processing should be done in this function. - * That should be done in the context of onNewSession() if needed. - * - * @param callbacks supplies the callbacks. - */ - virtual void initializeReadFilterCallbacks(ReadFilterCallbacks& callbacks) PURE; -}; - -using ReadFilterSharedPtr = std::shared_ptr; - -/** - * Return codes for write filter invocations. - */ -enum class WriteFilterStatus { - // Continue to further session filters. - Continue, - // Stop executing further session filters. - StopIteration, -}; - -/** - * Session write filter interface. - */ -class WriteFilter : public virtual FilterBase { -public: - ~WriteFilter() override = default; - - /** - * Called when data is to be written on the UDP session. - * @param data supplies the buffer to be written which may be modified. - * @return status used by the filter manager to manage further filter iteration. - */ - virtual WriteFilterStatus onWrite(Network::UdpRecvData& data) PURE; - - /** - * Initializes the write filter callbacks used to interact with the filter manager. It will be - * called by the filter manager a single time when the filter is first registered. - * - * IMPORTANT: No outbound networking or complex processing should be done in this function. - * That should be done in the context of ReadFilter::onNewSession() if needed. - * - * @param callbacks supplies the callbacks. - */ - virtual void initializeWriteFilterCallbacks(WriteFilterCallbacks& callbacks) PURE; -}; - -using WriteFilterSharedPtr = std::shared_ptr; - -/** - * A combination read and write filter. This allows a single filter instance to cover - * both the read and write paths. - */ -class Filter : public virtual ReadFilter, public virtual WriteFilter {}; -using FilterSharedPtr = std::shared_ptr; - -/** - * These callbacks are provided by the UDP session manager to the factory so that the factory - * can * build the filter chain in an application specific way. - */ -class FilterChainFactoryCallbacks { -public: - virtual ~FilterChainFactoryCallbacks() = default; - - /** - * Add a read filter that is used when reading UDP session data. - * @param filter supplies the filter to add. - */ - virtual void addReadFilter(ReadFilterSharedPtr filter) PURE; - - /** - * Add a write filter that is used when writing UDP session data. - * @param filter supplies the filter to add. - */ - virtual void addWriteFilter(WriteFilterSharedPtr filter) PURE; - - /** - * Add a bidirectional filter that is used when reading and writing UDP session data. - * @param filter supplies the filter to add. - */ - virtual void addFilter(FilterSharedPtr filter) PURE; -}; - -/** - * This function is used to wrap the creation of a UDP session filter chain for new sessions as they - * come in. Filter factories create the function at configuration initialization time, and then - * they are used at runtime. - * @param callbacks supplies the callbacks for the stream to install filters to. Typically the - * function will install a single filter, but it's technically possibly to install more than one - * if desired. - */ -using FilterFactoryCb = std::function; - -/** - * A FilterChainFactory is used by a UDP session manager to create a UDP session filter chain when - * a new session is created. - */ -class FilterChainFactory { -public: - virtual ~FilterChainFactory() = default; - - /** - * Called when a new UDP session is created. - * @param callbacks supplies the "sink" that is used for actually creating the filter chain. @see - * FilterChainFactoryCallbacks. - */ - virtual void createFilterChain(FilterChainFactoryCallbacks& callbacks) const PURE; -}; - -} // namespace SessionFilters -} // namespace UdpProxy -} // namespace UdpFilters -} // namespace Extensions -} // namespace Envoy diff --git a/source/extensions/filters/udp/udp_proxy/session_filters/filter_config.h b/source/extensions/filters/udp/udp_proxy/session_filters/filter_config.h deleted file mode 100644 index 07b41415b554..000000000000 --- a/source/extensions/filters/udp/udp_proxy/session_filters/filter_config.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "envoy/config/typed_config.h" -#include "envoy/server/filter_config.h" - -#include "source/common/common/macros.h" -#include "source/common/protobuf/protobuf.h" -#include "source/extensions/filters/udp/udp_proxy/session_filters/filter.h" - -namespace Envoy { -namespace Extensions { -namespace UdpFilters { -namespace UdpProxy { -namespace SessionFilters { - -/** - * Implemented by each UDP session filter and registered via Registry::registerFactory or the - * convenience class RegisterFactory. - */ -class NamedUdpSessionFilterConfigFactory : public Envoy::Config::TypedFactory { -public: - ~NamedUdpSessionFilterConfigFactory() override = default; - - /** - * Create a particular UDP session filter factory implementation. If the implementation is - * unable to produce a factory with the provided parameters, it should throw an EnvoyException - * in the case of general error. The returned callback should always be initialized. - * @param config supplies the configuration for the filter - * @param context supplies the filter's context. - * @return FilterFactoryCb the factory creation function. - */ - virtual FilterFactoryCb - createFilterFactoryFromProto(const Protobuf::Message& config, - Server::Configuration::FactoryContext& context) PURE; - - std::string category() const override { return "envoy.filters.udp.session"; } -}; - -} // namespace SessionFilters -} // namespace UdpProxy -} // namespace UdpFilters -} // namespace Extensions -} // namespace Envoy diff --git a/source/extensions/filters/udp/udp_proxy/session_filters/http_capsule/BUILD b/source/extensions/filters/udp/udp_proxy/session_filters/http_capsule/BUILD index 264c6840f882..bc9302c3fff0 100644 --- a/source/extensions/filters/udp/udp_proxy/session_filters/http_capsule/BUILD +++ b/source/extensions/filters/udp/udp_proxy/session_filters/http_capsule/BUILD @@ -17,7 +17,6 @@ envoy_cc_library( "//source/common/buffer:buffer_lib", "//source/common/common:assert_lib", "//source/common/common:hex_lib", - "//source/extensions/filters/udp/udp_proxy/session_filters:filter_interface", "@com_github_google_quiche//:quiche_common_capsule_lib", "@com_github_google_quiche//:quiche_common_connect_udp_datagram_payload_lib", "@envoy_api//envoy/extensions/filters/udp/udp_proxy/session/http_capsule/v3:pkg_cc_proto", diff --git a/source/extensions/filters/udp/udp_proxy/session_filters/http_capsule/config.cc b/source/extensions/filters/udp/udp_proxy/session_filters/http_capsule/config.cc index 9623e87472b3..57e3bbbac64b 100644 --- a/source/extensions/filters/udp/udp_proxy/session_filters/http_capsule/config.cc +++ b/source/extensions/filters/udp/udp_proxy/session_filters/http_capsule/config.cc @@ -14,7 +14,7 @@ namespace HttpCapsule { FilterFactoryCb HttpCapsuleFilterConfigFactory::createFilterFactoryFromProtoTyped( const FilterConfig&, Server::Configuration::FactoryContext& context) { - return [&context](FilterChainFactoryCallbacks& callbacks) -> void { + return [&context](Network::UdpSessionFilterChainFactoryCallbacks& callbacks) -> void { callbacks.addFilter( std::make_shared(context.serverFactoryContext().timeSource())); }; diff --git a/source/extensions/filters/udp/udp_proxy/session_filters/http_capsule/config.h b/source/extensions/filters/udp/udp_proxy/session_filters/http_capsule/config.h index 3ac7f37a5911..37f06e642327 100644 --- a/source/extensions/filters/udp/udp_proxy/session_filters/http_capsule/config.h +++ b/source/extensions/filters/udp/udp_proxy/session_filters/http_capsule/config.h @@ -14,6 +14,7 @@ namespace HttpCapsule { using FilterConfig = envoy::extensions::filters::udp::udp_proxy::session::http_capsule::v3::FilterConfig; +using FilterFactoryCb = Network::UdpSessionFilterFactoryCb; /** * Config registration for the http_capsule filter. @see diff --git a/source/extensions/filters/udp/udp_proxy/session_filters/http_capsule/http_capsule.h b/source/extensions/filters/udp/udp_proxy/session_filters/http_capsule/http_capsule.h index 0b5dea6d60a6..f165fa60c66a 100644 --- a/source/extensions/filters/udp/udp_proxy/session_filters/http_capsule/http_capsule.h +++ b/source/extensions/filters/udp/udp_proxy/session_filters/http_capsule/http_capsule.h @@ -1,9 +1,10 @@ #pragma once #include "envoy/extensions/filters/udp/udp_proxy/session/http_capsule/v3/http_capsule.pb.h" +#include "envoy/network/filter.h" +#include "envoy/network/listener.h" #include "source/common/common/logger.h" -#include "source/extensions/filters/udp/udp_proxy/session_filters/filter.h" #include "quiche/common/capsule.h" #include "quiche/common/simple_buffer_allocator.h" @@ -15,6 +16,12 @@ namespace UdpProxy { namespace SessionFilters { namespace HttpCapsule { +using Filter = Network::UdpSessionFilter; +using ReadFilterStatus = Network::UdpSessionReadFilterStatus; +using WriteFilterStatus = Network::UdpSessionWriteFilterStatus; +using ReadFilterCallbacks = Network::UdpSessionReadFilterCallbacks; +using WriteFilterCallbacks = Network::UdpSessionWriteFilterCallbacks; + class HttpCapsuleFilter : public Filter, public quiche::CapsuleParser::Visitor, Logger::Loggable { diff --git a/source/extensions/filters/udp/udp_proxy/session_filters/pass_through_filter.h b/source/extensions/filters/udp/udp_proxy/session_filters/pass_through_filter.h index 846667475a62..3fd2789ec504 100644 --- a/source/extensions/filters/udp/udp_proxy/session_filters/pass_through_filter.h +++ b/source/extensions/filters/udp/udp_proxy/session_filters/pass_through_filter.h @@ -1,6 +1,6 @@ #pragma once -#include "source/extensions/filters/udp/udp_proxy/session_filters/filter.h" +#include "envoy/network/filter.h" namespace Envoy { namespace Extensions { @@ -8,6 +8,14 @@ namespace UdpFilters { namespace UdpProxy { namespace SessionFilters { +using Filter = Network::UdpSessionFilter; +using ReadFilter = Network::UdpSessionReadFilter; +using WriteFilter = Network::UdpSessionWriteFilter; +using ReadFilterStatus = Network::UdpSessionReadFilterStatus; +using WriteFilterStatus = Network::UdpSessionWriteFilterStatus; +using ReadFilterCallbacks = Network::UdpSessionReadFilterCallbacks; +using WriteFilterCallbacks = Network::UdpSessionWriteFilterCallbacks; + /** * Pass through UDP session read filter. Continue at each state within the series of * transitions, and pass through the read data. diff --git a/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.h b/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.h index 9ebcd93b5a45..245c3a40dc82 100644 --- a/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.h +++ b/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.h @@ -31,8 +31,6 @@ #include "source/common/upstream/load_balancer_context_base.h" #include "source/extensions/filters/udp/udp_proxy/hash_policy_impl.h" #include "source/extensions/filters/udp/udp_proxy/router/router_impl.h" -#include "source/extensions/filters/udp/udp_proxy/session_filters/filter.h" -#include "source/extensions/filters/udp/udp_proxy/session_filters/filter_config.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" @@ -42,8 +40,6 @@ namespace Extensions { namespace UdpFilters { namespace UdpProxy { -using namespace UdpProxy::SessionFilters; - /** * All UDP proxy downstream stats. @see stats_macros.h */ @@ -116,6 +112,8 @@ class UdpTunnelingConfig { using UdpTunnelingConfigPtr = std::unique_ptr; +using UdpSessionFilterChainFactory = Network::UdpSessionFilterChainFactory; + class UdpProxyFilterConfig { public: virtual ~UdpProxyFilterConfig() = default; @@ -133,7 +131,7 @@ class UdpProxyFilterConfig { virtual const Network::ResolvedUdpSocketConfig& upstreamSocketConfig() const PURE; virtual const std::vector& sessionAccessLogs() const PURE; virtual const std::vector& proxyAccessLogs() const PURE; - virtual const FilterChainFactory& sessionFilterFactory() const PURE; + virtual const UdpSessionFilterChainFactory& sessionFilterFactory() const PURE; virtual bool hasSessionFilters() const PURE; virtual const UdpTunnelingConfigPtr& tunnelingConfig() const PURE; virtual bool flushAccessLogOnTunnelConnected() const PURE; @@ -469,6 +467,15 @@ class TunnelingConnectionPoolFactory { using TunnelingConnectionPoolFactoryPtr = std::unique_ptr; +using FilterSharedPtr = Network::UdpSessionFilterSharedPtr; +using ReadFilterSharedPtr = Network::UdpSessionReadFilterSharedPtr; +using WriteFilterSharedPtr = Network::UdpSessionWriteFilterSharedPtr; +using ReadFilterCallbacks = Network::UdpSessionReadFilterCallbacks; +using WriteFilterCallbacks = Network::UdpSessionWriteFilterCallbacks; +using ReadFilterStatus = Network::UdpSessionReadFilterStatus; +using WriteFilterStatus = Network::UdpSessionWriteFilterStatus; +using FilterChainFactoryCallbacks = Network::UdpSessionFilterChainFactoryCallbacks; + class UdpProxyFilter : public Network::UdpListenerReadFilter, public Upstream::ClusterUpdateCallbacks, Logger::Loggable { diff --git a/test/extensions/filters/udp/udp_proxy/BUILD b/test/extensions/filters/udp/udp_proxy/BUILD index 1e28d7588b80..e785b5ca694c 100644 --- a/test/extensions/filters/udp/udp_proxy/BUILD +++ b/test/extensions/filters/udp/udp_proxy/BUILD @@ -19,7 +19,6 @@ envoy_extension_cc_mock( extension_names = ["envoy.filters.udp_listener.udp_proxy"], deps = [ "//source/extensions/filters/udp/udp_proxy:udp_proxy_filter_lib", - "//source/extensions/filters/udp/udp_proxy/session_filters:filter_interface", "//test/mocks/network:network_mocks", "//test/mocks/stream_info:stream_info_mocks", "//test/mocks/upstream:upstream_mocks", @@ -95,8 +94,6 @@ envoy_extension_cc_test( "//envoy/network:filter_interface", "//envoy/server:filter_config_interface", "//source/extensions/filters/udp/udp_proxy:config", - "//source/extensions/filters/udp/udp_proxy/session_filters:filter_config_interface", - "//source/extensions/filters/udp/udp_proxy/session_filters:filter_interface", "//test/extensions/filters/udp/udp_proxy/session_filters:buffer_filter_config_lib", "//test/extensions/filters/udp/udp_proxy/session_filters:buffer_filter_proto_cc_proto", "//test/extensions/filters/udp/udp_proxy/session_filters:drainer_filter_config_lib", diff --git a/test/extensions/filters/udp/udp_proxy/mocks.h b/test/extensions/filters/udp/udp_proxy/mocks.h index 7a542e0d7583..fc9135e99b90 100644 --- a/test/extensions/filters/udp/udp_proxy/mocks.h +++ b/test/extensions/filters/udp/udp_proxy/mocks.h @@ -1,6 +1,5 @@ #pragma once -#include "source/extensions/filters/udp/udp_proxy/session_filters/filter.h" #include "source/extensions/filters/udp/udp_proxy/udp_proxy_filter.h" #include "test/mocks/stream_info/mocks.h" @@ -15,7 +14,7 @@ namespace UdpFilters { namespace UdpProxy { namespace SessionFilters { -class MockReadFilterCallbacks : public ReadFilterCallbacks { +class MockReadFilterCallbacks : public Network::UdpSessionReadFilterCallbacks { public: MockReadFilterCallbacks(); ~MockReadFilterCallbacks() override; @@ -29,7 +28,7 @@ class MockReadFilterCallbacks : public ReadFilterCallbacks { NiceMock stream_info_; }; -class MockWriteFilterCallbacks : public WriteFilterCallbacks { +class MockWriteFilterCallbacks : public Network::UdpSessionWriteFilterCallbacks { public: MockWriteFilterCallbacks(); ~MockWriteFilterCallbacks() override; diff --git a/test/extensions/filters/udp/udp_proxy/session_filters/BUILD b/test/extensions/filters/udp/udp_proxy/session_filters/BUILD index 2f20a55c2617..5163923af52f 100644 --- a/test/extensions/filters/udp/udp_proxy/session_filters/BUILD +++ b/test/extensions/filters/udp/udp_proxy/session_filters/BUILD @@ -36,7 +36,6 @@ envoy_cc_test_library( "//envoy/registry", "//source/common/router:string_accessor_lib", "//source/extensions/filters/udp/udp_proxy/session_filters:factory_base_lib", - "//source/extensions/filters/udp/udp_proxy/session_filters:filter_interface", "//test/test_common:utility_lib", ], alwayslink = 1, @@ -55,7 +54,6 @@ envoy_cc_test_library( "//envoy/registry", "//source/common/router:string_accessor_lib", "//source/extensions/filters/udp/udp_proxy/session_filters:factory_base_lib", - "//source/extensions/filters/udp/udp_proxy/session_filters:filter_interface", "//test/test_common:utility_lib", ], alwayslink = 1, diff --git a/test/extensions/filters/udp/udp_proxy/session_filters/buffer_filter.h b/test/extensions/filters/udp/udp_proxy/session_filters/buffer_filter.h index 8f32cb16b51c..56503da0aaf5 100644 --- a/test/extensions/filters/udp/udp_proxy/session_filters/buffer_filter.h +++ b/test/extensions/filters/udp/udp_proxy/session_filters/buffer_filter.h @@ -7,7 +7,6 @@ #include "source/common/config/utility.h" #include "source/common/router/string_accessor_impl.h" #include "source/extensions/filters/udp/udp_proxy/session_filters/factory_base.h" -#include "source/extensions/filters/udp/udp_proxy/session_filters/filter.h" #include "test/extensions/filters/udp/udp_proxy/session_filters/buffer_filter.pb.h" #include "test/extensions/filters/udp/udp_proxy/session_filters/buffer_filter.pb.validate.h" @@ -19,6 +18,12 @@ namespace UdpFilters { namespace UdpProxy { namespace SessionFilters { +using Filter = Network::UdpSessionFilter; +using ReadFilterStatus = Network::UdpSessionReadFilterStatus; +using WriteFilterStatus = Network::UdpSessionWriteFilterStatus; +using ReadFilterCallbacks = Network::UdpSessionReadFilterCallbacks; +using WriteFilterCallbacks = Network::UdpSessionWriteFilterCallbacks; + using BufferingFilterConfig = test::extensions::filters::udp::udp_proxy::session_filters::BufferingFilterConfig; @@ -128,7 +133,7 @@ class BufferingSessionFilterConfigFactory : public FactoryBase void { + return [config](Network::UdpSessionFilterChainFactoryCallbacks& callbacks) -> void { callbacks.addFilter(std::make_shared( config.downstream_datagrams_to_buffer(), config.upstream_datagrams_to_buffer(), config.continue_after_inject())); diff --git a/test/extensions/filters/udp/udp_proxy/session_filters/drainer_filter.h b/test/extensions/filters/udp/udp_proxy/session_filters/drainer_filter.h index e9a86a461ac3..a8d7cffbcb22 100644 --- a/test/extensions/filters/udp/udp_proxy/session_filters/drainer_filter.h +++ b/test/extensions/filters/udp/udp_proxy/session_filters/drainer_filter.h @@ -5,7 +5,6 @@ #include "source/common/config/utility.h" #include "source/common/router/string_accessor_impl.h" #include "source/extensions/filters/udp/udp_proxy/session_filters/factory_base.h" -#include "source/extensions/filters/udp/udp_proxy/session_filters/filter.h" #include "test/extensions/filters/udp/udp_proxy/session_filters/drainer_filter.pb.h" #include "test/extensions/filters/udp/udp_proxy/session_filters/drainer_filter.pb.validate.h" @@ -24,6 +23,14 @@ using WriteDrainerConfig = using DrainerConfig = test::extensions::filters::udp::udp_proxy::session_filters::DrainerUdpSessionFilterConfig; +using Filter = Network::UdpSessionFilter; +using ReadFilter = Network::UdpSessionReadFilter; +using WriteFilter = Network::UdpSessionWriteFilter; +using ReadFilterStatus = Network::UdpSessionReadFilterStatus; +using WriteFilterStatus = Network::UdpSessionWriteFilterStatus; +using ReadFilterCallbacks = Network::UdpSessionReadFilterCallbacks; +using WriteFilterCallbacks = Network::UdpSessionWriteFilterCallbacks; + class DrainerUdpSessionReadFilter : public virtual ReadFilter { public: DrainerUdpSessionReadFilter(int downstream_bytes_to_drain, bool stop_iteration_on_new_session, @@ -92,7 +99,7 @@ class DrainerUdpSessionReadFilterConfigFactory : public FactoryBase void { + return [config](Network::UdpSessionFilterChainFactoryCallbacks& callbacks) -> void { callbacks.addReadFilter(std::make_unique( config.downstream_bytes_to_drain(), config.stop_iteration_on_new_session(), config.stop_iteration_on_first_read(), config.continue_filter_chain())); @@ -135,10 +142,10 @@ class DrainerUdpSessionWriteFilterConfigFactory : public FactoryBase void { + return [config](Network::UdpSessionFilterChainFactoryCallbacks& callbacks) -> void { callbacks.addWriteFilter(std::make_unique( config.upstream_bytes_to_drain(), config.stop_iteration_on_first_write())); }; @@ -166,10 +173,10 @@ class DrainerUdpSessionFilterConfigFactory : public FactoryBase { DrainerUdpSessionFilterConfigFactory() : FactoryBase("test.udp_session.drainer") {} private: - FilterFactoryCb + Network::UdpSessionFilterFactoryCb createFilterFactoryFromProtoTyped(const DrainerConfig& config, Server::Configuration::FactoryContext&) override { - return [config](FilterChainFactoryCallbacks& callbacks) -> void { + return [config](Network::UdpSessionFilterChainFactoryCallbacks& callbacks) -> void { callbacks.addFilter(std::make_shared( config.downstream_bytes_to_drain(), config.upstream_bytes_to_drain(), config.stop_iteration_on_new_session(), config.stop_iteration_on_first_read(), diff --git a/test/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/BUILD b/test/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/BUILD index d1a7fada9d12..de0aceff4488 100644 --- a/test/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/BUILD +++ b/test/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/BUILD @@ -26,8 +26,6 @@ envoy_cc_test_library( "//envoy/registry", "//source/common/router:string_accessor_lib", "//source/common/stream_info:uint32_accessor_lib", - "//source/extensions/filters/udp/udp_proxy/session_filters:factory_base_lib", - "//source/extensions/filters/udp/udp_proxy/session_filters:filter_interface", "//test/test_common:utility_lib", ], alwayslink = 1, diff --git a/test/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/dfp_setter.h b/test/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/dfp_setter.h index 28d0e78bb155..c2a39c4d33c6 100644 --- a/test/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/dfp_setter.h +++ b/test/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/dfp_setter.h @@ -6,7 +6,6 @@ #include "source/common/router/string_accessor_impl.h" #include "source/common/stream_info/uint32_accessor_impl.h" #include "source/extensions/filters/udp/udp_proxy/session_filters/factory_base.h" -#include "source/extensions/filters/udp/udp_proxy/session_filters/filter.h" #include "test/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/dfp_setter.pb.h" #include "test/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy/dfp_setter.pb.validate.h" @@ -22,6 +21,10 @@ namespace DynamicForwardProxy { using DynamicForwardProxySetterFilterConfig = test::extensions::filters::udp::udp_proxy:: session_filters::DynamicForwardProxySetterFilterConfig; +using ReadFilter = Network::UdpSessionReadFilter; +using ReadFilterStatus = Network::UdpSessionReadFilterStatus; +using ReadFilterCallbacks = Network::UdpSessionReadFilterCallbacks; + class DynamicForwardProxySetterFilter : public virtual ReadFilter { public: DynamicForwardProxySetterFilter(const std::string host, uint32_t port) @@ -58,7 +61,7 @@ class DynamicForwardProxySetterFilterConfigFactory FilterFactoryCb createFilterFactoryFromProtoTyped(const DynamicForwardProxySetterFilterConfig& config, Server::Configuration::FactoryContext&) override { - return [config](FilterChainFactoryCallbacks& callbacks) -> void { + return [config](Network::UdpSessionFilterChainFactoryCallbacks& callbacks) -> void { callbacks.addReadFilter( std::make_unique(config.host(), config.port())); }; diff --git a/test/extensions/filters/udp/udp_proxy/udp_proxy_filter_test.cc b/test/extensions/filters/udp/udp_proxy/udp_proxy_filter_test.cc index d219f425da4a..51490fa491a7 100644 --- a/test/extensions/filters/udp/udp_proxy/udp_proxy_filter_test.cc +++ b/test/extensions/filters/udp/udp_proxy/udp_proxy_filter_test.cc @@ -1618,6 +1618,10 @@ stat_prefix: foo EXPECT_THAT(output_.front(), testing::HasSubstr("session_complete")); } +using MockUdpTunnelingConfig = SessionFilters::MockUdpTunnelingConfig; +using MockUpstreamTunnelCallbacks = SessionFilters::MockUpstreamTunnelCallbacks; +using MockTunnelCreationCallbacks = SessionFilters::MockTunnelCreationCallbacks; + class HttpUpstreamImplTest : public testing::Test { public: struct HeaderToAdd { @@ -1942,6 +1946,8 @@ TEST_F(HttpUpstreamImplTest, TargetHostPercentEncoding) { setAndExpectRequestEncoder(expected_headers, is_ssl); } +using MockHttpStreamCallbacks = SessionFilters::MockHttpStreamCallbacks; + class TunnelingConnectionPoolImplTest : public testing::Test { public: void setup() { From 06a20dd6ec9f5bd7c6a9e75cc1f54ee0396e7699 Mon Sep 17 00:00:00 2001 From: "Adi (Suissa) Peleg" Date: Thu, 15 Aug 2024 09:25:16 -0400 Subject: [PATCH 110/130] xDS: enable envoy.restart_features.use_eds_cache_for_ads by default (#35696) Signed-off-by: Adi Suissa-Peleg --- changelogs/current.yaml | 9 +++++++++ source/common/runtime/runtime_features.cc | 3 +-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 5c5e2dc62796..3a089b6852b2 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -19,6 +19,15 @@ behavior_changes: HttpConnectionManager to close the connection itself. Defaults to off ("unsafe" -- check \#34356) and is configurable via :ref:`http1_safe_max_connection_duration `. +- area: eds + change: | + Enabling caching caching of EDS assignments when used with ADS by default (introduced in Envoy v1.28). + Prior to this change, Envoy required that EDS assignments were sent after an EDS cluster was updated. + If no EDS assignment was received for the cluster, it ended up with an empty assignment. + Following this change, after a cluster update, Envoy waits for an EDS assignment until + :ref:`initial_fetch_timeout ` times out, and will then apply + the cached assignment and finish updating the warmed cluster. This change temporarily disabled by setting + the runtime flag ``envoy.restart_features.use_eds_cache_for_ads`` to ``false``. - area: stats scoped_rds change: | Added new tag extraction so that scoped rds stats have their :ref:'scope_route_config_name diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 2037d0487b19..b7bb16b0a543 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -99,6 +99,7 @@ RUNTIME_GUARD(envoy_reloadable_features_xdstp_path_avoid_colon_encoding); RUNTIME_GUARD(envoy_restart_features_allow_client_socket_creation_failure); RUNTIME_GUARD(envoy_restart_features_allow_slot_destroy_on_worker_threads); RUNTIME_GUARD(envoy_restart_features_quic_handle_certs_with_shared_tls_code); +RUNTIME_GUARD(envoy_restart_features_use_eds_cache_for_ads); RUNTIME_GUARD(envoy_restart_features_use_fast_protobuf_hash); // Begin false flags. Most of them should come with a TODO to flip true. @@ -128,8 +129,6 @@ FALSE_RUNTIME_GUARD(envoy_reloadable_features_quic_reject_all); // remove the feature flag and remove code path that relies on old technique to fetch credentials // via libcurl and remove the bazel steps to pull and test the curl dependency. FALSE_RUNTIME_GUARD(envoy_reloadable_features_use_http_client_to_fetch_aws_credentials); -// TODO(adisuissa): enable by default once this is tested in prod. -FALSE_RUNTIME_GUARD(envoy_restart_features_use_eds_cache_for_ads); // TODO(#10646) change to true when UHV is sufficiently tested // For more information about Universal Header Validation, please see // https://github.com/envoyproxy/envoy/issues/10646 From 9263d8801404bd4a9b657df0a38281f9bd2ddd95 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Thu, 15 Aug 2024 10:34:01 -0500 Subject: [PATCH 111/130] mobile: Rewrite JNI cache implementation (#35710) This PR rewrites the JNI cache implementation to fix the GlobalRef leak. Prior to this PR, some code would call `getObjectClass` in order to get a `jclass` instance. If the `jclass` was not already in the cache, the code would create a new `GlobalRef` and add it into the cache. Given that there's no guarantee that we will get the same `jclass` instance, the previous code ended up creating a lot of `new GlobalRef` instances due to the cache misses causing a leak. The new implementation updates the code to load the `jclass` from the cache directly via `findClassFromCache` instead of using `getObjectClass`. This guarantees that we will use the same `jclass` instance. The new implementation also changes the cache initialization to be eager instead of lazy, thereby eliminating the need for having `thread_local`, which can help to further reduce the number of `GlobalRef` instances. Risk Level: medium Testing: unit tests Docs Changes: n/a Release Notes: n/a Platform Specific Features: mobile Signed-off-by: Fredy Wijaya --- mobile/library/jni/android_jni_utility.cc | 10 +- mobile/library/jni/android_network_utility.cc | 18 +- mobile/library/jni/jni_helper.cc | 177 ++++++----- mobile/library/jni/jni_helper.h | 49 +++- mobile/library/jni/jni_impl.cc | 180 ++++++++---- mobile/library/jni/jni_utility.cc | 276 +++++++++++++----- mobile/library/jni/jni_utility.h | 11 +- .../envoymobile/jni/JniHelperTest.java | 45 ++- mobile/test/jni/jni_helper_test.cc | 86 +++++- .../jni/jni_http_proxy_test_server_factory.cc | 23 +- .../test/jni/jni_http_test_server_factory.cc | 55 +++- mobile/test/jni/jni_utility_test.cc | 15 +- 12 files changed, 678 insertions(+), 267 deletions(-) diff --git a/mobile/library/jni/android_jni_utility.cc b/mobile/library/jni/android_jni_utility.cc index 6d39da3cd814..62e65a7c3a0a 100644 --- a/mobile/library/jni/android_jni_utility.cc +++ b/mobile/library/jni/android_jni_utility.cc @@ -16,8 +16,8 @@ bool isCleartextPermitted(absl::string_view hostname) { JniHelper jni_helper(JniHelper::getThreadLocalEnv()); LocalRefUniquePtr java_host = cppStringToJavaString(jni_helper, std::string(hostname)); jclass java_android_network_library_class = - jni_helper.findClass("io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary"); - jmethodID java_is_cleartext_traffic_permitted_method_id = jni_helper.getStaticMethodId( + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary"); + jmethodID java_is_cleartext_traffic_permitted_method_id = jni_helper.getStaticMethodIdFromCache( java_android_network_library_class, "isCleartextTrafficPermitted", "(Ljava/lang/String;)Z"); jboolean result = jni_helper.callStaticBooleanMethod( java_android_network_library_class, java_is_cleartext_traffic_permitted_method_id, @@ -33,9 +33,9 @@ void tagSocket(int ifd, int uid, int tag) { #if defined(__ANDROID_API__) JniHelper jni_helper(JniHelper::getThreadLocalEnv()); jclass java_android_network_library_class = - jni_helper.findClass("io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary"); - jmethodID java_tag_socket_method_id = - jni_helper.getStaticMethodId(java_android_network_library_class, "tagSocket", "(III)V"); + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary"); + jmethodID java_tag_socket_method_id = jni_helper.getStaticMethodIdFromCache( + java_android_network_library_class, "tagSocket", "(III)V"); jni_helper.callStaticVoidMethod(java_android_network_library_class, java_tag_socket_method_id, ifd, uid, tag); #else diff --git a/mobile/library/jni/android_network_utility.cc b/mobile/library/jni/android_network_utility.cc index e418ffd6cdee..988c03a5d982 100644 --- a/mobile/library/jni/android_network_utility.cc +++ b/mobile/library/jni/android_network_utility.cc @@ -35,9 +35,9 @@ enum class CertVerifyStatus : int { bool jvmCertIsIssuedByKnownRoot(JniHelper& jni_helper, jobject result) { jclass jcls_AndroidCertVerifyResult = - jni_helper.findClass("io/envoyproxy/envoymobile/utilities/AndroidCertVerifyResult"); + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/utilities/AndroidCertVerifyResult"); jmethodID jmid_isIssuedByKnownRoot = - jni_helper.getMethodId(jcls_AndroidCertVerifyResult, "isIssuedByKnownRoot", "()Z"); + jni_helper.getMethodIdFromCache(jcls_AndroidCertVerifyResult, "isIssuedByKnownRoot", "()Z"); ASSERT(jmid_isIssuedByKnownRoot); bool is_issued_by_known_root = jni_helper.callBooleanMethod(result, jmid_isIssuedByKnownRoot); return is_issued_by_known_root; @@ -45,9 +45,9 @@ bool jvmCertIsIssuedByKnownRoot(JniHelper& jni_helper, jobject result) { CertVerifyStatus jvmCertGetStatus(JniHelper& jni_helper, jobject j_result) { jclass jcls_AndroidCertVerifyResult = - jni_helper.findClass("io/envoyproxy/envoymobile/utilities/AndroidCertVerifyResult"); + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/utilities/AndroidCertVerifyResult"); jmethodID jmid_getStatus = - jni_helper.getMethodId(jcls_AndroidCertVerifyResult, "getStatus", "()I"); + jni_helper.getMethodIdFromCache(jcls_AndroidCertVerifyResult, "getStatus", "()I"); ASSERT(jmid_getStatus); CertVerifyStatus result = static_cast(jni_helper.callIntMethod(j_result, jmid_getStatus)); @@ -57,9 +57,9 @@ CertVerifyStatus jvmCertGetStatus(JniHelper& jni_helper, jobject j_result) { LocalRefUniquePtr jvmCertGetCertificateChainEncoded(JniHelper& jni_helper, jobject result) { jclass jcls_AndroidCertVerifyResult = - jni_helper.findClass("io/envoyproxy/envoymobile/utilities/AndroidCertVerifyResult"); - jmethodID jmid_getCertificateChainEncoded = - jni_helper.getMethodId(jcls_AndroidCertVerifyResult, "getCertificateChainEncoded", "()[[B"); + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/utilities/AndroidCertVerifyResult"); + jmethodID jmid_getCertificateChainEncoded = jni_helper.getMethodIdFromCache( + jcls_AndroidCertVerifyResult, "getCertificateChainEncoded", "()[[B"); LocalRefUniquePtr certificate_chain = jni_helper.callObjectMethod(result, jmid_getCertificateChainEncoded); return certificate_chain; @@ -108,8 +108,8 @@ LocalRefUniquePtr callJvmVerifyX509CertChain(JniHelper& jni_helper, std::string auth_type, absl::string_view hostname) { jclass jcls_AndroidNetworkLibrary = - jni_helper.findClass("io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary"); - jmethodID jmid_verifyServerCertificates = jni_helper.getStaticMethodId( + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary"); + jmethodID jmid_verifyServerCertificates = jni_helper.getStaticMethodIdFromCache( jcls_AndroidNetworkLibrary, "verifyServerCertificates", "([[B[B[B)Lio/envoyproxy/envoymobile/utilities/AndroidCertVerifyResult;"); LocalRefUniquePtr chain_byte_array = diff --git a/mobile/library/jni/jni_helper.cc b/mobile/library/jni/jni_helper.cc index 1dd285e1116b..3e35365f071e 100644 --- a/mobile/library/jni/jni_helper.cc +++ b/mobile/library/jni/jni_helper.cc @@ -19,44 +19,19 @@ thread_local JNIEnv* jni_env_cache_ = nullptr; // `jclass_cache_map` contains `jclass` references that are statically populated. This field is // used by `FindClass` to find the `jclass` reference from a given class name. absl::flat_hash_map jclass_cache_map; -// The `jclass_cache_set` is a superset of `jclass_cache_map`. It contains `jclass` objects that are -// retrieve dynamically via `GetObjectClass`. -// -// The `jclass_cache_set` owns the `jclass` global refs, wrapped in `GlobalRefUniquePtr` to allow -// automatic `GlobalRef` destruction. The other fields, such as `jmethod_id_cache_map`, -// `static_jmethod_id_cache_map`, `jfield_id_cache_map`, and `static_jfield_id_cache_map` only -// borrow the `jclass` references. -// -// Note: all these fields are `thread_local` to avoid locking. -thread_local absl::flat_hash_set> jclass_cache_set; -thread_local absl::flat_hash_map< +absl::flat_hash_map< std::tuple, jmethodID> jmethod_id_cache_map; -thread_local absl::flat_hash_map< - std::tuple, - jmethodID /* signature */> +absl::flat_hash_map, + jmethodID /* signature */> static_jmethod_id_cache_map; -thread_local absl::flat_hash_map< - std::tuple, jfieldID /* signature */> +absl::flat_hash_map, + jfieldID /* signature */> jfield_id_cache_map; -thread_local absl::flat_hash_map< - std::tuple, jfieldID /* signature */> +absl::flat_hash_map, + jfieldID /* signature */> static_jfield_id_cache_map; - -/** - * This function checks if the `clazz` already exists in the `jclass` `GlobalRef` cache and creates - * a new `GlobalRef` if it does not already exist. This functions returns the `GlobalRef` of the - * specified `clazz`. - */ -jclass addClassToCacheIfNotExist(JNIEnv* env, jclass clazz) { - jclass java_class_global_ref = clazz; - if (auto it = jclass_cache_set.find(clazz); it == jclass_cache_set.end()) { - java_class_global_ref = reinterpret_cast(env->NewGlobalRef(clazz)); - jclass_cache_set.emplace(java_class_global_ref, GlobalRefDeleter()); - } - return java_class_global_ref; -} } // namespace void GlobalRefDeleter::operator()(jobject object) const { @@ -99,21 +74,67 @@ void JniHelper::finalize() { jfield_id_cache_map.clear(); static_jfield_id_cache_map.clear(); jclass_cache_map.clear(); - for (const auto& clazz : jclass_cache_set) { - env->DeleteGlobalRef(clazz.get()); + for (const auto& [_, clazz] : jclass_cache_map) { + env->DeleteGlobalRef(clazz); } - jclass_cache_set.clear(); } -void JniHelper::addClassToCache(const char* class_name) { +void JniHelper::addToCache(absl::string_view class_name, const std::vector& methods, + const std::vector& static_methods, + const std::vector& fields, + const std::vector& static_fields) { JNIEnv* env; jint result = getJavaVm()->GetEnv(reinterpret_cast(&env), getVersion()); ASSERT(result == JNI_OK, "Unable to get JNIEnv from the JavaVM."); - jclass java_class = env->FindClass(class_name); + jclass java_class = env->FindClass(class_name.data()); ASSERT(java_class != nullptr, absl::StrFormat("Unable to find class '%s'.", class_name)); jclass java_class_global_ref = reinterpret_cast(env->NewGlobalRef(java_class)); jclass_cache_map.emplace(class_name, java_class_global_ref); - jclass_cache_set.emplace(java_class_global_ref, GlobalRefDeleter()); + + for (const auto& [method_name, signature] : methods) { + jmethodID method_id = + env->GetMethodID(java_class_global_ref, method_name.data(), signature.data()); + ASSERT(method_id != nullptr, + absl::StrFormat("Unable to find method ID for class '%s', method '%s', signature '%s'.", + class_name, method_name, signature)); + jmethod_id_cache_map.emplace(std::tuple( + java_class_global_ref, method_name, signature), + method_id); + } + + for (const auto& [method_name, signature] : static_methods) { + jmethodID method_id = + env->GetStaticMethodID(java_class_global_ref, method_name.data(), signature.data()); + ASSERT(method_id != nullptr, + absl::StrFormat( + "Unable to find static method ID for class '%s', method '%s', signature '%s'.", + class_name, method_name, signature)); + static_jmethod_id_cache_map.emplace(std::tuple( + java_class_global_ref, method_name, signature), + method_id); + } + + for (const auto& [field_name, signature] : fields) { + jfieldID field_id = env->GetFieldID(java_class_global_ref, field_name.data(), signature.data()); + ASSERT(field_id != nullptr, + absl::StrFormat("Unable to find field ID for class '%s', field '%s', signature '%s'.", + class_name, field_name, signature)); + jfield_id_cache_map.emplace(std::tuple( + java_class_global_ref, field_name, signature), + field_id); + } + + for (const auto& [field_name, signature] : static_fields) { + jfieldID field_id = + env->GetStaticFieldID(java_class_global_ref, field_name.data(), signature.data()); + ASSERT(field_id != nullptr, + absl::StrFormat( + "Unable to find static field ID for class '%s', field '%s', signature '%s'.", + class_name, field_name, signature)); + static_jfield_id_cache_map.emplace(std::tuple( + java_class_global_ref, field_name, signature), + field_id); + } } JavaVM* JniHelper::getJavaVm() { return java_vm_cache_.load(std::memory_order_acquire); } @@ -141,33 +162,45 @@ JNIEnv* JniHelper::getThreadLocalEnv() { JNIEnv* JniHelper::getEnv() { return env_; } jfieldID JniHelper::getFieldId(jclass clazz, const char* name, const char* signature) { + jfieldID field_id = env_->GetFieldID(clazz, name, signature); + rethrowException(); + return field_id; +} + +jfieldID JniHelper::getFieldIdFromCache(jclass clazz, const char* name, const char* signature) { if (auto it = jfield_id_cache_map.find( std::tuple(clazz, name, signature)); it != jfield_id_cache_map.end()) { return it->second; } - jfieldID field_id = env_->GetFieldID(clazz, name, signature); - jclass clazz_global_ref = addClassToCacheIfNotExist(env_, clazz); - jfield_id_cache_map.emplace( - std::tuple(clazz_global_ref, name, signature), - field_id); + // In the debug mode, the code will fail if the field ID is not in the cache since this is most + // likely due to a bug in the code. In the release mode, the code will use the non-caching field + // ID. + ASSERT(false, absl::StrFormat("Unable to find field ID '%s', signature '%s' from the cache.", + name, signature)); + return getFieldId(clazz, name, signature); +} + +jfieldID JniHelper::getStaticFieldId(jclass clazz, const char* name, const char* signature) { + jfieldID field_id = env_->GetStaticFieldID(clazz, name, signature); rethrowException(); return field_id; } -jfieldID JniHelper::getStaticFieldId(jclass clazz, const char* name, const char* signature) { +jfieldID JniHelper::getStaticFieldIdFromCache(jclass clazz, const char* name, + const char* signature) { if (auto it = static_jfield_id_cache_map.find( std::tuple(clazz, name, signature)); it != static_jfield_id_cache_map.end()) { return it->second; } - jfieldID field_id = env_->GetStaticFieldID(clazz, name, signature); - jclass clazz_global_ref = addClassToCacheIfNotExist(env_, clazz); - static_jfield_id_cache_map.emplace( - std::tuple(clazz_global_ref, name, signature), - field_id); - rethrowException(); - return field_id; + // In the debug mode, the code will fail if the static field ID is not in the cache since this is + // most likely due to a bug in the code. In the release mode, the code will use the non-caching + // static field ID. + ASSERT(false, + absl::StrFormat("Unable to find static field ID '%s', signature '%s' from the cache.", + name, signature)); + return getStaticFieldId(clazz, name, signature); } #define DEFINE_GET_FIELD(JAVA_TYPE, JNI_TYPE) \ @@ -185,36 +218,48 @@ DEFINE_GET_FIELD(Double, jdouble) DEFINE_GET_FIELD(Boolean, jboolean) jmethodID JniHelper::getMethodId(jclass clazz, const char* name, const char* signature) { + jmethodID method_id = env_->GetMethodID(clazz, name, signature); + rethrowException(); + return method_id; +} + +jmethodID JniHelper::getMethodIdFromCache(jclass clazz, const char* name, const char* signature) { if (auto it = jmethod_id_cache_map.find( std::tuple(clazz, name, signature)); it != jmethod_id_cache_map.end()) { return it->second; } - jmethodID method_id = env_->GetMethodID(clazz, name, signature); - jclass clazz_global_ref = addClassToCacheIfNotExist(env_, clazz); - jmethod_id_cache_map.emplace( - std::tuple(clazz_global_ref, name, signature), - method_id); + // In the debug mode, the code will fail if the method ID is not in the cache since this is most + // likely due to a bug in the code. In the release mode, the code will use the non-caching method + // ID. + ASSERT(false, absl::StrFormat("Unable to find method ID '%s', signature '%s' from the cache.", + name, signature)); + return getMethodId(clazz, name, signature); +} + +jmethodID JniHelper::getStaticMethodId(jclass clazz, const char* name, const char* signature) { + jmethodID method_id = env_->GetStaticMethodID(clazz, name, signature); rethrowException(); return method_id; } -jmethodID JniHelper::getStaticMethodId(jclass clazz, const char* name, const char* signature) { +jmethodID JniHelper::getStaticMethodIdFromCache(jclass clazz, const char* name, + const char* signature) { if (auto it = static_jmethod_id_cache_map.find( std::tuple(clazz, name, signature)); it != static_jmethod_id_cache_map.end()) { return it->second; } - jmethodID method_id = env_->GetStaticMethodID(clazz, name, signature); - jclass clazz_global_ref = addClassToCacheIfNotExist(env_, clazz); - static_jmethod_id_cache_map.emplace( - std::tuple(clazz_global_ref, name, signature), - method_id); - rethrowException(); - return method_id; + // In the debug mode, the code will fail if the static method ID is not in the cache since this is + // most likely due to a bug in the code. In the release mode, the code will use the non-caching + // static method ID. + ASSERT(false, + absl::StrFormat("Unable to find static method ID '%s', signature '%s' from the cache.", + name, signature)); + return getStaticMethodId(clazz, name, signature); } -jclass JniHelper::findClass(const char* class_name) { +jclass JniHelper::findClassFromCache(const char* class_name) { if (auto i = jclass_cache_map.find(class_name); i != jclass_cache_map.end()) { return i->second; } @@ -227,7 +272,7 @@ LocalRefUniquePtr JniHelper::getObjectClass(jobject object) { } void JniHelper::throwNew(const char* java_class_name, const char* message) { - jclass java_class = findClass(java_class_name); + jclass java_class = findClassFromCache(java_class_name); if (java_class != nullptr) { jint error = env_->ThrowNew(java_class, message); ASSERT(error == JNI_OK, "Failed calling ThrowNew."); diff --git a/mobile/library/jni/jni_helper.h b/mobile/library/jni/jni_helper.h index cb3b07f20b84..82898283a421 100644 --- a/mobile/library/jni/jni_helper.h +++ b/mobile/library/jni/jni_helper.h @@ -4,6 +4,8 @@ #include +#include "absl/strings/string_view.h" + namespace Envoy { namespace JNI { @@ -119,6 +121,16 @@ class JniHelper { public: explicit JniHelper(JNIEnv* env) : env_(env) {} + struct Method { + absl::string_view name_; + absl::string_view signature_; + }; + + struct Field { + absl::string_view name_; + absl::string_view signature_; + }; + /** Gets the JNI version supported. */ static jint getVersion(); @@ -129,7 +141,8 @@ class JniHelper { static void finalize(); /** - * Adds the `jclass` object into a cache. This function is typically called inside `JNI_OnLoad`. + * Adds the `jclass`, `jmethodID`, and `jfieldID` objects into a cache. This function is typically + * called inside `JNI_OnLoad`. * * Caching the `jclass` can be useful for performance. * See https://developer.android.com/training/articles/perf-jni#jclass,-jmethodid,-and-jfieldid @@ -144,7 +157,9 @@ class JniHelper { * See * https://developer.android.com/training/articles/perf-jni#faq:-why-didnt-findclass-find-my-class */ - static void addClassToCache(const char* class_name); + static void addToCache(absl::string_view class_name, const std::vector& methods, + const std::vector& static_methods, + const std::vector& fields, const std::vector& static_fields); /** Gets the `JavaVM`. The `initialize(JavaVM*) must be called first. */ static JavaVM* getJavaVm(); @@ -174,6 +189,13 @@ class JniHelper { */ jfieldID getFieldId(jclass clazz, const char* name, const char* signature); + /** + * Gets the field ID for an instance field of a class from the cache. + * + * https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#getfieldid + */ + jfieldID getFieldIdFromCache(jclass clazz, const char* name, const char* signature); + /** * Gets the field ID for a static field of a class. * @@ -181,6 +203,13 @@ class JniHelper { */ jfieldID getStaticFieldId(jclass clazz, const char* name, const char* signature); + /** + * Gets the field ID for a static field of a class from the cache. + * + * https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#getstaticfieldid + */ + jfieldID getStaticFieldIdFromCache(jclass clazz, const char* name, const char* signature); + /** A macro to create `CallMethod` helper function. */ #define DECLARE_GET_FIELD(JAVA_TYPE, JNI_TYPE) \ JNI_TYPE get##JAVA_TYPE##Field(jobject object, jfieldID field_id); @@ -213,6 +242,13 @@ class JniHelper { */ jmethodID getMethodId(jclass clazz, const char* name, const char* signature); + /** + * Gets the object method with the given signature from the cache. + * + * https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#getmethodid + */ + jmethodID getMethodIdFromCache(jclass clazz, const char* name, const char* signature); + /** * Gets the static method with the given signature. * @@ -220,12 +256,19 @@ class JniHelper { */ jmethodID getStaticMethodId(jclass clazz, const char* name, const char* signature); + /** + * Gets the static method with the given signature from the cache. + * + * https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#getstaticmethodid + */ + jmethodID getStaticMethodIdFromCache(jclass clazz, const char* name, const char* signature); + /** * Finds the given `class_name` using from the cache. * * https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#findclass */ - [[nodiscard]] jclass findClass(const char* class_name); + [[nodiscard]] jclass findClassFromCache(const char* class_name); /** * Returns the class of a given `object`. diff --git a/mobile/library/jni/jni_impl.cc b/mobile/library/jni/jni_impl.cc index 847182a8c241..0e0c21d17e93 100644 --- a/mobile/library/jni/jni_impl.cc +++ b/mobile/library/jni/jni_impl.cc @@ -21,25 +21,72 @@ using Envoy::Platform::EngineBuilder; extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /* reserved */) { Envoy::JNI::JniHelper::initialize(vm); - Envoy::JNI::JniHelper::addClassToCache("java/lang/Object"); - Envoy::JNI::JniHelper::addClassToCache("java/lang/Integer"); - Envoy::JNI::JniHelper::addClassToCache("java/lang/ClassLoader"); - Envoy::JNI::JniHelper::addClassToCache("java/nio/ByteBuffer"); - Envoy::JNI::JniHelper::addClassToCache("java/lang/Throwable"); - Envoy::JNI::JniHelper::addClassToCache("java/lang/UnsupportedOperationException"); - Envoy::JNI::JniHelper::addClassToCache("[B"); - Envoy::JNI::JniHelper::addClassToCache("java/util/Map$Entry"); - Envoy::JNI::JniHelper::addClassToCache("java/util/LinkedHashMap"); - Envoy::JNI::JniHelper::addClassToCache("java/util/HashMap"); - Envoy::JNI::JniHelper::addClassToCache("java/util/List"); - Envoy::JNI::JniHelper::addClassToCache("java/util/ArrayList"); - Envoy::JNI::JniHelper::addClassToCache("io/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel"); - Envoy::JNI::JniHelper::addClassToCache( - "io/envoyproxy/envoymobile/engine/types/EnvoyFinalStreamIntel"); - Envoy::JNI::JniHelper::addClassToCache( - "io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary"); - Envoy::JNI::JniHelper::addClassToCache( - "io/envoyproxy/envoymobile/utilities/AndroidCertVerifyResult"); + Envoy::JNI::JniUtility::initCache(); + Envoy::JNI::JniHelper::addToCache( + "io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary", + /* methods= */ {}, + /* static_methods= */ + { + {"isCleartextTrafficPermitted", "(Ljava/lang/String;)Z"}, + {"tagSocket", "(III)V"}, + {"verifyServerCertificates", + "([[B[B[B)Lio/envoyproxy/envoymobile/utilities/AndroidCertVerifyResult;"}, + {"addTestRootCertificate", "([B)V"}, + {"clearTestRootCertificates", "()V"}, + + }, + /* fields= */ {}, /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("io/envoyproxy/envoymobile/utilities/AndroidCertVerifyResult", + /* methods= */ + { + {"isIssuedByKnownRoot", "()Z"}, + {"getStatus", "()I"}, + {"getCertificateChainEncoded", "()[[B"}, + }, + /* static_methods= */ {}, + /* fields= */ {}, /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("io/envoyproxy/envoymobile/engine/types/EnvoyOnEngineRunning", + /* methods= */ + { + {"invokeOnEngineRunning", "()Ljava/lang/Object;"}, + }, + /* static_methods= */ {}, + /* fields= */ {}, /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("io/envoyproxy/envoymobile/engine/types/EnvoyLogger", + /* methods= */ + { + {"log", "(ILjava/lang/String;)V"}, + }, + /* static_methods= */ {}, + /* fields= */ {}, /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("io/envoyproxy/envoymobile/engine/types/EnvoyEventTracker", + /* methods= */ + { + {"track", "(Ljava/util/Map;)V"}, + }, + /* static_methods= */ {}, + /* fields= */ {}, /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache( + "io/envoyproxy/envoymobile/engine/types/EnvoyHTTPCallbacks", + /* methods= */ + { + {"onHeaders", + "(Ljava/util/Map;ZLio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;)V"}, + {"onData", + "(Ljava/nio/ByteBuffer;ZLio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;)V"}, + {"onTrailers", + "(Ljava/util/Map;Lio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;)V"}, + {"onComplete", "(Lio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;Lio/envoyproxy/" + "envoymobile/engine/types/EnvoyFinalStreamIntel;)V"}, + {"onError", + "(ILjava/lang/String;ILio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;Lio/" + "envoyproxy/envoymobile/engine/types/EnvoyFinalStreamIntel;)V"}, + {"onCancel", "(Lio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;Lio/envoyproxy/" + "envoymobile/engine/types/EnvoyFinalStreamIntel;)V"}, + {"onSendWindowAvailable", "(Lio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;)V"}, + }, + /* static_methods= */ {}, + /* fields= */ {}, /* static_fields= */ {}); return Envoy::JNI::JniHelper::getVersion(); } @@ -63,10 +110,10 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr jobject on_engine_running_global_ref = env->NewGlobalRef(on_engine_running); callbacks->on_engine_running_ = [on_engine_running_global_ref] { Envoy::JNI::JniHelper jni_helper(Envoy::JNI::JniHelper::getThreadLocalEnv()); - Envoy::JNI::LocalRefUniquePtr java_on_engine_running_class = - jni_helper.getObjectClass(on_engine_running_global_ref); - jmethodID java_on_engine_running_method_id = jni_helper.getMethodId( - java_on_engine_running_class.get(), "invokeOnEngineRunning", "()Ljava/lang/Object;"); + auto java_on_engine_running_class = jni_helper.findClassFromCache( + "io/envoyproxy/envoymobile/engine/types/EnvoyOnEngineRunning"); + jmethodID java_on_engine_running_method_id = jni_helper.getMethodIdFromCache( + java_on_engine_running_class, "invokeOnEngineRunning", "()Ljava/lang/Object;"); Envoy::JNI::LocalRefUniquePtr unused = jni_helper.callObjectMethod( on_engine_running_global_ref, java_on_engine_running_method_id); jni_helper.getEnv()->DeleteGlobalRef(on_engine_running_global_ref); @@ -91,10 +138,10 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr Envoy::JNI::LocalRefUniquePtr java_message = Envoy::JNI::cppStringToJavaString(jni_helper, message); jint java_level = static_cast(level); - Envoy::JNI::LocalRefUniquePtr java_envoy_logger_class = - jni_helper.getObjectClass(envoy_logger_global_ref); + auto java_envoy_logger_class = + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/engine/types/EnvoyLogger"); jmethodID java_log_method_id = - jni_helper.getMethodId(java_envoy_logger_class.get(), "log", "(ILjava/lang/String;)V"); + jni_helper.getMethodIdFromCache(java_envoy_logger_class, "log", "(ILjava/lang/String;)V"); jni_helper.callVoidMethod(envoy_logger_global_ref, java_log_method_id, java_level, java_message.get()); }; @@ -114,10 +161,10 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr Envoy::JNI::JniHelper jni_helper(Envoy::JNI::JniHelper::getThreadLocalEnv()); Envoy::JNI::LocalRefUniquePtr java_events = Envoy::JNI::cppMapToJavaMap(jni_helper, events); - Envoy::JNI::LocalRefUniquePtr java_envoy_event_tracker_class = - jni_helper.getObjectClass(event_tracker_global_ref); - jmethodID java_track_method_id = jni_helper.getMethodId(java_envoy_event_tracker_class.get(), - "track", "(Ljava/util/Map;)V"); + auto java_envoy_event_tracker_class = + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/engine/types/EnvoyEventTracker"); + jmethodID java_track_method_id = jni_helper.getMethodIdFromCache( + java_envoy_event_tracker_class, "track", "(Ljava/util/Map;)V"); jni_helper.callVoidMethod(event_tracker_global_ref, java_track_method_id, java_events.get()); }; event_tracker->on_exit_ = [event_tracker_global_ref] { @@ -249,12 +296,12 @@ jvm_on_headers(const char* method, const Envoy::Types::ManagedEnvoyHeaders& head // Create a "no operation" result: // 1. Tell the filter chain to continue the iteration. // 2. Return headers received on as method's input as part of the method's output. - jclass jcls_object_array = jni_helper.findClass("java/lang/Object"); + jclass jcls_object_array = jni_helper.findClassFromCache("java/lang/Object"); Envoy::JNI::LocalRefUniquePtr noopResult = jni_helper.newObjectArray(2, jcls_object_array, NULL); - jclass jcls_int = jni_helper.findClass("java/lang/Integer"); - jmethodID jmid_intInit = jni_helper.getMethodId(jcls_int, "", "(I)V"); + jclass jcls_int = jni_helper.findClassFromCache("java/lang/Integer"); + jmethodID jmid_intInit = jni_helper.getMethodIdFromCache(jcls_int, "", "(I)V"); Envoy::JNI::LocalRefUniquePtr j_status = jni_helper.newObject(jcls_int, jmid_intInit, 0); // Set status to "0" (FilterHeadersStatus::Continue). Signal that the intent // is to continue the iteration of the filter chain. @@ -787,9 +834,10 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra Envoy::JNI::JniHelper jni_helper(Envoy::JNI::JniHelper::getThreadLocalEnv()); auto java_headers = Envoy::JNI::cppHeadersToJavaHeaders(jni_helper, headers); auto java_stream_intel = Envoy::JNI::cppStreamIntelToJavaStreamIntel(jni_helper, stream_intel); - auto java_stream_callbacks_class = jni_helper.getObjectClass(java_stream_callbacks_global_ref); - auto java_on_headers_method_id = jni_helper.getMethodId( - java_stream_callbacks_class.get(), "onHeaders", + auto java_stream_callbacks_class = + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/engine/types/EnvoyHTTPCallbacks"); + auto java_on_headers_method_id = jni_helper.getMethodIdFromCache( + java_stream_callbacks_class, "onHeaders", "(Ljava/util/Map;ZLio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;)V"); jni_helper.callVoidMethod(java_stream_callbacks_global_ref, java_on_headers_method_id, java_headers.get(), static_cast(end_stream), @@ -799,12 +847,13 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra const Envoy::Buffer::Instance& buffer, uint64_t length, bool end_stream, envoy_stream_intel stream_intel) { Envoy::JNI::JniHelper jni_helper(Envoy::JNI::JniHelper::getThreadLocalEnv()); - auto java_stream_callbacks_class = jni_helper.getObjectClass(java_stream_callbacks_global_ref); + auto java_stream_callbacks_class = + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/engine/types/EnvoyHTTPCallbacks"); auto java_byte_buffer = Envoy::JNI::cppBufferInstanceToJavaDirectByteBuffer(jni_helper, buffer, length); auto java_stream_intel = Envoy::JNI::cppStreamIntelToJavaStreamIntel(jni_helper, stream_intel); - auto java_on_data_method_id = jni_helper.getMethodId( - java_stream_callbacks_class.get(), "onData", + auto java_on_data_method_id = jni_helper.getMethodIdFromCache( + java_stream_callbacks_class, "onData", "(Ljava/nio/ByteBuffer;ZLio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;)V"); jni_helper.callVoidMethod(java_stream_callbacks_global_ref, java_on_data_method_id, java_byte_buffer.get(), static_cast(end_stream), @@ -816,9 +865,10 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra Envoy::JNI::JniHelper jni_helper(Envoy::JNI::JniHelper::getThreadLocalEnv()); auto java_trailers = Envoy::JNI::cppHeadersToJavaHeaders(jni_helper, trailers); auto java_stream_intel = Envoy::JNI::cppStreamIntelToJavaStreamIntel(jni_helper, stream_intel); - auto java_stream_callbacks_class = jni_helper.getObjectClass(java_stream_callbacks_global_ref); - auto java_on_trailers_method_id = jni_helper.getMethodId( - java_stream_callbacks_class.get(), "onTrailers", + auto java_stream_callbacks_class = + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/engine/types/EnvoyHTTPCallbacks"); + auto java_on_trailers_method_id = jni_helper.getMethodIdFromCache( + java_stream_callbacks_class, "onTrailers", "(Ljava/util/Map;Lio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;)V"); jni_helper.callVoidMethod(java_stream_callbacks_global_ref, java_on_trailers_method_id, java_trailers.get(), java_stream_intel.get()); @@ -830,11 +880,12 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra auto java_stream_intel = Envoy::JNI::cppStreamIntelToJavaStreamIntel(jni_helper, stream_intel); auto java_final_stream_intel = Envoy::JNI::cppFinalStreamIntelToJavaFinalStreamIntel(jni_helper, final_stream_intel); - auto java_stream_callbacks_class = jni_helper.getObjectClass(java_stream_callbacks_global_ref); - auto java_on_complete_method_id = - jni_helper.getMethodId(java_stream_callbacks_class.get(), "onComplete", - "(Lio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;" - "Lio/envoyproxy/envoymobile/engine/types/EnvoyFinalStreamIntel;)V"); + auto java_stream_callbacks_class = + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/engine/types/EnvoyHTTPCallbacks"); + auto java_on_complete_method_id = jni_helper.getMethodIdFromCache( + java_stream_callbacks_class, "onComplete", + "(Lio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;" + "Lio/envoyproxy/envoymobile/engine/types/EnvoyFinalStreamIntel;)V"); jni_helper.callVoidMethod(java_stream_callbacks_global_ref, java_on_complete_method_id, java_stream_intel.get(), java_final_stream_intel.get()); // on_complete_ is a terminal callback, delete the java_stream_callbacks_global_ref. @@ -847,9 +898,10 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra auto java_stream_intel = Envoy::JNI::cppStreamIntelToJavaStreamIntel(jni_helper, stream_intel); auto java_final_stream_intel = Envoy::JNI::cppFinalStreamIntelToJavaFinalStreamIntel(jni_helper, final_stream_intel); - auto java_stream_callbacks_class = jni_helper.getObjectClass(java_stream_callbacks_global_ref); - auto java_on_error_method_id = jni_helper.getMethodId( - java_stream_callbacks_class.get(), "onError", + auto java_stream_callbacks_class = + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/engine/types/EnvoyHTTPCallbacks"); + auto java_on_error_method_id = jni_helper.getMethodIdFromCache( + java_stream_callbacks_class, "onError", "(ILjava/lang/String;ILio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;" "Lio/envoyproxy/envoymobile/engine/types/EnvoyFinalStreamIntel;)V"); auto java_error_message = Envoy::JNI::cppStringToJavaString(jni_helper, error.message_); @@ -867,11 +919,12 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra auto java_stream_intel = Envoy::JNI::cppStreamIntelToJavaStreamIntel(jni_helper, stream_intel); auto java_final_stream_intel = Envoy::JNI::cppFinalStreamIntelToJavaFinalStreamIntel(jni_helper, final_stream_intel); - auto java_stream_callbacks_class = jni_helper.getObjectClass(java_stream_callbacks_global_ref); - auto java_on_cancel_method_id = - jni_helper.getMethodId(java_stream_callbacks_class.get(), "onCancel", - "(Lio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;" - "Lio/envoyproxy/envoymobile/engine/types/EnvoyFinalStreamIntel;)V"); + auto java_stream_callbacks_class = + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/engine/types/EnvoyHTTPCallbacks"); + auto java_on_cancel_method_id = jni_helper.getMethodIdFromCache( + java_stream_callbacks_class, "onCancel", + "(Lio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;" + "Lio/envoyproxy/envoymobile/engine/types/EnvoyFinalStreamIntel;)V"); jni_helper.callVoidMethod(java_stream_callbacks_global_ref, java_on_cancel_method_id, java_stream_intel.get(), java_final_stream_intel.get()); // on_cancel_ is a terminal callback, delete the java_stream_callbacks_global_ref. @@ -881,10 +934,11 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra envoy_stream_intel stream_intel) { Envoy::JNI::JniHelper jni_helper(Envoy::JNI::JniHelper::getThreadLocalEnv()); auto java_stream_intel = Envoy::JNI::cppStreamIntelToJavaStreamIntel(jni_helper, stream_intel); - auto java_stream_callbacks_class = jni_helper.getObjectClass(java_stream_callbacks_global_ref); - auto java_on_send_window_available_method_id = - jni_helper.getMethodId(java_stream_callbacks_class.get(), "onSendWindowAvailable", - "(Lio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;)V"); + auto java_stream_callbacks_class = + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/engine/types/EnvoyHTTPCallbacks"); + auto java_on_send_window_available_method_id = jni_helper.getMethodIdFromCache( + java_stream_callbacks_class, "onSendWindowAvailable", + "(Lio/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel;)V"); jni_helper.callVoidMethod(java_stream_callbacks_global_ref, java_on_send_window_available_method_id, java_stream_intel.get()); }; @@ -1323,8 +1377,8 @@ Java_io_envoyproxy_envoymobile_engine_JniLibrary_callAddTestRootCertificateFromN std::vector cpp_cert; Envoy::JNI::javaByteArrayToByteVector(jni_helper, java_cert, &cpp_cert); jclass java_android_network_library_class = - jni_helper.findClass("io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary"); - jmethodID java_add_test_root_certificate_method_id = jni_helper.getStaticMethodId( + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary"); + jmethodID java_add_test_root_certificate_method_id = jni_helper.getStaticMethodIdFromCache( java_android_network_library_class, "addTestRootCertificate", "([B)V"); Envoy::JNI::LocalRefUniquePtr cert_array = Envoy::JNI::byteArrayToJavaByteArray(jni_helper, cpp_cert.data(), cpp_cert.size()); @@ -1337,8 +1391,8 @@ Java_io_envoyproxy_envoymobile_engine_JniLibrary_callClearTestRootCertificateFro jclass) { Envoy::JNI::JniHelper jni_helper(Envoy::JNI::JniHelper::getThreadLocalEnv()); jclass java_android_network_library_class = - jni_helper.findClass("io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary"); - jmethodID java_clear_test_root_certificates_method_id = jni_helper.getStaticMethodId( + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/utilities/AndroidNetworkLibrary"); + jmethodID java_clear_test_root_certificates_method_id = jni_helper.getStaticMethodIdFromCache( java_android_network_library_class, "clearTestRootCertificates", "()V"); jni_helper.callStaticVoidMethod(java_android_network_library_class, java_clear_test_root_certificates_method_id); diff --git a/mobile/library/jni/jni_utility.cc b/mobile/library/jni/jni_utility.cc index b2308b8ca979..d2f5a5cc01bb 100644 --- a/mobile/library/jni/jni_utility.cc +++ b/mobile/library/jni/jni_utility.cc @@ -11,6 +11,126 @@ namespace Envoy { namespace JNI { +void JniUtility::initCache() { + Envoy::JNI::JniHelper::addToCache("java/lang/Object", /* methods= */ {}, + /* static_methods = */ {}, /* fields= */ {}, + /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("java/lang/Integer", + /* methods= */ + { + {"", "(I)V"}, + {"intValue", "()I"}, + }, + /* static_methods = */ {}, /* fields= */ {}, + /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("java/lang/ClassLoader", /* methods= */ {}, + /* static_methods = */ {}, /* fields= */ {}, + /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("java/nio/ByteBuffer", + /* methods= */ + { + {"array", "()[B"}, + {"isDirect", "()Z"}, + }, + /* static_methods = */ + { + {"wrap", "([B)Ljava/nio/ByteBuffer;"}, + }, + /* fields= */ {}, /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("java/lang/Throwable", + /* methods= */ + { + {"getMessage", "()Ljava/lang/String;"}, + }, + /* static_methods = */ {}, /* fields= */ {}, + /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("java/lang/UnsupportedOperationException", + /* methods= */ {}, /* static_methods = */ {}, /* fields= */ {}, + /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("[B", /* methods= */ {}, /* static_methods = */ {}, + /* fields= */ {}, /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache( + "java/util/LinkedHashMap", /* methods= */ + { + {"", "()V"}, + {"put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"}, + {"get", "(Ljava/lang/Object;)Ljava/lang/Object;"}, + }, + /* static_methods = */ {}, /* fields= */ {}, /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("java/util/Map", /* methods= */ + { + {"entrySet", "()Ljava/util/Set;"}, + + }, + /* static_methods = */ {}, /* fields= */ {}, + /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("java/util/Map$Entry", /* methods= */ + { + {"getKey", "()Ljava/lang/Object;"}, + {"getValue", "()Ljava/lang/Object;"}, + + }, + /* static_methods = */ {}, /* fields= */ {}, + /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("java/util/Set", /* methods= */ + { + {"iterator", "()Ljava/util/Iterator;"}, + + }, + /* static_methods = */ {}, /* fields= */ {}, + /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("java/util/Iterator", /* methods= */ + {{"hasNext", "()Z"}, {"next", "()Ljava/lang/Object;"} + + }, + /* static_methods = */ {}, /* fields= */ {}, + /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache( + "java/util/HashMap", /* methods= */ + {{"", "(I)V"}, {"put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"}}, + /* static_methods = */ {}, /* fields= */ {}, /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache( + "java/util/List", /* methods= */ {{"size", "()I"}, {"get", "(I)Ljava/lang/Object;"}}, + /* static_methods = */ {}, /* fields= */ {}, /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache( + "java/util/ArrayList", /* methods= */ {{"", "()V"}, {"add", "(Ljava/lang/Object;)Z"}}, + /* static_methods = */ {}, /* fields= */ {}, /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("io/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel", + /* methods= */ + { + {"", "(JJJJ)V"}, + {"getStreamId", "()J"}, + {"getConnectionId", "()J"}, + {"getAttemptCount", "()J"}, + {"getConsumedBytesFromResponse", "()J"}, + }, + /* static_methods = */ {}, /* fields= */ {}, + /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("io/envoyproxy/envoymobile/engine/types/EnvoyFinalStreamIntel", + /* methods= */ + { + {"", "(JJJJJJJJJJJZJJJJ)V"}, + {"getStreamStartMs", "()J"}, + {"getDnsStartMs", "()J"}, + {"getDnsEndMs", "()J"}, + {"getConnectStartMs", "()J"}, + {"getConnectEndMs", "()J"}, + {"getSslStartMs", "()J"}, + {"getSslEndMs", "()J"}, + {"getSendingStartMs", "()J"}, + {"getSendingEndMs", "()J"}, + {"getResponseStartMs", "()J"}, + {"getStreamEndMs", "()J"}, + {"getSocketReused", "()Z"}, + {"getSentByteCount", "()J"}, + {"getReceivedByteCount", "()J"}, + {"getResponseFlags", "()J"}, + {"getUpstreamProtocol", "()J"}, + }, + /* static_methods = */ {}, /* fields= */ {}, + /* static_fields= */ {}); +} + void jniDeleteGlobalRef(void* context) { JNIEnv* env = JniHelper::getThreadLocalEnv(); jobject ref = static_cast(context); @@ -22,8 +142,8 @@ void jniDeleteConstGlobalRef(const void* context) { } int javaIntegerToCppInt(JniHelper& jni_helper, jobject boxed_integer) { - jclass jcls_Integer = jni_helper.findClass("java/lang/Integer"); - jmethodID jmid_intValue = jni_helper.getMethodId(jcls_Integer, "intValue", "()I"); + jclass jcls_Integer = jni_helper.findClassFromCache("java/lang/Integer"); + jmethodID jmid_intValue = jni_helper.getMethodIdFromCache(jcls_Integer, "intValue", "()I"); return jni_helper.callIntMethod(boxed_integer, jmid_intValue); } @@ -99,11 +219,11 @@ envoy_data javaByteBufferToEnvoyData(JniHelper& jni_helper, jobject j_data) { jlong data_length = jni_helper.getDirectBufferCapacity(j_data); if (data_length < 0) { - jclass jcls_ByteBuffer = jni_helper.findClass("java/nio/ByteBuffer"); + jclass jcls_ByteBuffer = jni_helper.findClassFromCache("java/nio/ByteBuffer"); // We skip checking hasArray() because only direct ByteBuffers or array-backed ByteBuffers // are supported. We will crash here if this is an invalid buffer, but guards may be // implemented in the JVM layer. - jmethodID jmid_array = jni_helper.getMethodId(jcls_ByteBuffer, "array", "()[B"); + jmethodID jmid_array = jni_helper.getMethodIdFromCache(jcls_ByteBuffer, "array", "()[B"); LocalRefUniquePtr array = jni_helper.callObjectMethod(j_data, jmid_array); envoy_data native_data = javaByteArrayToEnvoyData(jni_helper, array.get()); @@ -118,11 +238,11 @@ envoy_data javaByteBufferToEnvoyData(JniHelper& jni_helper, jobject j_data, jlon uint8_t* direct_address = jni_helper.getDirectBufferAddress(j_data); if (direct_address == nullptr) { - jclass jcls_ByteBuffer = jni_helper.findClass("java/nio/ByteBuffer"); + jclass jcls_ByteBuffer = jni_helper.findClassFromCache("java/nio/ByteBuffer"); // We skip checking hasArray() because only direct ByteBuffers or array-backed ByteBuffers // are supported. We will crash here if this is an invalid buffer, but guards may be // implemented in the JVM layer. - jmethodID jmid_array = jni_helper.getMethodId(jcls_ByteBuffer, "array", "()[B"); + jmethodID jmid_array = jni_helper.getMethodIdFromCache(jcls_ByteBuffer, "array", "()[B"); LocalRefUniquePtr array = jni_helper.callObjectMethod(j_data, jmid_array); envoy_data native_data = javaByteArrayToEnvoyData(jni_helper, array.get(), data_length); @@ -208,7 +328,7 @@ envoy_map javaArrayOfObjectArrayToEnvoyMap(JniHelper& jni_helper, jobjectArray e LocalRefUniquePtr envoyHeadersToJavaArrayOfObjectArray(JniHelper& jni_helper, const Envoy::Types::ManagedEnvoyHeaders& map) { - jclass jcls_byte_array = jni_helper.findClass("java/lang/Object"); + jclass jcls_byte_array = jni_helper.findClassFromCache("java/lang/Object"); LocalRefUniquePtr javaArray = jni_helper.newObjectArray(2 * map.get().length, jcls_byte_array, nullptr); @@ -227,7 +347,7 @@ envoyHeadersToJavaArrayOfObjectArray(JniHelper& jni_helper, LocalRefUniquePtr vectorStringToJavaArrayOfByteArray(JniHelper& jni_helper, const std::vector& v) { - jclass jcls_byte_array = jni_helper.findClass("[B"); + jclass jcls_byte_array = jni_helper.findClassFromCache("[B"); LocalRefUniquePtr joa = jni_helper.newObjectArray(v.size(), jcls_byte_array, nullptr); @@ -339,28 +459,28 @@ absl::flat_hash_map javaMapToCppMap(JniHelper& jni_hel jobject java_map) { absl::flat_hash_map cpp_map; - auto java_map_class = jni_helper.getObjectClass(java_map); + auto java_map_class = jni_helper.findClassFromCache("java/util/Map"); auto java_entry_set_method_id = - jni_helper.getMethodId(java_map_class.get(), "entrySet", "()Ljava/util/Set;"); + jni_helper.getMethodIdFromCache(java_map_class, "entrySet", "()Ljava/util/Set;"); auto java_entry_set_object = jni_helper.callObjectMethod(java_map, java_entry_set_method_id); - auto java_set_class = jni_helper.getObjectClass(java_entry_set_object.get()); - jclass java_map_entry_class = jni_helper.findClass("java/util/Map$Entry"); + auto java_set_class = jni_helper.findClassFromCache("java/util/Set"); + jclass java_map_entry_class = jni_helper.findClassFromCache("java/util/Map$Entry"); auto java_iterator_method_id = - jni_helper.getMethodId(java_set_class.get(), "iterator", "()Ljava/util/Iterator;"); + jni_helper.getMethodIdFromCache(java_set_class, "iterator", "()Ljava/util/Iterator;"); auto java_get_key_method_id = - jni_helper.getMethodId(java_map_entry_class, "getKey", "()Ljava/lang/Object;"); + jni_helper.getMethodIdFromCache(java_map_entry_class, "getKey", "()Ljava/lang/Object;"); auto java_get_value_method_id = - jni_helper.getMethodId(java_map_entry_class, "getValue", "()Ljava/lang/Object;"); + jni_helper.getMethodIdFromCache(java_map_entry_class, "getValue", "()Ljava/lang/Object;"); auto java_iterator_object = jni_helper.callObjectMethod(java_entry_set_object.get(), java_iterator_method_id); - auto java_iterator_class = jni_helper.getObjectClass(java_iterator_object.get()); + auto java_iterator_class = jni_helper.findClassFromCache("java/util/Iterator"); auto java_has_next_method_id = - jni_helper.getMethodId(java_iterator_class.get(), "hasNext", "()Z"); + jni_helper.getMethodIdFromCache(java_iterator_class, "hasNext", "()Z"); auto java_next_method_id = - jni_helper.getMethodId(java_iterator_class.get(), "next", "()Ljava/lang/Object;"); + jni_helper.getMethodIdFromCache(java_iterator_class, "next", "()Ljava/lang/Object;"); while (jni_helper.callBooleanMethod(java_iterator_object.get(), java_has_next_method_id)) { auto java_entry_object = @@ -381,18 +501,18 @@ absl::flat_hash_map javaMapToCppMap(JniHelper& jni_hel LocalRefUniquePtr cppHeadersToJavaHeaders(JniHelper& jni_helper, const Http::HeaderMap& cpp_headers) { // Use LinkedHashMap to preserve the insertion order. - jclass java_map_class = jni_helper.findClass("java/util/LinkedHashMap"); - auto java_map_init_method_id = jni_helper.getMethodId(java_map_class, "", "()V"); - auto java_map_put_method_id = jni_helper.getMethodId( + jclass java_map_class = jni_helper.findClassFromCache("java/util/LinkedHashMap"); + auto java_map_init_method_id = jni_helper.getMethodIdFromCache(java_map_class, "", "()V"); + auto java_map_put_method_id = jni_helper.getMethodIdFromCache( java_map_class, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); - auto java_map_get_method_id = - jni_helper.getMethodId(java_map_class, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); + auto java_map_get_method_id = jni_helper.getMethodIdFromCache( + java_map_class, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); auto java_map_object = jni_helper.newObject(java_map_class, java_map_init_method_id); - jclass java_list_class = jni_helper.findClass("java/util/ArrayList"); - auto java_list_init_method_id = jni_helper.getMethodId(java_list_class, "", "()V"); + jclass java_list_class = jni_helper.findClassFromCache("java/util/ArrayList"); + auto java_list_init_method_id = jni_helper.getMethodIdFromCache(java_list_class, "", "()V"); auto java_list_add_method_id = - jni_helper.getMethodId(java_list_class, "add", "(Ljava/lang/Object;)Z"); + jni_helper.getMethodIdFromCache(java_list_class, "add", "(Ljava/lang/Object;)Z"); cpp_headers.iterate([&](const Http::HeaderEntry& header) -> Http::HeaderMap::Iterate { std::string cpp_key = std::string(header.key().getStringView()); @@ -428,33 +548,33 @@ LocalRefUniquePtr cppHeadersToJavaHeaders(JniHelper& jni_helper, void javaHeadersToCppHeaders(JniHelper& jni_helper, jobject java_headers, Http::HeaderMap& cpp_headers) { - auto java_map_class = jni_helper.getObjectClass(java_headers); + auto java_map_class = jni_helper.findClassFromCache("java/util/Map"); auto java_entry_set_method_id = - jni_helper.getMethodId(java_map_class.get(), "entrySet", "()Ljava/util/Set;"); + jni_helper.getMethodIdFromCache(java_map_class, "entrySet", "()Ljava/util/Set;"); auto java_entry_set_object = jni_helper.callObjectMethod(java_headers, java_entry_set_method_id); - auto java_set_class = jni_helper.getObjectClass(java_entry_set_object.get()); - jclass java_map_entry_class = jni_helper.findClass("java/util/Map$Entry"); + auto java_set_class = jni_helper.findClassFromCache("java/util/Set"); + jclass java_map_entry_class = jni_helper.findClassFromCache("java/util/Map$Entry"); auto java_map_iter_method_id = - jni_helper.getMethodId(java_set_class.get(), "iterator", "()Ljava/util/Iterator;"); + jni_helper.getMethodIdFromCache(java_set_class, "iterator", "()Ljava/util/Iterator;"); auto java_map_get_key_method_id = - jni_helper.getMethodId(java_map_entry_class, "getKey", "()Ljava/lang/Object;"); + jni_helper.getMethodIdFromCache(java_map_entry_class, "getKey", "()Ljava/lang/Object;"); auto java_map_get_value_method_id = - jni_helper.getMethodId(java_map_entry_class, "getValue", "()Ljava/lang/Object;"); + jni_helper.getMethodIdFromCache(java_map_entry_class, "getValue", "()Ljava/lang/Object;"); auto java_iter_object = jni_helper.callObjectMethod(java_entry_set_object.get(), java_map_iter_method_id); - auto java_iterator_class = jni_helper.getObjectClass(java_iter_object.get()); + auto java_iterator_class = jni_helper.findClassFromCache("java/util/Iterator"); auto java_iter_has_next_method_id = - jni_helper.getMethodId(java_iterator_class.get(), "hasNext", "()Z"); + jni_helper.getMethodIdFromCache(java_iterator_class, "hasNext", "()Z"); auto java_iter_next_method_id = - jni_helper.getMethodId(java_iterator_class.get(), "next", "()Ljava/lang/Object;"); + jni_helper.getMethodIdFromCache(java_iterator_class, "next", "()Ljava/lang/Object;"); - jclass java_list_class = jni_helper.findClass("java/util/List"); - auto java_list_size_method_id = jni_helper.getMethodId(java_list_class, "size", "()I"); + jclass java_list_class = jni_helper.findClassFromCache("java/util/List"); + auto java_list_size_method_id = jni_helper.getMethodIdFromCache(java_list_class, "size", "()I"); auto java_list_get_method_id = - jni_helper.getMethodId(java_list_class, "get", "(I)Ljava/lang/Object;"); + jni_helper.getMethodIdFromCache(java_list_class, "get", "(I)Ljava/lang/Object;"); while (jni_helper.callBooleanMethod(java_iter_object.get(), java_iter_has_next_method_id)) { auto java_entry_object = @@ -481,9 +601,9 @@ void javaHeadersToCppHeaders(JniHelper& jni_helper, jobject java_headers, } bool isJavaDirectByteBuffer(JniHelper& jni_helper, jobject java_byte_buffer) { - jclass java_byte_buffer_class = jni_helper.findClass("java/nio/ByteBuffer"); + jclass java_byte_buffer_class = jni_helper.findClassFromCache("java/nio/ByteBuffer"); auto java_byte_buffer_is_direct_method_id = - jni_helper.getMethodId(java_byte_buffer_class, "isDirect", "()Z"); + jni_helper.getMethodIdFromCache(java_byte_buffer_class, "isDirect", "()Z"); return jni_helper.callBooleanMethod(java_byte_buffer, java_byte_buffer_is_direct_method_id); } @@ -519,9 +639,9 @@ LocalRefUniquePtr cppBufferInstanceToJavaDirectByteBuffer( Buffer::InstancePtr javaNonDirectByteBufferToCppBufferInstance(JniHelper& jni_helper, jobject java_byte_buffer, jlong length) { - jclass java_byte_buffer_class = jni_helper.findClass("java/nio/ByteBuffer"); + jclass java_byte_buffer_class = jni_helper.findClassFromCache("java/nio/ByteBuffer"); auto java_byte_buffer_array_method_id = - jni_helper.getMethodId(java_byte_buffer_class, "array", "()[B"); + jni_helper.getMethodIdFromCache(java_byte_buffer_class, "array", "()[B"); auto java_byte_array = jni_helper.callObjectMethod(java_byte_buffer, java_byte_buffer_array_method_id); ASSERT(java_byte_array != nullptr, "The ByteBuffer argument is not a non-direct ByteBuffer."); @@ -534,9 +654,9 @@ Buffer::InstancePtr javaNonDirectByteBufferToCppBufferInstance(JniHelper& jni_he LocalRefUniquePtr cppBufferInstanceToJavaNonDirectByteBuffer( JniHelper& jni_helper, const Buffer::Instance& cpp_buffer_instance, uint64_t length) { - jclass java_byte_buffer_class = jni_helper.findClass("java/nio/ByteBuffer"); - auto java_byte_buffer_wrap_method_id = - jni_helper.getStaticMethodId(java_byte_buffer_class, "wrap", "([B)Ljava/nio/ByteBuffer;"); + jclass java_byte_buffer_class = jni_helper.findClassFromCache("java/nio/ByteBuffer"); + auto java_byte_buffer_wrap_method_id = jni_helper.getStaticMethodIdFromCache( + java_byte_buffer_class, "wrap", "([B)Ljava/nio/ByteBuffer;"); auto java_byte_array = jni_helper.newByteArray(static_cast(cpp_buffer_instance.length())); auto java_byte_array_elements = jni_helper.getByteArrayElements(java_byte_array.get(), nullptr); cpp_buffer_instance.copyOut(0, length, static_cast(java_byte_array_elements.get())); @@ -545,9 +665,9 @@ LocalRefUniquePtr cppBufferInstanceToJavaNonDirectByteBuffer( } std::string getJavaExceptionMessage(JniHelper& jni_helper, jthrowable throwable) { - jclass java_throwable_class = jni_helper.findClass("java/lang/Throwable"); + jclass java_throwable_class = jni_helper.findClassFromCache("java/lang/Throwable"); auto java_get_message_method_id = - jni_helper.getMethodId(java_throwable_class, "getMessage", "()Ljava/lang/String;"); + jni_helper.getMethodIdFromCache(java_throwable_class, "getMessage", "()Ljava/lang/String;"); auto java_exception_message = jni_helper.callObjectMethod(throwable, java_get_message_method_id); return javaStringToCppString(jni_helper, java_exception_message.get()); @@ -555,19 +675,20 @@ std::string getJavaExceptionMessage(JniHelper& jni_helper, jthrowable throwable) envoy_stream_intel javaStreamIntelToCppStreamIntel(JniHelper& jni_helper, jobject java_stream_intel) { - auto java_stream_intel_class = jni_helper.getObjectClass(java_stream_intel); + auto java_stream_intel_class = + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel"); jlong java_stream_id = jni_helper.callLongMethod( java_stream_intel, - jni_helper.getMethodId(java_stream_intel_class.get(), "getStreamId", "()J")); + jni_helper.getMethodIdFromCache(java_stream_intel_class, "getStreamId", "()J")); jlong java_connection_id = jni_helper.callLongMethod( java_stream_intel, - jni_helper.getMethodId(java_stream_intel_class.get(), "getConnectionId", "()J")); + jni_helper.getMethodIdFromCache(java_stream_intel_class, "getConnectionId", "()J")); jlong java_attempt_count = jni_helper.callLongMethod( java_stream_intel, - jni_helper.getMethodId(java_stream_intel_class.get(), "getAttemptCount", "()J")); + jni_helper.getMethodIdFromCache(java_stream_intel_class, "getAttemptCount", "()J")); jlong java_consumed_bytes_from_response = jni_helper.callLongMethod( - java_stream_intel, - jni_helper.getMethodId(java_stream_intel_class.get(), "getConsumedBytesFromResponse", "()J")); + java_stream_intel, jni_helper.getMethodIdFromCache(java_stream_intel_class, + "getConsumedBytesFromResponse", "()J")); return { /* stream_id= */ static_cast(java_stream_id), @@ -580,9 +701,9 @@ envoy_stream_intel javaStreamIntelToCppStreamIntel(JniHelper& jni_helper, LocalRefUniquePtr cppStreamIntelToJavaStreamIntel(JniHelper& jni_helper, const envoy_stream_intel& stream_intel) { auto java_stream_intel_class = - jni_helper.findClass("io/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel"); + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel"); auto java_stream_intel_init_method_id = - jni_helper.getMethodId(java_stream_intel_class, "", "(JJJJ)V"); + jni_helper.getMethodIdFromCache(java_stream_intel_class, "", "(JJJJ)V"); return jni_helper.newObject(java_stream_intel_class, java_stream_intel_init_method_id, static_cast(stream_intel.stream_id), static_cast(stream_intel.connection_id), @@ -592,55 +713,56 @@ LocalRefUniquePtr cppStreamIntelToJavaStreamIntel(JniHelper& jni_helper envoy_final_stream_intel javaFinalStreamIntelToCppFinalStreamIntel(JniHelper& jni_helper, jobject java_final_stream_intel) { - auto java_final_stream_intel_class = jni_helper.getObjectClass(java_final_stream_intel); + auto java_final_stream_intel_class = + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/engine/types/EnvoyFinalStreamIntel"); jlong java_stream_start_ms = jni_helper.callLongMethod( java_final_stream_intel, - jni_helper.getMethodId(java_final_stream_intel_class.get(), "getStreamStartMs", "()J")); + jni_helper.getMethodIdFromCache(java_final_stream_intel_class, "getStreamStartMs", "()J")); jlong java_dns_start_ms = jni_helper.callLongMethod( java_final_stream_intel, - jni_helper.getMethodId(java_final_stream_intel_class.get(), "getDnsStartMs", "()J")); + jni_helper.getMethodIdFromCache(java_final_stream_intel_class, "getDnsStartMs", "()J")); jlong java_dns_end_ms = jni_helper.callLongMethod( java_final_stream_intel, - jni_helper.getMethodId(java_final_stream_intel_class.get(), "getDnsEndMs", "()J")); + jni_helper.getMethodIdFromCache(java_final_stream_intel_class, "getDnsEndMs", "()J")); jlong java_connect_start_ms = jni_helper.callLongMethod( java_final_stream_intel, - jni_helper.getMethodId(java_final_stream_intel_class.get(), "getConnectStartMs", "()J")); + jni_helper.getMethodIdFromCache(java_final_stream_intel_class, "getConnectStartMs", "()J")); jlong java_connect_end_ms = jni_helper.callLongMethod( java_final_stream_intel, - jni_helper.getMethodId(java_final_stream_intel_class.get(), "getConnectEndMs", "()J")); + jni_helper.getMethodIdFromCache(java_final_stream_intel_class, "getConnectEndMs", "()J")); jlong java_ssl_start_ms = jni_helper.callLongMethod( java_final_stream_intel, - jni_helper.getMethodId(java_final_stream_intel_class.get(), "getSslStartMs", "()J")); + jni_helper.getMethodIdFromCache(java_final_stream_intel_class, "getSslStartMs", "()J")); jlong java_ssl_end_ms = jni_helper.callLongMethod( java_final_stream_intel, - jni_helper.getMethodId(java_final_stream_intel_class.get(), "getSslEndMs", "()J")); + jni_helper.getMethodIdFromCache(java_final_stream_intel_class, "getSslEndMs", "()J")); jlong java_sending_start_ms = jni_helper.callLongMethod( java_final_stream_intel, - jni_helper.getMethodId(java_final_stream_intel_class.get(), "getSendingStartMs", "()J")); + jni_helper.getMethodIdFromCache(java_final_stream_intel_class, "getSendingStartMs", "()J")); jlong java_sending_end_ms = jni_helper.callLongMethod( java_final_stream_intel, - jni_helper.getMethodId(java_final_stream_intel_class.get(), "getSendingEndMs", "()J")); + jni_helper.getMethodIdFromCache(java_final_stream_intel_class, "getSendingEndMs", "()J")); jlong java_response_start_ms = jni_helper.callLongMethod( java_final_stream_intel, - jni_helper.getMethodId(java_final_stream_intel_class.get(), "getResponseStartMs", "()J")); + jni_helper.getMethodIdFromCache(java_final_stream_intel_class, "getResponseStartMs", "()J")); jlong java_stream_end_ms = jni_helper.callLongMethod( java_final_stream_intel, - jni_helper.getMethodId(java_final_stream_intel_class.get(), "getStreamEndMs", "()J")); + jni_helper.getMethodIdFromCache(java_final_stream_intel_class, "getStreamEndMs", "()J")); jboolean java_socket_reused = jni_helper.callBooleanMethod( java_final_stream_intel, - jni_helper.getMethodId(java_final_stream_intel_class.get(), "getSocketReused", "()Z")); + jni_helper.getMethodIdFromCache(java_final_stream_intel_class, "getSocketReused", "()Z")); jlong java_sent_byte_count = jni_helper.callLongMethod( java_final_stream_intel, - jni_helper.getMethodId(java_final_stream_intel_class.get(), "getSentByteCount", "()J")); + jni_helper.getMethodIdFromCache(java_final_stream_intel_class, "getSentByteCount", "()J")); jlong java_received_byte_count = jni_helper.callLongMethod( - java_final_stream_intel, - jni_helper.getMethodId(java_final_stream_intel_class.get(), "getReceivedByteCount", "()J")); + java_final_stream_intel, jni_helper.getMethodIdFromCache(java_final_stream_intel_class, + "getReceivedByteCount", "()J")); jlong java_response_flags = jni_helper.callLongMethod( java_final_stream_intel, - jni_helper.getMethodId(java_final_stream_intel_class.get(), "getResponseFlags", "()J")); + jni_helper.getMethodIdFromCache(java_final_stream_intel_class, "getResponseFlags", "()J")); jlong java_upstream_protocol = jni_helper.callLongMethod( java_final_stream_intel, - jni_helper.getMethodId(java_final_stream_intel_class.get(), "getUpstreamProtocol", "()J")); + jni_helper.getMethodIdFromCache(java_final_stream_intel_class, "getUpstreamProtocol", "()J")); return { /* stream_start_ms= */ static_cast(java_stream_start_ms), @@ -666,9 +788,9 @@ LocalRefUniquePtr cppFinalStreamIntelToJavaFinalStreamIntel(JniHelper& jni_helper, const envoy_final_stream_intel& final_stream_intel) { auto java_final_stream_intel_class = - jni_helper.findClass("io/envoyproxy/envoymobile/engine/types/EnvoyFinalStreamIntel"); - auto java_final_stream_intel_init_method_id = - jni_helper.getMethodId(java_final_stream_intel_class, "", "(JJJJJJJJJJJZJJJJ)V"); + jni_helper.findClassFromCache("io/envoyproxy/envoymobile/engine/types/EnvoyFinalStreamIntel"); + auto java_final_stream_intel_init_method_id = jni_helper.getMethodIdFromCache( + java_final_stream_intel_class, "", "(JJJJJJJJJJJZJJJJ)V"); return jni_helper.newObject(java_final_stream_intel_class, java_final_stream_intel_init_method_id, static_cast(final_stream_intel.stream_start_ms), static_cast(final_stream_intel.dns_start_ms), diff --git a/mobile/library/jni/jni_utility.h b/mobile/library/jni/jni_utility.h index 0ca38f400899..caef665927dc 100644 --- a/mobile/library/jni/jni_utility.h +++ b/mobile/library/jni/jni_utility.h @@ -16,6 +16,11 @@ namespace Envoy { namespace JNI { +struct JniUtility { + /** Initializes the caches for the `JniUtility`. */ + static void initCache(); +}; + void jniDeleteGlobalRef(void* context); void jniDeleteConstGlobalRef(const void* context); @@ -109,9 +114,9 @@ LocalRefUniquePtr cppStringToJavaString(JniHelper& jni_helper, /** Converts from C++'s map-type to Java `HashMap`. */ template LocalRefUniquePtr cppMapToJavaMap(JniHelper& jni_helper, const MapType& cpp_map) { - jclass java_map_class = jni_helper.findClass("java/util/HashMap"); - auto java_map_init_method_id = jni_helper.getMethodId(java_map_class, "", "(I)V"); - auto java_map_put_method_id = jni_helper.getMethodId( + jclass java_map_class = jni_helper.findClassFromCache("java/util/HashMap"); + auto java_map_init_method_id = jni_helper.getMethodIdFromCache(java_map_class, "", "(I)V"); + auto java_map_put_method_id = jni_helper.getMethodIdFromCache( java_map_class, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); auto java_map_object = jni_helper.newObject(java_map_class, java_map_init_method_id, cpp_map.size()); diff --git a/mobile/test/java/io/envoyproxy/envoymobile/jni/JniHelperTest.java b/mobile/test/java/io/envoyproxy/envoymobile/jni/JniHelperTest.java index 08b6a5f823cd..6f6ad4c2f96e 100644 --- a/mobile/test/java/io/envoyproxy/envoymobile/jni/JniHelperTest.java +++ b/mobile/test/java/io/envoyproxy/envoymobile/jni/JniHelperTest.java @@ -18,7 +18,11 @@ public class JniHelperTest { // Native methods for testing. //================================================================================ public static native void getFieldId(Class clazz, String name, String signature); + public static native void getFieldIdFromCache(String className, String fieldName, + String signature); public static native void getStaticFieldId(Class clazz, String name, String signature); + public static native void getStaticFieldIdFromCache(String className, String fieldName, + String signature); public static native byte getByteField(Class clazz, Object instance, String name, String signature); public static native char getCharField(Class clazz, Object instance, String name, @@ -38,8 +42,12 @@ public static native boolean getBooleanField(Class clazz, Object instance, St public static native Object getObjectField(Class clazz, Object instance, String name, String signature); public static native void getMethodId(Class clazz, String name, String signature); + public static native void getMethodIdFromCache(String className, String methodName, + String signature); public static native void getStaticMethodId(Class clazz, String name, String signature); - public static native Class findClass(String className); + public static native void getStaticMethodIdFromCache(String className, String methodName, + String signature); + public static native Class findClassFromCache(String className); public static native Class getObjectClass(Object object); public static native Object newObject(Class clazz, String name, String signature); public static native void throwNew(String className, String message); @@ -160,21 +168,33 @@ public static void staticVoidMethod() {} static class Foo { private final int field = 1; private static int staticField = 2; + private static void staticMethod() {} } @Test public void testGetFieldId() { + getFieldId(Foo.class, "field", "I"); + } + + @Test + public void testGetFieldIdFromCache() { // Do it in a loop to test the cache. for (int i = 0; i < 10; i++) { - getFieldId(Foo.class, "field", "I"); + getFieldIdFromCache("io/envoyproxy/envoymobile/jni/JniHelperTest$Foo", "field", "I"); } } @Test public void testGetStaticFieldId() { + getStaticFieldId(Foo.class, "staticField", "I"); + } + + @Test + public void testGetStaticFieldIdFromCache() { // Do it in a loop to test the cache. for (int i = 0; i < 10; i++) { - getStaticFieldId(Foo.class, "staticField", "I"); + getStaticFieldIdFromCache("io/envoyproxy/envoymobile/jni/JniHelperTest$Foo", "staticField", + "I"); } } @@ -226,25 +246,36 @@ public void testGetObjectField() { @Test public void testGetMethodId() { + getMethodId(Foo.class, "", "()V"); + } + + @Test + public void testGetMethodIdFromCache() { // Do it in a loop to test the cache. for (int i = 0; i < 10; i++) { - getMethodId(Foo.class, "", "()V"); + getMethodIdFromCache("io/envoyproxy/envoymobile/jni/JniHelperTest$Foo", "", "()V"); } } @Test public void testGetStaticMethodId() { + getStaticMethodId(Foo.class, "staticMethod", "()V"); + } + + @Test + public void testGetStaticMethodIdFromCache() { // Do it in a loop to test the cache. for (int i = 0; i < 10; i++) { - getStaticMethodId(JniHelperTest.class, "staticVoidMethod", "()V"); + getStaticMethodIdFromCache("io/envoyproxy/envoymobile/jni/JniHelperTest$Foo", "staticMethod", + "()V"); } } @Test - public void testFindClass() { + public void testFindClassFromCache() { // Do it in a loop to test the cache. for (int i = 0; i < 10; i++) { - assertThat(findClass("java/lang/Exception")).isEqualTo(Exception.class); + assertThat(findClassFromCache("java/lang/Exception")).isEqualTo(Exception.class); } } diff --git a/mobile/test/jni/jni_helper_test.cc b/mobile/test/jni/jni_helper_test.cc index b215c95bc677..15d4130781bb 100644 --- a/mobile/test/jni/jni_helper_test.cc +++ b/mobile/test/jni/jni_helper_test.cc @@ -11,8 +11,29 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /* reserved */) { Envoy::JNI::JniHelper::initialize(vm); - Envoy::JNI::JniHelper::addClassToCache("java/lang/Exception"); - Envoy::JNI::JniHelper::addClassToCache("java/lang/RuntimeException"); + Envoy::JNI::JniHelper::addToCache("java/lang/Exception", /* methods= */ {}, + /* static_methods= */ {}, /* fields= */ {}, + /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("java/lang/RuntimeException", /* methods= */ {}, + /* static_methods= */ {}, /* fields= */ {}, + /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache( + "io/envoyproxy/envoymobile/jni/JniHelperTest$Foo", /* methods= */ + { + {"", "()V"}, + }, + /* static_methods= */ + { + {"staticMethod", "()V"}, + }, + /* fields= */ + { + {"field", "I"}, + }, + /* static_fields= */ + { + {"staticField", "I"}, + }); return Envoy::JNI::JniHelper::getVersion(); } @@ -28,6 +49,19 @@ extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_jni_JniHelperTe jni_helper.getFieldId(clazz, name_ptr.get(), sig_ptr.get()); } +extern "C" JNIEXPORT void JNICALL +Java_io_envoyproxy_envoymobile_jni_JniHelperTest_getFieldIdFromCache(JNIEnv* env, jclass, + jstring class_name, + jstring field_name, + jstring signature) { + Envoy::JNI::JniHelper jni_helper(env); + Envoy::JNI::StringUtfUniquePtr class_name_ptr = jni_helper.getStringUtfChars(class_name, nullptr); + Envoy::JNI::StringUtfUniquePtr field_name_ptr = jni_helper.getStringUtfChars(field_name, nullptr); + Envoy::JNI::StringUtfUniquePtr sig_ptr = jni_helper.getStringUtfChars(signature, nullptr); + auto clazz = jni_helper.findClassFromCache(class_name_ptr.get()); + jni_helper.getFieldIdFromCache(clazz, field_name_ptr.get(), sig_ptr.get()); +} + extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_jni_JniHelperTest_getStaticFieldId( JNIEnv* env, jclass, jclass clazz, jstring name, jstring signature) { Envoy::JNI::JniHelper jni_helper(env); @@ -36,6 +70,19 @@ extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_jni_JniHelperTe jni_helper.getStaticFieldId(clazz, name_ptr.get(), sig_ptr.get()); } +extern "C" JNIEXPORT void JNICALL +Java_io_envoyproxy_envoymobile_jni_JniHelperTest_getStaticFieldIdFromCache(JNIEnv* env, jclass, + jstring class_name, + jstring field_name, + jstring signature) { + Envoy::JNI::JniHelper jni_helper(env); + Envoy::JNI::StringUtfUniquePtr class_name_ptr = jni_helper.getStringUtfChars(class_name, nullptr); + Envoy::JNI::StringUtfUniquePtr field_name_ptr = jni_helper.getStringUtfChars(field_name, nullptr); + Envoy::JNI::StringUtfUniquePtr sig_ptr = jni_helper.getStringUtfChars(signature, nullptr); + auto clazz = jni_helper.findClassFromCache(class_name_ptr.get()); + jni_helper.getStaticFieldIdFromCache(clazz, field_name_ptr.get(), sig_ptr.get()); +} + #define DEFINE_JNI_GET_FIELD(JAVA_TYPE, JNI_TYPE) \ extern "C" JNIEXPORT JNI_TYPE JNICALL \ Java_io_envoyproxy_envoymobile_jni_JniHelperTest_get##JAVA_TYPE##Field( \ @@ -75,6 +122,20 @@ extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_jni_JniHelperTe jni_helper.getMethodId(clazz, name_ptr.get(), sig_ptr.get()); } +extern "C" JNIEXPORT void JNICALL +Java_io_envoyproxy_envoymobile_jni_JniHelperTest_getMethodIdFromCache(JNIEnv* env, jclass, + jstring class_name, + jstring method_name, + jstring signature) { + Envoy::JNI::JniHelper jni_helper(env); + Envoy::JNI::StringUtfUniquePtr class_name_ptr = jni_helper.getStringUtfChars(class_name, nullptr); + Envoy::JNI::StringUtfUniquePtr method_name_ptr = + jni_helper.getStringUtfChars(method_name, nullptr); + Envoy::JNI::StringUtfUniquePtr sig_ptr = jni_helper.getStringUtfChars(signature, nullptr); + auto clazz = jni_helper.findClassFromCache(class_name_ptr.get()); + jni_helper.getMethodIdFromCache(clazz, method_name_ptr.get(), sig_ptr.get()); +} + extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_jni_JniHelperTest_getStaticMethodId(JNIEnv* env, jclass, jclass clazz, jstring name, @@ -85,11 +146,26 @@ Java_io_envoyproxy_envoymobile_jni_JniHelperTest_getStaticMethodId(JNIEnv* env, jni_helper.getStaticMethodId(clazz, name_ptr.get(), sig_ptr.get()); } -extern "C" JNIEXPORT jclass JNICALL Java_io_envoyproxy_envoymobile_jni_JniHelperTest_findClass( - JNIEnv* env, jclass, jstring class_name) { +extern "C" JNIEXPORT void JNICALL +Java_io_envoyproxy_envoymobile_jni_JniHelperTest_getStaticMethodIdFromCache(JNIEnv* env, jclass, + jstring class_name, + jstring method_name, + jstring signature) { + Envoy::JNI::JniHelper jni_helper(env); + Envoy::JNI::StringUtfUniquePtr class_name_ptr = jni_helper.getStringUtfChars(class_name, nullptr); + Envoy::JNI::StringUtfUniquePtr method_name_ptr = + jni_helper.getStringUtfChars(method_name, nullptr); + Envoy::JNI::StringUtfUniquePtr sig_ptr = jni_helper.getStringUtfChars(signature, nullptr); + auto clazz = jni_helper.findClassFromCache(class_name_ptr.get()); + jni_helper.getStaticMethodIdFromCache(clazz, method_name_ptr.get(), sig_ptr.get()); +} + +extern "C" JNIEXPORT jclass JNICALL +Java_io_envoyproxy_envoymobile_jni_JniHelperTest_findClassFromCache(JNIEnv* env, jclass, + jstring class_name) { Envoy::JNI::JniHelper jni_helper(env); Envoy::JNI::StringUtfUniquePtr class_name_ptr = jni_helper.getStringUtfChars(class_name, nullptr); - return jni_helper.findClass(class_name_ptr.get()); + return jni_helper.findClassFromCache(class_name_ptr.get()); } extern "C" JNIEXPORT jclass JNICALL Java_io_envoyproxy_envoymobile_jni_JniHelperTest_getObjectClass( diff --git a/mobile/test/jni/jni_http_proxy_test_server_factory.cc b/mobile/test/jni/jni_http_proxy_test_server_factory.cc index 877dfa81dcfb..8167402060ef 100644 --- a/mobile/test/jni/jni_http_proxy_test_server_factory.cc +++ b/mobile/test/jni/jni_http_proxy_test_server_factory.cc @@ -9,9 +9,17 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /* reserved */) { Envoy::JNI::JniHelper::initialize(vm); - Envoy::JNI::JniHelper::addClassToCache("java/util/Map$Entry"); - Envoy::JNI::JniHelper::addClassToCache( - "io/envoyproxy/envoymobile/engine/testing/HttpProxyTestServerFactory$HttpProxyTestServer"); + Envoy::JNI::JniHelper::addToCache( + "io/envoyproxy/envoymobile/engine/testing/HttpProxyTestServerFactory$HttpProxyTestServer", + /* methods= */ + { + {"", "(JI)V"}, + }, + /* static_methods= */ {}, /* fields= */ + { + {"handle", "J"}, + }, + /* static_fields= */ {}); return Envoy::JNI::JniHelper::getVersion(); } @@ -24,10 +32,10 @@ Java_io_envoyproxy_envoymobile_engine_testing_HttpProxyTestServerFactory_start(J Envoy::TestServer* test_server = new Envoy::TestServer(); test_server->start(static_cast(type), 0); - jclass java_http_proxy_server_factory_class = jni_helper.findClass( + jclass java_http_proxy_server_factory_class = jni_helper.findClassFromCache( "io/envoyproxy/envoymobile/engine/testing/HttpProxyTestServerFactory$HttpProxyTestServer"); auto java_init_method_id = - jni_helper.getMethodId(java_http_proxy_server_factory_class, "", "(JI)V"); + jni_helper.getMethodIdFromCache(java_http_proxy_server_factory_class, "", "(JI)V"); int port = test_server->getPort(); return jni_helper .newObject(java_http_proxy_server_factory_class, java_init_method_id, @@ -39,8 +47,9 @@ extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_HttpProxyTestServerFactory_00024HttpProxyTestServer_shutdown( JNIEnv* env, jobject instance) { Envoy::JNI::JniHelper jni_helper(env); - auto java_class = jni_helper.getObjectClass(instance); - auto java_handle_field_id = jni_helper.getFieldId(java_class.get(), "handle", "J"); + auto java_class = jni_helper.findClassFromCache( + "io/envoyproxy/envoymobile/engine/testing/HttpProxyTestServerFactory$HttpProxyTestServer"); + auto java_handle_field_id = jni_helper.getFieldIdFromCache(java_class, "handle", "J"); jlong java_handle = jni_helper.getLongField(instance, java_handle_field_id); Envoy::TestServer* test_server = reinterpret_cast(java_handle); test_server->shutdown(); diff --git a/mobile/test/jni/jni_http_test_server_factory.cc b/mobile/test/jni/jni_http_test_server_factory.cc index eb68d597007d..16348bad04b4 100644 --- a/mobile/test/jni/jni_http_test_server_factory.cc +++ b/mobile/test/jni/jni_http_test_server_factory.cc @@ -10,9 +10,47 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /* reserved */) { Envoy::JNI::JniHelper::initialize(vm); - Envoy::JNI::JniHelper::addClassToCache("java/util/Map$Entry"); - Envoy::JNI::JniHelper::addClassToCache( - "io/envoyproxy/envoymobile/engine/testing/HttpTestServerFactory$HttpTestServer"); + Envoy::JNI::JniHelper::addToCache("java/util/Map", /* methods= */ + { + {"entrySet", "()Ljava/util/Set;"}, + + }, + /* static_methods = */ {}, + /* fields= */ {}, /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("java/util/Map$Entry", /* methods= */ + { + {"getKey", "()Ljava/lang/Object;"}, + {"getValue", "()Ljava/lang/Object;"}, + + }, + /* static_methods = */ {}, + /* fields= */ {}, /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("java/util/Set", /* methods= */ + { + {"iterator", "()Ljava/util/Iterator;"}, + + }, + /* static_methods = */ {}, + /* fields= */ {}, /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache("java/util/Iterator", /* methods= */ + { + {"hasNext", "()Z"}, + {"next", "()Ljava/lang/Object;"}, + + }, + /* static_methods = */ {}, + /* fields= */ {}, /* static_fields= */ {}); + Envoy::JNI::JniHelper::addToCache( + "io/envoyproxy/envoymobile/engine/testing/HttpTestServerFactory$HttpTestServer", + /* methods= */ + { + {"", "(JLjava/lang/String;ILjava/lang/String;)V"}, + }, + /* static_methods= */ {}, /* fields= */ + { + {"handle", "J"}, + }, + /* static_fields= */ {}); return Envoy::JNI::JniHelper::getVersion(); } @@ -31,10 +69,10 @@ Java_io_envoyproxy_envoymobile_engine_testing_HttpTestServerFactory_start( auto cpp_trailers = Envoy::JNI::javaMapToCppMap(jni_helper, trailers); test_server->setResponse(cpp_headers, cpp_body, cpp_trailers); - jclass java_http_server_factory_class = jni_helper.findClass( + jclass java_http_server_factory_class = jni_helper.findClassFromCache( "io/envoyproxy/envoymobile/engine/testing/HttpTestServerFactory$HttpTestServer"); - auto java_init_method_id = jni_helper.getMethodId(java_http_server_factory_class, "", - "(JLjava/lang/String;ILjava/lang/String;)V"); + auto java_init_method_id = jni_helper.getMethodIdFromCache( + java_http_server_factory_class, "", "(JLjava/lang/String;ILjava/lang/String;)V"); auto ip_address = Envoy::JNI::cppStringToJavaString(jni_helper, test_server->getIpAddress()); int port = test_server->getPort(); auto address = Envoy::JNI::cppStringToJavaString(jni_helper, test_server->getAddress()); @@ -49,8 +87,9 @@ extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_testing_HttpTestServerFactory_00024HttpTestServer_shutdown( JNIEnv* env, jobject instance) { Envoy::JNI::JniHelper jni_helper(env); - auto java_class = jni_helper.getObjectClass(instance); - auto java_handle_field_id = jni_helper.getFieldId(java_class.get(), "handle", "J"); + auto java_class = jni_helper.findClassFromCache( + "io/envoyproxy/envoymobile/engine/testing/HttpTestServerFactory$HttpTestServer"); + auto java_handle_field_id = jni_helper.getFieldIdFromCache(java_class, "handle", "J"); jlong java_handle = jni_helper.getLongField(instance, java_handle_field_id); Envoy::TestServer* test_server = reinterpret_cast(java_handle); test_server->shutdown(); diff --git a/mobile/test/jni/jni_utility_test.cc b/mobile/test/jni/jni_utility_test.cc index b3903d67e122..9799ddeb3dc9 100644 --- a/mobile/test/jni/jni_utility_test.cc +++ b/mobile/test/jni/jni_utility_test.cc @@ -13,20 +13,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /* reserved */) { Envoy::JNI::JniHelper::initialize(vm); - Envoy::JNI::JniHelper::addClassToCache("java/lang/Object"); - Envoy::JNI::JniHelper::addClassToCache("java/lang/Integer"); - Envoy::JNI::JniHelper::addClassToCache("java/lang/ClassLoader"); - Envoy::JNI::JniHelper::addClassToCache("java/nio/ByteBuffer"); - Envoy::JNI::JniHelper::addClassToCache("java/lang/Throwable"); - Envoy::JNI::JniHelper::addClassToCache("[B"); - Envoy::JNI::JniHelper::addClassToCache("java/util/Map$Entry"); - Envoy::JNI::JniHelper::addClassToCache("java/util/LinkedHashMap"); - Envoy::JNI::JniHelper::addClassToCache("java/util/HashMap"); - Envoy::JNI::JniHelper::addClassToCache("java/util/List"); - Envoy::JNI::JniHelper::addClassToCache("java/util/ArrayList"); - Envoy::JNI::JniHelper::addClassToCache("io/envoyproxy/envoymobile/engine/types/EnvoyStreamIntel"); - Envoy::JNI::JniHelper::addClassToCache( - "io/envoyproxy/envoymobile/engine/types/EnvoyFinalStreamIntel"); + Envoy::JNI::JniUtility::initCache(); return Envoy::JNI::JniHelper::getVersion(); } From 40dddc9ee82e63ef06903efac822a08e031473b6 Mon Sep 17 00:00:00 2001 From: "Antonio V. Leonti" <53806445+antoniovleonti@users.noreply.github.com> Date: Thu, 15 Aug 2024 13:18:47 -0400 Subject: [PATCH 112/130] =?UTF-8?q?Reapply=20"implement=20per-route=20over?= =?UTF-8?q?ride=20for=20rbac=20stat=20prefixes=20(#35531)=E2=80=A6=20(#356?= =?UTF-8?q?93)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …" (#35655) This reverts commit 7ae5ca51350a195ae653fb1928c8855305c595a0. The flakiness in https://github.com/envoyproxy/envoy/issues/35653 is not caused by this PR. If you look at [the failure](https://btx.cloud.google.com/invocations/08f19e64-5934-4ed9-9f4c-e0d14e113360/targets/%2F%2Ftest%2Fextensions%2Ffilters%2Fnetwork%2Frbac:integration_test;config=a48d3689347bfb6aaa2bb9b4c87edaab1bb2d2c4710bec9b6690537c48f15140/log) (you can also find this by going to the issue and clicking the link), the failing test is `RoleBasedAccessControlNetworkFilterIntegrationTest.DelayDenied`. That test was added in https://github.com/envoyproxy/envoy/pull/33875 and is still flaky at head. Signed-off-by: antoniovleonti --- changelogs/current.yaml | 4 + .../filters/http/rbac/rbac_filter.cc | 39 +++++- .../filters/http/rbac/rbac_filter.h | 25 ++-- .../filters/http/rbac/rbac_filter_test.cc | 128 ++++++++++++++++++ 4 files changed, 174 insertions(+), 22 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 3a089b6852b2..4d58495ab580 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -79,6 +79,10 @@ bug_fixes: change: | Add runtime guard for timeout error code 504 Gateway Timeout that is returned to downstream. If runtime flag ``envoy.reloadable_features.ext_proc_timeout_error`` is set to false, old error code 500 Internal Server Error will be returned. +- area: rbac + change: | + RBAC will now allow stat prefixes configured in per-route config to override the base config's + stat prefix. removed_config_or_runtime: # *Normally occurs at the end of the* :ref:`deprecation period ` diff --git a/source/extensions/filters/http/rbac/rbac_filter.cc b/source/extensions/filters/http/rbac/rbac_filter.cc index cefa97f010c4..e55924cfc861 100644 --- a/source/extensions/filters/http/rbac/rbac_filter.cc +++ b/source/extensions/filters/http/rbac/rbac_filter.cc @@ -1,3 +1,4 @@ +#include "rbac_filter.h" #include "source/extensions/filters/http/rbac/rbac_filter.h" #include "envoy/stats/scope.h" @@ -74,6 +75,29 @@ RoleBasedAccessControlFilterConfig::RoleBasedAccessControlFilterConfig( shadow_engine_(Filters::Common::RBAC::createShadowEngine( proto_config, context, validation_visitor, action_validation_visitor_)) {} +#define DEFINE_DYNAMIC_METADATA_STAT_KEY_GETTER(GETTER_NAME, PREFIX, ROUTE_LOCAL_PREFIX_OVERRIDE, \ + DYNAMIC_METADATA_KEY) \ + std::string RoleBasedAccessControlFilterConfig::GETTER_NAME( \ + const Http::StreamFilterCallbacks* callbacks) const { \ + const auto* route_local = Http::Utility::resolveMostSpecificPerFilterConfig< \ + RoleBasedAccessControlRouteSpecificFilterConfig>(callbacks); \ + std::string prefix = PREFIX; \ + if (route_local && !route_local->ROUTE_LOCAL_PREFIX_OVERRIDE().empty()) { \ + prefix = route_local->ROUTE_LOCAL_PREFIX_OVERRIDE(); \ + } \ + return prefix + \ + Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().DYNAMIC_METADATA_KEY; \ + } + +DEFINE_DYNAMIC_METADATA_STAT_KEY_GETTER(shadowEffectivePolicyIdField, shadow_rules_stat_prefix_, + shadowRulesStatPrefix, ShadowEffectivePolicyIdField) +DEFINE_DYNAMIC_METADATA_STAT_KEY_GETTER(shadowEngineResultField, shadow_rules_stat_prefix_, + shadowRulesStatPrefix, ShadowEngineResultField) +DEFINE_DYNAMIC_METADATA_STAT_KEY_GETTER(enforcedEffectivePolicyIdField, rules_stat_prefix_, + rulesStatPrefix, EnforcedEffectivePolicyIdField) +DEFINE_DYNAMIC_METADATA_STAT_KEY_GETTER(enforcedEngineResultField, rules_stat_prefix_, + rulesStatPrefix, EnforcedEngineResultField) + const Filters::Common::RBAC::RoleBasedAccessControlEngine* RoleBasedAccessControlFilterConfig::engine(const Http::StreamFilterCallbacks* callbacks, Filters::Common::RBAC::EnforcementMode mode) const { @@ -103,7 +127,9 @@ RoleBasedAccessControlRouteSpecificFilterConfig::RoleBasedAccessControlRouteSpec const envoy::extensions::filters::http::rbac::v3::RBACPerRoute& per_route_config, Server::Configuration::ServerFactoryContext& context, ProtobufMessage::ValidationVisitor& validation_visitor) - : per_rule_stats_(per_route_config.rbac().track_per_rule_stats()) { + : rules_stat_prefix_(per_route_config.rbac().rules_stat_prefix()), + shadow_rules_stat_prefix_(per_route_config.rbac().shadow_rules_stat_prefix()), + per_rule_stats_(per_route_config.rbac().track_per_rule_stats()) { // Moved from member initializer to ctor body to overcome clang false warning about memory // leak (clang-analyzer-cplusplus.NewDeleteLeaks,-warnings-as-errors). // Potentially https://lists.llvm.org/pipermail/llvm-bugs/2018-July/066769.html @@ -165,10 +191,11 @@ RoleBasedAccessControlFilter::decodeHeaders(Http::RequestHeaderMap& headers, boo } if (!effective_policy_id.empty()) { - *fields[config_->shadowEffectivePolicyIdField()].mutable_string_value() = effective_policy_id; + *fields[config_->shadowEffectivePolicyIdField(callbacks_)].mutable_string_value() = + effective_policy_id; } - *fields[config_->shadowEngineResultField()].mutable_string_value() = shadow_resp_code; + *fields[config_->shadowEngineResultField(callbacks_)].mutable_string_value() = shadow_resp_code; } const auto engine = config_->engine(callbacks_, Filters::Common::RBAC::EnforcementMode::Enforced); @@ -178,7 +205,7 @@ RoleBasedAccessControlFilter::decodeHeaders(Http::RequestHeaderMap& headers, boo callbacks_->streamInfo(), &effective_policy_id); const std::string log_policy_id = effective_policy_id.empty() ? "none" : effective_policy_id; if (!effective_policy_id.empty()) { - *fields[config_->enforcedEffectivePolicyIdField()].mutable_string_value() = + *fields[config_->enforcedEffectivePolicyIdField(callbacks_)].mutable_string_value() = effective_policy_id; } if (allowed) { @@ -188,7 +215,7 @@ RoleBasedAccessControlFilter::decodeHeaders(Http::RequestHeaderMap& headers, boo config_->stats().incPolicyAllowed(effective_policy_id); } - *fields[config_->enforcedEngineResultField()].mutable_string_value() = + *fields[config_->enforcedEngineResultField(callbacks_)].mutable_string_value() = Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().EngineResultAllowed; callbacks_->streamInfo().setDynamicMetadata("envoy.filters.http.rbac", metrics); @@ -203,7 +230,7 @@ RoleBasedAccessControlFilter::decodeHeaders(Http::RequestHeaderMap& headers, boo config_->stats().incPolicyDenied(effective_policy_id); } - *fields[config_->enforcedEngineResultField()].mutable_string_value() = + *fields[config_->enforcedEngineResultField(callbacks_)].mutable_string_value() = Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().EngineResultDenied; callbacks_->streamInfo().setDynamicMetadata("envoy.filters.http.rbac", metrics); diff --git a/source/extensions/filters/http/rbac/rbac_filter.h b/source/extensions/filters/http/rbac/rbac_filter.h index 9f021cb94e98..bb6748691fd2 100644 --- a/source/extensions/filters/http/rbac/rbac_filter.h +++ b/source/extensions/filters/http/rbac/rbac_filter.h @@ -35,10 +35,15 @@ class RoleBasedAccessControlRouteSpecificFilterConfig : public Router::RouteSpec return mode == Filters::Common::RBAC::EnforcementMode::Enforced ? engine_.get() : shadow_engine_.get(); } + std::string rulesStatPrefix() const { return rules_stat_prefix_; } + + std::string shadowRulesStatPrefix() const { return shadow_rules_stat_prefix_; } bool perRuleStatsEnabled() const { return per_rule_stats_; } private: + const std::string rules_stat_prefix_; + const std::string shadow_rules_stat_prefix_; ActionValidationVisitor action_validation_visitor_; std::unique_ptr engine_; std::unique_ptr shadow_engine_; @@ -57,23 +62,11 @@ class RoleBasedAccessControlFilterConfig { ProtobufMessage::ValidationVisitor& validation_visitor); Filters::Common::RBAC::RoleBasedAccessControlFilterStats& stats() { return stats_; } - std::string shadowEffectivePolicyIdField() const { - return shadow_rules_stat_prefix_ + - Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().ShadowEffectivePolicyIdField; - } - std::string shadowEngineResultField() const { - return shadow_rules_stat_prefix_ + - Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().ShadowEngineResultField; - } + std::string shadowEffectivePolicyIdField(const Http::StreamFilterCallbacks* callbacks) const; + std::string shadowEngineResultField(const Http::StreamFilterCallbacks* callbacks) const; - std::string enforcedEffectivePolicyIdField() const { - return rules_stat_prefix_ + Filters::Common::RBAC::DynamicMetadataKeysSingleton::get() - .EnforcedEffectivePolicyIdField; - } - std::string enforcedEngineResultField() const { - return rules_stat_prefix_ + - Filters::Common::RBAC::DynamicMetadataKeysSingleton::get().EnforcedEngineResultField; - } + std::string enforcedEffectivePolicyIdField(const Http::StreamFilterCallbacks* callbacks) const; + std::string enforcedEngineResultField(const Http::StreamFilterCallbacks* callbacks) const; const Filters::Common::RBAC::RoleBasedAccessControlEngine* engine(const Http::StreamFilterCallbacks* callbacks, diff --git a/test/extensions/filters/http/rbac/rbac_filter_test.cc b/test/extensions/filters/http/rbac/rbac_filter_test.cc index 2d772457a176..3877ab4046bc 100644 --- a/test/extensions/filters/http/rbac/rbac_filter_test.cc +++ b/test/extensions/filters/http/rbac/rbac_filter_test.cc @@ -576,6 +576,134 @@ TEST_F(RoleBasedAccessControlFilterTest, RouteLocalOverride) { checkAccessLogMetadata(LogResult::Undecided); } +TEST_F(RoleBasedAccessControlFilterTest, RouteLocalOverrideDynamicMetadataStats) { + setupPolicy(envoy::config::rbac::v3::RBAC::DENY, "original_rbac_rules_prefix_"); + + setDestinationPort(123); + setMetadata(); + + // Set up an allow route_config that overrides the deny policy. + envoy::extensions::filters::http::rbac::v3::RBACPerRoute route_config; + route_config.mutable_rbac()->mutable_rules()->set_action(envoy::config::rbac::v3::RBAC::ALLOW); + route_config.mutable_rbac()->mutable_shadow_rules()->set_action( + envoy::config::rbac::v3::RBAC::ALLOW); + route_config.mutable_rbac()->set_rules_stat_prefix("override_rules_stat_prefix_"); + route_config.mutable_rbac()->set_shadow_rules_stat_prefix("override_shadow_rules_stat_prefix_"); + + envoy::config::rbac::v3::Policy policy; + auto policy_rules = policy.add_permissions()->mutable_or_rules(); + policy_rules->add_rules()->set_destination_port(123); + policy.add_principals()->set_any(true); + + envoy::config::rbac::v3::Policy shadow_policy; + auto shadow_policy_rules = shadow_policy.add_permissions()->mutable_or_rules(); + shadow_policy_rules->add_rules()->set_destination_port(123); + shadow_policy.add_principals()->set_any(true); + + (*route_config.mutable_rbac()->mutable_rules()->mutable_policies())["foobar"] = policy; + (*route_config.mutable_rbac()->mutable_shadow_rules()->mutable_policies())["foobar"] = + shadow_policy; + NiceMock factory_context; + NiceMock engine{route_config.rbac().rules(), factory_context}; + NiceMock per_route_config_{route_config, + context_}; + + EXPECT_CALL(engine, handleAction(_, _, _, _)).WillRepeatedly(Return(true)); + EXPECT_CALL(per_route_config_, engine()).WillRepeatedly(ReturnRef(engine)); + + EXPECT_CALL(*callbacks_.route_, mostSpecificPerFilterConfig(_)) + .WillRepeatedly(Return(&per_route_config_)); + + // Filter iteration should continue since the route-specific policy is ALLOW + // and there are enforced and shadow rules. + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(headers_, true)); + ASSERT_TRUE(req_info_.dynamicMetadata().filter_metadata().contains("envoy.filters.http.rbac")); + auto filter_meta = req_info_.dynamicMetadata().filter_metadata().at("envoy.filters.http.rbac"); + + // We expect the route-specific rules and prefix to be used for the enforced + // engine and the shadow rules and prefix to be used for the shadow engine. + ASSERT_TRUE(filter_meta.fields().contains("override_rules_stat_prefix_enforced_engine_result")); + EXPECT_EQ( + "allowed", + filter_meta.fields().at("override_rules_stat_prefix_enforced_engine_result").string_value()); + ASSERT_TRUE( + filter_meta.fields().contains("override_rules_stat_prefix_enforced_effective_policy_id")); + EXPECT_EQ("foobar", filter_meta.fields() + .at("override_rules_stat_prefix_enforced_effective_policy_id") + .string_value()); + + ASSERT_TRUE( + filter_meta.fields().contains("override_shadow_rules_stat_prefix_shadow_engine_result")); + EXPECT_EQ("allowed", filter_meta.fields() + .at("override_shadow_rules_stat_prefix_shadow_engine_result") + .string_value()); + ASSERT_TRUE(filter_meta.fields().contains( + "override_shadow_rules_stat_prefix_shadow_effective_policy_id")); + EXPECT_EQ("foobar", filter_meta.fields() + .at("override_shadow_rules_stat_prefix_shadow_effective_policy_id") + .string_value()); +} + +TEST_F(RoleBasedAccessControlFilterTest, NoRouteLocalOverrideDynamicMetadataStatsEmpty) { + setupPolicy(envoy::config::rbac::v3::RBAC::DENY, "rules_stat_prefix_"); + + setDestinationPort(123); + setMetadata(); + + // Set up an allow route_config that overrides the deny policy. But do not set up stat prefixes. + envoy::extensions::filters::http::rbac::v3::RBACPerRoute route_config; + route_config.mutable_rbac()->mutable_rules()->set_action(envoy::config::rbac::v3::RBAC::ALLOW); + route_config.mutable_rbac()->mutable_shadow_rules()->set_action( + envoy::config::rbac::v3::RBAC::ALLOW); + + envoy::config::rbac::v3::Policy policy; + auto policy_rules = policy.add_permissions()->mutable_or_rules(); + policy_rules->add_rules()->set_destination_port(123); + policy.add_principals()->set_any(true); + + envoy::config::rbac::v3::Policy shadow_policy; + auto shadow_policy_rules = shadow_policy.add_permissions()->mutable_or_rules(); + shadow_policy_rules->add_rules()->set_destination_port(123); + shadow_policy.add_principals()->set_any(true); + + (*route_config.mutable_rbac()->mutable_rules()->mutable_policies())["foobar"] = policy; + (*route_config.mutable_rbac()->mutable_shadow_rules()->mutable_policies())["foobar"] = + shadow_policy; + NiceMock factory_context; + NiceMock engine{route_config.rbac().rules(), factory_context}; + NiceMock per_route_config_{route_config, + context_}; + + EXPECT_CALL(engine, handleAction(_, _, _, _)).WillRepeatedly(Return(true)); + EXPECT_CALL(per_route_config_, engine()).WillRepeatedly(ReturnRef(engine)); + + EXPECT_CALL(*callbacks_.route_, mostSpecificPerFilterConfig(_)) + .WillRepeatedly(Return(&per_route_config_)); + + // Filter iteration should continue since the route-specific policy is ALLOW and there are + // enforced and shadow rules. + EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(headers_, true)); + ASSERT_TRUE(req_info_.dynamicMetadata().filter_metadata().contains("envoy.filters.http.rbac")); + auto filter_meta = req_info_.dynamicMetadata().filter_metadata().at("envoy.filters.http.rbac"); + + // We expect the base rules and prefix to be used since no route-specific stat was set up. + ASSERT_TRUE(filter_meta.fields().contains("rules_stat_prefix_enforced_engine_result")); + EXPECT_EQ("allowed", + filter_meta.fields().at("rules_stat_prefix_enforced_engine_result").string_value()); + ASSERT_TRUE(filter_meta.fields().contains("rules_stat_prefix_enforced_effective_policy_id")); + EXPECT_EQ( + "foobar", + filter_meta.fields().at("rules_stat_prefix_enforced_effective_policy_id").string_value()); + + ASSERT_TRUE(filter_meta.fields().contains("shadow_rules_prefix_shadow_engine_result")); + EXPECT_EQ("allowed", + filter_meta.fields().at("shadow_rules_prefix_shadow_engine_result").string_value()); + ASSERT_TRUE(filter_meta.fields().contains("shadow_rules_prefix_shadow_effective_policy_id")); + EXPECT_EQ( + "foobar", + filter_meta.fields().at("shadow_rules_prefix_shadow_effective_policy_id").string_value()); +} + TEST_F(RoleBasedAccessControlFilterTest, RouteLocalOverrideWithPerRuleStats) { setupPolicy(envoy::config::rbac::v3::RBAC::ALLOW); From 3b73e4670db8e6a703e85650db62abd3b793d9b9 Mon Sep 17 00:00:00 2001 From: phlax Date: Thu, 15 Aug 2024 19:47:29 +0100 Subject: [PATCH 113/130] bazel: Shift `@envoy_repo` to separate file (#35718) For external repositories that want to access information about the envoy repo - eg current/stable/archived versions - this allows them to do so without having to load all of envoy's dependency chain Signed-off-by: Ryan Northey --- WORKSPACE | 4 ++ bazel/repo.bzl | 152 ++++++++++++++++++++++++++++++++++++++++ bazel/repositories.bzl | 154 ----------------------------------------- 3 files changed, 156 insertions(+), 154 deletions(-) create mode 100644 bazel/repo.bzl diff --git a/WORKSPACE b/WORKSPACE index 0f789feb23e5..9819ecb5ac1e 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -8,6 +8,10 @@ load("//bazel:api_repositories.bzl", "envoy_api_dependencies") envoy_api_dependencies() +load("//bazel:repo.bzl", "envoy_repo") + +envoy_repo() + load("//bazel:repositories.bzl", "envoy_dependencies") envoy_dependencies() diff --git a/bazel/repo.bzl b/bazel/repo.bzl new file mode 100644 index 000000000000..a2d909b3f99f --- /dev/null +++ b/bazel/repo.bzl @@ -0,0 +1,152 @@ +# `@envoy_repo` repository rule for managing the repo and querying its metadata. + +def _envoy_repo_impl(repository_ctx): + """This provides information about the Envoy repository + + You can access the current project and api versions and the path to the repository in + .bzl/BUILD files as follows: + + ```starlark + load("@envoy_repo//:version.bzl", "VERSION", "API_VERSION") + ``` + + `*VERSION` can be used to derive version-specific rules and can be passed + to the rules. + + The `VERSION`s and also the local `PATH` to the repo can be accessed in + python libraries/binaries. By adding `@envoy_repo` to `deps` they become + importable through the `envoy_repo` namespace. + + As the `PATH` is local to the machine, it is generally only useful for + jobs that will run locally. + + This can be useful, for example, for bazel run jobs to run bazel queries that cannot be run + within the constraints of a `genquery`, or that otherwise need access to the repository + files. + + Project and repo data can be accessed in JSON format using `@envoy_repo//:project`, eg: + + ```starlark + load("@aspect_bazel_lib//lib:jq.bzl", "jq") + + jq( + name = "project_version", + srcs = ["@envoy_repo//:data"], + out = "version.txt", + args = ["-r"], + filter = ".version", + ) + + ``` + + """ + repo_version_path = repository_ctx.path(repository_ctx.attr.envoy_version) + api_version_path = repository_ctx.path(repository_ctx.attr.envoy_api_version) + version = repository_ctx.read(repo_version_path).strip() + api_version = repository_ctx.read(api_version_path).strip() + repository_ctx.file("version.bzl", "VERSION = '%s'\nAPI_VERSION = '%s'" % (version, api_version)) + repository_ctx.file("path.bzl", "PATH = '%s'" % repo_version_path.dirname) + repository_ctx.file("__init__.py", "PATH = '%s'\nVERSION = '%s'\nAPI_VERSION = '%s'" % (repo_version_path.dirname, version, api_version)) + repository_ctx.file("WORKSPACE", "") + repository_ctx.file("BUILD", ''' +load("@rules_python//python:defs.bzl", "py_library") +load("@envoy//tools/base:envoy_python.bzl", "envoy_entry_point") +load("//:path.bzl", "PATH") + +py_library( + name = "envoy_repo", + srcs = ["__init__.py"], + visibility = ["//visibility:public"], +) + +envoy_entry_point( + name = "get_project_json", + pkg = "envoy.base.utils", + script = "envoy.project_data", + init_data = [":__init__.py"], +) + +genrule( + name = "project", + outs = ["project.json"], + cmd = """ + $(location :get_project_json) $$(dirname $(location @envoy//:VERSION.txt)) > $@ + """, + tools = [ + ":get_project_json", + "@envoy//:VERSION.txt", + "@envoy//changelogs", + ], + visibility = ["//visibility:public"], +) + +envoy_entry_point( + name = "release", + args = [ + "release", + PATH, + "--release-message-path=$(location @envoy//changelogs:summary)", + ], + data = ["@envoy//changelogs:summary"], + pkg = "envoy.base.utils", + script = "envoy.project", + init_data = [":__init__.py"], +) + +envoy_entry_point( + name = "dev", + args = [ + "dev", + PATH, + ], + pkg = "envoy.base.utils", + script = "envoy.project", + init_data = [":__init__.py"], +) + +envoy_entry_point( + name = "sync", + args = [ + "sync", + PATH, + ], + pkg = "envoy.base.utils", + script = "envoy.project", + init_data = [":__init__.py"], +) + +envoy_entry_point( + name = "publish", + args = [ + "publish", + PATH, + ], + pkg = "envoy.base.utils", + script = "envoy.project", + init_data = [":__init__.py"], +) + +envoy_entry_point( + name = "trigger", + args = [ + "trigger", + PATH, + ], + pkg = "envoy.base.utils", + script = "envoy.project", + init_data = [":__init__.py"], +) + +''') + +_envoy_repo = repository_rule( + implementation = _envoy_repo_impl, + attrs = { + "envoy_version": attr.label(default = "@envoy//:VERSION.txt"), + "envoy_api_version": attr.label(default = "@envoy//:API_VERSION.txt"), + }, +) + +def envoy_repo(): + if "envoy_repo" not in native.existing_rules().keys(): + _envoy_repo(name = "envoy_repo") diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index f9c13aaabd8f..b6a698103eed 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -70,157 +70,6 @@ _default_envoy_build_config = repository_rule( }, ) -def _envoy_repo_impl(repository_ctx): - """This provides information about the Envoy repository - - You can access the current project and api versions and the path to the repository in - .bzl/BUILD files as follows: - - ```starlark - load("@envoy_repo//:version.bzl", "VERSION", "API_VERSION") - ``` - - `*VERSION` can be used to derive version-specific rules and can be passed - to the rules. - - The `VERSION`s and also the local `PATH` to the repo can be accessed in - python libraries/binaries. By adding `@envoy_repo` to `deps` they become - importable through the `envoy_repo` namespace. - - As the `PATH` is local to the machine, it is generally only useful for - jobs that will run locally. - - This can be useful, for example, for bazel run jobs to run bazel queries that cannot be run - within the constraints of a `genquery`, or that otherwise need access to the repository - files. - - Project and repo data can be accessed in JSON format using `@envoy_repo//:project`, eg: - - ```starlark - load("@aspect_bazel_lib//lib:jq.bzl", "jq") - - jq( - name = "project_version", - srcs = ["@envoy_repo//:data"], - out = "version.txt", - args = ["-r"], - filter = ".version", - ) - - ``` - - """ - repo_version_path = repository_ctx.path(repository_ctx.attr.envoy_version) - api_version_path = repository_ctx.path(repository_ctx.attr.envoy_api_version) - version = repository_ctx.read(repo_version_path).strip() - api_version = repository_ctx.read(api_version_path).strip() - repository_ctx.file("version.bzl", "VERSION = '%s'\nAPI_VERSION = '%s'" % (version, api_version)) - repository_ctx.file("path.bzl", "PATH = '%s'" % repo_version_path.dirname) - repository_ctx.file("__init__.py", "PATH = '%s'\nVERSION = '%s'\nAPI_VERSION = '%s'" % (repo_version_path.dirname, version, api_version)) - repository_ctx.file("WORKSPACE", "") - repository_ctx.file("BUILD", ''' -load("@rules_python//python:defs.bzl", "py_library") -load("@envoy//tools/base:envoy_python.bzl", "envoy_entry_point") -load("//:path.bzl", "PATH") - -py_library( - name = "envoy_repo", - srcs = ["__init__.py"], - visibility = ["//visibility:public"], -) - -envoy_entry_point( - name = "get_project_json", - pkg = "envoy.base.utils", - script = "envoy.project_data", - init_data = [":__init__.py"], -) - -genrule( - name = "project", - outs = ["project.json"], - cmd = """ - $(location :get_project_json) $$(dirname $(location @envoy//:VERSION.txt)) > $@ - """, - tools = [ - ":get_project_json", - "@envoy//:VERSION.txt", - "@envoy//changelogs", - ], - visibility = ["//visibility:public"], -) - -envoy_entry_point( - name = "release", - args = [ - "release", - PATH, - "--release-message-path=$(location @envoy//changelogs:summary)", - ], - data = ["@envoy//changelogs:summary"], - pkg = "envoy.base.utils", - script = "envoy.project", - init_data = [":__init__.py"], -) - -envoy_entry_point( - name = "dev", - args = [ - "dev", - PATH, - ], - pkg = "envoy.base.utils", - script = "envoy.project", - init_data = [":__init__.py"], -) - -envoy_entry_point( - name = "sync", - args = [ - "sync", - PATH, - ], - pkg = "envoy.base.utils", - script = "envoy.project", - init_data = [":__init__.py"], -) - -envoy_entry_point( - name = "publish", - args = [ - "publish", - PATH, - ], - pkg = "envoy.base.utils", - script = "envoy.project", - init_data = [":__init__.py"], -) - -envoy_entry_point( - name = "trigger", - args = [ - "trigger", - PATH, - ], - pkg = "envoy.base.utils", - script = "envoy.project", - init_data = [":__init__.py"], -) - -''') - -_envoy_repo = repository_rule( - implementation = _envoy_repo_impl, - attrs = { - "envoy_version": attr.label(default = "@envoy//:VERSION.txt"), - "envoy_api_version": attr.label(default = "@envoy//:API_VERSION.txt"), - }, -) - -def envoy_repo(): - if "envoy_repo" not in native.existing_rules().keys(): - _envoy_repo(name = "envoy_repo") - # Bazel native C++ dependencies. For the dependencies that doesn't provide autoconf/automake builds. def _cc_deps(): external_http_archive("grpc_httpjson_transcoding") @@ -255,9 +104,6 @@ def _rust_deps(): ) def envoy_dependencies(skip_targets = []): - # Add a binding for repository variables. - envoy_repo() - # Setup Envoy developer tools. envoy_dev_binding() From e0c98ec32b4840f03bb17d3b934f8bba92a3f03f Mon Sep 17 00:00:00 2001 From: Keith Mattix II Date: Thu, 15 Aug 2024 14:31:30 -0500 Subject: [PATCH 114/130] Add new stream formatters for cert chain identifiers (#35513) Adds stream formatters to print the fingerprints and serial for entire downstream cert chain Fixes #35452 Signed-off-by: Keith Mattix II --- changelogs/current.yaml | 4 + .../observability/access_log/usage.rst | 18 +++ envoy/ssl/connection.h | 23 ++++ .../common/formatter/stream_info_formatter.cc | 24 ++++ .../common/tls/connection_info_impl_base.cc | 69 +++++++++++ source/common/tls/connection_info_impl_base.h | 6 + source/common/tls/utility.cc | 25 ++++ source/common/tls/utility.h | 9 ++ .../formatter/substitution_formatter_test.cc | 103 ++++++++++++++++ test/common/tls/ssl_socket_test.cc | 112 ++++++++++++++++++ test/common/tls/test_data/no_san_cert_info.h | 7 ++ test/common/tls/utility_test.cc | 21 ++++ test/mocks/ssl/mocks.h | 3 + 13 files changed, 424 insertions(+) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 4d58495ab580..9c75222d83e3 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -181,6 +181,10 @@ new_features: ` to allow overriding TLS certificate selection behavior. An extension can select certificate base on the incoming SNI, in both sync and async mode. +- area: access log + change: | + Added support for :ref:`%DOWNSTREAM_PEER_CHAIN_FINGERPRINTS_1% `, + ``%DOWNSTREAM_PEER_CHAIN_FINGERPRINTS_256``, and ``%DOWNSTREAM_PEER_CHAIN_SERIALS%``, as access log formatters. - area: matching change: | Added dynamic metadata matcher support :ref:`Dynamic metadata input ` diff --git a/docs/root/configuration/observability/access_log/usage.rst b/docs/root/configuration/observability/access_log/usage.rst index 657c485a9b7d..d0fc7098e53e 100644 --- a/docs/root/configuration/observability/access_log/usage.rst +++ b/docs/root/configuration/observability/access_log/usage.rst @@ -1111,6 +1111,24 @@ UDP UDP Not implemented ("-"). +%DOWNSTREAM_PEER_CHAIN_FINGERPRINTS_256% + HTTP/TCP/THRIFT + The comma-separated hex-encoded SHA256 fingerprints of all client certificates used to establish the downstream TLS connection. + UDP + Not implemented ("-"). + +%DOWNSTREAM_PEER_CHAIN_FINGERPRINTS_1% + HTTP/TCP/THRIFT + The comma-separated hex-encoded SHA1 fingerprints of all client certificates used to establish the downstream TLS connection. + UDP + Not implemented ("-"). + +%DOWNSTREAM_PEER_CHAIN_SERIALS% + HTTP/TCP/THRIFT + The comma-separated wserial numbers of all client certificates used to establish the downstream TLS connection. + UDP + Not implemented ("-"). + %DOWNSTREAM_PEER_CERT% HTTP/TCP/THRIFT The client certificate in the URL-encoded PEM format used to establish the downstream TLS connection. diff --git a/envoy/ssl/connection.h b/envoy/ssl/connection.h index 9cc4a77a25ac..cc4efd8ed242 100644 --- a/envoy/ssl/connection.h +++ b/envoy/ssl/connection.h @@ -60,6 +60,29 @@ class ConnectionInfo { **/ virtual const std::string& serialNumberPeerCertificate() const PURE; + /** + * @return absl::Span the SHA256 digests of all peer certificates. + * Returns an empty vector if there is no peer certificate which can happen in + * TLS (non mTLS) connections. + */ + virtual absl::Span sha256PeerCertificateChainDigests() const PURE; + + /** + * @return absl::Span the SHA1 digest of all peer certificates. + * Returns an empty vector if there is no peer certificate which can happen in + * TLS (non mTLS) connections. + */ + virtual absl::Span sha1PeerCertificateChainDigests() const PURE; + + /** + * @return absl::Span the serial numbers of all peer certificates. + * An empty vector indicates that there were no peer certificates which can happen + * in TLS (non mTLS) connections. + * A vector element with a "" value indicates that the certificate at that index in + * the cert chain did not have a serial number. + **/ + virtual absl::Span serialNumbersPeerCertificates() const PURE; + /** * @return std::string the issuer field of the peer certificate in RFC 2253 format. Returns "" if * there is no peer certificate, or no issuer. diff --git a/source/common/formatter/stream_info_formatter.cc b/source/common/formatter/stream_info_formatter.cc index f3c49418dafd..50681ffcda06 100644 --- a/source/common/formatter/stream_info_formatter.cc +++ b/source/common/formatter/stream_info_formatter.cc @@ -1532,6 +1532,30 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide return connection_info.serialNumberPeerCertificate(); }); }}}, + {"DOWNSTREAM_PEER_CHAIN_FINGERPRINTS_256", + {CommandSyntaxChecker::COMMAND_ONLY, + [](const absl::string_view, absl::optional) { + return std::make_unique( + [](const Ssl::ConnectionInfo& connection_info) { + return absl::StrJoin(connection_info.sha256PeerCertificateChainDigests(), ","); + }); + }}}, + {"DOWNSTREAM_PEER_CHAIN_FINGERPRINTS_1", + {CommandSyntaxChecker::COMMAND_ONLY, + [](const absl::string_view, absl::optional) { + return std::make_unique( + [](const Ssl::ConnectionInfo& connection_info) { + return absl::StrJoin(connection_info.sha1PeerCertificateChainDigests(), ","); + }); + }}}, + {"DOWNSTREAM_PEER_CHAIN_SERIALS", + {CommandSyntaxChecker::COMMAND_ONLY, + [](const absl::string_view, absl::optional) { + return std::make_unique( + [](const Ssl::ConnectionInfo& connection_info) { + return absl::StrJoin(connection_info.serialNumbersPeerCertificates(), ","); + }); + }}}, {"DOWNSTREAM_PEER_ISSUER", {CommandSyntaxChecker::COMMAND_ONLY, [](absl::string_view, absl::optional) { diff --git a/source/common/tls/connection_info_impl_base.cc b/source/common/tls/connection_info_impl_base.cc index 5ae15002163d..af03e2cc6853 100644 --- a/source/common/tls/connection_info_impl_base.cc +++ b/source/common/tls/connection_info_impl_base.cc @@ -1,10 +1,14 @@ #include "source/common/tls/connection_info_impl_base.h" +#include + #include "source/common/common/hex.h" #include "absl/strings/str_replace.h" #include "openssl/err.h" +#include "openssl/safestack.h" #include "openssl/x509v3.h" +#include "utility.h" namespace Envoy { namespace Extensions { @@ -77,6 +81,29 @@ const std::string& ConnectionInfoImplBase::sha256PeerCertificateDigest() const { return cached_sha_256_peer_certificate_digest_; } +absl::Span ConnectionInfoImplBase::sha256PeerCertificateChainDigests() const { + if (!cached_sha_256_peer_certificate_digests_.empty()) { + return cached_sha_256_peer_certificate_digests_; + } + + STACK_OF(X509)* cert_chain = SSL_get_peer_full_cert_chain(ssl()); + if (cert_chain == nullptr) { + ASSERT(cached_sha_256_peer_certificate_digests_.empty()); + return cached_sha_256_peer_certificate_digests_; + } + + cached_sha_256_peer_certificate_digests_ = + Utility::mapX509Stack(*cert_chain, [](X509& cert) -> std::string { + std::vector computed_hash(SHA256_DIGEST_LENGTH); + unsigned int n; + X509_digest(&cert, EVP_sha256(), computed_hash.data(), &n); + RELEASE_ASSERT(n == computed_hash.size(), ""); + return Hex::encode(computed_hash); + }); + + return cached_sha_256_peer_certificate_digests_; +} + const std::string& ConnectionInfoImplBase::sha1PeerCertificateDigest() const { if (!cached_sha_1_peer_certificate_digest_.empty()) { return cached_sha_1_peer_certificate_digest_; @@ -95,6 +122,29 @@ const std::string& ConnectionInfoImplBase::sha1PeerCertificateDigest() const { return cached_sha_1_peer_certificate_digest_; } +absl::Span ConnectionInfoImplBase::sha1PeerCertificateChainDigests() const { + if (!cached_sha_1_peer_certificate_digests_.empty()) { + return cached_sha_1_peer_certificate_digests_; + } + + STACK_OF(X509)* cert_chain = SSL_get_peer_full_cert_chain(ssl()); + if (cert_chain == nullptr) { + ASSERT(cached_sha_1_peer_certificate_digests_.empty()); + return cached_sha_1_peer_certificate_digests_; + } + + cached_sha_1_peer_certificate_digests_ = + Utility::mapX509Stack(*cert_chain, [](X509& cert) -> std::string { + std::vector computed_hash(SHA_DIGEST_LENGTH); + unsigned int n; + X509_digest(&cert, EVP_sha1(), computed_hash.data(), &n); + RELEASE_ASSERT(n == computed_hash.size(), ""); + return Hex::encode(computed_hash); + }); + + return cached_sha_1_peer_certificate_digests_; +} + const std::string& ConnectionInfoImplBase::urlEncodedPemEncodedPeerCertificate() const { if (!cached_url_encoded_pem_encoded_peer_certificate_.empty()) { return cached_url_encoded_pem_encoded_peer_certificate_; @@ -253,6 +303,25 @@ const std::string& ConnectionInfoImplBase::serialNumberPeerCertificate() const { return cached_serial_number_peer_certificate_; } +absl::Span ConnectionInfoImplBase::serialNumbersPeerCertificates() const { + if (!cached_serial_numbers_peer_certificates_.empty()) { + return cached_serial_numbers_peer_certificates_; + } + + STACK_OF(X509)* cert_chain = SSL_get_peer_full_cert_chain(ssl()); + if (cert_chain == nullptr) { + ASSERT(cached_serial_numbers_peer_certificates_.empty()); + return cached_serial_numbers_peer_certificates_; + } + + cached_serial_numbers_peer_certificates_ = + Utility::mapX509Stack(*cert_chain, [](X509& cert) -> std::string { + return Utility::getSerialNumberFromCertificate(cert); + }); + + return cached_serial_numbers_peer_certificates_; +} + const std::string& ConnectionInfoImplBase::issuerPeerCertificate() const { if (!cached_issuer_peer_certificate_.empty()) { return cached_issuer_peer_certificate_; diff --git a/source/common/tls/connection_info_impl_base.h b/source/common/tls/connection_info_impl_base.h index 8bcacdb80f7f..475fa21b6d0c 100644 --- a/source/common/tls/connection_info_impl_base.h +++ b/source/common/tls/connection_info_impl_base.h @@ -22,8 +22,11 @@ class ConnectionInfoImplBase : public Ssl::ConnectionInfo { bool peerCertificatePresented() const override; absl::Span uriSanLocalCertificate() const override; const std::string& sha256PeerCertificateDigest() const override; + absl::Span sha256PeerCertificateChainDigests() const override; const std::string& sha1PeerCertificateDigest() const override; + absl::Span sha1PeerCertificateChainDigests() const override; const std::string& serialNumberPeerCertificate() const override; + absl::Span serialNumbersPeerCertificates() const override; const std::string& issuerPeerCertificate() const override; const std::string& subjectPeerCertificate() const override; const std::string& subjectLocalCertificate() const override; @@ -48,8 +51,11 @@ class ConnectionInfoImplBase : public Ssl::ConnectionInfo { protected: mutable std::vector cached_uri_san_local_certificate_; mutable std::string cached_sha_256_peer_certificate_digest_; + mutable std::vector cached_sha_256_peer_certificate_digests_; mutable std::string cached_sha_1_peer_certificate_digest_; + mutable std::vector cached_sha_1_peer_certificate_digests_; mutable std::string cached_serial_number_peer_certificate_; + mutable std::vector cached_serial_numbers_peer_certificates_; mutable std::string cached_issuer_peer_certificate_; mutable std::string cached_subject_peer_certificate_; mutable std::string cached_subject_local_certificate_; diff --git a/source/common/tls/utility.cc b/source/common/tls/utility.cc index 6df4276a4e99..619f5da999e6 100644 --- a/source/common/tls/utility.cc +++ b/source/common/tls/utility.cc @@ -1,6 +1,7 @@ #include "source/common/tls/utility.h" #include +#include #include "source/common/common/assert.h" #include "source/common/common/empty_string.h" @@ -463,6 +464,30 @@ std::string Utility::getX509VerificationErrorInfo(X509_STORE_CTX* ctx) { return error_details; } +std::vector Utility::mapX509Stack(stack_st_X509& stack, + std::function field_extractor) { + std::vector result; + if (sk_X509_num(&stack) <= 0) { + IS_ENVOY_BUG("x509 stack is empty or NULL"); + return result; + } + if (field_extractor == nullptr) { + IS_ENVOY_BUG("field_extractor is nullptr"); + return result; + } + + for (uint64_t i = 0; i < sk_X509_num(&stack); i++) { + X509* cert = sk_X509_value(&stack, i); + if (!cert) { + result.push_back(""); // Add an empty string so it's clear something was omitted. + } else { + result.push_back(field_extractor(*cert)); + } + } + + return result; +} + } // namespace Tls } // namespace TransportSockets } // namespace Extensions diff --git a/source/common/tls/utility.h b/source/common/tls/utility.h index da9be3441174..4123e860bd34 100644 --- a/source/common/tls/utility.h +++ b/source/common/tls/utility.h @@ -48,6 +48,15 @@ bool labelWildcardMatch(absl::string_view dns_label, absl::string_view pattern); */ std::string getSerialNumberFromCertificate(X509& cert); +/** + * Maps a stack of x509 certificates to a vector of strings extracted from the certificates. + * @param stack the stack of certificates + * @param field_extractor the function to extract the field from each certificate. + * @return std::vector returns the list of fields extracted from the certificates. + */ +std::vector mapX509Stack(stack_st_X509& stack, + std::function field_extractor); + /** * Retrieves the subject alternate names of a certificate. * @param cert the certificate diff --git a/test/common/formatter/substitution_formatter_test.cc b/test/common/formatter/substitution_formatter_test.cc index d48d36c3d40e..a87f2f447e53 100644 --- a/test/common/formatter/substitution_formatter_test.cc +++ b/test/common/formatter/substitution_formatter_test.cc @@ -1699,6 +1699,109 @@ TEST(SubstitutionFormatterTest, streamInfoFormatterWithSsl) { EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info), ProtoEq(ValueUtil::nullValue())); } + { + NiceMock stream_info; + StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_CHAIN_FINGERPRINTS_256"); + auto connection_info = std::make_shared(); + std::vector expected_shas{ + "685a2db593d5f86d346cb1a297009c3b467ad77f1944aa799039a2fb3d531f3f", + "1af1dfa857bf1d8814fe1af8983c18080019922e557f15a8a"}; + auto joined_shas = absl::StrJoin(expected_shas, ","); + EXPECT_CALL(*connection_info, sha256PeerCertificateChainDigests()) + .WillRepeatedly(Return(expected_shas)); + stream_info.downstream_connection_info_provider_->setSslConnection(connection_info); + EXPECT_EQ(joined_shas, upstream_format.formatWithContext({}, stream_info)); + EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info), + ProtoEq(ValueUtil::stringValue(joined_shas))); + } + { + NiceMock stream_info; + StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_CHAIN_FINGERPRINTS_256"); + auto connection_info = std::make_shared(); + std::vector expected_shas; + EXPECT_CALL(*connection_info, sha256PeerCertificateChainDigests()) + .WillRepeatedly(Return(expected_shas)); + stream_info.downstream_connection_info_provider_->setSslConnection(connection_info); + EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info)); + EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info), + ProtoEq(ValueUtil::nullValue())); + } + { + NiceMock stream_info; + stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); + StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_CHAIN_FINGERPRINTS_256"); + EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info)); + EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info), + ProtoEq(ValueUtil::nullValue())); + } + { + NiceMock stream_info; + StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_CHAIN_FINGERPRINTS_1"); + auto connection_info = std::make_shared(); + std::vector expected_shas{ + "685a2db593d5f86d346cb1a297009c3b467ad77f1944aa799039a2fb3d531f3f", + "1af1dfa857bf1d8814fe1af8983c18080019922e557f15a8a"}; + auto joined_shas = absl::StrJoin(expected_shas, ","); + EXPECT_CALL(*connection_info, sha1PeerCertificateChainDigests()) + .WillRepeatedly(Return(expected_shas)); + stream_info.downstream_connection_info_provider_->setSslConnection(connection_info); + EXPECT_EQ(joined_shas, upstream_format.formatWithContext({}, stream_info)); + EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info), + ProtoEq(ValueUtil::stringValue(joined_shas))); + } + { + NiceMock stream_info; + StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_CHAIN_FINGERPRINTS_1"); + auto connection_info = std::make_shared(); + std::vector expected_shas; + EXPECT_CALL(*connection_info, sha1PeerCertificateChainDigests()) + .WillRepeatedly(Return(expected_shas)); + stream_info.downstream_connection_info_provider_->setSslConnection(connection_info); + EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info)); + EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info), + ProtoEq(ValueUtil::nullValue())); + } + { + NiceMock stream_info; + stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); + StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_CHAIN_FINGERPRINTS_1"); + EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info)); + EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info), + ProtoEq(ValueUtil::nullValue())); + } + { + NiceMock stream_info; + StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_CHAIN_SERIALS"); + auto connection_info = std::make_shared(); + std::vector serial_numbers{"b8b5ecc898f2124a", "9bf18bd79b46589902639871"}; + auto joined_serials = absl::StrJoin(serial_numbers, ","); + EXPECT_CALL(*connection_info, serialNumbersPeerCertificates()) + .WillRepeatedly(Return(serial_numbers)); + stream_info.downstream_connection_info_provider_->setSslConnection(connection_info); + EXPECT_EQ(joined_serials, upstream_format.formatWithContext({}, stream_info)); + EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info), + ProtoEq(ValueUtil::stringValue(joined_serials))); + } + { + NiceMock stream_info; + StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_CHAIN_SERIALS"); + std::vector empty_vec; + auto connection_info = std::make_shared(); + EXPECT_CALL(*connection_info, serialNumbersPeerCertificates()) + .WillRepeatedly(Return(empty_vec)); + stream_info.downstream_connection_info_provider_->setSslConnection(connection_info); + EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info)); + EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info), + ProtoEq(ValueUtil::nullValue())); + } + { + NiceMock stream_info; + stream_info.downstream_connection_info_provider_->setSslConnection(nullptr); + StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_CHAIN_SERIALS"); + EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info)); + EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info), + ProtoEq(ValueUtil::nullValue())); + } { NiceMock stream_info; StreamInfoFormatter upstream_format("DOWNSTREAM_PEER_ISSUER"); diff --git a/test/common/tls/ssl_socket_test.cc b/test/common/tls/ssl_socket_test.cc index 4ee59996b4d9..ba1ca0b65598 100644 --- a/test/common/tls/ssl_socket_test.cc +++ b/test/common/tls/ssl_socket_test.cc @@ -171,6 +171,12 @@ class TestUtilOptions : public TestUtilOptionsBase { const std::string& expectedSha256Digest() const { return expected_sha256_digest_; } + TestUtilOptions& setExpectedSha256Digests(std::vector& expected_sha256_digests) { + expected_sha256_digests_ = expected_sha256_digests; + return *this; + } + const std::vector expectedSha256Digests() const { return expected_sha256_digests_; } + TestUtilOptions& setExpectedSha1Digest(const std::string& expected_sha1_digest) { expected_sha1_digest_ = expected_sha1_digest; return *this; @@ -178,6 +184,13 @@ class TestUtilOptions : public TestUtilOptionsBase { const std::string& expectedSha1Digest() const { return expected_sha1_digest_; } + TestUtilOptions& setExpectedSha1Digests(std::vector& expected_sha1_digests) { + expected_sha1_digests_ = expected_sha1_digests; + return *this; + } + + const std::vector expectedSha1Digests() const { return expected_sha1_digests_; } + TestUtilOptions& setExpectedLocalUri(const std::string& expected_local_uri) { expected_local_uri_ = {expected_local_uri}; return *this; @@ -192,6 +205,13 @@ class TestUtilOptions : public TestUtilOptionsBase { const std::string& expectedSerialNumber() const { return expected_serial_number_; } + TestUtilOptions& setExpectedSerialNumbers(std::vector& expected_serial_numbers) { + expected_serial_numbers_ = expected_serial_numbers; + return *this; + } + + const std::vector expectedSerialNumbers() const { return expected_serial_numbers_; } + TestUtilOptions& setExpectedPeerIssuer(const std::string& expected_peer_issuer) { expected_peer_issuer_ = expected_peer_issuer; return *this; @@ -313,9 +333,12 @@ class TestUtilOptions : public TestUtilOptionsBase { NiceMock runtime_; Network::ConnectionEvent expected_server_close_event_{Network::ConnectionEvent::RemoteClose}; std::string expected_sha256_digest_; + std::vector expected_sha256_digests_; std::string expected_sha1_digest_; + std::vector expected_sha1_digests_; std::vector expected_local_uri_; std::string expected_serial_number_; + std::vector expected_serial_numbers_; std::string expected_peer_issuer_; std::string expected_peer_subject_; std::string expected_local_subject_; @@ -442,6 +465,13 @@ void testUtil(const TestUtilOptions& options) { EXPECT_EQ(options.expectedSha256Digest(), server_connection->ssl()->sha256PeerCertificateDigest()); } + if (!options.expectedSha256Digests().empty()) { + // Assert twice to ensure a cached value is returned and still valid. + EXPECT_EQ(options.expectedSha256Digests(), + server_connection->ssl()->sha256PeerCertificateChainDigests()); + EXPECT_EQ(options.expectedSha256Digests(), + server_connection->ssl()->sha256PeerCertificateChainDigests()); + } if (!options.expectedSha1Digest().empty()) { // Assert twice to ensure a cached value is returned and still valid. EXPECT_EQ(options.expectedSha1Digest(), @@ -449,6 +479,13 @@ void testUtil(const TestUtilOptions& options) { EXPECT_EQ(options.expectedSha1Digest(), server_connection->ssl()->sha1PeerCertificateDigest()); } + if (!options.expectedSha1Digests().empty()) { + // Assert twice to ensure a cached value is returned and still valid. + EXPECT_EQ(options.expectedSha1Digests(), + server_connection->ssl()->sha1PeerCertificateChainDigests()); + EXPECT_EQ(options.expectedSha1Digests(), + server_connection->ssl()->sha1PeerCertificateChainDigests()); + } // Assert twice to ensure a cached value is returned and still valid. EXPECT_EQ(options.expectedClientCertUri(), server_connection->ssl()->uriSanPeerCertificate()); EXPECT_EQ(options.expectedClientCertUri(), server_connection->ssl()->uriSanPeerCertificate()); @@ -458,18 +495,34 @@ void testUtil(const TestUtilOptions& options) { EXPECT_EQ(options.expectedLocalUri(), server_connection->ssl()->uriSanLocalCertificate()); EXPECT_EQ(options.expectedLocalUri(), server_connection->ssl()->uriSanLocalCertificate()); } + + EXPECT_EQ(options.expectedSerialNumber(), + server_connection->ssl()->serialNumberPeerCertificate()); EXPECT_EQ(options.expectedSerialNumber(), server_connection->ssl()->serialNumberPeerCertificate()); + if (!options.expectedSerialNumbers().empty()) { + // Assert twice to ensure a cached value is returned and still valid. + EXPECT_EQ(options.expectedSerialNumbers(), + server_connection->ssl()->serialNumbersPeerCertificates()); + EXPECT_EQ(options.expectedSerialNumbers(), + server_connection->ssl()->serialNumbersPeerCertificates()); + } if (!options.expectedPeerIssuer().empty()) { + // Assert twice to ensure a cached value is returned and still valid. + EXPECT_EQ(options.expectedPeerIssuer(), server_connection->ssl()->issuerPeerCertificate()); EXPECT_EQ(options.expectedPeerIssuer(), server_connection->ssl()->issuerPeerCertificate()); } if (!options.expectedPeerSubject().empty()) { EXPECT_EQ(options.expectedPeerSubject(), server_connection->ssl()->subjectPeerCertificate()); + EXPECT_EQ(options.expectedPeerSubject(), + server_connection->ssl()->subjectPeerCertificate()); } if (!options.expectedLocalSubject().empty()) { EXPECT_EQ(options.expectedLocalSubject(), server_connection->ssl()->subjectLocalCertificate()); + EXPECT_EQ(options.expectedLocalSubject(), + server_connection->ssl()->subjectLocalCertificate()); } if (!options.expectedPeerCert().empty()) { std::string urlencoded = absl::StrReplaceAll( @@ -817,6 +870,8 @@ void testUtilV2(const TestUtilOptionsV2& options) { if (!options.expectedALPNProtocol().empty()) { EXPECT_EQ(options.expectedALPNProtocol(), client_connection->nextProtocol()); } + // Assert twice to ensure a cached value is returned and still valid. + EXPECT_EQ(options.expectedClientCertUri(), server_connection->ssl()->uriSanPeerCertificate()); EXPECT_EQ(options.expectedClientCertUri(), server_connection->ssl()->uriSanPeerCertificate()); EXPECT_EQ(options.expectedClientCertIpSans(), server_connection->ssl()->ipSansPeerCertificate()); @@ -1098,6 +1153,63 @@ TEST_P(SslSocketTest, GetCertDigestInvalidFiles) { "")); } +TEST_P(SslSocketTest, GetCertDigests) { + const std::string client_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_rundir }}/test/common/tls/test_data/no_san_chain.pem" + private_key: + filename: "{{ test_rundir }}/test/common/tls/test_data/no_san_key.pem" +)EOF"; + + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_rundir }}/test/common/tls/test_data/no_san_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/common/tls/test_data/no_san_key.pem" + validation_context: + trusted_ca: + filename: "{{ test_rundir }}/test/common/tls/test_data/ca_cert.pem" +)EOF"; + + TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, version_); + std::vector sha256Digests = absl::StrSplit(TEST_NO_SAN_CERT_CHAIN_256_HASHES, ','); + std::vector sha1Digests = absl::StrSplit(TEST_NO_SAN_CERT_CHAIN_1_HASHES, ','); + std::vector serialNumbers = absl::StrSplit(TEST_NO_SAN_CERT_CHAIN_SERIALS, ','); + testUtil(test_options.setExpectedSha256Digests(sha256Digests) + .setExpectedSha1Digests(sha1Digests) + .setExpectedSerialNumber(TEST_NO_SAN_CERT_SERIAL) // test checks first serial # + .setExpectedSerialNumbers(serialNumbers)); +} + +TEST_P(SslSocketTest, GetCertDigestsInvalidFiles) { + const std::string client_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: +)EOF"; + + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_rundir }}/test/common/tls/test_data/san_dns_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/common/tls/test_data/san_dns_key.pem" + validation_context: + trusted_ca: + filename: "{{ test_rundir }}/test/common/tls/test_data/ca_cert.pem" +)EOF"; + + TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, version_); + std::vector emptyStringVec; + testUtil(test_options.setExpectedSha256Digests(emptyStringVec) + .setExpectedSha1Digests(emptyStringVec) + .setExpectedSerialNumbers(emptyStringVec)); +} + TEST_P(SslSocketTest, GetCertDigestInline) { envoy::config::listener::v3::Listener listener; envoy::config::listener::v3::FilterChain* filter_chain = listener.add_filter_chains(); diff --git a/test/common/tls/test_data/no_san_cert_info.h b/test/common/tls/test_data/no_san_cert_info.h index 22f671841618..9072c54c5509 100644 --- a/test/common/tls/test_data/no_san_cert_info.h +++ b/test/common/tls/test_data/no_san_cert_info.h @@ -3,8 +3,15 @@ // NOLINT(namespace-envoy) constexpr char TEST_NO_SAN_CERT_256_HASH[] = "1fa3b1626367eda0b93b20cab52a08ace81aeab55245abf29b7920873658ce11"; +constexpr char TEST_NO_SAN_CERT_CHAIN_256_HASHES[] = + "1fa3b1626367eda0b93b20cab52a08ace81aeab55245abf29b7920873658ce11," + "91b048a0941a41740a243a8db4509ba31abdf0c00a1aa9fa36fe2e28d029b22f"; constexpr char TEST_NO_SAN_CERT_1_HASH[] = "c773be7b9f642ceadaa4ac8b8c8417e900435955"; +constexpr char TEST_NO_SAN_CERT_CHAIN_1_HASHES[] = "c773be7b9f642ceadaa4ac8b8c8417e900435955," + "635057edf7e2eabf863ab7fb5748a443aaeffee6"; constexpr char TEST_NO_SAN_CERT_SPKI[] = "eMMC8S2gS0LSZAF9bFmxP4YrI5NeUp/T+UzDKhJEiGA="; constexpr char TEST_NO_SAN_CERT_SERIAL[] = "7c252c75e95aa57a88f1f1c5cc3ff4fa9c5aa4c1"; +constexpr char TEST_NO_SAN_CERT_CHAIN_SERIALS[] = "7c252c75e95aa57a88f1f1c5cc3ff4fa9c5aa4c1," + "7c252c75e95aa57a88f1f1c5cc3ff4fa9c5aa4bf"; constexpr char TEST_NO_SAN_CERT_NOT_BEFORE[] = "Aug 22 07:51:29 2022 GMT"; constexpr char TEST_NO_SAN_CERT_NOT_AFTER[] = "Aug 21 07:51:29 2024 GMT"; diff --git a/test/common/tls/utility_test.cc b/test/common/tls/utility_test.cc index 005ca1fe58ca..7f028922fa31 100644 --- a/test/common/tls/utility_test.cc +++ b/test/common/tls/utility_test.cc @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -209,6 +211,25 @@ TEST(UtilityTest, TestGetX509ErrorInfo) { "verification error"); } +TEST(UtilityTest, TestMapX509Stack) { + bssl::UniquePtr cert_chain = readCertChainFromFile( + TestEnvironment::substitute("{{ test_rundir }}/test/common/tls/test_data/no_san_chain.pem")); + + std::vector expected_subject{ + "CN=Test Server,OU=Lyft Engineering,O=Lyft,L=San " + "Francisco,ST=California,C=US", + "CN=Test Intermediate CA,OU=Lyft Engineering,O=Lyft,L=San Francisco," + "ST=California,C=US"}; + auto func = [](X509& cert) -> std::string { return Utility::getSubjectFromCertificate(cert); }; + EXPECT_EQ(expected_subject, Utility::mapX509Stack(*cert_chain, func)); + + EXPECT_ENVOY_BUG(Utility::mapX509Stack(*sk_X509_new_null(), func), "x509 stack is empty or NULL"); + EXPECT_ENVOY_BUG(Utility::mapX509Stack(*cert_chain, nullptr), "field_extractor is nullptr"); + bssl::UniquePtr fakeCertChain(sk_X509_new_null()); + sk_X509_push(fakeCertChain.get(), nullptr); + EXPECT_EQ(std::vector{""}, Utility::mapX509Stack(*fakeCertChain, func)); +} + } // namespace } // namespace Tls } // namespace TransportSockets diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index 211c7afae603..69e27613edae 100644 --- a/test/mocks/ssl/mocks.h +++ b/test/mocks/ssl/mocks.h @@ -46,8 +46,11 @@ class MockConnectionInfo : public ConnectionInfo { MOCK_METHOD(bool, peerCertificateValidated, (), (const)); MOCK_METHOD(absl::Span, uriSanLocalCertificate, (), (const)); MOCK_METHOD(const std::string&, sha256PeerCertificateDigest, (), (const)); + MOCK_METHOD(absl::Span, sha256PeerCertificateChainDigests, (), (const)); MOCK_METHOD(const std::string&, sha1PeerCertificateDigest, (), (const)); + MOCK_METHOD(absl::Span, sha1PeerCertificateChainDigests, (), (const)); MOCK_METHOD(const std::string&, serialNumberPeerCertificate, (), (const)); + MOCK_METHOD(absl::Span, serialNumbersPeerCertificates, (), (const)); MOCK_METHOD(const std::string&, issuerPeerCertificate, (), (const)); MOCK_METHOD(const std::string&, subjectPeerCertificate, (), (const)); MOCK_METHOD(absl::Span, uriSanPeerCertificate, (), (const)); From eb78944f6939531f2ebf2016a80a7d2e5f24a845 Mon Sep 17 00:00:00 2001 From: Greg Greenway Date: Thu, 15 Aug 2024 17:15:27 -0700 Subject: [PATCH 115/130] tls: update FIPS version to 2022-06-13 (#35534) Signed-off-by: Greg Greenway --- bazel/external/boringssl_fips.genrule_cmd | 33 +++++++++++++---------- bazel/repository_locations.bzl | 14 +++++++--- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/bazel/external/boringssl_fips.genrule_cmd b/bazel/external/boringssl_fips.genrule_cmd index 7d18fdd3a2fc..51e6b72c7829 100755 --- a/bazel/external/boringssl_fips.genrule_cmd +++ b/bazel/external/boringssl_fips.genrule_cmd @@ -23,23 +23,23 @@ ROOT=./external/boringssl_fips pushd "$ROOT" export HOME="$PWD" -# Build tools requirements (from section 12.1 of https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp4407.pdf): -# - Clang compiler version 12.0.0 (https://releases.llvm.org/download.html) -# - Go programming language version 1.16.5 (https://golang.org/dl/) +# Build tools requirements (from section 11 of https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp4735.pdf): +# - Clang compiler version 14.0.0 (https://releases.llvm.org/download.html) +# - Go programming language version 1.18.1 (https://golang.org/dl/) # - Ninja build system version 1.10.2 (https://github.com/ninja-build/ninja/releases) -# - Cmake version 3.20.1 (https://cmake.org/download/) +# - Cmake version 3.22.1 (https://cmake.org/download/) # Override $PATH for build tools, to avoid picking up anything else. export PATH="/usr/bin:/bin" # Clang -VERSION=12.0.0 +VERSION=14.0.0 if [[ "$ARCH" == "x86_64" ]]; then - PLATFORM="x86_64-linux-gnu-ubuntu-20.04" - SHA256=a9ff205eb0b73ca7c86afc6432eed1c2d49133bd0d49e47b15be59bbf0dd292e + PLATFORM="x86_64-linux-gnu-ubuntu-18.04" + SHA256=61582215dafafb7b576ea30cc136be92c877ba1f1c31ddbbd372d6d65622fef5 else PLATFORM="aarch64-linux-gnu" - SHA256=d05f0b04fb248ce1e7a61fcd2087e6be8bc4b06b2cc348792f383abf414dec48 + SHA256=1792badcd44066c79148ffeb1746058422cc9d838462be07e3cb19a4b724a1ee fi curl -sLO https://github.com/llvm/llvm-project/releases/download/llvmorg-"$VERSION"/clang+llvm-"$VERSION"-"$PLATFORM".tar.xz @@ -55,13 +55,13 @@ if [[ `clang --version | head -1 | awk '{print $3}'` != "$VERSION" ]]; then fi # Go -VERSION=1.16.5 +VERSION=1.18.1 if [[ "$ARCH" == "x86_64" ]]; then PLATFORM="linux-amd64" - SHA256=b12c23023b68de22f74c0524f10b753e7b08b1504cb7e417eccebdd3fae49061 + SHA256=b3b815f47ababac13810fc6021eb73d65478e0b2db4b09d348eefad9581a2334 else PLATFORM="linux-arm64" - SHA256=d5446b46ef6f36fdffa852f73dfbbe78c1ddf010b99fa4964944b9ae8b4d6799 + SHA256=56a91851c97fb4697077abbca38860f735c32b38993ff79b088dac46e4735633 fi curl -sLO https://dl.google.com/go/go"$VERSION"."$PLATFORM".tar.gz \ @@ -95,13 +95,13 @@ fi cd .. # CMake -VERSION=3.20.1 +VERSION=3.22.1 if [[ "$ARCH" == "x86_64" ]]; then PLATFORM="linux-x86_64" - SHA256=b8c141bd7a6d335600ab0a8a35e75af79f95b837f736456b5532f4d717f20a09 + SHA256=73565c72355c6652e9db149249af36bcab44d9d478c5546fd926e69ad6b43640 else PLATFORM="linux-aarch64" - SHA256=5ad1f8139498a1956df369c401658ec787f63c8cb4e9759f2edaa51626a86512 + SHA256=601443375aa1a48a1a076bda7e3cca73af88400463e166fffc3e1da3ce03540b fi curl -sLO https://github.com/Kitware/CMake/releases/download/v"$VERSION"/cmake-"$VERSION"-"$PLATFORM".tar.gz \ @@ -124,6 +124,11 @@ cd boringssl # because the FIPS module itself is already built with -fPIC. mkdir build && cd build && cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=${HOME}/toolchain -DFIPS=1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-fPIC" -DCMAKE_CXX_FLAGS="-fPIC" .. ninja + +# The `HostMatching` test contains hard-coded expired certificates and always fails due to the expiration. +# This should be removed during the next FIPS version upgrade, as the test code is fixed in later versions. +export GTEST_FILTER="-SSLTest.HostMatching" + ninja run_tests ./crypto/crypto_test diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 3e8fa43b10b8..0ea9c9a24b3c 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -163,11 +163,17 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_url = "https://boringssl.googlesource.com/boringssl/+/master/crypto/fipsmodule/FIPS.md", # When this is updated to a revision newer than 2022-08-12, # CertValidatorUtil::setIgnoreCertificateExpiration can be simplified. - version = "fips-20210429", - sha256 = "a4d069ccef6f3c7bc0c68de82b91414f05cb817494cd1ab483dcf3368883c7c2", - urls = ["https://commondatastorage.googleapis.com/chromium-boringssl-fips/boringssl-853ca1ea1168dff08011e5d42d94609cc0ca2e27.tar.xz"], + # + # !!! NOTE !!! + # Anytime the FIPS BoringSSL version is upgraded, `bazel/external/boringssl_fips.genrule_cmd` must be updated to use the toolchain + # specified in the associated accredidation certificate, which can be found linked from + # https://boringssl.googlesource.com/boringssl/+/refs/heads/master/crypto/fipsmodule/FIPS.md, for example + # https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4735. + version = "fips-20220613", + sha256 = "62f733289f2d677c2723f556aa58034c438f3a7bbca6c12b156538a88e38da8a", + urls = ["https://commondatastorage.googleapis.com/chromium-boringssl-fips/boringssl-0c6f40132b828e92ba365c6b7680e32820c63fa7.tar.xz"], use_category = ["controlplane", "dataplane_core"], - release_date = "2021-04-29", + release_date = "2022-06-13", cpe = "cpe:2.3:a:google:boringssl:*", ), aspect_bazel_lib = dict( From 92867602cab095a138b9ca3546ee0641dab4c287 Mon Sep 17 00:00:00 2001 From: Martin Duke Date: Thu, 15 Aug 2024 17:56:23 -0700 Subject: [PATCH 116/130] Fix Apple ECN dual stack behavior (#35196) Commit Message: Check the status of ECN in a DualStack socket Additional Description: Linux dual-stack sockets require IP_PROTO, IP_RECVTOS due to report ECN on incoming v4 sockets. Apple returns an error; IPV6_RECVTCLASS is sufficient for both IP versions. Risk Level: Low Testing: New unit tests to verify the sockopt is set, and ECN is reported on dual stack sockets Docs Changes: N/A Release Notes: N/A Platform Specific Features: N/A --------- Signed-off-by: Martin Duke --- source/common/quic/active_quic_listener.cc | 4 ++ test/common/quic/active_quic_listener_test.cc | 66 ++++++++++++++++--- tools/spelling/spelling_dictionary.txt | 1 + 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/source/common/quic/active_quic_listener.cc b/source/common/quic/active_quic_listener.cc index 08aca091dd0c..735472bbddf6 100644 --- a/source/common/quic/active_quic_listener.cc +++ b/source/common/quic/active_quic_listener.cc @@ -78,9 +78,13 @@ ActiveQuicListener::ActiveQuicListener( socklen_t optlen = sizeof(optval); if (udp_listener_->localAddress()->ip()->ipv6() != nullptr) { listen_socket_.setSocketOption(IPPROTO_IPV6, IPV6_RECVTCLASS, &optval, optlen); +#ifndef __APPLE__ + // Linux dual-stack sockets require setting IP_RECVTOS separately. Apple + // sockets will return an error. if (!udp_listener_->localAddress()->ip()->ipv6()->v6only()) { listen_socket_.setSocketOption(IPPROTO_IP, IP_RECVTOS, &optval, optlen); } +#endif // __APPLE__ } else { listen_socket_.setSocketOption(IPPROTO_IP, IP_RECVTOS, &optval, optlen); } diff --git a/test/common/quic/active_quic_listener_test.cc b/test/common/quic/active_quic_listener_test.cc index ce68869cf538..84b637abc5e1 100644 --- a/test/common/quic/active_quic_listener_test.cc +++ b/test/common/quic/active_quic_listener_test.cc @@ -127,7 +127,7 @@ class ActiveQuicListenerTest : public testing::TestWithParamallocateDispatcher("test_thread")), clock_(*dispatcher_), - local_address_(Network::Test::getCanonicalLoopbackAddress(version_)), + local_address_(Network::Test::getAnyAddress(version_, true)), connection_handler_(*dispatcher_, absl::nullopt), transport_socket_factory_(*Quic::QuicServerTransportSocketFactory::create( true, *store_.rootScope(), std::make_unique>(), @@ -259,14 +259,22 @@ class ActiveQuicListenerTest : public testing::TestWithParam(Network::Socket::Type::Datagram, local_address_, + std::make_unique(Network::Socket::Type::Datagram, client_address, nullptr, Network::SocketCreationOptions{})); // Set outgoing ECN marks on client packets. int level = IPPROTO_IP; int optname = IP_TOS; - if (local_address_->ip()->version() == Network::Address::IpVersion::v6) { + if (client_address->ip()->version() == Network::Address::IpVersion::v6) { level = IPPROTO_IPV6; optname = IPV6_TCLASS; } @@ -276,10 +284,21 @@ class ActiveQuicListenerTest : public testing::TestWithParamip()->version() == Network::Address::IpVersion::v4) { + dest_address = std::make_shared( + client_address->ip()->addressAsString(), + listen_socket_->connectionInfoProvider().localAddress()->ip()->port(), + &(listen_socket_->connectionInfoProvider().localAddress()->socketInterface())); + } else { + dest_address = std::make_shared( + client_address->ip()->addressAsString(), + listen_socket_->connectionInfoProvider().localAddress()->ip()->port(), + &(listen_socket_->connectionInfoProvider().localAddress()->socketInterface())); + } // Send a full CHLO to finish 0-RTT handshake. - auto send_rc = Network::Utility::writeToSocket( - client_sockets_.back()->ioHandle(), slice.data(), 1, nullptr, - *listen_socket_->connectionInfoProvider().localAddress()); + auto send_rc = Network::Utility::writeToSocket(client_sockets_.back()->ioHandle(), slice.data(), + 1, nullptr, *dest_address); ASSERT_EQ(slice[0].len_, send_rc.return_value_); } @@ -603,14 +622,23 @@ TEST_P(ActiveQuicListenerTest, EcnReportingIsEnabled) { Network::Socket& socket = ActiveQuicListenerPeer::socket(*quic_listener_); absl::optional version = socket.ipVersion(); EXPECT_TRUE(version.has_value()); - int optval; + int optval = 0; socklen_t optlen = sizeof(optval); Api::SysCallIntResult rv; if (*version == Network::Address::IpVersion::v6) { rv = socket.getSocketOption(IPPROTO_IPV6, IPV6_RECVTCLASS, &optval, &optlen); - } else { - rv = socket.getSocketOption(IPPROTO_IP, IP_RECVTOS, &optval, &optlen); + EXPECT_EQ(rv.return_value_, 0); + EXPECT_EQ(optval, 1); + EXPECT_FALSE(local_address_->ip()->ipv6()->v6only()); +#ifdef __APPLE__ + return; +#endif // __APPLE__ } + // Check the IPv4 version of the sockopt if the socket is v4 or dual-stack. + // Platform/ APIs for ECN reporting are poorly documented, but this test + // should uncover any issues. + optval = 0; + rv = socket.getSocketOption(IPPROTO_IP, IP_RECVTOS, &optval, &optlen); EXPECT_EQ(rv.return_value_, 0); EXPECT_EQ(optval, 1); } @@ -630,6 +658,24 @@ TEST_P(ActiveQuicListenerTest, EcnReporting) { EXPECT_EQ(stats.num_ecn_marks_received.ect1, 1); } +TEST_P(ActiveQuicListenerTest, EcnReportingDualStack) { + if (local_address_->ip()->version() == Network::Address::IpVersion::v4) { + return; + } + Runtime::maybeSetRuntimeGuard("envoy.reloadable_features.quic_receive_ecn", true); + initialize(); + maybeConfigureMocks(/* connection_count = */ 1); + quic::QuicConnectionId connection_id = quic::test::TestConnectionId(1); + sendCHLO(connection_id, /*dual_stack=*/true); + dispatcher_->run(Event::Dispatcher::RunType::Block); + quic::QuicConnection* connection = + quic::test::QuicDispatcherPeer::GetFirstSessionIfAny(quic_dispatcher_)->connection(); + EXPECT_EQ(connection->connection_id(), quic::test::TestConnectionId(1)); + ASSERT(connection != nullptr); + const quic::QuicConnectionStats& stats = connection->GetStats(); + EXPECT_EQ(stats.num_ecn_marks_received.ect1, 1); +} + class ActiveQuicListenerEmptyFlagConfigTest : public ActiveQuicListenerTest { protected: std::string yamlForQuicConfig() override { diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index c0c4b3c28739..263479eba905 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -382,6 +382,7 @@ RDS README RECVDSTADDR RECVPKTINFO +RECVTOS REFNIL REQ REUSEADDR From 6592faae7203ecba8d00ad6e965f9f1be44aea7a Mon Sep 17 00:00:00 2001 From: Misha Efimov Date: Fri, 16 Aug 2024 08:59:09 -0400 Subject: [PATCH 117/130] orca: Add ORCA Load Report processing to `Router::Filter`. (#35642) Signed-off-by: Misha Efimov --- api/envoy/config/cluster/v3/cluster.proto | 2 + envoy/upstream/upstream.h | 5 + source/common/orca/BUILD | 20 +++ source/common/orca/orca_load_metrics.cc | 74 +++++++++++ source/common/orca/orca_load_metrics.h | 18 +++ source/common/router/BUILD | 2 + source/common/router/router.cc | 38 ++++++ source/common/router/router.h | 8 +- source/common/upstream/BUILD | 1 + source/common/upstream/upstream_impl.cc | 5 + source/common/upstream/upstream_impl.h | 9 ++ test/common/orca/BUILD | 17 +++ test/common/orca/orca_load_metrics_test.cc | 136 +++++++++++++++++++++ test/common/router/router_test.cc | 70 +++++++++++ test/mocks/upstream/cluster_info.cc | 5 + test/mocks/upstream/cluster_info.h | 2 + 16 files changed, 411 insertions(+), 1 deletion(-) create mode 100644 source/common/orca/orca_load_metrics.cc create mode 100644 source/common/orca/orca_load_metrics.h create mode 100644 test/common/orca/orca_load_metrics_test.cc diff --git a/api/envoy/config/cluster/v3/cluster.proto b/api/envoy/config/cluster/v3/cluster.proto index 5f347ade6d3d..ef6bbe121b4c 100644 --- a/api/envoy/config/cluster/v3/cluster.proto +++ b/api/envoy/config/cluster/v3/cluster.proto @@ -1154,6 +1154,8 @@ message Cluster { // [#not-implemented-hide:] // A list of metric names from ORCA load reports to propagate to LRS. // + // If not specified, then ORCA load reports will not be propagated to LRS. + // // For map fields in the ORCA proto, the string will be of the form ``.``. // For example, the string ``named_metrics.foo`` will mean to look for the key ``foo`` in the ORCA // ``named_metrics`` field. diff --git a/envoy/upstream/upstream.h b/envoy/upstream/upstream.h index 45fd1a7cc58f..95a454abc799 100644 --- a/envoy/upstream/upstream.h +++ b/envoy/upstream/upstream.h @@ -1235,6 +1235,11 @@ class ClusterInfo : public Http::FilterChainFactory { virtual OptRef happyEyeballsConfig() const PURE; + /** + * @return Reference to the optional config for LRS endpoint metric reporting. + */ + virtual OptRef> lrsReportMetricNames() const PURE; + protected: /** * Invoked by extensionProtocolOptionsTyped. diff --git a/source/common/orca/BUILD b/source/common/orca/BUILD index 9337ab05ecaf..fab411692e2b 100644 --- a/source/common/orca/BUILD +++ b/source/common/orca/BUILD @@ -23,3 +23,23 @@ envoy_cc_library( "@com_github_cncf_xds//xds/data/orca/v3:pkg_cc_proto", ], ) + +envoy_cc_library( + name = "orca_load_metrics_lib", + srcs = ["orca_load_metrics.cc"], + hdrs = ["orca_load_metrics.h"], + external_deps = [ + "abseil_flat_hash_set", + "abseil_status", + "abseil_strings", + "abseil_statusor", + "fmtlib", + ], + deps = [ + ":orca_parser", + "//envoy/http:header_map_interface", + "//source/common/http:header_utility_lib", + "//source/common/protobuf:utility_lib_header", + "@com_github_cncf_xds//xds/data/orca/v3:pkg_cc_proto", + ], +) diff --git a/source/common/orca/orca_load_metrics.cc b/source/common/orca/orca_load_metrics.cc new file mode 100644 index 000000000000..9b931ff26380 --- /dev/null +++ b/source/common/orca/orca_load_metrics.cc @@ -0,0 +1,74 @@ +#include "source/common/orca/orca_load_metrics.h" + +#include + +#include "source/common/orca/orca_parser.h" + +#include "absl/strings/match.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Orca { +namespace { +// The following fields are the names of the metrics tracked in the ORCA load +// report proto. +static constexpr absl::string_view kApplicationUtilizationField = "application_utilization"; +static constexpr absl::string_view kCpuUtilizationField = "cpu_utilization"; +static constexpr absl::string_view kMemUtilizationField = "mem_utilization"; +static constexpr absl::string_view kEpsField = "eps"; +static constexpr absl::string_view kRpsFractionalField = "rps_fractional"; +static constexpr absl::string_view kNamedMetricsFieldPrefix = "named_metrics."; +static constexpr absl::string_view kRequestCostFieldPrefix = "request_cost."; +static constexpr absl::string_view kUtilizationFieldPrefix = "utilization."; +} // namespace + +void addOrcaNamedMetricToLoadMetricStats(const Protobuf::Map& metrics_map, + const absl::string_view metric_name, + const absl::string_view metric_name_prefix, + Upstream::LoadMetricStats& stats) { + absl::string_view metric_name_without_prefix = absl::StripPrefix(metric_name, metric_name_prefix); + // If the metric name is "*", add all metrics from the map. + if (metric_name_without_prefix == "*") { + for (const auto& [key, value] : metrics_map) { + stats.add(absl::StrCat(metric_name_prefix, key), value); + } + } else { + // Add the metric if it exists in the map. + const auto metric_it = metrics_map.find(metric_name_without_prefix); + if (metric_it != metrics_map.end()) { + stats.add(metric_name, metric_it->second); + } + } +} + +void addOrcaLoadReportToLoadMetricStats(const LrsReportMetricNames& metric_names, + const xds::data::orca::v3::OrcaLoadReport& report, + Upstream::LoadMetricStats& stats) { + // TODO(efimki): Use InlineMap to speed up this loop. + for (const std::string& metric_name : metric_names) { + if (metric_name == kCpuUtilizationField) { + stats.add(metric_name, report.cpu_utilization()); + } else if (metric_name == kMemUtilizationField) { + stats.add(metric_name, report.mem_utilization()); + } else if (metric_name == kApplicationUtilizationField) { + stats.add(metric_name, report.application_utilization()); + } else if (metric_name == kEpsField) { + stats.add(metric_name, report.eps()); + } else if (metric_name == kRpsFractionalField) { + stats.add(metric_name, report.rps_fractional()); + } else if (absl::StartsWith(metric_name, kNamedMetricsFieldPrefix)) { + addOrcaNamedMetricToLoadMetricStats(report.named_metrics(), metric_name, + kNamedMetricsFieldPrefix, stats); + } else if (absl::StartsWith(metric_name, kUtilizationFieldPrefix)) { + addOrcaNamedMetricToLoadMetricStats(report.utilization(), metric_name, + kUtilizationFieldPrefix, stats); + } else if (absl::StartsWith(metric_name, kRequestCostFieldPrefix)) { + addOrcaNamedMetricToLoadMetricStats(report.request_cost(), metric_name, + kRequestCostFieldPrefix, stats); + } + } +} + +} // namespace Orca +} // namespace Envoy diff --git a/source/common/orca/orca_load_metrics.h b/source/common/orca/orca_load_metrics.h new file mode 100644 index 000000000000..90c213ba1bf8 --- /dev/null +++ b/source/common/orca/orca_load_metrics.h @@ -0,0 +1,18 @@ +#pragma once + +#include "envoy/upstream/host_description.h" + +#include "xds/data/orca/v3/orca_load_report.pb.h" + +namespace Envoy { +namespace Orca { + +// List of metric names to report to the LRS. +typedef std::vector LrsReportMetricNames; + +void addOrcaLoadReportToLoadMetricStats(const LrsReportMetricNames& metric_names, + const xds::data::orca::v3::OrcaLoadReport& report, + Upstream::LoadMetricStats& stats); + +} // namespace Orca +} // namespace Envoy diff --git a/source/common/router/BUILD b/source/common/router/BUILD index 65458851c98a..70200e05eaff 100644 --- a/source/common/router/BUILD +++ b/source/common/router/BUILD @@ -366,6 +366,8 @@ envoy_cc_library( "//source/common/network:socket_option_factory_lib", "//source/common/network:transport_socket_options_lib", "//source/common/network:upstream_socket_options_filter_state_lib", + "//source/common/orca:orca_load_metrics_lib", + "//source/common/orca:orca_parser", "//source/common/stream_info:stream_info_lib", "//source/common/stream_info:uint32_accessor_lib", "//source/common/tracing:http_tracer_lib", diff --git a/source/common/router/router.cc b/source/common/router/router.cc index 02930fe93365..05ad0e6430c4 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -35,6 +35,8 @@ #include "source/common/network/upstream_server_name.h" #include "source/common/network/upstream_socket_options_filter_state.h" #include "source/common/network/upstream_subject_alt_names.h" +#include "source/common/orca/orca_load_metrics.h" +#include "source/common/orca/orca_parser.h" #include "source/common/router/config_impl.h" #include "source/common/router/debug_config.h" #include "source/common/router/retry_state_impl.h" @@ -1568,6 +1570,8 @@ void Filter::onUpstreamHeaders(uint64_t response_code, Http::ResponseHeaderMapPt } } + maybeProcessOrcaLoadReport(*headers, upstream_request); + if (grpc_status.has_value()) { upstream_request.upstreamHost()->outlierDetector().putHttpResponseCode(grpc_to_http_status); } else { @@ -1737,6 +1741,8 @@ void Filter::onUpstreamTrailers(Http::ResponseTrailerMapPtr&& trailers, } } + maybeProcessOrcaLoadReport(*trailers, upstream_request); + onUpstreamComplete(upstream_request); callbacks_->encodeTrailers(std::move(trailers)); @@ -2100,6 +2106,38 @@ bool Filter::checkDropOverload(Upstream::ThreadLocalCluster& cluster, return false; } +void Filter::maybeProcessOrcaLoadReport(const Envoy::Http::HeaderMap& headers_or_trailers, + UpstreamRequest& upstream_request) { + // Process the load report only once, so if response has report in headers, + // then don't process it in trailers. + if (orca_load_report_received_) { + return; + } + // Check whether we need to send the load report to the LRS or invoke the ORCA + // callbacks. + auto host = upstream_request.upstreamHost(); + const bool need_to_send_load_report = + (host != nullptr) && cluster_->lrsReportMetricNames().has_value(); + if (!need_to_send_load_report) { + return; + } + + absl::StatusOr orca_load_report = + Envoy::Orca::parseOrcaLoadReportHeaders(headers_or_trailers); + if (!orca_load_report.ok()) { + ENVOY_STREAM_LOG(trace, "Headers don't have orca load report: {}", *callbacks_, + orca_load_report.status().message()); + return; + } + + orca_load_report_received_ = true; + + ENVOY_STREAM_LOG(trace, "Adding ORCA load report {} to load metrics", *callbacks_, + orca_load_report->DebugString()); + Envoy::Orca::addOrcaLoadReportToLoadMetricStats( + cluster_->lrsReportMetricNames().value(), orca_load_report.value(), host->loadMetricStats()); +} + RetryStatePtr ProdFilter::createRetryState(const RetryPolicy& policy, Http::RequestHeaderMap& request_headers, const Upstream::ClusterInfo& cluster, const VirtualCluster* vcluster, diff --git a/source/common/router/router.h b/source/common/router/router.h index 4b93247e29fd..6e92950fbe1d 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -308,7 +308,7 @@ class Filter : Logger::Loggable, downstream_response_started_(false), downstream_end_stream_(false), is_retry_(false), request_buffer_overflowed_(false), streaming_shadows_(Runtime::runtimeFeatureEnabled( "envoy.reloadable_features.streaming_shadow")), - upstream_request_started_(false) {} + upstream_request_started_(false), orca_load_report_received_(false) {} ~Filter() override; @@ -559,6 +559,9 @@ class Filter : Logger::Loggable, Http::Context& httpContext() { return config_->http_context_; } bool checkDropOverload(Upstream::ThreadLocalCluster& cluster, std::function& modify_headers); + // Process Orca Load Report if necessary (e.g. cluster has lrsReportMetricNames). + void maybeProcessOrcaLoadReport(const Envoy::Http::HeaderMap& headers_or_trailers, + UpstreamRequest& upstream_request); RetryStatePtr retry_state_; const FilterConfigSharedPtr config_; @@ -611,6 +614,9 @@ class Filter : Logger::Loggable, bool request_buffer_overflowed_ : 1; const bool streaming_shadows_ : 1; bool upstream_request_started_ : 1; + // Indicate that ORCA report is received to process it only once in either response headers or + // trailers. + bool orca_load_report_received_ : 1; }; class ProdFilter : public Filter { diff --git a/source/common/upstream/BUILD b/source/common/upstream/BUILD index 1def579c48c7..011eec2f43a1 100644 --- a/source/common/upstream/BUILD +++ b/source/common/upstream/BUILD @@ -457,6 +457,7 @@ envoy_cc_library( "//source/common/http/http2:codec_stats_lib", "//source/common/http/http3:codec_stats_lib", "//source/common/init:manager_lib", + "//source/common/orca:orca_load_metrics_lib", "//source/common/shared_pool:shared_pool_lib", "//source/common/stats:deferred_creation", "//source/common/stats:isolated_store_lib", diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index 6c368861692d..3b75fb478f5c 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -1208,6 +1208,11 @@ ClusterInfoImpl::ClusterInfoImpl( envoy::config::cluster::v3::UpstreamConnectionOptions::HappyEyeballsConfig>( config.upstream_connection_options().happy_eyeballs_config()) : nullptr), + lrs_report_metric_names_(!config.lrs_report_endpoint_metrics().empty() + ? std::make_unique( + config.lrs_report_endpoint_metrics().begin(), + config.lrs_report_endpoint_metrics().end()) + : nullptr), per_connection_buffer_limit_bytes_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, per_connection_buffer_limit_bytes, 1024 * 1024)), max_response_headers_count_(PROTOBUF_GET_WRAPPED_OR_DEFAULT( diff --git a/source/common/upstream/upstream_impl.h b/source/common/upstream/upstream_impl.h index 70edb9c49d98..e7573e4f50fd 100644 --- a/source/common/upstream/upstream_impl.h +++ b/source/common/upstream/upstream_impl.h @@ -55,6 +55,7 @@ #include "source/common/http/http3/codec_stats.h" #include "source/common/init/manager_impl.h" #include "source/common/network/utility.h" +#include "source/common/orca/orca_load_metrics.h" #include "source/common/shared_pool/shared_pool.h" #include "source/common/stats/isolated_store_impl.h" #include "source/common/upstream/edf_scheduler.h" @@ -1023,6 +1024,13 @@ class ClusterInfoImpl : public ClusterInfo, return *happy_eyeballs_config_; } + OptRef lrsReportMetricNames() const override { + if (lrs_report_metric_names_ == nullptr) { + return absl::nullopt; + } + return *lrs_report_metric_names_; + } + protected: // Gets the retry budget percent/concurrency from the circuit breaker thresholds. If the retry // budget message is specified, defaults will be filled in if either params are unspecified. @@ -1111,6 +1119,7 @@ class ClusterInfoImpl : public ClusterInfo, UpstreamFactoryContextImpl upstream_context_; std::unique_ptr happy_eyeballs_config_; + const std::unique_ptr lrs_report_metric_names_; // Keep small values like bools and enums at the end of the class to reduce // overhead via alignment diff --git a/test/common/orca/BUILD b/test/common/orca/BUILD index 9122593cf921..ea012a118e6b 100644 --- a/test/common/orca/BUILD +++ b/test/common/orca/BUILD @@ -8,6 +8,23 @@ licenses(["notice"]) # Apache 2 envoy_package() +envoy_cc_test( + name = "orca_load_metrics_test", + srcs = ["orca_load_metrics_test.cc"], + external_deps = [ + "abseil_status", + "abseil_strings", + "fmtlib", + ], + deps = [ + "//source/common/orca:orca_load_metrics_lib", + "//source/common/upstream:upstream_lib", + "//test/test_common:status_utility_lib", + "//test/test_common:utility_lib", + "@com_github_cncf_xds//xds/data/orca/v3:pkg_cc_proto", + ], +) + envoy_cc_test( name = "orca_parser_test", srcs = ["orca_parser_test.cc"], diff --git a/test/common/orca/orca_load_metrics_test.cc b/test/common/orca/orca_load_metrics_test.cc new file mode 100644 index 000000000000..cce62960437e --- /dev/null +++ b/test/common/orca/orca_load_metrics_test.cc @@ -0,0 +1,136 @@ +#include "source/common/orca/orca_load_metrics.h" +#include "source/common/upstream/upstream_impl.h" + +#include "test/test_common/status_utility.h" +#include "test/test_common/utility.h" + +using ::Envoy::Upstream::LoadMetricStats; +using ::Envoy::Upstream::LoadMetricStatsImpl; +using ::testing::DoubleEq; +using ::testing::Field; +using ::testing::ReturnRef; + +namespace Envoy { +namespace Orca { +namespace { + +xds::data::orca::v3::OrcaLoadReport makeOrcaReport() { + xds::data::orca::v3::OrcaLoadReport report; + report.mutable_named_metrics()->insert({"nm_foo", 0.1}); + report.mutable_named_metrics()->insert({"nm_bar", 0.2}); + report.mutable_request_cost()->insert({"rc_foo", 0.4}); + report.mutable_request_cost()->insert({"rc_bar", 0.5}); + report.mutable_utilization()->insert({"ut_foo", 0.6}); + report.mutable_utilization()->insert({"ut_bar", 0.7}); + report.set_application_utilization(0.8); + report.set_cpu_utilization(0.9); + report.set_mem_utilization(1.0); + report.set_eps(10); + report.set_rps_fractional(11); + return report; +} + +TEST(OrcaLoadMetricsTest, AddCpuUtilization) { + Envoy::Orca::LrsReportMetricNames metric_names; + metric_names.push_back("cpu_utilization"); + + Envoy::Upstream::LoadMetricStatsImpl stats; + Envoy::Orca::addOrcaLoadReportToLoadMetricStats(metric_names, makeOrcaReport(), stats); + auto load_stats_map = stats.latch(); + ASSERT_NE(load_stats_map, nullptr); + EXPECT_EQ(load_stats_map->size(), 1); + + EXPECT_EQ(load_stats_map->at("cpu_utilization").total_metric_value, 0.9); + EXPECT_EQ(load_stats_map->at("cpu_utilization").num_requests_with_metric, 1); +} + +TEST(OrcaLoadMetricsTest, AddSpecificNamedMetrics) { + Envoy::Orca::LrsReportMetricNames metric_names; + metric_names.push_back("named_metrics.foo"); + metric_names.push_back("named_metrics.not-in-report"); + + xds::data::orca::v3::OrcaLoadReport report; + report.mutable_named_metrics()->insert({"foo", 0.7}); + report.mutable_named_metrics()->insert({"not-in-config", 0.3}); + + Envoy::Upstream::LoadMetricStatsImpl stats; + Envoy::Orca::addOrcaLoadReportToLoadMetricStats(metric_names, report, stats); + auto load_stats_map = stats.latch(); + ASSERT_NE(load_stats_map, nullptr); + EXPECT_EQ(load_stats_map->size(), 1); + EXPECT_EQ(load_stats_map->at("named_metrics.foo").total_metric_value, 0.7); +} + +TEST(OrcaLoadMetricsTest, AddWildcardUtilization) { + Envoy::Orca::LrsReportMetricNames metric_names; + metric_names.push_back("utilization.*"); + + Envoy::Upstream::LoadMetricStatsImpl stats; + Envoy::Orca::addOrcaLoadReportToLoadMetricStats(metric_names, makeOrcaReport(), stats); + auto load_stats_map = stats.latch(); + ASSERT_NE(load_stats_map, nullptr); + EXPECT_THAT(*load_stats_map, + UnorderedElementsAre( + Pair("utilization.ut_foo", + AllOf(Field(&LoadMetricStats::Stat::num_requests_with_metric, 1), + Field(&LoadMetricStats::Stat::total_metric_value, DoubleEq(0.6)))), + Pair("utilization.ut_bar", + AllOf(Field(&LoadMetricStats::Stat::num_requests_with_metric, 1), + Field(&LoadMetricStats::Stat::total_metric_value, DoubleEq(0.7)))))); +} + +TEST(OrcaLoadMetricsTest, AddAllReportedMetrics) { + Envoy::Orca::LrsReportMetricNames metric_names; + metric_names.push_back("application_utilization"); + metric_names.push_back("cpu_utilization"); + metric_names.push_back("mem_utilization"); + metric_names.push_back("eps"); + metric_names.push_back("rps_fractional"); + metric_names.push_back("named_metrics.*"); + metric_names.push_back("utilization.*"); + metric_names.push_back("request_cost.*"); + + Envoy::Upstream::LoadMetricStatsImpl stats; + Envoy::Orca::addOrcaLoadReportToLoadMetricStats(metric_names, makeOrcaReport(), stats); + auto load_stats_map = stats.latch(); + ASSERT_NE(load_stats_map, nullptr); + EXPECT_THAT( + *load_stats_map, + UnorderedElementsAre( + Pair("named_metrics.nm_foo", + AllOf(Field(&LoadMetricStats::Stat::num_requests_with_metric, 1), + Field(&LoadMetricStats::Stat::total_metric_value, DoubleEq(0.1)))), + Pair("named_metrics.nm_bar", + AllOf(Field(&LoadMetricStats::Stat::num_requests_with_metric, 1), + Field(&LoadMetricStats::Stat::total_metric_value, DoubleEq(0.2)))), + Pair("request_cost.rc_foo", + AllOf(Field(&LoadMetricStats::Stat::num_requests_with_metric, 1), + Field(&LoadMetricStats::Stat::total_metric_value, DoubleEq(0.4)))), + Pair("request_cost.rc_bar", + AllOf(Field(&LoadMetricStats::Stat::num_requests_with_metric, 1), + Field(&LoadMetricStats::Stat::total_metric_value, DoubleEq(0.5)))), + Pair("utilization.ut_foo", + AllOf(Field(&LoadMetricStats::Stat::num_requests_with_metric, 1), + Field(&LoadMetricStats::Stat::total_metric_value, DoubleEq(0.6)))), + Pair("utilization.ut_bar", + AllOf(Field(&LoadMetricStats::Stat::num_requests_with_metric, 1), + Field(&LoadMetricStats::Stat::total_metric_value, DoubleEq(0.7)))), + Pair("application_utilization", + AllOf(Field(&LoadMetricStats::Stat::num_requests_with_metric, 1), + Field(&LoadMetricStats::Stat::total_metric_value, DoubleEq(0.8)))), + Pair("cpu_utilization", + AllOf(Field(&LoadMetricStats::Stat::num_requests_with_metric, 1), + Field(&LoadMetricStats::Stat::total_metric_value, DoubleEq(0.9)))), + Pair("mem_utilization", + AllOf(Field(&LoadMetricStats::Stat::num_requests_with_metric, 1), + Field(&LoadMetricStats::Stat::total_metric_value, DoubleEq(1.0)))), + Pair("eps", AllOf(Field(&LoadMetricStats::Stat::num_requests_with_metric, 1), + Field(&LoadMetricStats::Stat::total_metric_value, DoubleEq(10)))), + Pair("rps_fractional", + AllOf(Field(&LoadMetricStats::Stat::num_requests_with_metric, 1), + Field(&LoadMetricStats::Stat::total_metric_value, DoubleEq(11)))))); +} + +} // namespace +} // namespace Orca +} // namespace Envoy diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index 1ccd9ef87620..ecc13f1b1e24 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -12,6 +12,7 @@ #include "envoy/type/v3/percent.pb.h" #include "source/common/buffer/buffer_impl.h" +#include "source/common/common/base64.h" #include "source/common/common/empty_string.h" #include "source/common/config/metadata.h" #include "source/common/config/well_known_names.h" @@ -6675,5 +6676,74 @@ TEST_F(RouterTest, OverwriteSchemeWithUpstreamTransportProtocol) { callbacks_.route_->virtual_host_.virtual_cluster_.stats().upstream_rq_total_.value()); } +TEST_F(RouterTest, OrcaLoadReport) { + EXPECT_CALL(callbacks_.route_->route_entry_, timeout()) + .WillOnce(Return(std::chrono::milliseconds(0))); + EXPECT_CALL(callbacks_.dispatcher_, createTimer_(_)).Times(0); + + NiceMock encoder; + Http::ResponseDecoder* response_decoder = nullptr; + expectNewStreamWithImmediateEncoder(encoder, &response_decoder, Http::Protocol::Http10); + + Http::TestRequestHeaderMapImpl headers; + HttpTestUtility::addDefaultHeaders(headers); + router_->decodeHeaders(headers, true); + + // Create LRS endpoint metric reporting config with three metrics. + Envoy::Orca::LrsReportMetricNames metric_names; + metric_names.push_back("cpu_utilization"); + metric_names.push_back("named_metrics.good"); + metric_names.push_back("named_metrics.not-in-report"); + ON_CALL(*cm_.thread_local_cluster_.cluster_.info_, lrsReportMetricNames()) + .WillByDefault(Return(makeOptRef(metric_names))); + // Send three metrics, one of which is not in the config. + xds::data::orca::v3::OrcaLoadReport orca_load_report; + orca_load_report.set_cpu_utilization(0.5); + orca_load_report.mutable_named_metrics()->insert({"not-in-config", 0.1}); + orca_load_report.mutable_named_metrics()->insert({"good", 0.7}); + std::string proto_string = TestUtility::getProtobufBinaryStringFromMessage(orca_load_report); + std::string orca_load_report_header_bin = + Envoy::Base64::encode(proto_string.c_str(), proto_string.length()); + Http::ResponseHeaderMapPtr response_headers(new Http::TestResponseHeaderMapImpl{ + {":status", "200"}, {"endpoint-load-metrics-bin", orca_load_report_header_bin}}); + response_decoder->decodeHeaders(std::move(response_headers), true); + auto load_metric_stats_map = + cm_.thread_local_cluster_.conn_pool_.host_->loadMetricStats().latch(); + ASSERT_NE(load_metric_stats_map, nullptr); + EXPECT_EQ(load_metric_stats_map->size(), 2); + EXPECT_EQ(load_metric_stats_map->at("cpu_utilization").total_metric_value, 0.5); + EXPECT_EQ(load_metric_stats_map->at("cpu_utilization").num_requests_with_metric, 1); + EXPECT_EQ(load_metric_stats_map->at("named_metrics.good").total_metric_value, 0.7); + EXPECT_EQ(load_metric_stats_map->at("named_metrics.good").num_requests_with_metric, 1); +} + +TEST_F(RouterTest, OrcaLoadReport_NoConfiguredMetricNames) { + EXPECT_CALL(callbacks_.route_->route_entry_, timeout()) + .WillOnce(Return(std::chrono::milliseconds(0))); + EXPECT_CALL(callbacks_.dispatcher_, createTimer_(_)).Times(0); + + NiceMock encoder; + Http::ResponseDecoder* response_decoder = nullptr; + expectNewStreamWithImmediateEncoder(encoder, &response_decoder, Http::Protocol::Http10); + + Http::TestRequestHeaderMapImpl headers; + HttpTestUtility::addDefaultHeaders(headers); + router_->decodeHeaders(headers, true); + + // Verify that no load metric stats are added when there are no configured metric names. + xds::data::orca::v3::OrcaLoadReport orca_load_report; + orca_load_report.set_cpu_utilization(0.5); + orca_load_report.mutable_named_metrics()->insert({"good", 0.7}); + std::string proto_string = TestUtility::getProtobufBinaryStringFromMessage(orca_load_report); + std::string orca_load_report_header_bin = + Envoy::Base64::encode(proto_string.c_str(), proto_string.length()); + Http::ResponseHeaderMapPtr response_headers(new Http::TestResponseHeaderMapImpl{ + {":status", "200"}, {"endpoint-load-metrics-bin", orca_load_report_header_bin}}); + response_decoder->decodeHeaders(std::move(response_headers), true); + auto load_metric_stats_map = + cm_.thread_local_cluster_.conn_pool_.host_->loadMetricStats().latch(); + ASSERT_EQ(load_metric_stats_map, nullptr); +} + } // namespace Router } // namespace Envoy diff --git a/test/mocks/upstream/cluster_info.cc b/test/mocks/upstream/cluster_info.cc index 4038e57bd8c1..380c7a7c8bcd 100644 --- a/test/mocks/upstream/cluster_info.cc +++ b/test/mocks/upstream/cluster_info.cc @@ -171,6 +171,11 @@ MockClusterInfo::MockClusterInfo() protocol, codecStats(protocol)) : nullptr; })); + ON_CALL(*this, lrsReportMetricNames()) + .WillByDefault(Invoke([this]() -> OptRef { + return makeOptRefFromPtr( + lrs_report_metric_names_.get()); + })); } MockClusterInfo::~MockClusterInfo() = default; diff --git a/test/mocks/upstream/cluster_info.h b/test/mocks/upstream/cluster_info.h index d9ec12addef6..8f5a72973f49 100644 --- a/test/mocks/upstream/cluster_info.h +++ b/test/mocks/upstream/cluster_info.h @@ -172,6 +172,7 @@ class MockClusterInfo : public ClusterInfo { MOCK_METHOD( OptRef, happyEyeballsConfig, (), (const)); + MOCK_METHOD(OptRef>, lrsReportMetricNames, (), (const)); ::Envoy::Http::HeaderValidatorStats& codecStats(Http::Protocol protocol) const; Http::Http1::CodecStats& http1CodecStats() const override; Http::Http2::CodecStats& http2CodecStats() const override; @@ -235,6 +236,7 @@ class MockClusterInfo : public ClusterInfo { Http::HeaderValidatorFactoryPtr header_validator_factory_; absl::optional happy_eyeballs_config_; + const std::unique_ptr lrs_report_metric_names_; }; class MockIdleTimeEnabledClusterInfo : public MockClusterInfo { From 978d911fa4b47d96b95f6c8d1ae9cd11b319caa8 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Fri, 16 Aug 2024 14:02:02 -0500 Subject: [PATCH 118/130] mobile: Use the non-caching class in release mode (#35720) This PR updates `findClassFromCache` to have a fallback when the `jclass` does not exist in the cache instead of crashing similar to the implementation of `get(Static)?[Method|Field]IdFromCache` for consistency. Risk Level: low Testing: unit test Docs Changes: n/a Release Notes: n/a Platform Specific Features: mobile Signed-off-by: Fredy Wijaya --- mobile/library/jni/jni_helper.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mobile/library/jni/jni_helper.cc b/mobile/library/jni/jni_helper.cc index 3e35365f071e..67dfd1aa9d4f 100644 --- a/mobile/library/jni/jni_helper.cc +++ b/mobile/library/jni/jni_helper.cc @@ -263,8 +263,10 @@ jclass JniHelper::findClassFromCache(const char* class_name) { if (auto i = jclass_cache_map.find(class_name); i != jclass_cache_map.end()) { return i->second; } + // In the debug mode, the code will fail if the class is not in the cache since this is most + // likely due to a bug in the code. In the release mode, the code will use the non-caching class. ASSERT(false, absl::StrFormat("Unable to find class '%s'.", class_name)); - return nullptr; + return env_->FindClass(class_name); } LocalRefUniquePtr JniHelper::getObjectClass(jobject object) { From 157a4b29d08c806e161bf4cddf8990768bc1914e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 17 Aug 2024 10:12:25 +0000 Subject: [PATCH 119/130] build(deps): bump envoy-base-utils from 0.5.1 to 0.5.2 in /tools/base (#35738) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/base/requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index 4d99a19158dc..9e1bd10ef742 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -497,9 +497,9 @@ docutils==0.20.1 \ # envoy-docs-sphinx-runner # sphinx # sphinx-rtd-theme -envoy-base-utils==0.5.1 \ - --hash=sha256:477ab036d4e523a962f7f37382ed55d8b04c78efbcc3db276b5e1711f4838d29 \ - --hash=sha256:fa4a3f2ea55e558979b99ab7d0baeb4cd30a9eb50c3f7cf63e6b2129625e4d58 +envoy-base-utils==0.5.2 \ + --hash=sha256:29d1384aded102b1c2cbfab44b07dbb35bc08a181e9e87a990ea0b90540ea54c \ + --hash=sha256:facd099692d3ec02d0597a2a2ff002a1702ccea1da31633d171420a791265e09 # via # -r requirements.in # envoy-code-check From dbb35fa0d4b61f29c6ae81aca2cbd88184207d18 Mon Sep 17 00:00:00 2001 From: yanjunxiang-google <78807980+yanjunxiang-google@users.noreply.github.com> Date: Sat, 17 Aug 2024 11:18:58 -0400 Subject: [PATCH 120/130] Adding http client support for ext_proc filter (#35676) Risk Level: low Testing: n/a Docs Changes: n/a Release Notes: inline Fixes: Description: This is to address the 2st step of https://github.com/envoyproxy/envoy/issues/35488, i.e, the ext_proc HTTP client framework. --------- Signed-off-by: Yanjun Xiang --- .../filters/http/ext_proc/http_client/BUILD | 29 +++++++++ .../http/ext_proc/http_client/client_base.h | 33 ++++++++++ .../ext_proc/http_client/http_client_impl.cc | 45 +++++++++++++ .../ext_proc/http_client/http_client_impl.h | 47 ++++++++++++++ .../filters/http/ext_proc/http_client/BUILD | 26 ++++++++ .../ext_proc/http_client/http_client_test.cc | 65 +++++++++++++++++++ 6 files changed, 245 insertions(+) create mode 100644 source/extensions/filters/http/ext_proc/http_client/BUILD create mode 100644 source/extensions/filters/http/ext_proc/http_client/client_base.h create mode 100644 source/extensions/filters/http/ext_proc/http_client/http_client_impl.cc create mode 100644 source/extensions/filters/http/ext_proc/http_client/http_client_impl.h create mode 100644 test/extensions/filters/http/ext_proc/http_client/BUILD create mode 100644 test/extensions/filters/http/ext_proc/http_client/http_client_test.cc diff --git a/source/extensions/filters/http/ext_proc/http_client/BUILD b/source/extensions/filters/http/ext_proc/http_client/BUILD new file mode 100644 index 000000000000..16afa5f69849 --- /dev/null +++ b/source/extensions/filters/http/ext_proc/http_client/BUILD @@ -0,0 +1,29 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_library( + name = "client_base_interface", + hdrs = ["client_base.h"], + tags = ["skip_on_windows"], + deps = [], +) + +envoy_cc_library( + name = "http_client_lib", + srcs = ["http_client_impl.cc"], + hdrs = ["http_client_impl.h"], + tags = ["skip_on_windows"], + deps = [ + "client_base_interface", + "//source/common/common:enum_to_int", + "//source/common/http:utility_lib", + "//source/extensions/filters/http/ext_proc", + ], +) diff --git a/source/extensions/filters/http/ext_proc/http_client/client_base.h b/source/extensions/filters/http/ext_proc/http_client/client_base.h new file mode 100644 index 000000000000..fd9ae5ce7c7f --- /dev/null +++ b/source/extensions/filters/http/ext_proc/http_client/client_base.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace ExternalProcessing { + +/** + * Async callbacks used during external processing. + */ +class RequestCallbacks { +public: + virtual ~RequestCallbacks() = default; + virtual void onComplete() PURE; +}; + +/** + * Async client base class used during external processing. + */ +class ClientBase { +public: + virtual ~ClientBase() = default; + + virtual void sendRequest() PURE; + virtual void cancel() PURE; +}; + +} // namespace ExternalProcessing +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/ext_proc/http_client/http_client_impl.cc b/source/extensions/filters/http/ext_proc/http_client/http_client_impl.cc new file mode 100644 index 000000000000..abc172192389 --- /dev/null +++ b/source/extensions/filters/http/ext_proc/http_client/http_client_impl.cc @@ -0,0 +1,45 @@ +#include "source/extensions/filters/http/ext_proc/http_client/http_client_impl.h" + +#include "source/common/common/enum_to_int.h" +#include "source/common/http/utility.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace ExternalProcessing { + +void ExtProcHttpClient::onSuccess(const Http::AsyncClient::Request&, + Http::ResponseMessagePtr&& response) { + auto status = Envoy::Http::Utility::getResponseStatusOrNullopt(response->headers()); + if (status.has_value()) { + uint64_t status_code = status.value(); + if (status_code == Envoy::enumToInt(Envoy::Http::Code::OK)) { + ENVOY_LOG(error, "Response status is OK"); + } else { + ENVOY_LOG(error, "Response status is not OK, status: {}", status_code); + onError(); + } + } else { + // This occurs if the response headers are invalid. + ENVOY_LOG(error, "Failed to get the response because response headers are not valid."); + onError(); + } +} + +void ExtProcHttpClient::onFailure(const Http::AsyncClient::Request&, + Http::AsyncClient::FailureReason reason) { + ASSERT(reason == Http::AsyncClient::FailureReason::Reset || + reason == Http::AsyncClient::FailureReason::ExceedResponseBufferLimit); + ENVOY_LOG(error, "Request failed: stream has been reset"); + onError(); +} + +void ExtProcHttpClient::onError() { + // Cancel if the request is active. + cancel(); +} + +} // namespace ExternalProcessing +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/http/ext_proc/http_client/http_client_impl.h b/source/extensions/filters/http/ext_proc/http_client/http_client_impl.h new file mode 100644 index 000000000000..fa2df5afd12d --- /dev/null +++ b/source/extensions/filters/http/ext_proc/http_client/http_client_impl.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include "envoy/http/async_client.h" + +#include "source/common/common/logger.h" +#include "source/extensions/filters/http/ext_proc/ext_proc.h" +#include "source/extensions/filters/http/ext_proc/http_client/client_base.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace ExternalProcessing { + +class ExtProcHttpClient : public ClientBase, + public Http::AsyncClient::Callbacks, + public Logger::Loggable { +public: + ExtProcHttpClient(const envoy::extensions::filters::http::ext_proc::v3::ExternalProcessor& config, + Server::Configuration::ServerFactoryContext& context) + : config_(config), context_(context) {} + + ~ExtProcHttpClient() { cancel(); } + + void sendRequest() override {} + void cancel() override {} + void onBeforeFinalizeUpstreamSpan(Tracing::Span&, const Http::ResponseHeaderMap*) override {} + + // Http::AsyncClient::Callbacks implemented by this class. + void onSuccess(const Http::AsyncClient::Request& request, + Http::ResponseMessagePtr&& response) override; + void onFailure(const Http::AsyncClient::Request& request, + Http::AsyncClient::FailureReason reason) override; + + Server::Configuration::ServerFactoryContext& context() const { return context_; } + +private: + void onError(); + envoy::extensions::filters::http::ext_proc::v3::ExternalProcessor config_; + Server::Configuration::ServerFactoryContext& context_; +}; + +} // namespace ExternalProcessing +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/filters/http/ext_proc/http_client/BUILD b/test/extensions/filters/http/ext_proc/http_client/BUILD new file mode 100644 index 000000000000..d5c2826982dd --- /dev/null +++ b/test/extensions/filters/http/ext_proc/http_client/BUILD @@ -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 = "http_client_test", + size = "small", + srcs = ["http_client_test.cc"], + extension_names = ["envoy.filters.http.ext_proc"], + tags = ["skip_on_windows"], + deps = [ + "//source/common/http:message_lib", + "//source/extensions/filters/http/ext_proc/http_client:http_client_lib", + "//test/mocks/server:factory_context_mocks", + "@envoy_api//envoy/extensions/filters/http/ext_proc/v3:pkg_cc_proto", + ], +) diff --git a/test/extensions/filters/http/ext_proc/http_client/http_client_test.cc b/test/extensions/filters/http/ext_proc/http_client/http_client_test.cc new file mode 100644 index 000000000000..27a6d83c9ef1 --- /dev/null +++ b/test/extensions/filters/http/ext_proc/http_client/http_client_test.cc @@ -0,0 +1,65 @@ +#include "envoy/extensions/filters/http/ext_proc/v3/ext_proc.pb.h" + +#include "source/common/http/message_impl.h" +#include "source/extensions/filters/http/ext_proc/http_client/http_client_impl.h" + +#include "test/mocks/server/server_factory_context.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace HttpFilters { +namespace ExternalProcessing { +namespace { + +class ExtProcHttpClientTest : public testing::Test { +public: + ~ExtProcHttpClientTest() override = default; + + void SetUp() override { client_ = std::make_unique(config_, context_); } + +protected: + envoy::extensions::filters::http::ext_proc::v3::ExternalProcessor config_; + testing::NiceMock context_; + Upstream::MockClusterManager& cm_{context_.cluster_manager_}; + std::unique_ptr client_; + testing::NiceMock async_request_{ + &cm_.thread_local_cluster_.async_client_}; +}; + +TEST_F(ExtProcHttpClientTest, Basic) { + SetUp(); + client_->sendRequest(); + client_->cancel(); + client_->context(); + Tracing::MockSpan parent_span; + client_->onBeforeFinalizeUpstreamSpan(parent_span, nullptr); + Http::AsyncClient::FailureReason reason = Envoy::Http::AsyncClient::FailureReason::Reset; + client_->onFailure(async_request_, reason); + + Http::ResponseHeaderMapPtr resp_headers_ok(new Http::TestResponseHeaderMapImpl({ + {":status", "200"}, + })); + Http::ResponseMessagePtr response_ok(new Http::ResponseMessageImpl(std::move(resp_headers_ok))); + client_->onSuccess(async_request_, std::move(response_ok)); + + Http::ResponseHeaderMapPtr resp_headers(new Http::TestResponseHeaderMapImpl({ + {":status", "403"}, + })); + Http::ResponseMessagePtr response(new Http::ResponseMessageImpl(std::move(resp_headers))); + client_->onSuccess(async_request_, std::move(response)); + + Http::ResponseHeaderMapPtr resp_headers_foo(new Http::TestResponseHeaderMapImpl({ + {":status", "foo"}, + })); + Http::ResponseMessagePtr response_foo(new Http::ResponseMessageImpl(std::move(resp_headers_foo))); + client_->onSuccess(async_request_, std::move(response_foo)); +} + +} // namespace +} // namespace ExternalProcessing +} // namespace HttpFilters +} // namespace Extensions +} // namespace Envoy From 6648db236f64218611f36e91285716c8bec67abb Mon Sep 17 00:00:00 2001 From: Dvoriankin Evgenii Date: Mon, 19 Aug 2024 15:32:31 +0200 Subject: [PATCH 121/130] Made naming of some FilterChainOptions instances more consistent (#35500) Signed-off-by: Dvoriankin Evgenii --- envoy/http/filter_factory.h | 2 +- .../network/http_connection_manager/config.cc | 4 +-- .../network/http_connection_manager/config.h | 2 +- test/common/upstream/upstream_impl_test.cc | 2 +- .../config_filter_chain_test.cc | 32 +++++++++---------- test/mocks/http/mocks.h | 2 +- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/envoy/http/filter_factory.h b/envoy/http/filter_factory.h index fdad6281d845..a53e8977a995 100644 --- a/envoy/http/filter_factory.h +++ b/envoy/http/filter_factory.h @@ -113,7 +113,7 @@ class FilterChainFactory { virtual bool createUpgradeFilterChain( absl::string_view upgrade, const UpgradeMap* per_route_upgrade_map, FilterChainManager& manager, - const Http::FilterChainOptions& option = EmptyFilterChainOptions{}) const PURE; + const FilterChainOptions& options = EmptyFilterChainOptions{}) const PURE; }; } // namespace Http diff --git a/source/extensions/filters/network/http_connection_manager/config.cc b/source/extensions/filters/network/http_connection_manager/config.cc index e54182218b1c..a8a036a70a94 100644 --- a/source/extensions/filters/network/http_connection_manager/config.cc +++ b/source/extensions/filters/network/http_connection_manager/config.cc @@ -783,7 +783,7 @@ bool HttpConnectionManagerConfig::createFilterChain(Http::FilterChainManager& ma bool HttpConnectionManagerConfig::createUpgradeFilterChain( absl::string_view upgrade_type, const Http::FilterChainFactory::UpgradeMap* per_route_upgrade_map, - Http::FilterChainManager& callbacks, const Http::FilterChainOptions& option) const { + Http::FilterChainManager& callbacks, const Http::FilterChainOptions& options) const { bool route_enabled = false; if (per_route_upgrade_map) { auto route_it = findUpgradeBoolCaseInsensitive(*per_route_upgrade_map, upgrade_type); @@ -808,7 +808,7 @@ bool HttpConnectionManagerConfig::createUpgradeFilterChain( filters_to_use = it->second.filter_factories.get(); } - Http::FilterChainUtility::createFilterChainForFactories(callbacks, option, *filters_to_use); + Http::FilterChainUtility::createFilterChainForFactories(callbacks, options, *filters_to_use); return true; } diff --git a/source/extensions/filters/network/http_connection_manager/config.h b/source/extensions/filters/network/http_connection_manager/config.h index 29130a2f1793..566ff72bc996 100644 --- a/source/extensions/filters/network/http_connection_manager/config.h +++ b/source/extensions/filters/network/http_connection_manager/config.h @@ -149,7 +149,7 @@ class HttpConnectionManagerConfig : Logger::Loggable, bool createUpgradeFilterChain(absl::string_view upgrade_type, const Http::FilterChainFactory::UpgradeMap* per_route_upgrade_map, Http::FilterChainManager& manager, - const Http::FilterChainOptions& option) const override; + const Http::FilterChainOptions& options) const override; // Http::ConnectionManagerConfig const Http::RequestIDExtensionSharedPtr& requestIDExtension() override { diff --git a/test/common/upstream/upstream_impl_test.cc b/test/common/upstream/upstream_impl_test.cc index e8267dbf2843..b0d5217b2bb2 100644 --- a/test/common/upstream/upstream_impl_test.cc +++ b/test/common/upstream/upstream_impl_test.cc @@ -6118,7 +6118,7 @@ TEST_F(ClusterInfoImplTest, FilterChain) { auto cluster = makeCluster(yaml); Http::MockFilterChainManager manager; - Http::EmptyFilterChainOptions options; + const Http::EmptyFilterChainOptions options; EXPECT_FALSE(cluster->info()->createUpgradeFilterChain("foo", nullptr, manager, options)); EXPECT_CALL(manager, applyFilterFactoryCb(_, _)); diff --git a/test/extensions/filters/network/http_connection_manager/config_filter_chain_test.cc b/test/extensions/filters/network/http_connection_manager/config_filter_chain_test.cc index 9cc93b5749b9..29048d827af6 100644 --- a/test/extensions/filters/network/http_connection_manager/config_filter_chain_test.cc +++ b/test/extensions/filters/network/http_connection_manager/config_filter_chain_test.cc @@ -186,15 +186,15 @@ TEST_F(FilterChainTest, CreateUpgradeFilterChain) { ASSERT_TRUE(creation_status_.ok()); NiceMock manager; - Http::EmptyFilterChainOptions option; - ; + const Http::EmptyFilterChainOptions options; + // Check the case where WebSockets are configured in the HCM, and no router // config is present. We should create an upgrade filter chain for // WebSockets. { EXPECT_CALL(manager.callbacks_, addStreamFilter(_)); // Buffer EXPECT_CALL(manager.callbacks_, addStreamDecoderFilter(_)); // Router - EXPECT_TRUE(config.createUpgradeFilterChain("WEBSOCKET", nullptr, manager, option)); + EXPECT_TRUE(config.createUpgradeFilterChain("WEBSOCKET", nullptr, manager, options)); } // Check the case where WebSockets are configured in the HCM, and no router @@ -202,7 +202,7 @@ TEST_F(FilterChainTest, CreateUpgradeFilterChain) { { EXPECT_CALL(manager.callbacks_, addStreamFilter(_)).Times(0); EXPECT_CALL(manager.callbacks_, addStreamDecoderFilter(_)).Times(0); - EXPECT_FALSE(config.createUpgradeFilterChain("foo", nullptr, manager, option)); + EXPECT_FALSE(config.createUpgradeFilterChain("foo", nullptr, manager, options)); } // Now override the HCM with a route-specific disabling of WebSocket to @@ -210,7 +210,7 @@ TEST_F(FilterChainTest, CreateUpgradeFilterChain) { { std::map upgrade_map; upgrade_map.emplace(std::make_pair("WebSocket", false)); - EXPECT_FALSE(config.createUpgradeFilterChain("WEBSOCKET", &upgrade_map, manager, option)); + EXPECT_FALSE(config.createUpgradeFilterChain("WEBSOCKET", &upgrade_map, manager, options)); } // For paranoia's sake make sure route-specific enabling doesn't break @@ -220,7 +220,7 @@ TEST_F(FilterChainTest, CreateUpgradeFilterChain) { EXPECT_CALL(manager.callbacks_, addStreamDecoderFilter(_)); // Router std::map upgrade_map; upgrade_map.emplace(std::make_pair("WebSocket", true)); - EXPECT_TRUE(config.createUpgradeFilterChain("WEBSOCKET", &upgrade_map, manager, option)); + EXPECT_TRUE(config.createUpgradeFilterChain("WEBSOCKET", &upgrade_map, manager, options)); } } @@ -237,23 +237,23 @@ TEST_F(FilterChainTest, CreateUpgradeFilterChainHCMDisabled) { ASSERT_TRUE(creation_status_.ok()); NiceMock manager; - Http::EmptyFilterChainOptions option; + const Http::EmptyFilterChainOptions options; // Check the case where WebSockets are off in the HCM, and no router config is present. - { EXPECT_FALSE(config.createUpgradeFilterChain("WEBSOCKET", nullptr, manager, option)); } + { EXPECT_FALSE(config.createUpgradeFilterChain("WEBSOCKET", nullptr, manager, options)); } // Check the case where WebSockets are off in the HCM and in router config. { std::map upgrade_map; upgrade_map.emplace(std::make_pair("WebSocket", false)); - EXPECT_FALSE(config.createUpgradeFilterChain("WEBSOCKET", &upgrade_map, manager, option)); + EXPECT_FALSE(config.createUpgradeFilterChain("WEBSOCKET", &upgrade_map, manager, options)); } // With a route-specific enabling for WebSocket, WebSocket should work. { std::map upgrade_map; upgrade_map.emplace(std::make_pair("WebSocket", true)); - EXPECT_TRUE(config.createUpgradeFilterChain("WEBSOCKET", &upgrade_map, manager, option)); + EXPECT_TRUE(config.createUpgradeFilterChain("WEBSOCKET", &upgrade_map, manager, options)); } // With only a route-config we should do what the route config says. @@ -261,9 +261,9 @@ TEST_F(FilterChainTest, CreateUpgradeFilterChainHCMDisabled) { std::map upgrade_map; upgrade_map.emplace(std::make_pair("foo", true)); upgrade_map.emplace(std::make_pair("bar", false)); - EXPECT_TRUE(config.createUpgradeFilterChain("foo", &upgrade_map, manager, option)); - EXPECT_FALSE(config.createUpgradeFilterChain("bar", &upgrade_map, manager, option)); - EXPECT_FALSE(config.createUpgradeFilterChain("eep", &upgrade_map, manager, option)); + EXPECT_TRUE(config.createUpgradeFilterChain("foo", &upgrade_map, manager, options)); + EXPECT_FALSE(config.createUpgradeFilterChain("bar", &upgrade_map, manager, options)); + EXPECT_FALSE(config.createUpgradeFilterChain("eep", &upgrade_map, manager, options)); } } @@ -294,7 +294,7 @@ TEST_F(FilterChainTest, CreateCustomUpgradeFilterChain) { filter_config_provider_manager_, creation_status_); ASSERT_TRUE(creation_status_.ok()); - Http::EmptyFilterChainOptions option; + const Http::EmptyFilterChainOptions options; { NiceMock manager; @@ -306,14 +306,14 @@ TEST_F(FilterChainTest, CreateCustomUpgradeFilterChain) { { NiceMock manager; EXPECT_CALL(manager.callbacks_, addStreamDecoderFilter(_)); - EXPECT_TRUE(config.createUpgradeFilterChain("websocket", nullptr, manager, option)); + EXPECT_TRUE(config.createUpgradeFilterChain("websocket", nullptr, manager, options)); } { NiceMock manager; EXPECT_CALL(manager.callbacks_, addStreamDecoderFilter(_)); EXPECT_CALL(manager.callbacks_, addStreamFilter(_)).Times(2); // Buffer - EXPECT_TRUE(config.createUpgradeFilterChain("Foo", nullptr, manager, option)); + EXPECT_TRUE(config.createUpgradeFilterChain("Foo", nullptr, manager, options)); } } diff --git a/test/mocks/http/mocks.h b/test/mocks/http/mocks.h index 351db788e577..2aa5f34cb1b9 100644 --- a/test/mocks/http/mocks.h +++ b/test/mocks/http/mocks.h @@ -209,7 +209,7 @@ class MockFilterChainFactory : public FilterChainFactory { MOCK_METHOD(bool, createFilterChain, (FilterChainManager & manager), (const)); MOCK_METHOD(bool, createUpgradeFilterChain, (absl::string_view upgrade_type, const FilterChainFactory::UpgradeMap* upgrade_map, - FilterChainManager& manager, const Http::FilterChainOptions&), + FilterChainManager& manager, const FilterChainOptions&), (const)); }; From 7a09bfbe7f50a3b76242e73a2f7c6bc4de5a5534 Mon Sep 17 00:00:00 2001 From: Elisha Ziskind Date: Mon, 19 Aug 2024 14:03:52 -0400 Subject: [PATCH 122/130] ext_proc: expose main stream info from the async client steam info (#35650) Some upstream HTTP filters running in the ext_proc sidestream may require access to metadata associated with the main stream established for the client. This change exposes the main ("parent") stream_info from the async client's stream info. We need to clear this (to avoid a dangling reference) in the case of deferred stream closure, where the main stream closes before the async client stream. --------- Signed-off-by: Elisha Ziskind --- envoy/grpc/async_client.h | 1 + envoy/http/async_client.h | 1 + envoy/stream_info/stream_info.h | 17 +++++++++++++++++ source/common/grpc/async_client_impl.h | 1 + source/common/grpc/google_async_client_impl.h | 1 + source/common/grpc/typed_async_client.h | 1 + source/common/http/async_client_impl.cc | 4 ++++ source/common/http/async_client_impl.h | 2 +- source/common/stream_info/stream_info_impl.h | 10 ++++++++++ .../extensions/filters/http/ext_proc/client.h | 1 + .../filters/http/ext_proc/client_impl.h | 9 +++++++-- test/common/http/async_client_impl_test.cc | 12 ++++++++++++ .../filters/http/ext_proc/mock_server.h | 1 + .../http/ext_proc/unit_test_fuzz/mocks.h | 1 + test/mocks/grpc/mocks.h | 1 + test/mocks/http/mocks.h | 1 + test/mocks/stream_info/mocks.h | 3 +++ 17 files changed, 64 insertions(+), 3 deletions(-) diff --git a/envoy/grpc/async_client.h b/envoy/grpc/async_client.h index d263b128fd78..eea2a37701b4 100644 --- a/envoy/grpc/async_client.h +++ b/envoy/grpc/async_client.h @@ -70,6 +70,7 @@ class RawAsyncStream { * @returns the stream info object associated with this stream. */ virtual const StreamInfo::StreamInfo& streamInfo() const PURE; + virtual StreamInfo::StreamInfo& streamInfo() PURE; /*** * Register a callback to be called when high/low write buffer watermark events occur on the diff --git a/envoy/http/async_client.h b/envoy/http/async_client.h index 24e47c136038..e48c50b2de8c 100644 --- a/envoy/http/async_client.h +++ b/envoy/http/async_client.h @@ -234,6 +234,7 @@ class AsyncClient { * @returns the stream info object associated with the stream. */ virtual const StreamInfo::StreamInfo& streamInfo() const PURE; + virtual StreamInfo::StreamInfo& streamInfo() PURE; }; /*** diff --git a/envoy/stream_info/stream_info.h b/envoy/stream_info/stream_info.h index f3b8232967d6..39ffd18cef6a 100644 --- a/envoy/stream_info/stream_info.h +++ b/envoy/stream_info/stream_info.h @@ -980,6 +980,23 @@ class StreamInfo { */ virtual bool shouldDrainConnectionUponCompletion() const PURE; + /** + * Set the parent for this StreamInfo. This is used to associate the + * stream info of an async client with the stream info of the downstream + * connection. + */ + virtual void setParentStreamInfo(const StreamInfo& parent_stream_info) PURE; + + /** + * Get the parent for this StreamInfo, if available. + */ + virtual OptRef parentStreamInfo() const PURE; + + /** + * Clear the parent for this StreamInfo. + */ + virtual void clearParentStreamInfo() PURE; + /** * Called if the connection decides to drain itself after serving this request. * @param should_drain true to close the connection once this stream has diff --git a/source/common/grpc/async_client_impl.h b/source/common/grpc/async_client_impl.h index b1788812a0e9..aa4fb580455b 100644 --- a/source/common/grpc/async_client_impl.h +++ b/source/common/grpc/async_client_impl.h @@ -87,6 +87,7 @@ class AsyncStreamImpl : public RawAsyncStream, bool hasResetStream() const { return http_reset_; } const StreamInfo::StreamInfo& streamInfo() const override { return stream_->streamInfo(); } + StreamInfo::StreamInfo& streamInfo() override { return stream_->streamInfo(); } void setWatermarkCallbacks(Http::SidestreamWatermarkCallbacks& callbacks) override { stream_->setWatermarkCallbacks(callbacks); diff --git a/source/common/grpc/google_async_client_impl.h b/source/common/grpc/google_async_client_impl.h index 23bb2b6fbfdf..9ee8bdc28b30 100644 --- a/source/common/grpc/google_async_client_impl.h +++ b/source/common/grpc/google_async_client_impl.h @@ -236,6 +236,7 @@ class GoogleAsyncStreamImpl : public RawAsyncStream, return bytes_in_write_pending_queue_ > parent_.perStreamBufferLimitBytes(); } const StreamInfo::StreamInfo& streamInfo() const override { return unused_stream_info_; } + StreamInfo::StreamInfo& streamInfo() override { return unused_stream_info_; } // Google-gRPC code doesn't use Envoy watermark buffers, so the functions below are not used. void setWatermarkCallbacks(Http::SidestreamWatermarkCallbacks&) override {} diff --git a/source/common/grpc/typed_async_client.h b/source/common/grpc/typed_async_client.h index 294cb98de3e4..c4ed83f74bf5 100644 --- a/source/common/grpc/typed_async_client.h +++ b/source/common/grpc/typed_async_client.h @@ -58,6 +58,7 @@ template class AsyncStream /* : public RawAsyncStream */ { bool operator==(RawAsyncStream* stream) const { return stream_ == stream; } bool operator!=(RawAsyncStream* stream) const { return stream_ != stream; } const StreamInfo::StreamInfo& streamInfo() const { return stream_->streamInfo(); } + StreamInfo::StreamInfo& streamInfo() { return stream_->streamInfo(); } private: RawAsyncStream* stream_{}; diff --git a/source/common/http/async_client_impl.cc b/source/common/http/async_client_impl.cc index a0bec05e0d5a..493beb35c9a7 100644 --- a/source/common/http/async_client_impl.cc +++ b/source/common/http/async_client_impl.cc @@ -133,6 +133,10 @@ AsyncStreamImpl::AsyncStreamImpl(AsyncClientImpl& parent, AsyncClient::StreamCal stream_info_.setUpstreamClusterInfo(parent_.cluster_); stream_info_.route_ = route_; + if (options.parent_context.stream_info != nullptr) { + stream_info_.setParentStreamInfo(*options.parent_context.stream_info); + } + if (options.buffer_body_for_retry) { buffered_body_ = std::make_unique(account_); } diff --git a/source/common/http/async_client_impl.h b/source/common/http/async_client_impl.h index 91214a81a605..efbceb7e93d7 100644 --- a/source/common/http/async_client_impl.h +++ b/source/common/http/async_client_impl.h @@ -150,6 +150,7 @@ class AsyncStreamImpl : public virtual AsyncClient::Stream, void reset() override; bool isAboveWriteBufferHighWatermark() const override { return high_watermark_calls_ > 0; } const StreamInfo::StreamInfo& streamInfo() const override { return stream_info_; } + StreamInfo::StreamInfoImpl& streamInfo() override { return stream_info_; } protected: AsyncStreamImpl(AsyncClientImpl& parent, AsyncClient::StreamCallbacks& callbacks, @@ -157,7 +158,6 @@ class AsyncStreamImpl : public virtual AsyncClient::Stream, bool remoteClosed() { return remote_closed_; } void closeLocal(bool end_stream); - StreamInfo::StreamInfoImpl& streamInfo() override { return stream_info_; } AsyncClientImpl& parent_; // Callback to listen for stream destruction. diff --git a/source/common/stream_info/stream_info_impl.h b/source/common/stream_info/stream_info_impl.h index bcd5d6cc17eb..f74ad7cdd406 100644 --- a/source/common/stream_info/stream_info_impl.h +++ b/source/common/stream_info/stream_info_impl.h @@ -416,6 +416,7 @@ struct StreamInfoImpl : public StreamInfo { upstream_bytes_meter_ = info.getUpstreamBytesMeter(); bytes_sent_ = info.bytesSent(); is_shadow_ = info.isShadow(); + parent_stream_info_ = info.parentStreamInfo(); } void setIsShadow(bool is_shadow) { is_shadow_ = is_shadow; } @@ -441,6 +442,14 @@ struct StreamInfoImpl : public StreamInfo { should_drain_connection_ = should_drain; } + void setParentStreamInfo(const StreamInfo& parent_stream_info) override { + parent_stream_info_ = parent_stream_info; + } + + OptRef parentStreamInfo() const override { return parent_stream_info_; } + + void clearParentStreamInfo() override { parent_stream_info_.reset(); } + TimeSource& time_source_; SystemTime start_time_; MonotonicTime start_time_monotonic_; @@ -489,6 +498,7 @@ struct StreamInfoImpl : public StreamInfo { std::string downstream_transport_failure_reason_; bool should_scheme_match_upstream_{false}; bool should_drain_connection_{false}; + OptRef parent_stream_info_; }; } // namespace StreamInfo diff --git a/source/extensions/filters/http/ext_proc/client.h b/source/extensions/filters/http/ext_proc/client.h index 79913d16489b..54493c094fe3 100644 --- a/source/extensions/filters/http/ext_proc/client.h +++ b/source/extensions/filters/http/ext_proc/client.h @@ -24,6 +24,7 @@ class ExternalProcessorStream { // Idempotent close. Return true if it actually closed. virtual bool close() PURE; virtual const StreamInfo::StreamInfo& streamInfo() const PURE; + virtual StreamInfo::StreamInfo& streamInfo() PURE; virtual void notifyFilterDestroy() PURE; }; diff --git a/source/extensions/filters/http/ext_proc/client_impl.h b/source/extensions/filters/http/ext_proc/client_impl.h index d0c08d24f507..745bd3f167c8 100644 --- a/source/extensions/filters/http/ext_proc/client_impl.h +++ b/source/extensions/filters/http/ext_proc/client_impl.h @@ -60,8 +60,12 @@ class ExternalProcessorStreamImpl : public ExternalProcessorStream, // Unregister the watermark callbacks(if any) to prevent access of filter callbacks after // the filter object is destroyed. - if (grpc_side_stream_flow_control_ && !stream_closed_) { - stream_.removeWatermarkCallbacks(); + if (!stream_closed_) { + // Remove the parent stream info to avoid a dangling reference. + stream_.streamInfo().clearParentStreamInfo(); + if (grpc_side_stream_flow_control_) { + stream_.removeWatermarkCallbacks(); + } } } @@ -74,6 +78,7 @@ class ExternalProcessorStreamImpl : public ExternalProcessorStream, void onReceiveTrailingMetadata(Http::ResponseTrailerMapPtr&& metadata) override; void onRemoteClose(Grpc::Status::GrpcStatus status, const std::string& message) override; const StreamInfo::StreamInfo& streamInfo() const override { return stream_.streamInfo(); } + StreamInfo::StreamInfo& streamInfo() override { return stream_.streamInfo(); } bool grpcSidestreamFlowControl() { return grpc_side_stream_flow_control_; } diff --git a/test/common/http/async_client_impl_test.cc b/test/common/http/async_client_impl_test.cc index 2d5bc5892611..0bd9c5c5e502 100644 --- a/test/common/http/async_client_impl_test.cc +++ b/test/common/http/async_client_impl_test.cc @@ -2195,6 +2195,18 @@ TEST_F(AsyncClientImplTest, DumpState) { EXPECT_CALL(stream_callbacks_, onReset()); } +TEST_F(AsyncClientImplTest, ParentStreamInfo) { + NiceMock parent_stream_info; + auto options = AsyncClient::StreamOptions(); + options.parent_context.stream_info = &parent_stream_info; + AsyncClient::Stream* stream = client_.start(stream_callbacks_, options); + EXPECT_TRUE(stream->streamInfo().parentStreamInfo().has_value()); + EXPECT_EQ(stream->streamInfo().parentStreamInfo().ptr(), + dynamic_cast(&parent_stream_info)); + stream->streamInfo().clearParentStreamInfo(); + EXPECT_FALSE(stream->streamInfo().parentStreamInfo().has_value()); +} + } // namespace // Must not be in anonymous namespace for friend to work. diff --git a/test/extensions/filters/http/ext_proc/mock_server.h b/test/extensions/filters/http/ext_proc/mock_server.h index 6ee9d92b32aa..d0b0389b0fd4 100644 --- a/test/extensions/filters/http/ext_proc/mock_server.h +++ b/test/extensions/filters/http/ext_proc/mock_server.h @@ -26,6 +26,7 @@ class MockStream : public ExternalProcessorStream { MOCK_METHOD(void, send, (envoy::service::ext_proc::v3::ProcessingRequest&&, bool)); MOCK_METHOD(bool, close, ()); MOCK_METHOD(const StreamInfo::StreamInfo&, streamInfo, (), (const override)); + MOCK_METHOD(StreamInfo::StreamInfo&, streamInfo, ()); MOCK_METHOD(void, notifyFilterDestroy, ()); }; diff --git a/test/extensions/filters/http/ext_proc/unit_test_fuzz/mocks.h b/test/extensions/filters/http/ext_proc/unit_test_fuzz/mocks.h index f346ee34b8e9..49ff067dd353 100644 --- a/test/extensions/filters/http/ext_proc/unit_test_fuzz/mocks.h +++ b/test/extensions/filters/http/ext_proc/unit_test_fuzz/mocks.h @@ -21,6 +21,7 @@ class MockStream : public ExternalProcessing::ExternalProcessorStream { (envoy::service::ext_proc::v3::ProcessingRequest && request, bool end_stream)); MOCK_METHOD(bool, close, ()); MOCK_METHOD(const StreamInfo::StreamInfo&, streamInfo, (), (const override)); + MOCK_METHOD(StreamInfo::StreamInfo&, streamInfo, ()); MOCK_METHOD(void, notifyFilterDestroy, ()); }; diff --git a/test/mocks/grpc/mocks.h b/test/mocks/grpc/mocks.h index bd9287102642..d1d41f211115 100644 --- a/test/mocks/grpc/mocks.h +++ b/test/mocks/grpc/mocks.h @@ -39,6 +39,7 @@ class MockAsyncStream : public RawAsyncStream { MOCK_METHOD(void, resetStream, ()); MOCK_METHOD(bool, isAboveWriteBufferHighWatermark, (), (const)); MOCK_METHOD(const StreamInfo::StreamInfo&, streamInfo, (), (const)); + MOCK_METHOD(StreamInfo::StreamInfo&, streamInfo, (), ()); MOCK_METHOD(void, setWatermarkCallbacks, (Http::SidestreamWatermarkCallbacks&)); MOCK_METHOD(void, removeWatermarkCallbacks, ()); }; diff --git a/test/mocks/http/mocks.h b/test/mocks/http/mocks.h index 2aa5f34cb1b9..cdf2690c2a1c 100644 --- a/test/mocks/http/mocks.h +++ b/test/mocks/http/mocks.h @@ -577,6 +577,7 @@ class MockAsyncClientStream : public virtual AsyncClient::Stream { (override)); MOCK_METHOD(void, removeWatermarkCallbacks, (), (override)); MOCK_METHOD(const StreamInfo::StreamInfo&, streamInfo, (), (const override)); + MOCK_METHOD(StreamInfo::StreamInfo&, streamInfo, (), (override)); private: absl::optional destructor_callback_; diff --git a/test/mocks/stream_info/mocks.h b/test/mocks/stream_info/mocks.h index d1c2df8d0fef..fc81b0fcf6d6 100644 --- a/test/mocks/stream_info/mocks.h +++ b/test/mocks/stream_info/mocks.h @@ -172,6 +172,9 @@ class MockStreamInfo : public StreamInfo { MOCK_METHOD(void, setShouldSchemeMatchUpstream, (bool)); MOCK_METHOD(bool, shouldDrainConnectionUponCompletion, (), (const)); MOCK_METHOD(void, setShouldDrainConnectionUponCompletion, (bool)); + MOCK_METHOD(void, setParentStreamInfo, (const StreamInfo&), ()); + MOCK_METHOD(void, clearParentStreamInfo, ()); + MOCK_METHOD(OptRef, parentStreamInfo, (), (const)); Envoy::Event::SimulatedTimeSystem ts_; SystemTime start_time_; From c61838567f214812b991f0c76db935a54300d94a Mon Sep 17 00:00:00 2001 From: "Adi (Suissa) Peleg" Date: Mon, 19 Aug 2024 14:14:37 -0400 Subject: [PATCH 123/130] http: modify authority validation to allow @ character (#35602) Envoy uses the `authorityIsValid()` to validate the authority header for both H/1 and H/2 codecs. Previously Envoy used the nghttp2 validator and in #24943 this was changed to oghttp2's implementation. The two implementations differ in the way they handle the "@" character (nghttp2 allows it, and oghttp2 doesn't). According to the H/2 spec, the "@" character is not allowed as part of the authority header. However, for H/1 it is allowed as part of the "user-info@host:port" structure of the authority header. This PR changes the validator to be similar to the nghttp2 implemenation. The change can be temporarily dis In the future, when Envoy fully supports UHV (#10646), the H/1 and H/2 validation parts should be decoupled, and the oghttp2 authority validation can be used for H/2. --------- Signed-off-by: Adi Suissa-Peleg --- changelogs/current.yaml | 6 ++ source/common/http/header_utility.cc | 89 +++++++++++++++++++++++ source/common/runtime/runtime_features.cc | 1 + test/common/http/header_utility_test.cc | 18 +++++ 4 files changed, 114 insertions(+) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 9c75222d83e3..8f28648b8043 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -55,6 +55,12 @@ minor_behavior_changes: change: | When Lua script executes httpCall, backpressure is exercised when receiving body from downstream client. This behavior can be reverted by setting the runtime guard ``envoy.reloadable_features.lua_flow_control_while_http_call`` to false. +- area: http + change: | + Modified the authority header value validator to allow the same characters as oghttp2 + plus the "@" character. This is compliant with nghttp2, and supports the HTTP/1 use-cases + that allow user-info@ as part of the authority. This behavior can be reverted by setting + the runtime guard ``envoy.reloadable_features.internal_authority_header_validator`` to false. - area: sni change: | When computing SNI and SAN value for the auto-sni and auto-san verification feature, diff --git a/source/common/http/header_utility.cc b/source/common/http/header_utility.cc index 9091f8696835..bf8dd0b8d323 100644 --- a/source/common/http/header_utility.cc +++ b/source/common/http/header_utility.cc @@ -235,7 +235,96 @@ bool HeaderUtility::headerNameContainsUnderscore(const absl::string_view header_ return header_name.find('_') != absl::string_view::npos; } +namespace { +// This function validates the authority header for both HTTP/1 and HTTP/2. +// Note the HTTP/1 spec allows "user-info@host:port" for the authority, whereas +// the HTTP/2 spec only allows "host:port". Thus, this function permits all the +// HTTP/2 valid characters (similar to oghttp2's implementation) and the "@" character. +// Once UHV is used, this function should be removed, and the HTTP/1 and HTTP/2 +// authority validations should be different. +bool check_authority_h1_h2(const absl::string_view header_value) { + static constexpr char ValidAuthorityChars[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, + 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, + 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, + 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, + 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, + 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, + 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, + 0 /* SPC */, 1 /* ! */, 0 /* " */, 0 /* # */, + 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, + 1 /* , */, 1 /* - */, 1 /* . */, 0 /* / */, + 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, + 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, + 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, + 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */, + 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, + 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, + 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, + 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, + 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, + 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */, + 0 /* \ */, 1 /* ] */, 0 /* ^ */, 1 /* _ */, + 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, + 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, + 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, + 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, + 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, + 0 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */, + 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */, + 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */, + 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, + 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, + 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */, + 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */, + 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, + 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, + 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */, + 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */, + 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, + 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, + 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */, + 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */, + 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, + 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, + 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */, + 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */, + 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, + 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, + 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */, + 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */, + 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, + 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, + 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */, + 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */ + }; + + for (const uint8_t c : header_value) { + if (!ValidAuthorityChars[c]) { + return false; + } + } + return true; +} +} // namespace + bool HeaderUtility::authorityIsValid(const absl::string_view header_value) { + if (Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.internal_authority_header_validator")) { + return check_authority_h1_h2(header_value); + } + #ifdef ENVOY_NGHTTP2 if (!Runtime::runtimeFeatureEnabled( "envoy.reloadable_features.http2_validate_authority_with_quiche")) { diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index b7bb16b0a543..1e97077b7166 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -62,6 +62,7 @@ RUNTIME_GUARD(envoy_reloadable_features_http_filter_avoid_reentrant_local_reply) // Delay deprecation and decommission until UHV is enabled. RUNTIME_GUARD(envoy_reloadable_features_http_reject_path_with_fragment); RUNTIME_GUARD(envoy_reloadable_features_http_route_connect_proxy_by_default); +RUNTIME_GUARD(envoy_reloadable_features_internal_authority_header_validator); RUNTIME_GUARD(envoy_reloadable_features_jwt_authn_remove_jwt_from_query_params); RUNTIME_GUARD(envoy_reloadable_features_jwt_authn_validate_uri); RUNTIME_GUARD(envoy_reloadable_features_lua_flow_control_while_http_call); diff --git a/test/common/http/header_utility_test.cc b/test/common/http/header_utility_test.cc index 971f0e953f68..577f36eea951 100644 --- a/test/common/http/header_utility_test.cc +++ b/test/common/http/header_utility_test.cc @@ -1152,6 +1152,24 @@ TEST(HeaderIsValidTest, ValidHeaderValuesAreAccepted) { TEST(HeaderIsValidTest, AuthorityIsValid) { EXPECT_TRUE(HeaderUtility::authorityIsValid("strangebutlegal$-%&'")); EXPECT_FALSE(HeaderUtility::authorityIsValid("illegal{}")); + // Validate that the "@" character is allowed. + // TODO(adisuissa): Once the envoy.reloadable_features.internal_authority_header_validator + // runtime flag is deprecated, this test should only validate the assignment + // to "true". + { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.internal_authority_header_validator", "true"}}); + EXPECT_TRUE(HeaderUtility::authorityIsValid("username@example.com'")); + } + { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.internal_authority_header_validator", "false"}}); + // When the above is false, Envoy should use oghttp2's validator which will + // reject the "@" character. + EXPECT_FALSE(HeaderUtility::authorityIsValid("username@example.com'")); + } } TEST(HeaderIsValidTest, IsConnect) { From 56fcd4dcbf05b9f3b53ac8b245adbdf3a59c15a3 Mon Sep 17 00:00:00 2001 From: Ting Pan Date: Tue, 20 Aug 2024 10:23:14 -0400 Subject: [PATCH 124/130] cluster: flip envoy_reloadable_features_use_config_in_happy_eyeballs to true (#35697) Commit Message: flip envoy_reloadable_features_use_config_in_happy_eyeballs to true Additional Description: This flag has been flipped to true internally since March Risk Level: low Testing: tested internally for 4 month Docs Changes: Release Notes: Make flag envoy_reloadable_features_use_config_in_happy_eyeballs default to true --------- Signed-off-by: Ting Pan --- changelogs/current.yaml | 6 ++++++ source/common/runtime/runtime_features.cc | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 8f28648b8043..0ceb2e4e091b 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -214,5 +214,11 @@ new_features: change: | Added :ref:`delay_deny ` to support deny connection after the configured duration. +- area: cluster + change: | + Customizing the happy eyeballs algorithm for an upstream cluster by configuring + :ref:`happy_eyeballs_config `. + A default configuration will be used if not provided. This behavior can be reverted + by setting the runtime guard ``envoy.reloadable_features.use_config_in_happy_eyeballs`` to false. deprecated: diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 1e97077b7166..cff3db99a0bb 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -90,6 +90,7 @@ RUNTIME_GUARD(envoy_reloadable_features_test_feature_true); RUNTIME_GUARD(envoy_reloadable_features_udp_socket_apply_aggregated_read_limit); RUNTIME_GUARD(envoy_reloadable_features_uhv_allow_malformed_url_encoding); RUNTIME_GUARD(envoy_reloadable_features_upstream_remote_address_use_connection); +RUNTIME_GUARD(envoy_reloadable_features_use_config_in_happy_eyeballs); RUNTIME_GUARD(envoy_reloadable_features_use_http3_header_normalisation); RUNTIME_GUARD(envoy_reloadable_features_use_route_host_mutation_for_auto_sni_san); RUNTIME_GUARD(envoy_reloadable_features_use_typed_metadata_in_proxy_protocol_listener); @@ -136,8 +137,6 @@ FALSE_RUNTIME_GUARD(envoy_reloadable_features_use_http_client_to_fetch_aws_crede FALSE_RUNTIME_GUARD(envoy_reloadable_features_enable_universal_header_validator); // TODO(pksohn): enable after canarying fix for https://github.com/envoyproxy/envoy/issues/29930 FALSE_RUNTIME_GUARD(envoy_reloadable_features_quic_defer_logging_to_ack_listener); -// TODO(panting): flip this to true after some test time. -FALSE_RUNTIME_GUARD(envoy_reloadable_features_use_config_in_happy_eyeballs); // TODO(#33474) removed it once GRO packet dropping is fixed. FALSE_RUNTIME_GUARD(envoy_reloadable_features_prefer_quic_client_udp_gro); // TODO(alyssar) evaluate and either make this a config knob or remove. From e77a708dc3c80595105af03dceb062e46982cd54 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Tue, 20 Aug 2024 10:26:06 -0500 Subject: [PATCH 125/130] mobile: Load envoy_repo in the WORKSPACE (#35758) This is to fix this error. ``` ERROR: Skipping '@envoy//tools/code_format:check_format': error loading package '@envoy//tools/code_format': Unable to find package for @envoy_repo//:path.bzl: The repository '@envoy_repo' could not be resolved: Repository '@envoy_repo' is not defined. ``` Risk Level: low Testing: `tools/check_format.sh fix` Docs Changes: n/a Release Notes: n/a Platform Specific Features: n/a Signed-off-by: Fredy Wijaya --- mobile/WORKSPACE | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mobile/WORKSPACE b/mobile/WORKSPACE index cdb172f0cd24..84ee61649ac2 100644 --- a/mobile/WORKSPACE +++ b/mobile/WORKSPACE @@ -46,6 +46,10 @@ load("@envoy//bazel:api_repositories.bzl", "envoy_api_dependencies") envoy_api_dependencies() +load("@envoy//bazel:repo.bzl", "envoy_repo") + +envoy_repo() + load("@envoy//bazel:repositories.bzl", "envoy_dependencies") envoy_dependencies() From e6b1c8b58427f22b7f5b34d34a94aed2c1457050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E6=B3=BD=E8=BD=A9?= Date: Tue, 20 Aug 2024 23:45:11 +0800 Subject: [PATCH 126/130] golang: add OnStreamComplete callback to mutate final metadata (#35742) As discussed from https://github.com/envoyproxy/envoy/pull/35595#issuecomment-2278413889, the `AccessLogHandler::log` is not designed for mutating the StreamInfo. It's recommended to use `OnStreamComplete` to do the final metadata management. Signed-off-by: spacewander --- contrib/golang/common/dso/dso.cc | 8 ++++++++ contrib/golang/common/dso/dso.h | 3 +++ contrib/golang/common/dso/libgolang.h | 4 ++++ contrib/golang/common/dso/test/mocks.h | 1 + contrib/golang/common/dso/test/test_data/simple.go | 4 ++++ contrib/golang/common/go/api/filter.go | 5 ++++- contrib/golang/filters/http/source/go/pkg/http/shim.go | 9 +++++++++ contrib/golang/filters/http/source/golang_filter.cc | 8 ++++++++ contrib/golang/filters/http/source/golang_filter.h | 3 +-- .../golang/filters/http/test/golang_filter_fuzz_test.cc | 2 ++ .../golang/filters/http/test/golang_integration_test.cc | 6 ++++++ .../filters/http/test/test_data/access_log/filter.go | 4 ++++ 12 files changed, 54 insertions(+), 3 deletions(-) diff --git a/contrib/golang/common/dso/dso.cc b/contrib/golang/common/dso/dso.cc index 699ebf4b8579..0736854abf5f 100644 --- a/contrib/golang/common/dso/dso.cc +++ b/contrib/golang/common/dso/dso.cc @@ -59,6 +59,9 @@ HttpFilterDsoImpl::HttpFilterDsoImpl(const std::string dso_name) : HttpFilterDso envoy_go_filter_on_http_data_, handler_, dso_name, "envoyGoFilterOnHttpData"); loaded_ &= dlsymInternal( envoy_go_filter_on_http_log_, handler_, dso_name, "envoyGoFilterOnHttpLog"); + loaded_ &= dlsymInternal( + envoy_go_filter_on_http_stream_complete_, handler_, dso_name, + "envoyGoFilterOnHttpStreamComplete"); loaded_ &= dlsymInternal( envoy_go_filter_on_http_destroy_, handler_, dso_name, "envoyGoFilterOnHttpDestroy"); loaded_ &= dlsymInternal( @@ -103,6 +106,11 @@ void HttpFilterDsoImpl::envoyGoFilterOnHttpLog(httpRequest* p0, int p1, processS envoy_go_filter_on_http_log_(p0, GoUint64(p1), p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); } +void HttpFilterDsoImpl::envoyGoFilterOnHttpStreamComplete(httpRequest* p0) { + ASSERT(envoy_go_filter_on_http_stream_complete_ != nullptr); + envoy_go_filter_on_http_stream_complete_(p0); +} + void HttpFilterDsoImpl::envoyGoFilterOnHttpDestroy(httpRequest* p0, int p1) { ASSERT(envoy_go_filter_on_http_destroy_ != nullptr); envoy_go_filter_on_http_destroy_(p0, GoUint64(p1)); diff --git a/contrib/golang/common/dso/dso.h b/contrib/golang/common/dso/dso.h index 293072a2daa9..1301b1af72b9 100644 --- a/contrib/golang/common/dso/dso.h +++ b/contrib/golang/common/dso/dso.h @@ -46,6 +46,7 @@ class HttpFilterDso : public Dso { virtual void envoyGoFilterOnHttpLog(httpRequest* p0, int p1, processState* p2, processState* p3, GoUint64 p4, GoUint64 p5, GoUint64 p6, GoUint64 p7, GoUint64 p8, GoUint64 p9, GoUint64 p10, GoUint64 p11) PURE; + virtual void envoyGoFilterOnHttpStreamComplete(httpRequest* p0) PURE; virtual void envoyGoFilterOnHttpDestroy(httpRequest* p0, int p1) PURE; virtual void envoyGoRequestSemaDec(httpRequest* p0) PURE; }; @@ -66,6 +67,7 @@ class HttpFilterDsoImpl : public HttpFilterDso { void envoyGoFilterOnHttpLog(httpRequest* p0, int p1, processState* p2, processState* p3, GoUint64 p4, GoUint64 p5, GoUint64 p6, GoUint64 p7, GoUint64 p8, GoUint64 p9, GoUint64 p10, GoUint64 p11) override; + void envoyGoFilterOnHttpStreamComplete(httpRequest* p0) override; void envoyGoFilterOnHttpDestroy(httpRequest* p0, int p1) override; void envoyGoRequestSemaDec(httpRequest* p0) override; void cleanup() override; @@ -83,6 +85,7 @@ class HttpFilterDsoImpl : public HttpFilterDso { GoUint64 p4, GoUint64 p5, GoUint64 p6, GoUint64 p7, GoUint64 p8, GoUint64 p9, GoUint64 p10, GoUint64 p11) = {nullptr}; + void (*envoy_go_filter_on_http_stream_complete_)(httpRequest* p0) = {nullptr}; void (*envoy_go_filter_on_http_destroy_)(httpRequest* p0, GoUint64 p1) = {nullptr}; void (*envoy_go_filter_go_request_sema_dec_)(httpRequest* p0) = {nullptr}; void (*envoy_go_filter_cleanup_)() = {nullptr}; diff --git a/contrib/golang/common/dso/libgolang.h b/contrib/golang/common/dso/libgolang.h index 695ff00761f3..eab784cb53ee 100644 --- a/contrib/golang/common/dso/libgolang.h +++ b/contrib/golang/common/dso/libgolang.h @@ -127,6 +127,10 @@ extern void envoyGoFilterOnHttpLog(httpRequest* r, GoUint64 type, processState* GoUint64 resp_header_bytes, GoUint64 resp_trailer_num, GoUint64 resp_trailer_bytes); +// go:linkname envoyGoFilterOnHttpStreamComplete +// github.com/envoyproxy/envoy/contrib/golang/filters/http/source/go/pkg/http.envoyGoFilterOnHttpStreamComplete +extern void envoyGoFilterOnHttpStreamComplete(httpRequest* r); + // go:linkname envoyGoFilterOnHttpDestroy // github.com/envoyproxy/envoy/contrib/golang/filters/http/source/go/pkg/http.envoyGoFilterOnHttpDestroy extern void envoyGoFilterOnHttpDestroy(httpRequest* r, GoUint64 reason); diff --git a/contrib/golang/common/dso/test/mocks.h b/contrib/golang/common/dso/test/mocks.h index 082251738863..14247799e829 100644 --- a/contrib/golang/common/dso/test/mocks.h +++ b/contrib/golang/common/dso/test/mocks.h @@ -25,6 +25,7 @@ class MockHttpFilterDsoImpl : public HttpFilterDso { (httpRequest * p0, int p1, processState* p2, processState* p3, GoUint64 p4, GoUint64 p5, GoUint64 p6, GoUint64 p7, GoUint64 p8, GoUint64 p9, GoUint64 p10, GoUint64 p11)); + MOCK_METHOD(void, envoyGoFilterOnHttpStreamComplete, (httpRequest * p0)); MOCK_METHOD(void, envoyGoFilterOnHttpDestroy, (httpRequest * p0, int p1)); MOCK_METHOD(void, envoyGoRequestSemaDec, (httpRequest * p0)); MOCK_METHOD(void, envoyGoFilterCleanUp, ()); diff --git a/contrib/golang/common/dso/test/test_data/simple.go b/contrib/golang/common/dso/test/test_data/simple.go index 019d075b9958..22d36a1cbd3e 100644 --- a/contrib/golang/common/dso/test/test_data/simple.go +++ b/contrib/golang/common/dso/test/test_data/simple.go @@ -62,6 +62,10 @@ func envoyGoFilterOnHttpLog(r *C.httpRequest, logType uint64, decodingState *C.p respHeaderNum, respHeaderBytes, respTrailerNum, respTrailerBytes uint64) { } +//export envoyGoFilterOnHttpStreamComplete +func envoyGoFilterOnHttpStreamComplete(r *C.httpRequest) { +} + //export envoyGoFilterOnHttpDestroy func envoyGoFilterOnHttpDestroy(r *C.httpRequest, reason uint64) { } diff --git a/contrib/golang/common/go/api/filter.go b/contrib/golang/common/go/api/filter.go index ade8732ae475..43ba5507f251 100644 --- a/contrib/golang/common/go/api/filter.go +++ b/contrib/golang/common/go/api/filter.go @@ -87,7 +87,7 @@ type StreamFilter interface { // destroy filter OnDestroy(DestroyReason) - // TODO add more for stream complete + OnStreamComplete() } func (*PassThroughStreamFilter) OnLog(RequestHeaderMap, RequestTrailerMap, ResponseHeaderMap, ResponseTrailerMap) { @@ -102,6 +102,9 @@ func (*PassThroughStreamFilter) OnLogDownstreamPeriodic(RequestHeaderMap, Reques func (*PassThroughStreamFilter) OnDestroy(DestroyReason) { } +func (*PassThroughStreamFilter) OnStreamComplete() { +} + type StreamFilterConfigParser interface { // Parse the proto message to any Go value, and return error to reject the config. // This is called when Envoy receives the config from the control plane. diff --git a/contrib/golang/filters/http/source/go/pkg/http/shim.go b/contrib/golang/filters/http/source/go/pkg/http/shim.go index 4ae55bf75c27..8b2ffc1a88d7 100644 --- a/contrib/golang/filters/http/source/go/pkg/http/shim.go +++ b/contrib/golang/filters/http/source/go/pkg/http/shim.go @@ -348,6 +348,15 @@ func envoyGoFilterOnHttpLog(r *C.httpRequest, logType uint64, } } +//export envoyGoFilterOnHttpStreamComplete +func envoyGoFilterOnHttpStreamComplete(r *C.httpRequest) { + req := getRequest(r) + defer req.recoverPanic() + + f := req.httpFilter + f.OnStreamComplete() +} + //export envoyGoFilterOnHttpDestroy func envoyGoFilterOnHttpDestroy(r *C.httpRequest, reason uint64) { req := getRequest(r) diff --git a/contrib/golang/filters/http/source/golang_filter.cc b/contrib/golang/filters/http/source/golang_filter.cc index 067e1daa5d41..b181b6142c52 100644 --- a/contrib/golang/filters/http/source/golang_filter.cc +++ b/contrib/golang/filters/http/source/golang_filter.cc @@ -131,6 +131,14 @@ Http::FilterTrailersStatus Filter::encodeTrailers(Http::ResponseTrailerMap& trai return done ? Http::FilterTrailersStatus::Continue : Http::FilterTrailersStatus::StopIteration; } +void Filter::onStreamComplete() { + // We reuse the same flag for both onStreamComplete & log to save the space, + // since they are exclusive and serve for the access log purpose. + req_->is_golang_processing_log = 1; + dynamic_lib_->envoyGoFilterOnHttpStreamComplete(req_); + req_->is_golang_processing_log = 0; +} + void Filter::onDestroy() { ENVOY_LOG(debug, "golang filter on destroy"); diff --git a/contrib/golang/filters/http/source/golang_filter.h b/contrib/golang/filters/http/source/golang_filter.h index 763c94934e80..651903e94f37 100644 --- a/contrib/golang/filters/http/source/golang_filter.h +++ b/contrib/golang/filters/http/source/golang_filter.h @@ -223,6 +223,7 @@ class Filter : public Http::StreamFilter, } // Http::StreamFilterBase + void onStreamComplete() override; void onDestroy() ABSL_LOCKS_EXCLUDED(mutex_) override; Http::LocalErrorStatus onLocalReply(const LocalReplyData&) override; @@ -255,8 +256,6 @@ class Filter : public Http::StreamFilter, void log(const Formatter::HttpFormatterContext& log_context, const StreamInfo::StreamInfo& info) override; - void onStreamComplete() override {} - CAPIStatus clearRouteCache(); CAPIStatus continueStatus(ProcessorState& state, GolangStatus status); diff --git a/contrib/golang/filters/http/test/golang_filter_fuzz_test.cc b/contrib/golang/filters/http/test/golang_filter_fuzz_test.cc index cc324d410482..4c777a0b01f4 100644 --- a/contrib/golang/filters/http/test/golang_filter_fuzz_test.cc +++ b/contrib/golang/filters/http/test/golang_filter_fuzz_test.cc @@ -58,6 +58,8 @@ DEFINE_PROTO_FUZZER(const envoy::extensions::filters::http::golang::GolangFilter .WillByDefault( Invoke([&](httpRequest*, int, processState*, processState*, GoUint64, GoUint64, GoUint64, GoUint64, GoUint64, GoUint64, GoUint64, GoUint64) -> void {})); + ON_CALL(*dso_lib.get(), envoyGoFilterOnHttpStreamComplete(_)) + .WillByDefault(Invoke([&](httpRequest*) -> void {})); ON_CALL(*dso_lib.get(), envoyGoFilterOnHttpDestroy(_, _)) .WillByDefault(Invoke([&](httpRequest* p0, int) -> void { // delete the filter->req_, make LeakSanitizer happy. diff --git a/contrib/golang/filters/http/test/golang_integration_test.cc b/contrib/golang/filters/http/test/golang_integration_test.cc index a1af5a0e40bb..d51d968413d1 100644 --- a/contrib/golang/filters/http/test/golang_integration_test.cc +++ b/contrib/golang/filters/http/test/golang_integration_test.cc @@ -11,6 +11,8 @@ namespace Envoy { +using testing::HasSubstr; + // helper function absl::string_view getHeader(const Http::HeaderMap& headers, absl::string_view key) { auto values = headers.get(Http::LowerCaseString(key)); @@ -888,6 +890,7 @@ TEST_P(GolangIntegrationTest, Property) { } TEST_P(GolangIntegrationTest, AccessLog) { + useAccessLog("%DYNAMIC_METADATA(golang:access_log_var)%"); initializeBasicFilter(ACCESSLOG, "test.com"); auto path = "/test"; @@ -924,6 +927,9 @@ TEST_P(GolangIntegrationTest, AccessLog) { ASSERT_TRUE(response->waitForEndStream()); codec_client_->close(); + std::string log = waitForAccessLog(access_log_name_); + EXPECT_THAT(log, HasSubstr("access_log_var written by Golang filter")); + // use the second request to get the logged data codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http")))); response = sendRequestAndWaitForResponse(request_headers, 0, default_response_headers_, 0); diff --git a/contrib/golang/filters/http/test/test_data/access_log/filter.go b/contrib/golang/filters/http/test/test_data/access_log/filter.go index 5713b36d1082..ecace4cdd4fb 100644 --- a/contrib/golang/filters/http/test/test_data/access_log/filter.go +++ b/contrib/golang/filters/http/test/test_data/access_log/filter.go @@ -117,6 +117,10 @@ func (f *filter) OnLogDownstreamPeriodic(reqHeader api.RequestHeaderMap, reqTrai }() } +func (f *filter) OnStreamComplete() { + f.callbacks.StreamInfo().DynamicMetadata().Set("golang", "access_log_var", "access_log_var written by Golang filter") +} + func (f *filter) OnLog(reqHeader api.RequestHeaderMap, reqTrailer api.RequestTrailerMap, respHeader api.ResponseHeaderMap, respTrailer api.ResponseTrailerMap) { referer, err := f.callbacks.GetProperty("request.referer") if err != nil { From 6dcd221dbe65cfb6edd5c5568ff1bbf69368cc62 Mon Sep 17 00:00:00 2001 From: ohadvano <49730675+ohadvano@users.noreply.github.com> Date: Tue, 20 Aug 2024 23:42:55 +0300 Subject: [PATCH 127/130] test_utils: add config modifier for UDP proxy (#35725) Additional Description: Adding ``addConfigModifier`` testing utility for UdpProxy configurations. This will be used in a separate PR, but wanted to separate this to another so it's easier to review Risk Level: low Testing: none Docs Changes: none Release Notes: none Platform Specific Features: none Signed-off-by: ohadvano <49730675+ohadvano@users.noreply.github.com> --- test/config/BUILD | 1 + test/config/utility.cc | 34 ++++++++++++++++++++++++++++++++++ test/config/utility.h | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/test/config/BUILD b/test/config/BUILD index e45e7a26cd56..c338643e0709 100644 --- a/test/config/BUILD +++ b/test/config/BUILD @@ -37,6 +37,7 @@ envoy_cc_test_library( "@envoy_api//envoy/extensions/access_loggers/file/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/http/upstream_codec/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/filters/udp/udp_proxy/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/transport_sockets/quic/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/upstreams/http/v3:pkg_cc_proto", diff --git a/test/config/utility.cc b/test/config/utility.cc index 69891e86f60b..668d4f290882 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -1575,6 +1575,21 @@ envoy::config::listener::v3::Filter* ConfigHelper::getFilterFromListener(const s return nullptr; } +envoy::config::listener::v3::ListenerFilter* +ConfigHelper::getListenerFilterFromListener(const std::string& name) { + RELEASE_ASSERT(!finalized_, ""); + if (bootstrap_.mutable_static_resources()->listeners_size() == 0) { + return nullptr; + } + auto* listener = bootstrap_.mutable_static_resources()->mutable_listeners(0); + for (ssize_t i = 0; i < listener->listener_filters_size(); i++) { + if (listener->mutable_listener_filters(i)->name() == name) { + return listener->mutable_listener_filters(i); + } + } + return nullptr; +} + void ConfigHelper::addNetworkFilter(const std::string& filter_yaml) { RELEASE_ASSERT(!finalized_, ""); auto* filter_chain = @@ -1639,6 +1654,14 @@ void ConfigHelper::storeHttpConnectionManager( "http", hcm); } +bool ConfigHelper::loadUdpProxyFilter(UdpProxyConfig& udp_proxy) { + return loadListenerFilter("udp_proxy", udp_proxy); +} + +void ConfigHelper::storeUdpProxyFilter(const UdpProxyConfig& udp_proxy) { + return storeListenerFilter("udp_proxy", udp_proxy); +} + void ConfigHelper::addConfigModifier(ConfigModifierFunction function) { RELEASE_ASSERT(!finalized_, ""); config_modifiers_.push_back(std::move(function)); @@ -1656,6 +1679,17 @@ void ConfigHelper::addConfigModifier(HttpModifierFunction function) { }); } +void ConfigHelper::addConfigModifier(UdpProxyModifierFunction function) { + addConfigModifier([function, this](envoy::config::bootstrap::v3::Bootstrap&) -> void { + UdpProxyConfig udp_proxy_config; + if (!loadUdpProxyFilter(udp_proxy_config)) { + return; + } + function(udp_proxy_config); + storeUdpProxyFilter(udp_proxy_config); + }); +} + void ConfigHelper::setLds(absl::string_view version_info) { applyConfigModifiers(); diff --git a/test/config/utility.h b/test/config/utility.h index eb34d534bbec..9410d2e84ec7 100644 --- a/test/config/utility.h +++ b/test/config/utility.h @@ -14,6 +14,7 @@ #include "envoy/config/listener/v3/listener_components.pb.h" #include "envoy/config/route/v3/route_components.pb.h" #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h" +#include "envoy/extensions/filters/udp/udp_proxy/v3/udp_proxy.pb.h" #include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" #include "envoy/extensions/transport_sockets/tls/v3/common.pb.h" #include "envoy/extensions/upstreams/http/v3/http_protocol_options.pb.h" @@ -34,6 +35,8 @@ class ConfigHelper { public: using HttpConnectionManager = envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager; + using UdpProxyConfig = envoy::extensions::filters::udp::udp_proxy::v3::UdpProxyConfig; + struct ServerSslOptions { ServerSslOptions& setAllowExpiredCertificate(bool allow) { allow_expired_certificate_ = allow; @@ -185,6 +188,7 @@ class ConfigHelper { const ServerSslOptions& options); using ConfigModifierFunction = std::function; using HttpModifierFunction = std::function; + using UdpProxyModifierFunction = std::function; // A basic configuration (admin port, cluster_0, no listeners) with no network filters. static std::string baseConfigNoListeners(); @@ -377,6 +381,10 @@ class ConfigHelper { // Modifiers will be applied just before ports are modified in finalize void addConfigModifier(HttpModifierFunction function); + // Allows callers to easily modify the UDP Proxy configuration. + // Modifiers will be applied just before ports are modified in finalize + void addConfigModifier(UdpProxyModifierFunction function); + // Allows callers to easily modify the filter named 'name' from the first filter chain from the // first listener. Modifiers will be applied just before ports are modified in finalize template @@ -462,6 +470,11 @@ class ConfigHelper { // Take the contents of the provided HCM proto and stuff them into the first HCM // struct of the first listener. void storeHttpConnectionManager(const HttpConnectionManager& hcm); + // Load the first UDP Proxy from the first listener into a parsed proto. + bool loadUdpProxyFilter(UdpProxyConfig& udp_proxy); + // Take the contents of the provided UDP Proxy and stuff them into the first HCM + // struct of the first listener. + void storeUdpProxyFilter(const UdpProxyConfig& udp_proxy); private: // Load the first FilterType struct from the first listener into a parsed proto. @@ -484,9 +497,35 @@ class ConfigHelper { filter_config_any->PackFrom(filter); } + // Load the first FilterType struct from the first listener filters into a parsed proto. + template bool loadListenerFilter(const std::string& name, FilterType& filter) { + RELEASE_ASSERT(!finalized_, ""); + auto* filter_config = getListenerFilterFromListener(name); + if (filter_config) { + auto* config = filter_config->mutable_typed_config(); + filter = MessageUtil::anyConvert(*config); + return true; + } + return false; + } + + // Take the contents of the provided FilterType proto and stuff them into the first FilterType + // struct of the first listener. + template + void storeListenerFilter(const std::string& name, const FilterType& filter) { + RELEASE_ASSERT(!finalized_, ""); + auto* filter_config_any = getListenerFilterFromListener(name)->mutable_typed_config(); + + filter_config_any->PackFrom(filter); + } + // Finds the filter named 'name' from the first filter chain from the first listener. envoy::config::listener::v3::Filter* getFilterFromListener(const std::string& name); + // Finds the filter named 'name' from the first listener filter from the first listener. + envoy::config::listener::v3::ListenerFilter* + getListenerFilterFromListener(const std::string& name); + // The bootstrap proto Envoy will start up with. envoy::config::bootstrap::v3::Bootstrap bootstrap_; From 8a5d7b0241b419854a4dd1c16c382a9784cdca4a Mon Sep 17 00:00:00 2001 From: Huabing Zhao Date: Wed, 21 Aug 2024 06:48:45 +0800 Subject: [PATCH 128/130] OAuth2: add domain attribute to token sessions (#35491) Signed-off-by: Huabing Zhao --- .../filters/http/oauth2/v3/oauth.proto | 5 + changelogs/current.yaml | 4 + .../extensions/filters/http/oauth2/filter.cc | 10 +- .../extensions/filters/http/oauth2/filter.h | 2 + .../filters/http/oauth2/config_test.cc | 2 + .../filters/http/oauth2/filter_test.cc | 105 +++++++++++++++++- 6 files changed, 125 insertions(+), 3 deletions(-) diff --git a/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto b/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto index 3a1d42731d1b..a1622b3631d1 100644 --- a/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto +++ b/api/envoy/extensions/filters/http/oauth2/v3/oauth.proto @@ -24,6 +24,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#extension: envoy.filters.http.oauth2] // +// [#next-free-field: 6] message OAuth2Credentials { // [#next-free-field: 6] message CookieNames { @@ -70,6 +71,10 @@ message OAuth2Credentials { // The cookie names used in OAuth filters flow. CookieNames cookie_names = 4; + + // The domain to set the cookie on. If not set, the cookie will default to the host of the request, not including the subdomains. + // This is useful when token cookies need to be shared across multiple subdomains. + string cookie_domain = 5; } // OAuth config diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 0ceb2e4e091b..46141707e0fa 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -187,6 +187,10 @@ new_features: ` to allow overriding TLS certificate selection behavior. An extension can select certificate base on the incoming SNI, in both sync and async mode. +- area: oauth + change: | + Added :ref:`cookie_domain ` + field to OAuth2 filter to allow setting the domain of cookies. - area: access log change: | Added support for :ref:`%DOWNSTREAM_PEER_CHAIN_FINGERPRINTS_1% `, diff --git a/source/extensions/filters/http/oauth2/filter.cc b/source/extensions/filters/http/oauth2/filter.cc index fa854156d456..77e2496f2234 100644 --- a/source/extensions/filters/http/oauth2/filter.cc +++ b/source/extensions/filters/http/oauth2/filter.cc @@ -41,6 +41,7 @@ Http::RegisterCustomInlineHeadercookieDomain().empty()) { + cookie_tail_http_only = absl::StrCat( + fmt::format(CookieDomainFormatString, config_->cookieDomain()), cookie_tail_http_only); + } + const CookieNames& cookie_names = config_->cookieNames(); headers.addReferenceKey( diff --git a/source/extensions/filters/http/oauth2/filter.h b/source/extensions/filters/http/oauth2/filter.h index 4075869ed5ff..7ba18e318b00 100644 --- a/source/extensions/filters/http/oauth2/filter.h +++ b/source/extensions/filters/http/oauth2/filter.h @@ -141,6 +141,7 @@ class FilterConfig { FilterStats& stats() { return stats_; } const std::string& encodedResourceQueryParams() const { return encoded_resource_query_params_; } const CookieNames& cookieNames() const { return cookie_names_; } + const std::string& cookieDomain() const { return cookie_domain_; } const AuthType& authType() const { return auth_type_; } bool useRefreshToken() const { return use_refresh_token_; } std::chrono::seconds defaultExpiresIn() const { return default_expires_in_; } @@ -168,6 +169,7 @@ class FilterConfig { const std::vector pass_through_header_matchers_; const std::vector deny_redirect_header_matchers_; const CookieNames cookie_names_; + const std::string cookie_domain_; const AuthType auth_type_; const std::chrono::seconds default_expires_in_; const std::chrono::seconds default_refresh_token_expires_in_; diff --git a/test/extensions/filters/http/oauth2/config_test.cc b/test/extensions/filters/http/oauth2/config_test.cc index e16fb0ca9b9d..8810337f1a07 100644 --- a/test/extensions/filters/http/oauth2/config_test.cc +++ b/test/extensions/filters/http/oauth2/config_test.cc @@ -95,6 +95,7 @@ TEST(ConfigTest, CreateFilter) { oauth_expires: OauthExpires id_token: IdToken refresh_token: RefreshToken + cookie_domain: example.com authorization_endpoint: https://oauth.com/oauth/authorize/ redirect_uri: "%REQ(x-forwarded-proto)%://%REQ(:authority)%/callback" redirect_path_matcher: @@ -222,6 +223,7 @@ TEST(ConfigTest, WrongCombinationOfPreserveAuthorizationAndForwardBearer) { oauth_expires: OauthExpires id_token: IdToken refresh_token: RefreshToken + cookie_domain: example.com authorization_endpoint: https://oauth.com/oauth/authorize/ redirect_uri: "%REQ(x-forwarded-proto)%://%REQ(:authority)%/callback" redirect_path_matcher: diff --git a/test/extensions/filters/http/oauth2/filter_test.cc b/test/extensions/filters/http/oauth2/filter_test.cc index 41039b9322a7..a32ae62de283 100644 --- a/test/extensions/filters/http/oauth2/filter_test.cc +++ b/test/extensions/filters/http/oauth2/filter_test.cc @@ -115,7 +115,7 @@ class OAuth2Test : public testing::TestWithParam { ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType:: OAuth2Config_AuthType_URL_ENCODED_BODY, int default_refresh_token_expires_in = 0, bool preserve_authorization_header = false, - bool disable_id_token_set_cookie = false) { + bool disable_id_token_set_cookie = false, bool set_cookie_domain = false) { envoy::extensions::filters::http::oauth2::v3::OAuth2Config p; auto* endpoint = p.mutable_token_endpoint(); endpoint->set_cluster("auth.example.com"); @@ -156,6 +156,9 @@ class OAuth2Test : public testing::TestWithParam { credentials->mutable_hmac_secret()->set_name("hmac"); // Skipping setting credentials.cookie_names field should give default cookie names: // BearerToken, OauthHMAC, and OauthExpires. + if (set_cookie_domain) { + credentials->set_cookie_domain("example.com"); + } MessageUtil::validate(p, ProtobufMessage::getStrictValidationVisitor()); @@ -1312,6 +1315,106 @@ TEST_F(OAuth2Test, OAuthTestFullFlowPostWithParametersFillRefreshAndIdToken) { filter_->finishGetAccessTokenFlow(); } +/** + * Testing oauth state with cookie domain. + * + * Expected behavior: Cookie domain should be set to the domain in the config. + */ +TEST_F(OAuth2Test, OAuthTestFullFlowPostWithCookieDomain) { + { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues({ + {"envoy.reloadable_features.hmac_base64_encoding_only", "true"}, + }); + init(getConfig(true, false, + ::envoy::extensions::filters::http::oauth2::v3::OAuth2Config_AuthType:: + OAuth2Config_AuthType_URL_ENCODED_BODY, + 0, false, false, true /* set_cookie_domain */)); + // First construct the initial request to the oauth filter with URI parameters. + Http::TestRequestHeaderMapImpl first_request_headers{ + {Http::Headers::get().Path.get(), "/test?name=admin&level=trace"}, + {Http::Headers::get().Host.get(), "traffic.example.com"}, + {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Post}, + {Http::Headers::get().Scheme.get(), "https"}, + }; + + // This is the immediate response - a redirect to the auth cluster. + Http::TestResponseHeaderMapImpl first_response_headers{ + {Http::Headers::get().Status.get(), "302"}, + {Http::Headers::get().Location.get(), + "https://auth.example.com/oauth/" + "authorize/?client_id=" + + TEST_CLIENT_ID + + "&redirect_uri=https%3A%2F%2Ftraffic.example.com%2F_oauth" + "&response_type=code" + "&scope=" + + TEST_ENCODED_AUTH_SCOPES + + "&state=https%3A%2F%2Ftraffic.example.com%2Ftest%3Fname%3Dadmin%26level%3Dtrace" + "&resource=oauth2-resource&resource=http%3A%2F%2Fexample.com" + "&resource=https%3A%2F%2Fexample.com%2Fsome%2Fpath%252F..%252F%2Futf8%C3%83%3Bfoo%" + "3Dbar%" + "3Fvar1%3D1%26var2%3D2"}, + }; + + // Fail the validation to trigger the OAuth flow. + EXPECT_CALL(*validator_, setParams(_, _)); + EXPECT_CALL(*validator_, isValid()).WillOnce(Return(false)); + + // Check that the redirect includes URL encoded query parameter characters. + EXPECT_CALL(decoder_callbacks_, + encodeHeaders_(HeaderMapEqualRef(&first_response_headers), true)); + + // This represents the beginning of the OAuth filter. + EXPECT_EQ(Http::FilterHeadersStatus::StopIteration, + filter_->decodeHeaders(first_request_headers, false)); + + // This represents the callback request from the authorization server. + Http::TestRequestHeaderMapImpl second_request_headers{ + {Http::Headers::get().Path.get(), + "/_oauth?code=123&state=https%3A%2F%2Ftraffic.example.com%" + "2Ftest%3Fname%3Dadmin%26level%3Dtrace"}, + {Http::Headers::get().Host.get(), "traffic.example.com"}, + {Http::Headers::get().Method.get(), Http::Headers::get().MethodValues.Get}, + {Http::Headers::get().Scheme.get(), "https"}, + }; + // Deliberately fail the HMAC validation check. + EXPECT_CALL(*validator_, setParams(_, _)); + EXPECT_CALL(*validator_, isValid()).WillOnce(Return(false)); + + EXPECT_CALL(*oauth_client_, + asyncGetAccessToken("123", TEST_CLIENT_ID, "asdf_client_secret_fdsa", + "https://traffic.example.com" + TEST_CALLBACK, + AuthType::UrlEncodedBody)); + + // Invoke the callback logic. As a side effect, state_ will be populated. + EXPECT_EQ(Http::FilterHeadersStatus::StopAllIterationAndBuffer, + filter_->decodeHeaders(second_request_headers, false)); + + EXPECT_EQ(1, config_->stats().oauth_unauthorized_rq_.value()); + EXPECT_EQ(config_->clusterName(), "auth.example.com"); + + // Expected response after the callback & validation is complete - verifying we kept the + // state and method of the original request, including the query string parameters. + Http::TestRequestHeaderMapImpl second_response_headers{ + {Http::Headers::get().Status.get(), "302"}, + {Http::Headers::get().SetCookie.get(), + "OauthHMAC=fV62OgLipChTQQC3UFgDp+l5sCiSb3zt7nCoJiVivWw=;" + "domain=example.com;path=/;Max-Age=;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "OauthExpires=;domain=example.com;path=/;Max-Age=;secure;HttpOnly"}, + {Http::Headers::get().SetCookie.get(), + "BearerToken=;domain=example.com;path=/;Max-Age=;secure;HttpOnly"}, + {Http::Headers::get().Location.get(), + "https://traffic.example.com/test?name=admin&level=trace"}, + }; + + EXPECT_CALL(decoder_callbacks_, + encodeHeaders_(HeaderMapEqualRef(&second_response_headers), true)); + + filter_->finishGetAccessTokenFlow(); + } +} + class DisabledIdTokenTests : public OAuth2Test { public: DisabledIdTokenTests() : OAuth2Test(false) { From c8f58b61c36dbbd37d92f9826a90288136897df9 Mon Sep 17 00:00:00 2001 From: botengyao Date: Tue, 20 Aug 2024 21:41:41 -0400 Subject: [PATCH 129/130] RLQS: fix statusor typo to avoid potential template argument deduction Commit Message: Additional Description: Risk Level: low Testing: Docs Changes: Release Notes: --------- Signed-off-by: Boteng Yao --- source/extensions/filters/http/rate_limit_quota/filter.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/extensions/filters/http/rate_limit_quota/filter.cc b/source/extensions/filters/http/rate_limit_quota/filter.cc index 60eede663322..7cbb29fcdb4f 100644 --- a/source/extensions/filters/http/rate_limit_quota/filter.cc +++ b/source/extensions/filters/http/rate_limit_quota/filter.cc @@ -30,7 +30,8 @@ Http::FilterHeadersStatus RateLimitQuotaFilter::decodeHeaders(Http::RequestHeade // succeeds. const RateLimitOnMatchAction& match_action = match_result.value()->getTyped(); - absl::StatusOr ret = match_action.generateBucketId(*data_ptr_, factory_context_, visitor_); + absl::StatusOr ret = + match_action.generateBucketId(*data_ptr_, factory_context_, visitor_); if (!ret.ok()) { // When it failed to generate the bucket id for this specific request, the request is ALLOWED by // default (i.e., fail-open). From e37ffcf469495562139abe8db9320df531a69c56 Mon Sep 17 00:00:00 2001 From: Raven Black Date: Wed, 21 Aug 2024 09:19:56 -0400 Subject: [PATCH 130/130] http3: Unhide http3_protocol_options in HttpConnectionManager (#35763) Signed-off-by: Raven Black --- .../v3/http_connection_manager.proto | 6 ++---- changelogs/current.yaml | 3 +++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto b/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto index 9b9e61edb839..712512ddb0b6 100644 --- a/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto +++ b/api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto @@ -58,9 +58,8 @@ message HttpConnectionManager { // Prior knowledge is allowed). HTTP2 = 2; - // [#not-implemented-hide:] QUIC implementation is not production ready yet. Use this enum with - // caution to prevent accidental execution of QUIC code. I.e. `!= HTTP2` is no longer sufficient - // to distinguish HTTP1 and HTTP2 traffic. + // The connection manager will assume that the client is speaking HTTP/3. + // This needs to be consistent with listener and transport socket config. HTTP3 = 3; } @@ -474,7 +473,6 @@ message HttpConnectionManager { [(udpa.annotations.security).configure_for_untrusted_downstream = true]; // Additional HTTP/3 settings that are passed directly to the HTTP/3 codec. - // [#not-implemented-hide:] config.core.v3.Http3ProtocolOptions http3_protocol_options = 44; // An optional override that the connection manager will write to the server diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 46141707e0fa..43b611011d06 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -218,6 +218,9 @@ new_features: change: | Added :ref:`delay_deny ` to support deny connection after the configured duration. +- area: http3 + change: | + ``http3_protocol_options`` in ``HttpConnectionManager`` has been upgraded to general access. - area: cluster change: | Customizing the happy eyeballs algorithm for an upstream cluster by configuring

    ?y#PhKmAEtSunBwRr_OqWmv+HTSeJ*$kgM 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 c^5luh2Nf+*n*aa+ diff --git a/examples/wasm-cc/verify.sh b/examples/wasm-cc/verify.sh deleted file mode 100755 index 179982db5ed5..000000000000 --- a/examples/wasm-cc/verify.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash -e - -export NAME=wasm-cc -export UID - - -# shellcheck source=examples/verify-common.sh -. "$(dirname "${BASH_SOURCE[0]}")/../verify-common.sh" - -run_log "Test connection" -responds_with \ - "Hello, world" \ - http://localhost:8000 - -run_log "Test content-type header" -responds_with_header \ - "content-type: text/plain" \ - http://localhost:8000 - -run_log "Test custom Wasm header" -responds_with_header \ - "x-wasm-custom: FOO" \ - http://localhost:8000 - -run_log "Bring down the proxy" -"${DOCKER_COMPOSE[@]}" stop proxy - -run_log "Compile updated Wasm filter" -"${DOCKER_COMPOSE[@]}" -f docker-compose-wasm.yaml up --quiet-pull --remove-orphans wasm_compile_update - -run_log "Check for the compiled update" -ls -l lib/*updated*wasm - -run_log "Edit the Docker recipe to use the updated binary" -sed -i'.bak' s/\\.\\/lib\\/envoy_filter_http_wasm_example.wasm/.\\/lib\\/envoy_filter_http_wasm_updated_example.wasm/ Dockerfile-proxy - -run_log "Bring the proxy back up" -"${DOCKER_COMPOSE[@]}" up --build -d proxy -wait_for 10 bash -c "\ - responds_with \ - 'Hello, Wasm world' \ - http://localhost:8000" - -run_log "Test updated connection" -responds_with \ - "Hello, Wasm world" \ - http://localhost:8000 - -run_log "Test updated content-type header" -responds_with_header \ - "content-type: text/html" \ - http://localhost:8000 - -run_log "Test updated Wasm header" -responds_with_header \ - "x-wasm-custom: BAR" \ - http://localhost:8000 - -# Restore original Dockerfile -mv Dockerfile-proxy.bak Dockerfile-proxy diff --git a/examples/websocket/README.md b/examples/websocket/README.md deleted file mode 100644 index 8473888649c3..000000000000 --- a/examples/websocket/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/websocket.html). diff --git a/examples/websocket/docker-compose.yaml b/examples/websocket/docker-compose.yaml deleted file mode 100644 index 198b486c03ff..000000000000 --- a/examples/websocket/docker-compose.yaml +++ /dev/null @@ -1,63 +0,0 @@ -services: - - client-ws: - build: - context: . - dockerfile: ../shared/websocket/Dockerfile - target: websocket-client - network_mode: host - restart: "no" - deploy: - replicas: 0 - - proxy-ws: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: ./envoy-ws.yaml - ports: - - "${PORT_PROXY0:-10000}:10000" - - proxy-ws-route: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: ./envoy-ws-route.yaml - ports: - - "${PORT_PROXY1:-15000}:10000" - - proxy-wss-wss: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: ./envoy-wss.yaml - ports: - - "${PORT_PROXY2:-20000}:10000" - - proxy-wss-passthrough: - build: - context: . - dockerfile: ../shared/envoy/Dockerfile - args: - ENVOY_CONFIG: ./envoy-wss-passthrough.yaml - ports: - - "${PORT_PROXY3:-30000}:10000" - - service-ws: - build: - context: . - dockerfile: ../shared/websocket/Dockerfile - hostname: service-ws - command: -E ws-listen:0.0.0.0:80 literalreply:'[ws] HELO' - - service-wss: - build: - context: . - dockerfile: ../shared/websocket/Dockerfile - hostname: service-wss - command: wss-listen:0.0.0.0:443 literalreply:"[wss] HELO" --pkcs12-der /certs/output.pkcs12 - volumes: - - ./certs/output.pkcs12:/certs/output.pkcs12 diff --git a/examples/websocket/envoy-ws-route.yaml b/examples/websocket/envoy-ws-route.yaml deleted file mode 100644 index 04af21da098a..000000000000 --- a/examples/websocket/envoy-ws-route.yaml +++ /dev/null @@ -1,49 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_ws_to_ws - route_config: - name: local_route - virtual_hosts: - - name: app-ws - domains: - - "*" - routes: - - match: - prefix: "/ws" - route: - cluster: service_ws - upgrade_configs: - - upgrade_type: websocket - - match: - prefix: "/" - direct_response: - status: 200 - body: - inline_string: "NotWebSocket\n" - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - clusters: - - name: service_ws - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service_ws - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-ws - port_value: 80 diff --git a/examples/websocket/envoy-ws.yaml b/examples/websocket/envoy-ws.yaml deleted file mode 100644 index 7538081847b3..000000000000 --- a/examples/websocket/envoy-ws.yaml +++ /dev/null @@ -1,43 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_ws_to_ws - upgrade_configs: - - upgrade_type: websocket - route_config: - name: local_route - virtual_hosts: - - name: app - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service_ws - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - clusters: - - name: service_ws - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service_ws - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-ws - port_value: 80 diff --git a/examples/websocket/envoy-wss-passthrough.yaml b/examples/websocket/envoy-wss-passthrough.yaml deleted file mode 100644 index 6634d483def6..000000000000 --- a/examples/websocket/envoy-wss-passthrough.yaml +++ /dev/null @@ -1,27 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.tcp_proxy - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - cluster: service_wss_passthrough - stat_prefix: wss_passthrough - - clusters: - - name: service_wss_passthrough - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service_wss_passthrough - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-wss - port_value: 443 diff --git a/examples/websocket/envoy-wss.yaml b/examples/websocket/envoy-wss.yaml deleted file mode 100644 index 039025ed0986..000000000000 --- a/examples/websocket/envoy-wss.yaml +++ /dev/null @@ -1,111 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 10000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - stat_prefix: ingress_wss_to_wss - upgrade_configs: - - upgrade_type: websocket - route_config: - name: local_route - virtual_hosts: - - name: app - domains: - - "*" - routes: - - match: - prefix: "/" - route: - cluster: service_wss - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext - common_tls_context: - tls_certificates: - # The following self-signed certificate pair is generated using: - # $ openssl req -x509 -newkey rsa:2048 -keyout a/front-proxy-key.pem -out a/front-proxy-crt.pem \ - # -days 3650 -nodes -subj '/CN=front-envoy' - # - # Instead of feeding it as an inline_string, certificate pair can also be fed to Envoy - # via filename. Reference: https://envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#config-core-v3-datasource. - # - # Or in a dynamic configuration scenario, certificate pair can be fetched remotely via - # Secret Discovery Service (SDS). Reference: https://envoyproxy.io/docs/envoy/latest/configuration/security/secret. - - certificate_chain: - inline_string: | - -----BEGIN CERTIFICATE----- - MIICqDCCAZACCQCquzpHNpqBcDANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtm - cm9udC1lbnZveTAeFw0yMDA3MDgwMTMxNDZaFw0zMDA3MDYwMTMxNDZaMBYxFDAS - BgNVBAMMC2Zyb250LWVudm95MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC - AQEAthnYkqVQBX+Wg7aQWyCCb87hBce1hAFhbRM8Y9dQTqxoMXZiA2n8G089hUou - oQpEdJgitXVS6YMFPFUUWfwcqxYAynLK4X5im26Yfa1eO8La8sZUS+4Bjao1gF5/ - VJxSEo2yZ7fFBo8M4E44ZehIIocipCRS+YZehFs6dmHoq/MGvh2eAHIa+O9xssPt - ofFcQMR8rwBHVbKy484O10tNCouX4yUkyQXqCRy6HRu7kSjOjNKSGtjfG+h5M8bh - 10W7ZrsJ1hWhzBulSaMZaUY3vh5ngpws1JATQVSK1Jm/dmMRciwlTK7KfzgxHlSX - 58ENpS7yPTISkEICcLbXkkKGEQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCmj6Hg - vwOxWz0xu+6fSfRL6PGJUGq6wghCfUvjfwZ7zppDUqU47fk+yqPIOzuGZMdAqi7N - v1DXkeO4A3hnMD22Rlqt25vfogAaZVToBeQxCPd/ALBLFrvLUFYuSlS3zXSBpQqQ - Ny2IKFYsMllz5RSROONHBjaJOn5OwqenJ91MPmTAG7ujXKN6INSBM0PjX9Jy4Xb9 - zT+I85jRDQHnTFce1WICBDCYidTIvJtdSSokGSuy4/xyxAAc/BpZAfOjBQ4G1QRe - 9XwOi790LyNUYFJVyeOvNJwveloWuPLHb9idmY5YABwikUY6QNcXwyHTbRCkPB2I - m+/R4XnmL4cKQ+5Z - -----END CERTIFICATE----- - private_key: - inline_string: | - -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2GdiSpVAFf5aD - tpBbIIJvzuEFx7WEAWFtEzxj11BOrGgxdmIDafwbTz2FSi6hCkR0mCK1dVLpgwU8 - VRRZ/ByrFgDKcsrhfmKbbph9rV47wtryxlRL7gGNqjWAXn9UnFISjbJnt8UGjwzg - Tjhl6EgihyKkJFL5hl6EWzp2Yeir8wa+HZ4Achr473Gyw+2h8VxAxHyvAEdVsrLj - zg7XS00Ki5fjJSTJBeoJHLodG7uRKM6M0pIa2N8b6HkzxuHXRbtmuwnWFaHMG6VJ - oxlpRje+HmeCnCzUkBNBVIrUmb92YxFyLCVMrsp/ODEeVJfnwQ2lLvI9MhKQQgJw - tteSQoYRAgMBAAECggEAeDGdEkYNCGQLe8pvg8Z0ccoSGpeTxpqGrNEKhjfi6NrB - NwyVav10iq4FxEmPd3nobzDPkAftfvWc6hKaCT7vyTkPspCMOsQJ39/ixOk+jqFx - lNa1YxyoZ9IV2DIHR1iaj2Z5gB367PZUoGTgstrbafbaNY9IOSyojCIO935ubbcx - DWwL24XAf51ez6sXnI8V5tXmrFlNXhbhJdH8iIxNyM45HrnlUlOk0lCK4gmLJjy9 - 10IS2H2Wh3M5zsTpihH1JvM56oAH1ahrhMXs/rVFXXkg50yD1KV+HQiEbglYKUxO - eMYtfaY9i2CuLwhDnWp3oxP3HfgQQhD09OEN3e0IlQKBgQDZ/3poG9TiMZSjfKqL - xnCABMXGVQsfFWNC8THoW6RRx5Rqi8q08yJrmhCu32YKvccsOljDQJQQJdQO1g09 - e/adJmCnTrqxNtjPkX9txV23Lp6Ak7emjiQ5ICu7iWxrcO3zf7hmKtj7z+av8sjO - mDI7NkX5vnlE74nztBEjp3eC0wKBgQDV2GeJV028RW3b/QyP3Gwmax2+cKLR9PKR - nJnmO5bxAT0nQ3xuJEAqMIss/Rfb/macWc2N/6CWJCRT6a2vgy6xBW+bqG6RdQMB - xEZXFZl+sSKhXPkc5Wjb4lQ14YWyRPrTjMlwez3k4UolIJhJmwl+D7OkMRrOUERO - EtUvc7odCwKBgBi+nhdZKWXveM7B5N3uzXBKmmRz3MpPdC/yDtcwJ8u8msUpTv4R - JxQNrd0bsIqBli0YBmFLYEMg+BwjAee7vXeDFq+HCTv6XMva2RsNryCO4yD3I359 - XfE6DJzB8ZOUgv4Dvluie3TB2Y6ZQV/p+LGt7G13yG4hvofyJYvlg3RPAoGAcjDg - +OH5zLN2eqah8qBN0CYa9/rFt0AJ19+7/smLTJ7QvQq4g0gwS1couplcCEnNGWiK - 72y1n/ckvvplmPeAE19HveMvR9UoCeV5ej86fACy8V/oVpnaaLBvL2aCMjPLjPP9 - DWeCIZp8MV86cvOrGfngf6kJG2qZTueXl4NAuwkCgYEArKkhlZVXjwBoVvtHYmN2 - o+F6cGMlRJTLhNc391WApsgDZfTZSdeJsBsvvzS/Nc0burrufJg0wYioTlpReSy4 - ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU - q5sGxGrC1RECGB5Zwx2S2ZY= - -----END PRIVATE KEY----- - - clusters: - - name: service_wss - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service_wss - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service-wss - port_value: 443 - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext diff --git a/examples/websocket/example.rst b/examples/websocket/example.rst deleted file mode 100644 index c0226584f55c..000000000000 --- a/examples/websocket/example.rst +++ /dev/null @@ -1,196 +0,0 @@ -.. _install_sandboxes_websocket: - -WebSockets -========== - -.. sidebar:: Requirements - - .. include:: _include/docker-env-setup-link.rst - - :ref:`openssl ` - Generate ``SSL`` keys and certificates. - -This example walks through some of the ways that Envoy can be configured to proxy WebSockets. - -It demonstrates terminating a WebSocket connection with and without ``TLS``, and provides some basic examples -of proxying to encrypted and non-encrypted upstream sockets. - -.. warning:: - - For the sake of simplicity, the examples provided here do not authenticate any client certificates, - or validate any of the provided certificates. - - When using ``TLS``, you are strongly encouraged to :ref:`validate ` - all certificates wherever possible. - - You should also :ref:`authenticate clients ` - where you control both sides of the connection, or relevant protocols are available. - -Step 1: Create a certificate file for wss -***************************************** - -Change directory to ``examples/websocket`` in the Envoy repository. - -.. code-block:: console - - $ pwd - envoy/examples/websocket - $ mkdir -p certs - $ openssl req -batch -new -x509 -nodes -keyout certs/key.pem -out certs/cert.pem - Generating a RSA private key - ..................................................................................................................+++++ - ......+++++ - writing new private key to 'certs/key.pem' - ----- - $ openssl pkcs12 -export -passout pass: -out certs/output.pkcs12 -inkey certs/key.pem -in certs/cert.pem - -Step 2: Build and start the sandbox -*********************************** - -This starts four proxies listening on ``localhost`` ports ``10000``, ``15000``, ``20000``, ``30000``. - -It also starts two upstream services, one ``ws`` and one ``wss``. - -The upstream services listen on the internal Docker network on ports ``80`` and ``443`` respectively. - -The socket servers are very trivial implementations, that simply output ``[ws] HELO`` and -``[wss] HELO`` in response to any input. - -.. code-block:: console - - $ docker compose pull - $ docker compose up --build -d - $ docker compose ps - - Name Command State Ports - --------------------------------------------------------------------------------------------------- - websocket_proxy-ws_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:10000->10000/tcp - websocket_proxy-ws-route_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:15000->10000/tcp - websocket_proxy-wss_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:20000->10000/tcp - websocket_proxy-wss-passthrough_1 /docker-entrypoint.sh /usr ... Up 0.0.0.0:30000->10000/tcp - websocket_service-ws_1 websocat -E ws-listen:0.0. ... Up - websocket_service-wss_1 websocat wss-listen:0.0.0. ... Up - -Step 3: Test proxying ``ws`` -> ``ws`` -************************************** - -The proxy listening on port ``10000`` terminates the WebSocket connection without ``TLS`` and then proxies -to an upstream socket, also without ``TLS``. - -In order for Envoy to terminate the WebSocket connection, the -:ref:`upgrade_configs ` -in :ref:`HttpConnectionManager ` -must be set, as can be seen in the provided :download:`ws -> ws configuration <_include/websocket/envoy-ws.yaml>`: - -.. literalinclude:: _include/websocket/envoy-ws.yaml - :language: yaml - :lines: 1-29 - :linenos: - :emphasize-lines: 13-14 - -You can start an interactive session with the socket as follows: - -.. code-block:: console - - $ docker run -ti --network=host solsson/websocat ws://localhost:10000 - HELO - [ws] HELO - GOODBYE - [ws] HELO - -Type ``Ctrl-c`` to exit the socket session. - -Step 4: Test proxying ``ws`` -> ``ws`` on specific route -******************************************************** - -The proxy listening on port ``15000`` terminates the WebSocket connection without ``TLS`` on specific route ``/ws`` and then proxies -to an upstream socket, also without ``TLS``. - -In order for Envoy to terminate the WebSocket connection, the -:ref:`upgrade_configs ` -in :ref:`RouteAction ` -must be set, as can be seen in the provided :download:`ws -> ws configuration <_include/websocket/envoy-ws-route.yaml>`: - -.. literalinclude:: _include/websocket/envoy-ws-route.yaml - :language: yaml - :lines: 19-25 - :linenos: - :emphasize-lines: 6-7 - :caption: :download:`envoy-ws-route.yaml <_include/websocket/envoy-ws-route.yaml>` - -You can start an interactive session with the socket as follows: - -.. code-block:: console - - $ docker run -ti --network=host solsson/websocat ws://localhost:15000/ws - HELO - [ws] HELO - GOODBYE - [ws] HELO - - $ curl http://localhost:15000 - NotWebSocket - - $ curl http://localhost:15000/ws - Only WebSocket connections are welcome here - -Type ``Ctrl-c`` to exit the socket session. - -Step 5: Test proxying ``wss`` -> ``wss`` -**************************************** - -The proxy listening on port ``20000`` terminates the WebSocket connection with ``TLS`` and then proxies -to an upstream ``TLS`` WebSocket. - -In addition to the -:ref:`upgrade_configs ` -in :ref:`HttpConnectionManager `, -the :download:`wss -> wss configuration <_include/websocket/envoy-wss.yaml>` adds a ``TLS`` -:ref:`transport_socket ` to both the -:ref:`listener ` and the -:ref:`cluster `. - -You can start an interactive session with the socket as follows: - -.. code-block:: console - - $ docker run -ti --network=host solsson/websocat --insecure wss://localhost:20000 - HELO - [wss] HELO - GOODBYE - [wss] HELO - -Type ``Ctrl-c`` to exit the socket session. - -Step 6: Test proxying ``wss`` passthrough -***************************************** - -The proxy listening on port ``30000`` passes through all ``TCP`` traffic to an upstream ``TLS`` WebSocket. - -The :download:`wss passthrough configuration <_include/websocket/envoy-wss-passthrough.yaml>` requires no ``TLS`` -or ``HTTP`` setup, and instead uses a simple -:ref:`tcp_proxy `. - -You can start an interactive session with the socket as follows: - -.. code-block:: console - - $ docker run -ti --network=host solsson/websocat --insecure wss://localhost:30000 - HELO - [wss] HELO - GOODBYE - [wss] HELO - -Type ``Ctrl-c`` to exit the socket session. - -.. seealso:: - - :ref:`Securing Envoy quick start guide ` - Outline of key concepts for securing Envoy. - - :ref:`Double proxy sandbox ` - An example of securing traffic between proxies with validation and - mutual authentication using ``mTLS`` with non-``HTTP`` traffic. - - :ref:`TLS sandbox ` - Examples of various ``TLS`` termination patterns with Envoy. diff --git a/examples/websocket/interact.sh b/examples/websocket/interact.sh deleted file mode 100644 index 6f507d5427b7..000000000000 --- a/examples/websocket/interact.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -e - -interact_ws () { - local port="$1" \ - protocol="$2" \ - backend="$3" \ - insecure="" - if [ "$protocol" == "wss" ]; then - insecure="--insecure" - fi - expect < ws" -"${DOCKER_COMPOSE[@]}" run client-ws "${PORT_PROXY0}" ws ws - -run_log "Interact with web socket ws -> ws for specific route" -"${DOCKER_COMPOSE[@]}" run client-ws "${PORT_PROXY1}/ws" ws ws - -run_log "Interact with web socket wss -> wss" -"${DOCKER_COMPOSE[@]}" run client-ws "${PORT_PROXY2}" wss wss - -run_log "Interact with web socket wss passthrough" -"${DOCKER_COMPOSE[@]}" run client-ws "${PORT_PROXY3}" wss wss diff --git a/examples/win32-front-proxy/Dockerfile-frontenvoy b/examples/win32-front-proxy/Dockerfile-frontenvoy deleted file mode 100644 index 3a3494f54c0d..000000000000 --- a/examples/win32-front-proxy/Dockerfile-frontenvoy +++ /dev/null @@ -1,6 +0,0 @@ -FROM envoyproxy/envoy-windows-dev:latest - -COPY ./front-envoy.yaml './front-envoy.yaml' -COPY ./start_envoy.ps1 ./start_envoy.ps1 -ENTRYPOINT ["powershell.exe", "./start_envoy.ps1"] - diff --git a/examples/win32-front-proxy/Dockerfile-service b/examples/win32-front-proxy/Dockerfile-service deleted file mode 100644 index 925bf95e6c96..000000000000 --- a/examples/win32-front-proxy/Dockerfile-service +++ /dev/null @@ -1,12 +0,0 @@ -FROM envoyproxy/envoy-windows-dev:latest - -COPY ./setup_python.ps1 / - -RUN powershell.exe .\\setup_python.ps1 -RUN pip3 install -q Flask==2.0.3 requests==2.18.4 - -RUN powershell mkdir code -ADD ./service.py ./code -ADD ./service-envoy.yaml ./service-envoy.yaml -ADD ./start_service.ps1 ./start_service.ps1 -ENTRYPOINT ["powershell.exe", "./start_service.ps1"] diff --git a/examples/win32-front-proxy/README.md b/examples/win32-front-proxy/README.md deleted file mode 100644 index cca1fdaf55f9..000000000000 --- a/examples/win32-front-proxy/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/win32_front_proxy.html) diff --git a/examples/win32-front-proxy/docker-compose.yaml b/examples/win32-front-proxy/docker-compose.yaml deleted file mode 100644 index edac3f7a95ad..000000000000 --- a/examples/win32-front-proxy/docker-compose.yaml +++ /dev/null @@ -1,24 +0,0 @@ -services: - - front-envoy: - build: - context: . - dockerfile: Dockerfile-frontenvoy - ports: - - "8080:8080" - - "8443:8443" - - "8003:8003" - - service1: - build: - context: . - dockerfile: Dockerfile-service - environment: - - SERVICE_NAME=1 - - service2: - build: - context: . - dockerfile: Dockerfile-service - environment: - - SERVICE_NAME=2 diff --git a/examples/win32-front-proxy/front-envoy.yaml b/examples/win32-front-proxy/front-envoy.yaml deleted file mode 100644 index de22d10fd204..000000000000 --- a/examples/win32-front-proxy/front-envoy.yaml +++ /dev/null @@ -1,170 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 8080 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/service/1" - route: - cluster: service1 - - match: - prefix: "/service/2" - route: - cluster: service2 - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - - address: - socket_address: - address: 0.0.0.0 - port_value: 8443 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: backend - domains: - - "*" - routes: - - match: - prefix: "/service/1" - route: - cluster: service1 - - match: - prefix: "/service/2" - route: - cluster: service2 - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - - transport_socket: - name: envoy.transport_sockets.tls - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext - common_tls_context: - tls_certificates: - # The following self-signed certificate pair is generated using: - # $ openssl req -x509 -newkey rsa:2048 -keyout a/front-proxy-key.pem -out a/front-proxy-crt.pem \ - # -days 3650 -nodes -subj '/CN=front-envoy' - # - # Instead of feeding it as an inline_string, certificate pair can also be fed to Envoy - # via filename. Reference: https://envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#config-core-v3-datasource. - # - # Or in a dynamic configuration scenario, certificate pair can be fetched remotely via - # Secret Discovery Service (SDS). Reference: https://envoyproxy.io/docs/envoy/latest/configuration/security/secret. - - certificate_chain: - inline_string: | - -----BEGIN CERTIFICATE----- - MIICqDCCAZACCQCquzpHNpqBcDANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtm - cm9udC1lbnZveTAeFw0yMDA3MDgwMTMxNDZaFw0zMDA3MDYwMTMxNDZaMBYxFDAS - BgNVBAMMC2Zyb250LWVudm95MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC - AQEAthnYkqVQBX+Wg7aQWyCCb87hBce1hAFhbRM8Y9dQTqxoMXZiA2n8G089hUou - oQpEdJgitXVS6YMFPFUUWfwcqxYAynLK4X5im26Yfa1eO8La8sZUS+4Bjao1gF5/ - VJxSEo2yZ7fFBo8M4E44ZehIIocipCRS+YZehFs6dmHoq/MGvh2eAHIa+O9xssPt - ofFcQMR8rwBHVbKy484O10tNCouX4yUkyQXqCRy6HRu7kSjOjNKSGtjfG+h5M8bh - 10W7ZrsJ1hWhzBulSaMZaUY3vh5ngpws1JATQVSK1Jm/dmMRciwlTK7KfzgxHlSX - 58ENpS7yPTISkEICcLbXkkKGEQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCmj6Hg - vwOxWz0xu+6fSfRL6PGJUGq6wghCfUvjfwZ7zppDUqU47fk+yqPIOzuGZMdAqi7N - v1DXkeO4A3hnMD22Rlqt25vfogAaZVToBeQxCPd/ALBLFrvLUFYuSlS3zXSBpQqQ - Ny2IKFYsMllz5RSROONHBjaJOn5OwqenJ91MPmTAG7ujXKN6INSBM0PjX9Jy4Xb9 - zT+I85jRDQHnTFce1WICBDCYidTIvJtdSSokGSuy4/xyxAAc/BpZAfOjBQ4G1QRe - 9XwOi790LyNUYFJVyeOvNJwveloWuPLHb9idmY5YABwikUY6QNcXwyHTbRCkPB2I - m+/R4XnmL4cKQ+5Z - -----END CERTIFICATE----- - private_key: - inline_string: | - -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2GdiSpVAFf5aD - tpBbIIJvzuEFx7WEAWFtEzxj11BOrGgxdmIDafwbTz2FSi6hCkR0mCK1dVLpgwU8 - VRRZ/ByrFgDKcsrhfmKbbph9rV47wtryxlRL7gGNqjWAXn9UnFISjbJnt8UGjwzg - Tjhl6EgihyKkJFL5hl6EWzp2Yeir8wa+HZ4Achr473Gyw+2h8VxAxHyvAEdVsrLj - zg7XS00Ki5fjJSTJBeoJHLodG7uRKM6M0pIa2N8b6HkzxuHXRbtmuwnWFaHMG6VJ - oxlpRje+HmeCnCzUkBNBVIrUmb92YxFyLCVMrsp/ODEeVJfnwQ2lLvI9MhKQQgJw - tteSQoYRAgMBAAECggEAeDGdEkYNCGQLe8pvg8Z0ccoSGpeTxpqGrNEKhjfi6NrB - NwyVav10iq4FxEmPd3nobzDPkAftfvWc6hKaCT7vyTkPspCMOsQJ39/ixOk+jqFx - lNa1YxyoZ9IV2DIHR1iaj2Z5gB367PZUoGTgstrbafbaNY9IOSyojCIO935ubbcx - DWwL24XAf51ez6sXnI8V5tXmrFlNXhbhJdH8iIxNyM45HrnlUlOk0lCK4gmLJjy9 - 10IS2H2Wh3M5zsTpihH1JvM56oAH1ahrhMXs/rVFXXkg50yD1KV+HQiEbglYKUxO - eMYtfaY9i2CuLwhDnWp3oxP3HfgQQhD09OEN3e0IlQKBgQDZ/3poG9TiMZSjfKqL - xnCABMXGVQsfFWNC8THoW6RRx5Rqi8q08yJrmhCu32YKvccsOljDQJQQJdQO1g09 - e/adJmCnTrqxNtjPkX9txV23Lp6Ak7emjiQ5ICu7iWxrcO3zf7hmKtj7z+av8sjO - mDI7NkX5vnlE74nztBEjp3eC0wKBgQDV2GeJV028RW3b/QyP3Gwmax2+cKLR9PKR - nJnmO5bxAT0nQ3xuJEAqMIss/Rfb/macWc2N/6CWJCRT6a2vgy6xBW+bqG6RdQMB - xEZXFZl+sSKhXPkc5Wjb4lQ14YWyRPrTjMlwez3k4UolIJhJmwl+D7OkMRrOUERO - EtUvc7odCwKBgBi+nhdZKWXveM7B5N3uzXBKmmRz3MpPdC/yDtcwJ8u8msUpTv4R - JxQNrd0bsIqBli0YBmFLYEMg+BwjAee7vXeDFq+HCTv6XMva2RsNryCO4yD3I359 - XfE6DJzB8ZOUgv4Dvluie3TB2Y6ZQV/p+LGt7G13yG4hvofyJYvlg3RPAoGAcjDg - +OH5zLN2eqah8qBN0CYa9/rFt0AJ19+7/smLTJ7QvQq4g0gwS1couplcCEnNGWiK - 72y1n/ckvvplmPeAE19HveMvR9UoCeV5ej86fACy8V/oVpnaaLBvL2aCMjPLjPP9 - DWeCIZp8MV86cvOrGfngf6kJG2qZTueXl4NAuwkCgYEArKkhlZVXjwBoVvtHYmN2 - o+F6cGMlRJTLhNc391WApsgDZfTZSdeJsBsvvzS/Nc0burrufJg0wYioTlpReSy4 - ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU - q5sGxGrC1RECGB5Zwx2S2ZY= - -----END PRIVATE KEY----- - - clusters: - - name: service1 - connect_timeout: 0.25s - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service1 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service1 - port_value: 8000 - - name: service2 - connect_timeout: 0.25s - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: service2 - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: service2 - port_value: 8000 -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 8001 -layered_runtime: - layers: - - name: static_layer_0 - static_layer: - envoy: - resource_limits: - listener: - example_listener_name: - connection_limit: 10000 diff --git a/examples/win32-front-proxy/service-envoy.yaml b/examples/win32-front-proxy/service-envoy.yaml deleted file mode 100644 index f944baa6ff87..000000000000 --- a/examples/win32-front-proxy/service-envoy.yaml +++ /dev/null @@ -1,47 +0,0 @@ -static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 8000 - filter_chains: - - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager - codec_type: AUTO - stat_prefix: ingress_http - route_config: - name: local_route - virtual_hosts: - - name: service - domains: - - "*" - routes: - - match: - prefix: "/service" - route: - cluster: local_service - http_filters: - - name: envoy.filters.http.router - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - clusters: - - name: local_service - connect_timeout: 0.25s - type: STRICT_DNS - lb_policy: ROUND_ROBIN - load_assignment: - cluster_name: local_service - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: 127.0.0.1 - port_value: 8080 -admin: - address: - socket_address: - address: 0.0.0.0 - port_value: 8081 diff --git a/examples/win32-front-proxy/service.py b/examples/win32-front-proxy/service.py deleted file mode 100644 index d08b0a897c9e..000000000000 --- a/examples/win32-front-proxy/service.py +++ /dev/null @@ -1,31 +0,0 @@ -from flask import Flask -import os -import requests -import socket -import sys - -app = Flask(__name__) - - -@app.route('/service/') -def hello(service_number): - return ( - 'Hello from behind Envoy (service {})! hostname: {} resolved' - 'hostname: {}\n'.format( - os.environ['SERVICE_NAME'], socket.gethostname(), - socket.gethostbyname(socket.gethostname()))) - - -@app.route('/trace/') -def trace(service_number): - if int(os.environ['SERVICE_NAME']) == 1: - requests.get("http://localhost:9000/trace/2") - return ( - 'Hello from behind Envoy (service {})! hostname: {} resolved' - 'hostname: {}\n'.format( - os.environ['SERVICE_NAME'], socket.gethostname(), - socket.gethostbyname(socket.gethostname()))) - - -if __name__ == "__main__": - app.run(host='127.0.0.1', port=8080, debug=True) diff --git a/examples/win32-front-proxy/setup_python.ps1 b/examples/win32-front-proxy/setup_python.ps1 deleted file mode 100644 index 0905a4b03062..000000000000 --- a/examples/win32-front-proxy/setup_python.ps1 +++ /dev/null @@ -1,54 +0,0 @@ -$ErrorActionPreference = "Stop"; - -function DownloadAndCheck -{ - param([string]$to, [string]$url, [string]$sha256) - - Write-Host "Downloading $url to $to..." - (New-Object System.Net.WebClient).DownloadFile($url, $to) - $actual = (Get-FileHash -Path $to -Algorithm SHA256).Hash - if ($actual -ne $sha256) { - Write-Host "Download of $url to $to is invalid, expected sha256: $sha256, but got: $actual"; - exit 1 - } - Write-Host "done." -} - -function AddToPath -{ - param([string] $directory) - - $oldPath = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path - $newPath = "$oldPath;$directory" - Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value $newPath - # Add to local path so subsequent commands have access to the executables they need - $env:PATH += ";$directory" - Write-Host "Added $directory to PATH" -} - -function RunAndCheckError -{ - param([string] $exe, [string[]] $argList, [Parameter(Mandatory=$false)] $isInstaller = $false) - - Write-Host "Running '$exe $argList'..." - if ($isInstaller) { - Write-Host "(running as Windows software installer)" - Start-Process $exe -ArgumentList "$argList" -Wait -NoNewWindow - } else { - &$exe $argList - if ($LASTEXITCODE -ne 0) { - Write-Host "$exe $argList exited with code $LASTEXITCODE" - exit $LASTEXITCODE - } - } - Write-Host "done." -} - -# Python3 (do not install via msys2 or the MS store's flavors, this version follows Win32 semantics) -DownloadAndCheck $env:TEMP\python3-installer.exe ` - https://www.python.org/ftp/python/3.8.5/python-3.8.5-amd64.exe ` - cd427c7b17337d7c13761ca20877d2d8be661bd30415ddc17072a31a65a91b64 -# python installer needs to be run as an installer with Start-Process -RunAndCheckError "$env:TEMP\python3-installer.exe" @("/quiet", "InstallAllUsers=1", "Include_launcher=0", "InstallLauncherAllUsers=0") $true -AddToPath $env:ProgramFiles\Python38 -AddToPath $env:ProgramFiles\Python38\Scripts diff --git a/examples/win32-front-proxy/start_envoy.ps1 b/examples/win32-front-proxy/start_envoy.ps1 deleted file mode 100644 index efe1e2592e24..000000000000 --- a/examples/win32-front-proxy/start_envoy.ps1 +++ /dev/null @@ -1 +0,0 @@ -& 'C:\\Program Files\\envoy\\envoy.exe' --config-path .\front-envoy.yaml --service-cluster front-proxy diff --git a/examples/win32-front-proxy/start_service.ps1 b/examples/win32-front-proxy/start_service.ps1 deleted file mode 100644 index 0aabb3b0b3de..000000000000 --- a/examples/win32-front-proxy/start_service.ps1 +++ /dev/null @@ -1,4 +0,0 @@ -Start-Process -FilePath "python" -ArgumentList @("./code/service.py") - -$serviceName = "service$env:ServiceId" -& 'C:\\Program Files\\envoy\\envoy.exe' --config-path .\service-envoy.yaml --service-cluster $serviceName diff --git a/examples/zipkin/Dockerfile-zipkin b/examples/zipkin/Dockerfile-zipkin deleted file mode 100644 index f78c42d87ec3..000000000000 --- a/examples/zipkin/Dockerfile-zipkin +++ /dev/null @@ -1 +0,0 @@ -FROM openzipkin/zipkin:latest@sha256:da8177371c4a7aed7fd673196593c2ff104ce908f300e1971493587622f7a297 diff --git a/examples/zipkin/README.md b/examples/zipkin/README.md deleted file mode 100644 index 173c2fbd8b0d..000000000000 --- a/examples/zipkin/README.md +++ /dev/null @@ -1,2 +0,0 @@ -To learn about this sandbox and for instructions on how to run it please head over -to the [envoy docs](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/zipkin) diff --git a/examples/zipkin/_static/zipkin-ui-dependency.png b/examples/zipkin/_static/zipkin-ui-dependency.png deleted file mode 100644 index 3f0c682008207e2c932d92f68f9377631d54ba9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 122831 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 z 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 >3$$dPjf9^~C`W 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 etp7V}xhf>pqwV{`-dG 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

    20HNNFcLr)R-eQA^l?L-3J8rFXxPriRBP;M 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