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 <2226815...@qq.com>
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]

Reply via email to