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 <agil...@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] +