You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by me...@apache.org on 2020/01/13 14:22:28 UTC

[incubator-apisix] branch master updated: feature: implemented new `fault-injection` plugin. (#1042)

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/incubator-apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new 9758a0e  feature: implemented new `fault-injection` plugin. (#1042)
9758a0e is described below

commit 9758a0e3344c2242d8fc55e2621383ab707b55f9
Author: agile6v <ag...@agile6v.com>
AuthorDate: Mon Jan 13 22:22:21 2020 +0800

    feature: implemented new `fault-injection` plugin. (#1042)
---
 conf/config.yaml                       |   1 +
 lua/apisix/plugins/fault-injection.lua |  74 +++++
 t/admin/plugins.t                      |   2 +-
 t/debug/debug-mode.t                   |   1 +
 t/plugin/fault-injection.t             | 526 +++++++++++++++++++++++++++++++++
 5 files changed, 603 insertions(+), 1 deletion(-)

diff --git a/conf/config.yaml b/conf/config.yaml
index 844070e..495c709 100644
--- a/conf/config.yaml
+++ b/conf/config.yaml
@@ -93,6 +93,7 @@ plugins:                          # plugin list
   - proxy-rewrite
   - redirect
   - response-rewrite
+  - fault-injection
 
 stream_plugins:
   - mqtt-proxy
diff --git a/lua/apisix/plugins/fault-injection.lua b/lua/apisix/plugins/fault-injection.lua
new file mode 100644
index 0000000..583ca0f
--- /dev/null
+++ b/lua/apisix/plugins/fault-injection.lua
@@ -0,0 +1,74 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You 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.
+--
+local core          = require("apisix.core")
+local sleep         = ngx.sleep
+
+local plugin_name   = "fault-injection"
+
+local schema = {
+    type = "object",
+    properties = {
+        abort = {
+            type = "object",
+            properties = {
+                http_status = {type = "integer", minimum = 200},
+                body = {type = "string", minLength = 0},
+            },
+            required = {"http_status"}
+        },
+        delay = {
+            type = "object",
+            properties = {
+                duration = {type = "number", minimum = 0},
+            },
+            required = {"duration"}
+        }
+    },
+    minProperties = 1,
+}
+
+local _M = {
+    version = 0.1,
+    priority = 11000,
+    name = plugin_name,
+    schema = schema,
+}
+
+function _M.check_schema(conf)
+    local ok, err = core.schema.check(schema, conf)
+    if not ok then
+        return false, err
+    end
+
+    return true
+end
+
+
+function _M.rewrite(conf, ctx)
+    core.log.info("plugin rewrite phase, conf: ", core.json.delay_encode(conf))
+
+    if conf.delay and conf.delay.duration ~= nil then
+        sleep(conf.delay.duration)
+    end
+
+    if conf.abort and conf.abort.http_status ~= nil then
+        core.response.exit(conf.abort.http_status, conf.abort.body)
+    end
+end
+
+
+return _M
diff --git a/t/admin/plugins.t b/t/admin/plugins.t
index eae4a6d..e9fe92a 100644
--- a/t/admin/plugins.t
+++ b/t/admin/plugins.t
@@ -30,6 +30,6 @@ __DATA__
 --- request
 GET /apisix/admin/plugins/list
 --- response_body_like eval
-qr/\["limit-req","limit-count","limit-conn","key-auth","basic-auth","prometheus","node-status","jwt-auth","zipkin","ip-restriction","grpc-transcode","serverless-pre-function","serverless-post-function","openid-connect","proxy-rewrite","redirect","response-rewrite"\]/
+qr/\["limit-req","limit-count","limit-conn","key-auth","basic-auth","prometheus","node-status","jwt-auth","zipkin","ip-restriction","grpc-transcode","serverless-pre-function","serverless-post-function","openid-connect","proxy-rewrite","redirect","response-rewrite","fault-injection"\]/
 --- no_error_log
 [error]
diff --git a/t/debug/debug-mode.t b/t/debug/debug-mode.t
index 3e18a0f..bc2dd4d 100644
--- a/t/debug/debug-mode.t
+++ b/t/debug/debug-mode.t
@@ -55,6 +55,7 @@ done
 --- grep_error_log eval
 qr/loaded plugin and sort by priority: [-\d]+ name: [\w-]+/
 --- grep_error_log_out
+loaded plugin and sort by priority: 11000 name: fault-injection
 loaded plugin and sort by priority: 10000 name: serverless-pre-function
 loaded plugin and sort by priority: 3000 name: ip-restriction
 loaded plugin and sort by priority: 2599 name: openid-connect
diff --git a/t/plugin/fault-injection.t b/t/plugin/fault-injection.t
new file mode 100644
index 0000000..14e3770
--- /dev/null
+++ b/t/plugin/fault-injection.t
@@ -0,0 +1,526 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+BEGIN {
+    if ($ENV{TEST_NGINX_CHECK_LEAK}) {
+        $SkipReason = "unavailable for the hup tests";
+
+    } else {
+        $ENV{TEST_NGINX_USE_HUP} = 1;
+        undef $ENV{TEST_NGINX_USE_STAP};
+    }
+}
+
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_shuffle();
+no_root_location();
+log_level('info');
+run_tests;
+
+__DATA__
+
+=== TEST 1: set route(invalid http_status in the abort property)
+--- 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": 100,
+                                       "body": "Fault Injection!"
+                                   }
+                               },
+                               "proxy-rewrite": {
+                                   "uri": "/hello"
+                               }
+                           },
+                           "uri": "/hello"
+                   }]]
+                   )
+
+               if code >= 300 then
+                   ngx.status = code
+               end
+               ngx.say(body)
+           }
+       }
+--- request
+GET /t
+--- error_code: 400
+--- response_body eval
+qr/validation failed/
+--- no_error_log
+[error]
+
+
+
+=== TEST 2: set route(without http_status in the abort property)
+--- 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": {
+                                   }
+                               },
+                               "proxy-rewrite": {
+                                   "uri": "/hello"
+                               }
+                           },
+                           "uri": "/hello"
+                   }]]
+                   )
+
+               if code >= 300 then
+                   ngx.status = code
+               end
+               ngx.say(body)
+           }
+       }
+--- request
+GET /t
+--- error_code: 400
+--- response_body eval
+qr/validation failed/
+--- no_error_log
+[error]
+
+
+
+=== TEST 3: set route(without abort & delay properties)
+--- 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": {
+                               },
+                               "proxy-rewrite": {
+                                   "uri": "/hello"
+                               }
+                           },
+                           "uri": "/hello"
+                   }]]
+                   )
+
+               if code >= 300 then
+                   ngx.status = code
+               end
+               ngx.say(body)
+           }
+       }
+--- request
+GET /t
+--- error_code: 400
+--- response_body eval
+qr/expect object to have at least 1 properties/
+--- no_error_log
+[error]
+
+
+
+=== TEST 4: set route(without duration in the delay property)
+--- 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": {
+                                   "delay": {
+                                   }
+                               },
+                               "proxy-rewrite": {
+                                   "uri": "/hello"
+                               }
+                           },
+                           "uri": "/hello"
+                   }]]
+                   )
+
+               if code >= 300 then
+                   ngx.status = code
+               end
+               ngx.say(body)
+           }
+       }
+--- request
+GET /t
+--- error_code: 400
+--- response_body eval
+qr/validation failed/
+--- no_error_log
+[error]
+
+
+
+=== TEST 5: set route(invalid duration with string in the delay property)
+--- 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": {
+                                   "delay": {
+                                       "duration": "test"
+                                   }
+                               },
+                               "proxy-rewrite": {
+                                   "uri": "/hello"
+                               }
+                           },
+                           "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: 400
+--- response_body eval
+qr/wrong type: expected number, got string/
+--- no_error_log
+[error]
+
+
+
+=== TEST 6: set route(invalid duration with duoble dot in the delay property)
+--- 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": {
+                                   "delay": {
+                                       "duration": 0.1.1
+                                   }
+                               },
+                               "proxy-rewrite": {
+                                   "uri": "/hello"
+                               }
+                           },
+                           "uri": "/hello"
+                   }]]
+                   )
+
+               if code >= 300 then
+                   ngx.status = code
+               end
+               ngx.say(body)
+           }
+       }
+--- request
+GET /t
+--- error_code: 400
+--- response_body eval
+qr/invalid request body/
+--- error_log eval
+qr/invalid request body/
+
+
+
+=== TEST 7: set route(invalid duration with whitespace in the delay property)
+--- 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": {
+                                   "delay": {
+                                       "duration": 0. 1
+                                   }
+                               },
+                               "proxy-rewrite": {
+                                   "uri": "/hello"
+                               }
+                           },
+                           "uri": "/hello"
+                   }]]
+                   )
+
+               if code >= 300 then
+                   ngx.status = code
+               end
+               ngx.say(body)
+           }
+       }
+--- request
+GET /t
+--- error_code: 400
+--- response_body eval
+qr/invalid request body/
+--- error_log eval
+qr/invalid request body/
+
+
+
+=== TEST 8: set route(delay 1 seconds)
+--- 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": {
+                                   "delay": {
+                                       "duration": 1
+                                   }
+                               },
+                               "proxy-rewrite": {
+                                   "uri": "/hello"
+                               }
+                           },
+                           "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 9: hit route(delay 1 seconds and return hello world)
+--- request
+GET /hello HTTP/1.1
+--- response_body
+hello world
+--- no_error_log
+[error]
+
+
+
+=== TEST 10: set route(abort with http status 200 and return "Fault Injection!")
+--- 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": 200,
+                                      "body": "Fault Injection!"
+                                   }
+                               },
+                               "proxy-rewrite": {
+                                   "uri": "/hello"
+                               }
+                           },
+                           "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 11: hit route(abort with http code 200 and return "Fault Injection!")
+--- request
+GET /hello HTTP/1.1
+--- error_code: 200
+--- response_body
+Fault Injection!
+--- no_error_log
+[error]
+
+
+
+=== TEST 12: set route(abort with http status 405 and return "Fault Injection!")
+--- 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": 405,
+                                      "body": "Fault Injection!"
+                                   }
+                               },
+                               "proxy-rewrite": {
+                                   "uri": "/hello"
+                               }
+                           },
+                           "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 13: hit route(abort with http status 405 and return "Fault Injection!")
+--- request
+GET /hello HTTP/1.1
+--- error_code: 405
+--- response_body
+Fault Injection!
+--- no_error_log
+[error]
+
+
+
+=== TEST 14: set route(play with redirect plugin)
+--- 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": 200,
+                                      "body": "Fault Injection!"
+                                   }
+                               },
+                               "redirect": {
+                                   "uri": "/hello/world",
+                                   "ret_code": 302
+                               }
+                           },
+                           "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 15: hit route(abort with http status 200 and return "Fault Injection!")
+--- request
+GET /hello HTTP/1.1
+--- error_code: 200
+--- response_body
+Fault Injection!
+--- no_error_log
+[error]
+