This is an automated email from the ASF dual-hosted git repository.
membphis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new fb9123d feat(fault-injection): support conditional fault injection
using nginx variables (#3363)
fb9123d is described below
commit fb9123d4298372820e4969ac74751435fd5b4ed8
Author: Yuelin Zheng <[email protected]>
AuthorDate: Mon Jan 25 23:44:00 2021 +0800
feat(fault-injection): support conditional fault injection using nginx
variables (#3363)
close #2511
---
apisix/plugins/fault-injection.lua | 64 ++++++-
doc/plugins/fault-injection.md | 329 +++++++++++++++++++++++++++++++++
doc/zh-cn/plugins/fault-injection.md | 329 +++++++++++++++++++++++++++++++++
t/plugin/fault-injection.t | 345 +++++++++++++++++++++++++++++++++++
4 files changed, 1063 insertions(+), 4 deletions(-)
diff --git a/apisix/plugins/fault-injection.lua
b/apisix/plugins/fault-injection.lua
index 7a5afb1..ac2846d 100644
--- a/apisix/plugins/fault-injection.lua
+++ b/apisix/plugins/fault-injection.lua
@@ -15,9 +15,11 @@
-- limitations under the License.
--
local core = require("apisix.core")
+local expr = require("resty.expr.v1")
local sleep = core.sleep
local random = math.random
+local ipairs = ipairs
local plugin_name = "fault-injection"
@@ -30,7 +32,11 @@ local schema = {
properties = {
http_status = {type = "integer", minimum = 200},
body = {type = "string", minLength = 0},
- percentage = {type = "integer", minimum = 0, maximum = 100}
+ percentage = {type = "integer", minimum = 0, maximum = 100},
+ vars = {
+ type = "array",
+ maxItems = 20
+ }
},
required = {"http_status"},
},
@@ -38,7 +44,11 @@ local schema = {
type = "object",
properties = {
duration = {type = "number", minimum = 0},
- percentage = {type = "integer", minimum = 0, maximum = 100}
+ percentage = {type = "integer", minimum = 0, maximum = 100},
+ vars = {
+ type = "array",
+ maxItems = 20
+ }
},
required = {"duration"},
}
@@ -64,12 +74,46 @@ local function sample_hit(percentage)
end
+local function vars_match(vars, ctx)
+ local match_result
+ for _, var in ipairs(vars) do
+ local expr, _ = expr.new(var)
+ match_result = expr:eval(ctx.var)
+ if match_result then
+ break
+ end
+ end
+
+ return match_result
+end
+
+
function _M.check_schema(conf)
local ok, err = core.schema.check(schema, conf)
if not ok then
return false, err
end
+ if conf.abort and conf.abort.vars then
+ for _, var in ipairs(conf.abort.vars) do
+ local _, err = expr.new(var)
+ if err then
+ core.log.error("failed to create vars expression: ", err)
+ return false, err
+ end
+ end
+ end
+
+ if conf.delay and conf.delay.vars then
+ for _, var in ipairs(conf.delay.vars) do
+ local _, err = expr.new(var)
+ if err then
+ core.log.error("failed to create vars expression: ", err)
+ return false, err
+ end
+ end
+ end
+
return true
end
@@ -77,11 +121,23 @@ end
function _M.rewrite(conf, ctx)
core.log.info("plugin rewrite phase, conf: ", core.json.delay_encode(conf))
- if conf.delay and sample_hit(conf.delay.percentage) then
+ local abort_vars = true
+ if conf.abort and conf.abort.vars then
+ abort_vars = vars_match(conf.abort.vars, ctx)
+ end
+ core.log.info("abort_vars: ", abort_vars)
+
+ local delay_vars = true
+ if conf.delay and conf.delay.vars then
+ delay_vars = vars_match(conf.delay.vars, ctx)
+ end
+ core.log.info("delay_vars: ", delay_vars)
+
+ if conf.delay and sample_hit(conf.delay.percentage) and delay_vars then
sleep(conf.delay.duration)
end
- if conf.abort and sample_hit(conf.abort.percentage) then
+ if conf.abort and sample_hit(conf.abort.percentage) and abort_vars then
return conf.abort.http_status, core.utils.resolve_var(conf.abort.body,
ctx.var)
end
end
diff --git a/doc/plugins/fault-injection.md b/doc/plugins/fault-injection.md
index 3e96c27..c0b2556 100644
--- a/doc/plugins/fault-injection.md
+++ b/doc/plugins/fault-injection.md
@@ -30,11 +30,29 @@ Fault injection plugin, this plugin can be used with other
plugins and will be e
| abort.http_status | integer | required | | [200, ...] |
user-specified http code returned to the client. |
| abort.body | string | optional | | | response
data returned to the client. Nginx variable can be used inside, like `client
addr: $remote_addr\n` |
| abort.percentage | integer | optional | | [0, 100] |
percentage of requests to be aborted. |
+| abort.vars | array[] | optional | | | The rules
for executing fault injection will only be executed when the rules are matched.
`vars` is a list of expressions, which is from the
[lua-resty-expr](https://github.com/api7/lua-resty-expr). |
| delay.duration | number | required | | | delay
time (can be decimal). |
| delay.percentage | integer | optional | | [0, 100] |
percentage of requests to be delayed. |
+| delay.vars | array[] | optional | | | Execute
the request delay rule, and the request will be delayed only after the rule
matches. `vars` is a list of expressions, which is from the
[lua-resty-expr](https://github.com/api7/lua-resty-expr). |
Note: One of `abort` and `delay` must be specified.
+The `vars` is a list of expression which is from the `lua-resty-expr`, which
can flexibly implement the `and/or` relationship between rules. Example:
+
+```json
+[
+ [
+ [ "arg_name","==","jack" ],
+ [ "arg_age","==",18 ]
+ ],
+ [
+ [ "arg_name2","==","allen" ]
+ ]
+]
+```
+
+This means that the relationship between the first two expressions is `and`,
and the relationship between the first two expressions and the third expression
is `or`.
+
## How To Enable
### Enable the plugin
@@ -119,6 +137,317 @@ user 0m0.007s
sys 0m0.010s
```
+Example 3: Enable the `fault-injection` plugin for a specific route and
specify the vars rule of the abort parameter.
+
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "plugins": {
+ "fault-injection": {
+ "abort": {
+ "http_status": 403,
+ "body": "Fault Injection!\n",
+ "vars": [
+ [
+ [ "arg_name","==","jack" ]
+ ]
+ ]
+ }
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+}'
+```
+
+Test plugin:
+
+1. The vars rule fails to match, and the request returns upstream response
data:
+
+```shell
+$ curl "http://127.0.0.1:9080/hello?name=allen" -i
+HTTP/1.1 200 OK
+Content-Type: application/octet-stream
+Transfer-Encoding: chunked
+Connection: keep-alive
+Date: Wed, 20 Jan 2021 07:21:57 GMT
+Server: APISIX/2.2
+
+hello
+```
+
+2. The vars rule is successfully matched and fault injection is performed:
+
+```shell
+$ curl "http://127.0.0.1:9080/hello?name=jack" -i
+HTTP/1.1 403 Forbidden
+Date: Wed, 20 Jan 2021 07:23:37 GMT
+Content-Type: text/plain; charset=utf-8
+Transfer-Encoding: chunked
+Connection: keep-alive
+Server: APISIX/2.2
+
+Fault Injection!
+```
+
+Example 4: Enable the `fault-injection` plugin for a specific route and
specify the vars rule for the delay parameter.
+
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "plugins": {
+ "fault-injection": {
+ "delay": {
+ "duration": 2,
+ "vars": [
+ [
+ [ "arg_name","==","jack" ]
+ ]
+ ]
+ }
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+}'
+```
+
+Test plugin:
+
+1. The vars rule fails to match and the request is not delayed:
+
+```shell
+$ time curl "http://127.0.0.1:9080/hello?name=allen" -i
+HTTP/1.1 200 OK
+Content-Type: application/octet-stream
+Transfer-Encoding: chunked
+Connection: keep-alive
+Date: Wed, 20 Jan 2021 07:26:17 GMT
+Server: APISIX/2.2
+
+hello
+
+real 0m0.007s
+user 0m0.003s
+sys 0m0.003s
+```
+
+2. The vars rule is successfully matched, and the request is delayed for two
seconds:
+
+```shell
+$ time curl "http://127.0.0.1:9080/hello?name=jack" -i
+HTTP/1.1 200 OK
+Content-Type: application/octet-stream
+Transfer-Encoding: chunked
+Connection: keep-alive
+Date: Wed, 20 Jan 2021 07:57:50 GMT
+Server: APISIX/2.2
+
+hello
+
+real 0m2.009s
+user 0m0.004s
+sys 0m0.004s
+```
+
+Example 5: Enable the `fault-injection` plugin for a specific route, and
specify the vars rules for the abort and delay parameters.
+
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "plugins": {
+ "fault-injection": {
+ "abort": {
+ "http_status": 403,
+ "body": "Fault Injection!\n",
+ "vars": [
+ [
+ [ "arg_name","==","jack" ]
+ ]
+ ]
+ },
+ "delay": {
+ "duration": 2,
+ "vars": [
+ [
+ [ "http_age","==","18" ]
+ ]
+ ]
+ }
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+}'
+```
+
+Test plugin:
+
+1. The vars rules of abort and delay fail to match:
+
+```shell
+$ time curl "http://127.0.0.1:9080/hello?name=allen" -H 'age: 20' -i
+HTTP/1.1 200 OK
+Content-Type: application/octet-stream
+Transfer-Encoding: chunked
+Connection: keep-alive
+Date: Wed, 20 Jan 2021 08:01:43 GMT
+Server: APISIX/2.2
+
+hello
+
+real 0m0.007s
+user 0m0.003s
+sys 0m0.003s
+```
+
+2. The abort vars rule fails to match, no fault injection is performed, but
the request is delayed:
+
+```shell
+$ time curl "http://127.0.0.1:9080/hello?name=allen" -H 'age: 18' -i
+HTTP/1.1 200 OK
+Content-Type: application/octet-stream
+Transfer-Encoding: chunked
+Connection: keep-alive
+Date: Wed, 20 Jan 2021 08:19:03 GMT
+Server: APISIX/2.2
+
+hello
+
+real 0m2.009s
+user 0m0.001s
+sys 0m0.006s
+```
+
+3. The vars rule of delay fails to match, the request is not delayed, but
fault injection is performed:
+
+```shell
+$ time curl "http://127.0.0.1:9080/hello?name=jack" -H 'age: 20' -i
+HTTP/1.1 403 Forbidden
+Date: Wed, 20 Jan 2021 08:20:18 GMT
+Content-Type: text/plain; charset=utf-8
+Transfer-Encoding: chunked
+Connection: keep-alive
+Server: APISIX/2.2
+
+Fault Injection!
+
+real 0m0.007s
+user 0m0.002s
+sys 0m0.004s
+```
+
+4. The vars rules of abort and delay parameters match successfully, perform
fault injection, and delay the request:
+
+```shell
+$ time curl "http://127.0.0.1:9080/hello?name=jack" -H 'age: 18' -i
+HTTP/1.1 403 Forbidden
+Date: Wed, 20 Jan 2021 08:21:17 GMT
+Content-Type: text/plain; charset=utf-8
+Transfer-Encoding: chunked
+Connection: keep-alive
+Server: APISIX/2.2
+
+Fault Injection!
+
+real 0m2.006s
+user 0m0.001s
+sys 0m0.005s
+```
+
+Example 6: Enable the `fault-injection` plugin for a specific route, and
specify the vars rule of the abort parameter (the relationship of `or`).
+
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "plugins": {
+ "fault-injection": {
+ "abort": {
+ "http_status": 403,
+ "body": "Fault Injection!\n",
+ "vars": [
+ [
+ ["arg_name","==","jack"],
+ ["arg_age","!","<",18]
+ ],
+ [
+ ["http_apikey","==","apisix-key"]
+ ]
+ ]
+ }
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+}'
+```
+
+Indicates that when the request parameters name and age satisfy both `name ==
"jack"` and `age >= 18`, fault injection is performed. Or when the request
header apikey satisfies `apikey == "apisix-key"`, fault injection is performed.
+
+Test plugin:
+
+1. The request parameter name and age match successfully, and the request
header `apikey` is missing, and fault injection is performed:
+
+```shell
+$ curl "http://127.0.0.1:9080/hello?name=jack&age=19" -i
+HTTP/1.1 403 Forbidden
+Date: Fri, 22 Jan 2021 11:05:46 GMT
+Content-Type: text/plain; charset=utf-8
+Transfer-Encoding: chunked
+Connection: keep-alive
+Server: APISIX/2.2
+
+Fault Injection!
+```
+
+2. The request header `apikey` is successfully matched, and the request
parameters are missing, and fault injection is performed:
+
+```shell
+$ curl http://127.0.0.1:9080/hello -H "apikey: apisix-key" -i
+HTTP/1.1 403 Forbidden
+Date: Fri, 22 Jan 2021 11:08:34 GMT
+Content-Type: text/plain; charset=utf-8
+Transfer-Encoding: chunked
+Connection: keep-alive
+Server: APISIX/2.2
+
+Fault Injection!
+```
+
+3. Both request parameters and request headers fail to match, and fault
injection is not performed:
+
+```shell
+$ curl http://127.0.0.1:9080/hello -i
+HTTP/1.1 200 OK
+Content-Type: application/octet-stream
+Transfer-Encoding: chunked
+Connection: keep-alive
+Date: Fri, 22 Jan 2021 11:11:17 GMT
+Server: APISIX/2.2
+
+hello
+```
+
## Disable Plugin
Remove the corresponding JSON in the plugin configuration to disable the
plugin immediately without restarting the service:
diff --git a/doc/zh-cn/plugins/fault-injection.md
b/doc/zh-cn/plugins/fault-injection.md
index 45e5936..2931bbd 100644
--- a/doc/zh-cn/plugins/fault-injection.md
+++ b/doc/zh-cn/plugins/fault-injection.md
@@ -30,11 +30,29 @@
| abort.http_status | integer | 必需 | | [200, ...] | 返回给客户端的 http 状态码 |
| abort.body | string | 可选 | | | 返回给客户端的响应数据。支持使用
Nginx 变量,如 `client addr: $remote_addr\n`|
| abort.percentage | integer | 可选 | | [0, 100] | 将被中断的请求占比
|
+| abort.vars | array[] | 可选 | | |
执行故障注入的规则,当规则匹配通过后才会执行故障注。`vars` 是一个表达式的列表,来自
[lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list)。 |
| delay.duration | number | 必需 | | | 延迟时间,可以指定小数 |
| delay.percentage | integer | 可选 | | [0, 100] | 将被延迟的请求占比
|
+| delay.vars | array[] | 可选 | | |
执行请求延迟的规则,当规则匹配通过后才会延迟请求。`vars` 是一个表达式列表,来自
[lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list)。 |
注:参数 abort 和 delay 至少要存在一个。
+`vars` 是由 `lua-resty-expr` 的表达式组成的列表,它可以灵活的实现规则之间的 `and/or` 关系,示例:
+
+```json
+[
+ [
+ [ "arg_name","==","jack" ],
+ [ "arg_age","==",18 ]
+ ],
+ [
+ [ "arg_name2","==","allen" ]
+ ]
+]
+```
+
+这表示前两个表达式之间的关系是 `and` ,而前两个和第三个表达式之间的关系是 `or`。
+
## 示例
### 启用插件
@@ -119,6 +137,317 @@ user 0m0.007s
sys 0m0.010s
```
+示例3:为特定路由启用 `fault-injection` 插件,并指定 abort 参数的 vars 规则。
+
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "plugins": {
+ "fault-injection": {
+ "abort": {
+ "http_status": 403,
+ "body": "Fault Injection!\n",
+ "vars": [
+ [
+ [ "arg_name","==","jack" ]
+ ]
+ ]
+ }
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+}'
+```
+
+测试:
+
+1、vars 规则匹配失败,请求返回上游响应数据:
+
+```shell
+$ curl "http://127.0.0.1:9080/hello?name=allen" -i
+HTTP/1.1 200 OK
+Content-Type: application/octet-stream
+Transfer-Encoding: chunked
+Connection: keep-alive
+Date: Wed, 20 Jan 2021 07:21:57 GMT
+Server: APISIX/2.2
+
+hello
+```
+
+2、vars 规则匹配成功,执行故障注入:
+
+```shell
+$ curl "http://127.0.0.1:9080/hello?name=jack" -i
+HTTP/1.1 403 Forbidden
+Date: Wed, 20 Jan 2021 07:23:37 GMT
+Content-Type: text/plain; charset=utf-8
+Transfer-Encoding: chunked
+Connection: keep-alive
+Server: APISIX/2.2
+
+Fault Injection!
+```
+
+示例4:为特定路由启用 `fault-injection` 插件,并指定 delay 参数的 vars 规则。
+
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "plugins": {
+ "fault-injection": {
+ "delay": {
+ "duration": 2,
+ "vars": [
+ [
+ [ "arg_name","==","jack" ]
+ ]
+ ]
+ }
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+}'
+```
+
+测试:
+
+1、vars 规则匹配失败,不延迟请求:
+
+```shell
+$ time "curl http://127.0.0.1:9080/hello?name=allen" -i
+HTTP/1.1 200 OK
+Content-Type: application/octet-stream
+Transfer-Encoding: chunked
+Connection: keep-alive
+Date: Wed, 20 Jan 2021 07:26:17 GMT
+Server: APISIX/2.2
+
+hello
+
+real 0m0.007s
+user 0m0.003s
+sys 0m0.003s
+```
+
+2、vars 规则匹配成功,延迟请求两秒:
+
+```shell
+$ time curl "http://127.0.0.1:9080/hello?name=jack" -i
+HTTP/1.1 200 OK
+Content-Type: application/octet-stream
+Transfer-Encoding: chunked
+Connection: keep-alive
+Date: Wed, 20 Jan 2021 07:57:50 GMT
+Server: APISIX/2.2
+
+hello
+
+real 0m2.009s
+user 0m0.004s
+sys 0m0.004s
+```
+
+示例5:为特定路由启用 `fault-injection` 插件,并指定 abort 和 delay 参数的 vars 规则。
+
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "plugins": {
+ "fault-injection": {
+ "abort": {
+ "http_status": 403,
+ "body": "Fault Injection!\n",
+ "vars": [
+ [
+ [ "arg_name","==","jack" ]
+ ]
+ ]
+ },
+ "delay": {
+ "duration": 2,
+ "vars": [
+ [
+ [ "http_age","==","18" ]
+ ]
+ ]
+ }
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+}'
+```
+
+测试:
+
+1、abort 和 delay 的 vars 规则匹配失败:
+
+```shell
+$ time curl "http://127.0.0.1:9080/hello?name=allen" -H 'age: 20' -i
+HTTP/1.1 200 OK
+Content-Type: application/octet-stream
+Transfer-Encoding: chunked
+Connection: keep-alive
+Date: Wed, 20 Jan 2021 08:01:43 GMT
+Server: APISIX/2.2
+
+hello
+
+real 0m0.007s
+user 0m0.003s
+sys 0m0.003s
+```
+
+2、abort 的 vars 规则匹配失败,不执行故障注入,但延迟请求:
+
+```shell
+$ time curl "http://127.0.0.1:9080/hello?name=allen" -H 'age: 18' -i
+HTTP/1.1 200 OK
+Content-Type: application/octet-stream
+Transfer-Encoding: chunked
+Connection: keep-alive
+Date: Wed, 20 Jan 2021 08:19:03 GMT
+Server: APISIX/2.2
+
+hello
+
+real 0m2.009s
+user 0m0.001s
+sys 0m0.006s
+```
+
+3、delay 的 vars 规则匹配失败,不延迟请求,但执行故障注入:
+
+```shell
+$ time curl "http://127.0.0.1:9080/hello?name=jack" -H 'age: 20' -i
+HTTP/1.1 403 Forbidden
+Date: Wed, 20 Jan 2021 08:20:18 GMT
+Content-Type: text/plain; charset=utf-8
+Transfer-Encoding: chunked
+Connection: keep-alive
+Server: APISIX/2.2
+
+Fault Injection!
+
+real 0m0.007s
+user 0m0.002s
+sys 0m0.004s
+```
+
+4、abort 和 delay 参数的 vars 规则匹配成功,执行故障注入,并延迟请求:
+
+```shell
+$ time curl "http://127.0.0.1:9080/hello?name=jack" -H 'age: 18' -i
+HTTP/1.1 403 Forbidden
+Date: Wed, 20 Jan 2021 08:21:17 GMT
+Content-Type: text/plain; charset=utf-8
+Transfer-Encoding: chunked
+Connection: keep-alive
+Server: APISIX/2.2
+
+Fault Injection!
+
+real 0m2.006s
+user 0m0.001s
+sys 0m0.005s
+```
+
+示例6:为特定路由启用 `fault-injection` 插件,并指定 abort 参数的 vars 规则(`or` 的关系)。
+
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "plugins": {
+ "fault-injection": {
+ "abort": {
+ "http_status": 403,
+ "body": "Fault Injection!\n",
+ "vars": [
+ [
+ ["arg_name","==","jack"],
+ ["arg_age","!","<",18]
+ ],
+ [
+ ["http_apikey","==","apisix-key"]
+ ]
+ ]
+ }
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+}'
+```
+
+表示当请求参数 name 和 age 同时满足 `name == "jack"`、`age >= 18` 时,执行故障注入。或请求头 apikey 满足
`apikey == "apisix-key"` 时,执行故障注入。
+
+测试:
+
+1、请求参数 name 和 age 匹配成功,缺少请求头 `apikey`, 执行故障注入:
+
+```shell
+$ curl "http://127.0.0.1:9080/hello?name=jack&age=19" -i
+HTTP/1.1 403 Forbidden
+Date: Fri, 22 Jan 2021 11:05:46 GMT
+Content-Type: text/plain; charset=utf-8
+Transfer-Encoding: chunked
+Connection: keep-alive
+Server: APISIX/2.2
+
+Fault Injection!
+```
+
+2、请求头 `apikey` 匹配成功,缺少请求参数,执行故障注入:
+
+```shell
+$ curl http://127.0.0.1:9080/hello -H "apikey: apisix-key" -i
+HTTP/1.1 403 Forbidden
+Date: Fri, 22 Jan 2021 11:08:34 GMT
+Content-Type: text/plain; charset=utf-8
+Transfer-Encoding: chunked
+Connection: keep-alive
+Server: APISIX/2.2
+
+Fault Injection!
+```
+
+3、请求参数与请求头都匹配失败,不执行故障注入:
+
+```shell
+$ curl http://127.0.0.1:9080/hello -i
+HTTP/1.1 200 OK
+Content-Type: application/octet-stream
+Transfer-Encoding: chunked
+Connection: keep-alive
+Date: Fri, 22 Jan 2021 11:11:17 GMT
+Server: APISIX/2.2
+
+hello
+```
+
### 禁用插件
移除插件配置中相应的 JSON 配置可立即禁用该插件,无需重启服务:
diff --git a/t/plugin/fault-injection.t b/t/plugin/fault-injection.t
index 4393811..d0469f5 100644
--- a/t/plugin/fault-injection.t
+++ b/t/plugin/fault-injection.t
@@ -741,3 +741,348 @@ GET /hello
--- response_body
--- no_error_log
[error]
+
+
+
+=== TEST 24: vars schema validation passed
+--- config
+ location /t {
+ content_by_lua_block {
+ local plugin = require("apisix.plugins.fault-injection")
+ local ok, err = plugin.check_schema({
+ abort = {
+ http_status = 403,
+ body = "Fault Injection!\n",
+ vars = {
+ {
+ {"arg_name","==","jack"},
+ {"arg_age","!","<",18}
+ },
+ {
+ {"http_apikey","==","api-key"}
+ }
+ }
+ },
+ delay = {
+ duration = 2,
+ vars = {
+ {
+ {"arg_name","==","jack"},
+ {"arg_age","!","<",18}
+ },
+ {
+ {"http_apikey","==","api-key"}
+ }
+ }
+ }
+ })
+ if not ok then
+ ngx.say(err)
+ end
+
+ ngx.say("done")
+ }
+ }
+--- request
+GET /t
+--- response_body
+done
+--- no_error_log
+[error]
+
+
+
+=== TEST 25: vars schema validation failed(abort failed)
+--- config
+ location /t {
+ content_by_lua_block {
+ local plugin = require("apisix.plugins.fault-injection")
+ local ok, err = plugin.check_schema({
+ abort = {
+ http_status = 403,
+ body = "Fault Injection!\n",
+ vars = {
+ {"arg_name","==","jack"}
+ }
+ },
+ delay = {
+ duration = 2,
+ vars = {
+ {
+ {"arg_name","==","jack"},
+ {"arg_age","!","<",18}
+ },
+ {
+ {"http_apikey","==","api-key"}
+ }
+ }
+ }
+ })
+ if not ok then
+ ngx.say(err)
+ end
+
+ ngx.say("done")
+ }
+ }
+--- request
+GET /t
+--- response_body
+invalid expression
+done
+--- error_log eval
+qr/failed to create vars expression:.*/
+
+
+
+=== TEST 26: set route and configure the vars rule in abort
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [=[{
+ "plugins": {
+ "fault-injection": {
+ "abort": {
+ "http_status": 403,
+ "body": "Fault Injection!\n",
+ "vars": [
+ [
+ ["arg_name","==","jack"],
+ [ "arg_age","!","<",18 ]
+ ],
+ [
+ [ "http_apikey","==","api-key"
]
+ ]
+ ]
+ }
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]=]
+ )
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- error_code: 200
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 27: hit the route (all vars rules pass), execute abort
+--- request
+GET /hello?name=jack&age=18
+--- more_headers
+apikey: api-key
+--- error_code: 403
+--- response_body
+Fault Injection!
+--- no_error_log
+[error]
+
+
+
+=== TEST 28: hit the route (missing apikey), execute abort
+--- request
+GET /hello?name=jack&age=20
+--- error_code: 403
+--- response_body
+Fault Injection!
+--- no_error_log
+[error]
+
+
+
+=== TEST 29: hit the route (missing request parameters), execute abort
+--- request
+GET /hello
+--- more_headers
+apikey:api-key
+--- error_code: 403
+--- response_body
+Fault Injection!
+--- no_error_log
+[error]
+
+
+
+=== TEST 30: hit route(`vars` do not match, `age` is missing)
+--- request
+GET /hello?name=allen
+--- response_body
+hello world
+--- no_error_log
+[error]
+
+
+
+=== TEST 31: hit route(all `vars` do not match)
+--- request
+GET /hello
+--- response_body
+hello world
+--- no_error_log
+[error]
+
+
+
+=== TEST 32: set route and configure the vars rule in delay
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [=[{
+ "uri": "/hello",
+ "plugins": {
+ "fault-injection": {
+ "delay": {
+ "duration": 2,
+ "vars": [
+ [
+ ["arg_name","==","jack"],
+ [ "arg_age","!","<",18 ]
+ ]
+ ]
+ }
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ }
+ }]=]
+ )
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- error_code: 200
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 33: hit route(delay 2 seconds and return hello world)
+--- request
+GET /hello?name=jack&age=22
+--- response_body
+hello world
+--- no_error_log
+[error]
+
+
+
+=== TEST 34: hit route (no wait and return hello1 world)
+--- request
+GET /hello HTTP/1.1
+--- error_code: 200
+--- response_body
+hello world
+--- no_error_log
+[error]
+
+
+
+=== TEST 35: set route and configure the vars rule in abort and delay
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [=[{
+ "plugins": {
+ "fault-injection": {
+ "abort": {
+ "http_status": 403,
+ "body": "Fault Injection!\n",
+ "vars": [
+ [
+ ["arg_name","==","jack"],
+ ["arg_age","!","<",18]
+ ]
+ ]
+ },
+ "delay": {
+ "duration": 2,
+ "vars": [
+ [
+ ["http_apikey","==","api-key"]
+ ]
+ ]
+ }
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]=]
+ )
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- error_code: 200
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 36: hit the route (all vars rules are passed), execute abort and delay
+--- request
+GET /hello?name=jack&age=18
+--- more_headers
+apikey: api-key
+--- error_code: 403
+--- response_body
+Fault Injection!
+--- no_error_log
+[error]
+
+
+
+=== TEST 37: hit the route (abort rule does not match), only execute delay
+--- request
+GET /hello?name=jack&age=16
+--- more_headers
+apikey: api-key
+--- response_body
+hello world
+--- no_error_log
+[error]