Skip to content

Commit

Permalink
JSON Schema: HTTP Filter schemas (#405)
Browse files Browse the repository at this point in the history
  • Loading branch information
ccaraman authored Feb 1, 2017
1 parent 65f89c1 commit 62b3edd
Show file tree
Hide file tree
Showing 28 changed files with 659 additions and 179 deletions.
96 changes: 59 additions & 37 deletions docs/configuration/http_filters/fault_filter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ providing the ability to stage different failure scenarios such as service
failures, service overloads, high network latency, network partitions,
etc. Faults injection can be limited to a specific set of requests based on
the (destination) upstream cluster of a request and/or a set of pre-defined
request headers.
request headers.

The scope of failures is restricted to those that are observable by an
application communicating over the network. CPU and disk failures on the
Expand All @@ -33,57 +33,80 @@ including the router filter.*

.. code-block:: json
{
"type" : "decoder",
"name" : "fault",
"config" : {
"abort" : {
"abort_percent" : "...",
"http_status" : "..."
},
"delay" : {
"type" : "...",
"fixed_delay_percent" : "...",
"fixed_duration_ms" : "..."
},
"upstream_cluster" : "...",
"headers" : []
}
{
"type" : "decoder",
"name" : "fault",
"config" : {
"abort" : "{...}",
"delay" : "{...}",
"upstream_cluster" : "...",
"headers" : []
}
}
abort.abort_percent
:ref:`abort <config_http_filters_fault_injection_abort>`
*(sometimes required, object)* If specified, the filter will abort requests based on
the values in the object. At least *abort* or *delay* must be specified.

:ref:`delay <config_http_filters_fault_injection_delay>`
*(sometimes required, object)* If specified, the filter will inject delays based on the values
in the object. At least *abort* or *delay* must be specified.

upstream_cluster:
*(optional, string)* Specifies the name of the (destination) upstream
cluster that the filter should match on. Fault injection will be
restricted to requests bound to the specific upstream cluster.

:ref:`headers <config_http_filters_fault_injection_headers>`
*(optional, array)* Specifies a set of headers that the filter should match on.

The abort and delay blocks can be omitted. If they are not specified in the
configuration file, their respective values will be obtained from the
runtime.

.. _config_http_filters_fault_injection_abort:

Abort
-----
.. code-block:: json
{
"abort_percent" : "...",
"http_status" : "..."
}
abort_percent
*(required, integer)* The percentage of requests that
should be aborted with the specified *http_status* code. Valid values
range from 0 to 100.

abort.http_status
http_status
*(required, integer)* The HTTP status code that will be used as the
response code for the request being aborted.

delay.type:
.. _config_http_filters_fault_injection_delay:

Delay
-----
.. code-block:: json
{
"type" : "...",
"fixed_delay_percent" : "...",
"fixed_duration_ms" : "..."
}
type:
*(required, string)* Specifies the type of delay being
injected. Currently only *fixed* delay type (step function) is supported.

delay.fixed_delay_percent:
fixed_delay_percent:
*(required, integer)* The percentage of requests that will
be delayed for the duration specified by *fixed_duration_ms*. Valid
values range from 0 to 100.

delay.fixed_duration_ms:
*(required, integer)* The delay duration in
milliseconds. Must be greater than 0.

upstream_cluster:
*(optional, string)* Specifies the name of the (destination) upstream
cluster that the filter should match on. Fault injection will be
restricted to requests bound to the specific upstream cluster.

:ref:`headers <config_http_filters_fault_injection_headers>`
*(optional, array)* Specifies a set of headers that the filter should match on.

The abort and delay blocks can be omitted. If they are not specified in the
configuration file, their respective values will be obtained from the
runtime.
fixed_duration_ms:
*(required, integer)* The delay duration in milliseconds. Must be greater than 0.

Runtime
-------
Expand Down Expand Up @@ -158,4 +181,3 @@ prefix <config_http_conn_man_stat_prefix>` comes from the owning HTTP connection

delays_injected, Counter, Total requests that were delayed
aborts_injected, Counter, Total requests that were aborted

5 changes: 5 additions & 0 deletions include/envoy/json/json_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ class Object {
* valid.
*/
virtual void validateSchema(const std::string& schema) const PURE;

/**
* @return true if the JSON object is empty;
*/
virtual bool empty() const PURE;
};

} // Json
16 changes: 12 additions & 4 deletions source/common/http/filter/fault_filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "common/http/codes.h"
#include "common/http/header_map_impl.h"
#include "common/http/headers.h"
#include "common/json/config_schemas.h"
#include "common/router/config_impl.h"

namespace Http {
Expand All @@ -18,8 +19,16 @@ FaultFilterConfig::FaultFilterConfig(const Json::Object& json_config, Runtime::L
const std::string& stat_prefix, Stats::Store& stats)
: runtime_(runtime), stats_(generateStats(stat_prefix, stats)) {

if (json_config.hasObject("abort")) {
const Json::ObjectPtr& abort = json_config.getObject("abort");
json_config.validateSchema(Json::Schema::FAULT_HTTP_FILTER_SCHEMA);

const Json::ObjectPtr abort = json_config.getObject("abort", true);
const Json::ObjectPtr delay = json_config.getObject("delay", true);

if (abort->empty() && delay->empty()) {
throw EnvoyException("fault filter must have at least abort or delay specified in the config.");
}

if (!abort->empty()) {
abort_percent_ = static_cast<uint64_t>(abort->getInteger("abort_percent", 0));

if (abort_percent_ > 0) {
Expand All @@ -36,8 +45,7 @@ FaultFilterConfig::FaultFilterConfig(const Json::Object& json_config, Runtime::L
}
}

if (json_config.hasObject("delay")) {
const Json::ObjectPtr& delay = json_config.getObject("delay");
if (!delay->empty()) {
const std::string type = delay->getString("type", "empty");
if (type == "fixed") {
fixed_delay_percent_ = static_cast<uint64_t>(delay->getInteger("fixed_delay_percent", 0));
Expand Down
9 changes: 9 additions & 0 deletions source/common/http/filter/ratelimit.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,20 @@
#include "common/common/empty_string.h"
#include "common/common/enum_to_int.h"
#include "common/http/codes.h"
#include "common/json/config_schemas.h"
#include "common/router/config_impl.h"

namespace Http {
namespace RateLimit {

FilterConfig::FilterConfig(const Json::Object& config, const LocalInfo::LocalInfo& local_info,
Stats::Store& global_store, Runtime::Loader& runtime,
Upstream::ClusterManager& cm)
: domain_(config.getString("domain")), stage_(config.getInteger("stage", 0)),
local_info_(local_info), global_store_(global_store), runtime_(runtime), cm_(cm) {
config.validateSchema(Json::Schema::RATE_LIMIT_HTTP_FILTER_SCHEMA);
}

const Http::HeaderMapPtr Filter::TOO_MANY_REQUESTS_HEADER{new Http::HeaderMapImpl{
{Http::Headers::get().Status, std::to_string(enumToInt(Code::TooManyRequests))}}};

Expand Down
4 changes: 1 addition & 3 deletions source/common/http/filter/ratelimit.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ namespace RateLimit {
class FilterConfig {
public:
FilterConfig(const Json::Object& config, const LocalInfo::LocalInfo& local_info,
Stats::Store& global_store, Runtime::Loader& runtime, Upstream::ClusterManager& cm)
: domain_(config.getString("domain")), stage_(config.getInteger("stage", 0)),
local_info_(local_info), global_store_(global_store), runtime_(runtime), cm_(cm) {}
Stats::Store& global_store, Runtime::Loader& runtime, Upstream::ClusterManager& cm);

const std::string& domain() const { return domain_; }
const LocalInfo::LocalInfo& localInfo() const { return local_info_; }
Expand Down
117 changes: 111 additions & 6 deletions source/common/json/config_schemas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -187,12 +187,12 @@ const std::string Json::Schema::RATELIMIT_NETWORK_FILTER_SCHEMA(R"EOF(

const std::string Json::Schema::REDIS_PROXY_NETWORK_FILTER_SCHEMA(R"EOF(
{
"$schema": "http://json-schema.org/schema#",
"properties":{
"cluster_name" : {"type" : "string"}
},
"required": ["cluster_name"],
"additionalProperties": false
"$schema": "http://json-schema.org/schema#",
"properties":{
"cluster_name" : {"type" : "string"}
},
"required": ["cluster_name"],
"additionalProperties": false
}
)EOF");

Expand Down Expand Up @@ -462,3 +462,108 @@ const std::string Json::Schema::HTTP_RATE_LIMITS_CONFIGURATION_SCHEMA(R"EOF(
"additionalProperties" : false
}
)EOF");

const std::string Json::Schema::BUFFER_HTTP_FILTER_SCHEMA(R"EOF(
{
"$schema": "http://json-schema.org/schema#",
"properties" : {
"max_request_bytes" : {"type" : "integer"},
"max_request_time_s" : {"type" : "integer"}
},
"required" : ["max_request_bytes", "max_request_time_s"],
"additionalProperties" : false
}
)EOF");

const std::string Json::Schema::FAULT_HTTP_FILTER_SCHEMA(R"EOF(
{
"$schema": "http://json-schema.org/schema#",
"properties" : {
"abort": {
"type" : "object",
"properties" : {
"abort_percent" : {
"type" : "integer",
"minimum" : 0,
"maximum" : 100
},
"http_status" : {"type" : "integer"}
},
"required" : ["abort_percent", "http_status"],
"additionalProperties" : false
},
"delay" : {
"type" : "object",
"properties" : {
"type" : {
"type" : "string",
"enum" : ["fixed"]
},
"fixed_delay_percent" : {
"type" : "integer",
"minimum" : 0,
"maximum" : 100
},
"fixed_duration_ms" : {
"type" : "integer",
"minimum" : 0,
"exclusiveMinimum" : true
}
},
"required" : ["type", "fixed_delay_percent", "fixed_duration_ms"],
"additionalProperties" : false
},
"upstream_cluster" : {"type" : "string"},
"headers" : {
"type" : "array",
"minItems" : 1,
"items" : {
"type" : "object",
"properties" : {
"name" : {"type" : "string"},
"value" : {"type" : "string"},
"regex" : {"type" : "boolean"}
},
"required" : ["name"],
"additionalProperties" : false
}
}
},
"additionalProperties" : false
}
)EOF");

const std::string Json::Schema::HEALTH_CHECK_HTTP_FILTER_SCHEMA(R"EOF(
{
"$schema": "http://json-schema.org/schema#",
"properties" : {
"pass_through_mode" : {"type" : "boolean"},
"endpoint" : {"type" : "string"},
"cache_time_ms" : {"type" : "integer"}
},
"required" : ["pass_through_mode", "endpoint"],
"additionalProperties" : false
}
)EOF");

const std::string Json::Schema::RATE_LIMIT_HTTP_FILTER_SCHEMA(R"EOF(
{
"$schema": "http://json-schema.org/schema#",
"properties" : {
"domain" : {"type" : "string"},
"stage" : {"type" : "integer"}
},
"required" : ["domain"],
"additionalProperties" : false
}
)EOF");

const std::string Json::Schema::ROUTER_HTTP_FILTER_SCHEMA(R"EOF(
{
"$schema": "http://json-schema.org/schema#",
"properties" : {
"dynamic_stats" : {"type" : "boolean"}
},
"additionalProperties" : false
}
)EOF");
7 changes: 7 additions & 0 deletions source/common/json/config_schemas.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ class Schema {
static const std::string VIRTUAL_HOST_CONFIGURATION_SCHEMA;
static const std::string ROUTE_ENTRY_CONFIGURATION_SCHEMA;
static const std::string HTTP_RATE_LIMITS_CONFIGURATION_SCHEMA;

// HTTP Filter Schemas
static const std::string BUFFER_HTTP_FILTER_SCHEMA;
static const std::string FAULT_HTTP_FILTER_SCHEMA;
static const std::string HEALTH_CHECK_HTTP_FILTER_SCHEMA;
static const std::string RATE_LIMIT_HTTP_FILTER_SCHEMA;
static const std::string ROUTER_HTTP_FILTER_SCHEMA;
};

} // Json
2 changes: 2 additions & 0 deletions source/common/json/json_loader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ class ObjectImplBase : public Object {
}
}

bool empty() const override { return value_.IsObject() && value_.ObjectEmpty(); }

private:
const std::string name_;
const rapidjson::Value& value_;
Expand Down
Loading

0 comments on commit 62b3edd

Please sign in to comment.