Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Route-local fault filter config #3084

Merged
merged 45 commits into from
Apr 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
636e605
Router: Route/VHost/W.Cluster local filter configuration
Apr 6, 2018
b2d0263
format
Apr 10, 2018
134734b
missed async client types
Apr 10, 2018
0cc834f
fix repo locations
Apr 11, 2018
35f37f7
s/RDS/Route/
Apr 11, 2018
4820c48
remove unneccessary template var
Apr 11, 2018
d76ad4d
the nullptr likes to be called just nullptr
Apr 11, 2018
6309d41
checkpoint
Apr 12, 2018
9897138
s/ASSERT/EXPECT/
Apr 12, 2018
118c728
dependency shuffle
Apr 12, 2018
711f59f
format + convert test to use fixtures with dummy filter
Apr 12, 2018
0aa06d6
Merge branch 'route-local-config' of https://github.com/rodaine/envoy…
Apr 13, 2018
472f7ae
Merge branch 'master' into route-local-config
rodaine Apr 13, 2018
aad91ea
compilation fixes post repo reorg
Apr 16, 2018
99039ab
converting Fault Filter to perfilterconfig
Apr 16, 2018
c3d6347
Merge branch 'master' of https://github.com/envoyproxy/envoy into per…
Apr 16, 2018
fe20045
Merge branch 'route-local-config' of https://github.com/rodaine/envoy…
Apr 16, 2018
c2f915b
comment nits
Apr 16, 2018
cbd19d4
Merge branch 'master' into route-local-config
rodaine Apr 16, 2018
c78fca0
Merge branch 'route-local-config' of https://github.com/rodaine/envoy…
Apr 16, 2018
eb56fdd
Merge branch 'master' of https://github.com/envoyproxy/envoy into per…
Apr 16, 2018
298e226
well known names
Apr 16, 2018
73f2f66
remove unnecessary includes
Apr 16, 2018
295f82a
missing includes
Apr 17, 2018
0d5fe62
format
Apr 17, 2018
6d1838f
Merge branch 'master' of https://github.com/envoyproxy/envoy into per…
Apr 17, 2018
c0c6f3a
moar compile fixes
Apr 17, 2018
80221f7
ternary
Apr 18, 2018
10559af
Merge branch 'master' of https://github.com/envoyproxy/envoy into per…
Apr 18, 2018
8870ac3
format
Apr 18, 2018
8124930
Merge branch 'master' of https://github.com/envoyproxy/envoy into per…
Apr 18, 2018
28356e8
nits
Apr 18, 2018
ca301f9
Merge remote-tracking branch 'origin/master' into perfilterconfig
Apr 19, 2018
739e572
rework based on new interface
Apr 20, 2018
70f13d0
format
Apr 20, 2018
ce73907
fixes
Apr 20, 2018
897f78f
cleanup
Apr 20, 2018
c095075
Merge branch 'master' of https://github.com/envoyproxy/envoy into per…
Apr 20, 2018
e58e958
fixes
Apr 20, 2018
46dbf9f
format
Apr 20, 2018
80a3aaa
const faultfilterconfig
Apr 20, 2018
9a15b9f
const everything
Apr 20, 2018
5982b5f
unconst
Apr 20, 2018
1e585b6
alignment fix
Apr 20, 2018
5e893aa
fix conditional assignment
Apr 20, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions source/extensions/filters/http/fault/config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ FaultFilterFactory::createFilterFactoryFromProto(const Protobuf::Message& proto_
stats_prefix, context);
}

Router::RouteSpecificFilterConfigConstSharedPtr
FaultFilterFactory::createRouteSpecificFilterConfig(const ProtobufWkt::Struct& struct_config) {
envoy::config::filter::http::fault::v2::HTTPFault proto_config;
MessageUtil::jsonConvert(struct_config, proto_config);
return std::make_shared<const Router::RouteSpecificFilterConfig>(
Fault::FaultSettings(proto_config));
}

/**
* Static registration for the fault filter. @see RegisterFactory.
*/
Expand Down
3 changes: 3 additions & 0 deletions source/extensions/filters/http/fault/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class FaultFilterFactory : public Server::Configuration::NamedHttpFilterConfigFa
return ProtobufTypes::MessagePtr{new envoy::config::filter::http::fault::v2::HTTPFault()};
}

Router::RouteSpecificFilterConfigConstSharedPtr
createRouteSpecificFilterConfig(const ProtobufWkt::Struct&) override;

std::string name() override { return HttpFilterNames::get().FAULT; }

private:
Expand Down
62 changes: 39 additions & 23 deletions source/extensions/filters/http/fault/fault_filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include "common/protobuf/utility.h"
#include "common/router/config_impl.h"

#include "extensions/filters/http/well_known_names.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
Expand All @@ -30,15 +32,7 @@ const std::string FaultFilter::ABORT_PERCENT_KEY = "fault.http.abort.abort_perce
const std::string FaultFilter::DELAY_DURATION_KEY = "fault.http.delay.fixed_duration_ms";
const std::string FaultFilter::ABORT_HTTP_STATUS_KEY = "fault.http.abort.http_status";

FaultFilterConfig::FaultFilterConfig(const envoy::config::filter::http::fault::v2::HTTPFault& fault,
Runtime::Loader& runtime, const std::string& stats_prefix,
Stats::Scope& scope)
: runtime_(runtime), stats_(generateStats(stats_prefix, scope)), stats_prefix_(stats_prefix),
scope_(scope) {

if (!fault.has_abort() && !fault.has_delay()) {
throw EnvoyException("fault filter must have at least abort or delay specified in the config.");
}
FaultSettings::FaultSettings(const envoy::config::filter::http::fault::v2::HTTPFault& fault) {

if (fault.has_abort()) {
abort_percent_ = fault.abort().percent();
Expand All @@ -62,6 +56,12 @@ FaultFilterConfig::FaultFilterConfig(const envoy::config::filter::http::fault::v
}
}

FaultFilterConfig::FaultFilterConfig(const envoy::config::filter::http::fault::v2::HTTPFault& fault,
Runtime::Loader& runtime, const std::string& stats_prefix,
Stats::Scope& scope)
: settings_(fault), runtime_(runtime), stats_(generateStats(stats_prefix, scope)),
stats_prefix_(stats_prefix), scope_(scope) {}

FaultFilter::FaultFilter(FaultFilterConfigSharedPtr config) : config_(config) {}

FaultFilter::~FaultFilter() { ASSERT(!delay_timer_); }
Expand All @@ -71,6 +71,21 @@ FaultFilter::~FaultFilter() { ASSERT(!delay_timer_); }
// if we inject a delay, then we will inject the abort in the delay timer
// callback.
Http::FilterHeadersStatus FaultFilter::decodeHeaders(Http::HeaderMap& headers, bool) {
// Route-level configuration overrides filter-level configuration
// NOTE: We should not use runtime when reading from route-level
// faults. In other words, runtime is supported only when faults are
// configured at the filter level.
fault_settings_ = config_->settings();
if (callbacks_->route() && callbacks_->route()->routeEntry()) {
const std::string& name = Extensions::HttpFilters::HttpFilterNames::get().FAULT;
const auto* route_entry = callbacks_->route()->routeEntry();

const FaultSettings* per_route_settings_ =
route_entry->perFilterConfigTyped<FaultSettings>(name)
?: route_entry->virtualHost().perFilterConfigTyped<FaultSettings>(name);
fault_settings_ = per_route_settings_ ?: fault_settings_;
}

if (!matchesTargetUpstreamCluster()) {
return Http::FilterHeadersStatus::Continue;
}
Expand All @@ -80,7 +95,7 @@ Http::FilterHeadersStatus FaultFilter::decodeHeaders(Http::HeaderMap& headers, b
}

// Check for header matches
if (!Router::ConfigUtility::matchHeaders(headers, config_->filterHeaders())) {
if (!Router::ConfigUtility::matchHeaders(headers, fault_settings_->filterHeaders())) {
return Http::FilterHeadersStatus::Continue;
}

Expand Down Expand Up @@ -115,24 +130,24 @@ Http::FilterHeadersStatus FaultFilter::decodeHeaders(Http::HeaderMap& headers, b
}

bool FaultFilter::isDelayEnabled() {
bool enabled =
config_->runtime().snapshot().featureEnabled(DELAY_PERCENT_KEY, config_->delayPercent());
bool enabled = config_->runtime().snapshot().featureEnabled(DELAY_PERCENT_KEY,
fault_settings_->delayPercent());

if (!downstream_cluster_delay_percent_key_.empty()) {
enabled |= config_->runtime().snapshot().featureEnabled(downstream_cluster_delay_percent_key_,
config_->delayPercent());
fault_settings_->delayPercent());
}

return enabled;
}

bool FaultFilter::isAbortEnabled() {
bool enabled =
config_->runtime().snapshot().featureEnabled(ABORT_PERCENT_KEY, config_->abortPercent());
bool enabled = config_->runtime().snapshot().featureEnabled(ABORT_PERCENT_KEY,
fault_settings_->abortPercent());

if (!downstream_cluster_abort_percent_key_.empty()) {
enabled |= config_->runtime().snapshot().featureEnabled(downstream_cluster_abort_percent_key_,
config_->abortPercent());
fault_settings_->abortPercent());
}

return enabled;
Expand All @@ -145,8 +160,8 @@ absl::optional<uint64_t> FaultFilter::delayDuration() {
return ret;
}

uint64_t duration =
config_->runtime().snapshot().getInteger(DELAY_DURATION_KEY, config_->delayDuration());
uint64_t duration = config_->runtime().snapshot().getInteger(DELAY_DURATION_KEY,
fault_settings_->delayDuration());
if (!downstream_cluster_delay_duration_key_.empty()) {
duration =
config_->runtime().snapshot().getInteger(downstream_cluster_delay_duration_key_, duration);
Expand All @@ -163,7 +178,7 @@ absl::optional<uint64_t> FaultFilter::delayDuration() {
uint64_t FaultFilter::abortHttpStatus() {
// TODO(mattklein123): check http status codes obtained from runtime.
uint64_t http_status =
config_->runtime().snapshot().getInteger(ABORT_HTTP_STATUS_KEY, config_->abortCode());
config_->runtime().snapshot().getInteger(ABORT_HTTP_STATUS_KEY, fault_settings_->abortCode());

if (!downstream_cluster_abort_http_status_key_.empty()) {
http_status = config_->runtime().snapshot().getInteger(
Expand Down Expand Up @@ -244,17 +259,17 @@ void FaultFilter::abortWithHTTPStatus() {
bool FaultFilter::matchesTargetUpstreamCluster() {
bool matches = true;

if (!config_->upstreamCluster().empty()) {
if (!fault_settings_->upstreamCluster().empty()) {
Router::RouteConstSharedPtr route = callbacks_->route();
matches = route && route->routeEntry() &&
(route->routeEntry()->clusterName() == config_->upstreamCluster());
(route->routeEntry()->clusterName() == fault_settings_->upstreamCluster());
}

return matches;
}

bool FaultFilter::matchesDownstreamNodes(const Http::HeaderMap& headers) {
if (config_->downstreamNodes().empty()) {
if (fault_settings_->downstreamNodes().empty()) {
return true;
}

Expand All @@ -263,7 +278,8 @@ bool FaultFilter::matchesDownstreamNodes(const Http::HeaderMap& headers) {
}

const std::string downstream_node = headers.EnvoyDownstreamServiceNode()->value().c_str();
return config_->downstreamNodes().find(downstream_node) != config_->downstreamNodes().end();
return fault_settings_->downstreamNodes().find(downstream_node) !=
fault_settings_->downstreamNodes().end();
}

void FaultFilter::resetTimerState() {
Expand Down
47 changes: 30 additions & 17 deletions source/extensions/filters/http/fault/fault_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,33 @@ struct FaultFilterStats {
ALL_FAULT_FILTER_STATS(GENERATE_COUNTER_STRUCT)
};

/**
* Configuration for fault injection.
*/
class FaultSettings : public Router::RouteSpecificFilterConfig {
public:
FaultSettings(const envoy::config::filter::http::fault::v2::HTTPFault& fault);

const std::vector<Router::ConfigUtility::HeaderData>& filterHeaders() const {
return fault_filter_headers_;
}
uint64_t abortPercent() const { return abort_percent_; }
uint64_t delayPercent() const { return fixed_delay_percent_; }
uint64_t delayDuration() const { return fixed_duration_ms_; }
uint64_t abortCode() const { return http_status_; }
const std::string& upstreamCluster() const { return upstream_cluster_; }
const std::unordered_set<std::string>& downstreamNodes() const { return downstream_nodes_; }

private:
uint64_t abort_percent_{}; // 0-100
uint64_t http_status_{}; // HTTP or gRPC return codes
uint64_t fixed_delay_percent_{}; // 0-100
uint64_t fixed_duration_ms_{}; // in milliseconds
std::string upstream_cluster_; // restrict faults to specific upstream cluster
std::vector<Router::ConfigUtility::HeaderData> fault_filter_headers_;
std::unordered_set<std::string> downstream_nodes_{}; // Inject failures for specific downstream
};

/**
* Configuration for the fault filter.
*/
Expand All @@ -43,31 +70,16 @@ class FaultFilterConfig {
FaultFilterConfig(const envoy::config::filter::http::fault::v2::HTTPFault& fault,
Runtime::Loader& runtime, const std::string& stats_prefix, Stats::Scope& scope);

const std::vector<Router::ConfigUtility::HeaderData>& filterHeaders() {
return fault_filter_headers_;
}
uint64_t abortPercent() { return abort_percent_; }
uint64_t delayPercent() { return fixed_delay_percent_; }
uint64_t delayDuration() { return fixed_duration_ms_; }
uint64_t abortCode() { return http_status_; }
const std::string& upstreamCluster() { return upstream_cluster_; }
Runtime::Loader& runtime() { return runtime_; }
FaultFilterStats& stats() { return stats_; }
const std::unordered_set<std::string>& downstreamNodes() { return downstream_nodes_; }
const std::string& statsPrefix() { return stats_prefix_; }
Stats::Scope& scope() { return scope_; }
const FaultSettings* settings() { return &settings_; }

private:
static FaultFilterStats generateStats(const std::string& prefix, Stats::Scope& scope);

uint64_t abort_percent_{}; // 0-100
uint64_t http_status_{}; // HTTP or gRPC return codes
uint64_t fixed_delay_percent_{}; // 0-100
uint64_t fixed_duration_ms_{}; // in milliseconds
std::string upstream_cluster_; // restrict faults to specific upstream cluster
std::vector<Router::ConfigUtility::HeaderData> fault_filter_headers_;
std::unordered_set<std::string> downstream_nodes_{}; // Inject failures for specific downstream
// nodes. If not set then inject for all.
const FaultSettings settings_;
Runtime::Loader& runtime_;
FaultFilterStats stats_;
const std::string stats_prefix_;
Expand Down Expand Up @@ -112,6 +124,7 @@ class FaultFilter : public Http::StreamDecoderFilter {
Event::TimerPtr delay_timer_;
std::string downstream_cluster_{};
bool stream_destroyed_{};
const FaultSettings* fault_settings_;

std::string downstream_cluster_delay_percent_key_{};
std::string downstream_cluster_abort_percent_key_{};
Expand Down
15 changes: 5 additions & 10 deletions test/extensions/filters/http/fault/config_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,14 @@ TEST(FaultFilterConfigTest, FaultFilterCorrectProto) {
cb(filter_callback);
}

TEST(FaultFilterConfigTest, InvalidFaultFilterInProto) {
envoy::config::filter::http::fault::v2::HTTPFault config{};
NiceMock<Server::Configuration::MockFactoryContext> context;
FaultFilterFactory factory;
EXPECT_THROW(factory.createFilterFactoryFromProto(config, "stats", context), EnvoyException);
}

TEST(FaultFilterConfigTest, FaultFilterEmptyProto) {
NiceMock<Server::Configuration::MockFactoryContext> context;
FaultFilterFactory factory;
EXPECT_THROW(
factory.createFilterFactoryFromProto(*factory.createEmptyConfigProto(), "stats", context),
EnvoyException);
Server::Configuration::HttpFilterFactoryCb cb =
factory.createFilterFactoryFromProto(*factory.createEmptyConfigProto(), "stats", context);
Http::MockFilterChainFactoryCallbacks filter_callback;
EXPECT_CALL(filter_callback, addStreamDecoderFilter(_));
cb(filter_callback);
}

} // namespace Fault
Expand Down
Loading