From 5b344543c173cf31f468f8c2f17dea1154c7a6f2 Mon Sep 17 00:00:00 2001 From: tzssangglass Date: Thu, 3 Mar 2022 18:46:31 +0800 Subject: [PATCH] fix: rerun rewrite phase for newly added plugins in consumer Closes #6405 Signed-off-by: tzssangglass --- apisix/init.lua | 8 +- apisix/plugin.lua | 54 ++++++++- t/admin/consumer-plugins.t | 223 +++++++++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+), 4 deletions(-) create mode 100644 t/admin/consumer-plugins.t diff --git a/apisix/init.lua b/apisix/init.lua index 2f9befd4107b..fc72e3f3d371 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -454,8 +454,14 @@ function _M.http_access_phase() if changed then api_ctx.matched_route = route + local runned_plugins = core.table.deepcopy(api_ctx.plugins) core.table.clear(api_ctx.plugins) - api_ctx.plugins = plugin.filter(api_ctx, route, api_ctx.plugins) + local unrunn_plugins + api_ctx.plugins, unrunn_plugins = plugin.filter(api_ctx, route, api_ctx.plugins, nil, runned_plugins) + if unrunn_plugins then + -- rerun rewrite phase for newly added plugins in consumer + plugin.run_newly_added_plugins_in_consumer(unrunn_plugins, api_ctx) + end end end plugin.run_plugin("access", plugins, api_ctx) diff --git a/apisix/plugin.lua b/apisix/plugin.lua index 0d9b707f4552..af6de6cacd4d 100644 --- a/apisix/plugin.lua +++ b/apisix/plugin.lua @@ -355,7 +355,17 @@ local function trace_plugins_info_for_debug(ctx, plugins) end -function _M.filter(ctx, conf, plugins, route_conf) +local function filter_runned_plugins(runned_plugins) + local runned_plugins_names = core.table.new(#runned_plugins / 2, 0) + for i = 1, #runned_plugins, 2 do + core.table.insert(runned_plugins_names, runned_plugins[i].name) + end + + return runned_plugins_names +end + + +function _M.filter(ctx, conf, plugins, route_conf, runned_plugins) local user_plugin_conf = conf.value.plugins if user_plugin_conf == nil or core.table.nkeys(user_plugin_conf) == 0 then @@ -365,7 +375,14 @@ function _M.filter(ctx, conf, plugins, route_conf) return plugins or core.tablepool.fetch("plugins", 0, 0) end - local route_plugin_conf = route_conf and route_conf.value.plugins + local unrunn_plugins + local runned_plugins_names + if runned_plugins and type(runned_plugins) == "table" then + runned_plugins_names = filter_runned_plugins(runned_plugins) + unrunn_plugins = core.table.new(4, 0) + end + + local route_plugin_conf = route_conf and route_conf.value and route_conf.value.plugins plugins = plugins or core.tablepool.fetch("plugins", 32, 0) for _, plugin_obj in ipairs(local_plugins) do local name = plugin_obj.name @@ -379,6 +396,11 @@ function _M.filter(ctx, conf, plugins, route_conf) end end + if runned_plugins_names and not core.table.array_find(runned_plugins_names, name) then + core.table.insert(unrunn_plugins, plugin_obj) + core.table.insert(unrunn_plugins, plugin_conf) + end + core.table.insert(plugins, plugin_obj) core.table.insert(plugins, plugin_conf) @@ -388,7 +410,7 @@ function _M.filter(ctx, conf, plugins, route_conf) trace_plugins_info_for_debug(ctx, plugins) - return plugins + return plugins, unrunn_plugins end @@ -805,4 +827,30 @@ function _M.run_global_rules(api_ctx, global_rules, phase_name) end +function _M.run_newly_added_plugins_in_consumer(unrunn_plugins, api_ctx) + for i = 1, #unrunn_plugins, 2 do + -- only run rewrite phase of newly added plugins + local phase_func = unrunn_plugins[i]["rewrite"] + if phase_func then + local code, body = phase_func(unrunn_plugins[i + 1], api_ctx) + if code or body then + if is_http then + if code >= 400 then + core.log.warn(unrunn_plugins[i].name, " exits with http status code ", code) + end + + core.response.exit(code, body) + else + if code >= 400 then + core.log.warn(unrunn_plugins[i].name, " exits with status code ", code) + end + + ngx_exit(1) + end + end + end + end +end + + return _M diff --git a/t/admin/consumer-plugins.t b/t/admin/consumer-plugins.t new file mode 100644 index 000000000000..e073b9d4aaf0 --- /dev/null +++ b/t/admin/consumer-plugins.t @@ -0,0 +1,223 @@ +use t::APISIX 'no_plan'; + +log_level('info'); +repeat_each(1); +no_long_string(); +no_root_location(); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->response_body) { + $block->set_value("response_body", "passed\n"); + } + + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } +}); + + +our $debug_config = t::APISIX::read_file("conf/debug.yaml"); +$debug_config =~ s/basic:\n enable: false/basic:\n enable: true/; +$debug_config =~ s/hook_conf:\n enable: false/hook_conf:\n enable: true/; + +run_tests; + +__DATA__ + +=== TEST 1: configure non-auth plugins in the consumer and run ti's rewrite phase +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack', + ngx.HTTP_PUT, + [[{ + "username": "jack", + "plugins": { + "key-auth": { + "key": "auth-jack" + }, + "proxy-rewrite": { + "uri": "/uri/plugin_proxy_rewrite", + "headers": { + "X-Api-Engine": "APISIX", + "X-CONSUMER-ID": "1" + } + } + } + }]] + ) + + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "key-auth": {} + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 2: hit routes +--- request +GET /hello +--- more_headers +apikey: auth-jack +--- response_body +uri: /uri/plugin_proxy_rewrite +apikey: auth-jack +host: localhost +x-api-engine: APISIX +x-consumer-id: 1 +x-real-ip: 127.0.0.1 + + + +=== TEST 3: trace plugins info for debug +--- debug_config eval: $::debug_config +--- config + location /t { + content_by_lua_block { + local json = require("toolkit.json") + local ngx_re = require("ngx.re") + local http = require "resty.http" + local httpc = http.new() + local headers = {} + headers["apikey"] = "auth-jack" + local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" + local res, err = httpc:request_uri(uri, { + method = "GET", + headers = headers, + }) + local debug_header = res.headers["Apisix-Plugins"] + local arr = ngx_re.split(debug_header, ", ") + local hash = {} + for i, v in ipairs(arr) do + hash[v] = true + end + ngx.status = res.status + ngx.say(json.encode(hash)) + } + } +--- response_body +{"key-auth":true,"proxy-rewrite":true} + + + +=== TEST 4: configure non-auth plugins in the route and run ti's rewrite phase +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers/jack', + ngx.HTTP_PUT, + [[{ + "username": "jack", + "plugins": { + "key-auth": { + "key": "auth-jack" + } + } + }]] + ) + + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "key-auth": {}, + "proxy-rewrite": { + "uri": "/uri/plugin_proxy_rewrite", + "headers": { + "X-Api-Engine": "APISIX", + "X-CONSUMER-ID": "1" + } + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 5: hit routes +--- request +GET /hello +--- more_headers +apikey: auth-jack +--- response_body +uri: /uri/plugin_proxy_rewrite +apikey: auth-jack +host: localhost +x-api-engine: APISIX +x-consumer-id: 1 +x-real-ip: 127.0.0.1 + + + +=== TEST 6: trace plugins info for debug +--- debug_config eval: $::debug_config +--- config + location /t { + content_by_lua_block { + local json = require("toolkit.json") + local ngx_re = require("ngx.re") + local http = require "resty.http" + local httpc = http.new() + local headers = {} + headers["apikey"] = "auth-jack" + local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" + local res, err = httpc:request_uri(uri, { + method = "GET", + headers = headers, + }) + local debug_header = res.headers["Apisix-Plugins"] + local arr = ngx_re.split(debug_header, ", ") + local hash = {} + for i, v in ipairs(arr) do + hash[v] = true + end + ngx.status = res.status + ngx.say(json.encode(hash)) + } + } +--- response_body +{"key-auth":true,"proxy-rewrite":true}