This is an automated email from the ASF dual-hosted git repository.

juzhiyuan 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 3987963  fix: reload plugin when plugin attribute is cahnged (#2704)
3987963 is described below

commit 39879630ae9a23f9f195963fb75f5488b4f5195e
Author: 罗泽轩 <[email protected]>
AuthorDate: Fri Nov 13 00:23:30 2020 +0800

    fix: reload plugin when plugin attribute is cahnged (#2704)
    
    Close #2655
---
 apisix/core/table.lua             | 28 +++++++++++++++
 apisix/plugin.lua                 | 34 ++++++++++++++++--
 apisix/plugins/example-plugin.lua |  5 +++
 t/admin/plugins-reload.t          | 72 +++++++++++++++++++++++++++++++++++++++
 t/core/table.t                    | 35 +++++++++++++++++++
 5 files changed, 172 insertions(+), 2 deletions(-)

diff --git a/apisix/core/table.lua b/apisix/core/table.lua
index ed998d0..b0ef7c9 100644
--- a/apisix/core/table.lua
+++ b/apisix/core/table.lua
@@ -189,4 +189,32 @@ function _M.set_eq(a, b)
 end
 
 
+-- Compare two elements, including their descendants
+local function deep_eq(a, b)
+    local type_a = type(a)
+    local type_b = type(b)
+
+    if type_a ~= 'table' or type_b ~= 'table' then
+        return a == b
+    end
+
+    local n_a = nkeys(a)
+    local n_b = nkeys(b)
+    if n_a ~= n_b then
+        return false
+    end
+
+    for k, v_a in pairs(a) do
+        local v_b = b[k]
+        local eq = deep_eq(v_a, v_b)
+        if not eq then
+            return false
+        end
+    end
+
+    return true
+end
+_M.deep_eq = deep_eq
+
+
 return _M
diff --git a/apisix/plugin.lua b/apisix/plugin.lua
index ae1aedd..85a8f5b 100644
--- a/apisix/plugin.lua
+++ b/apisix/plugin.lua
@@ -51,6 +51,14 @@ local _M = {
 }
 
 
+local function plugin_attr(name)
+    -- TODO: get attr from synchronized data
+    local local_conf = core.config.local_conf()
+    return core.table.try_read_attr(local_conf, "plugin_attr", name)
+end
+_M.plugin_attr = plugin_attr
+
+
 local function sort_plugin(l, r)
     return l.priority > r.priority
 end
@@ -103,6 +111,7 @@ local function load_plugin(name, plugins_list, 
is_stream_plugin)
     end
 
     plugin.name = name
+    plugin.attr = plugin_attr(name)
     core.table.insert(plugins_list, plugin)
 
     if plugin.init then
@@ -113,6 +122,25 @@ local function load_plugin(name, plugins_list, 
is_stream_plugin)
 end
 
 
+local function plugins_eq(old, new)
+    local eq = core.table.set_eq(old, new)
+    if not eq then
+        core.log.info("plugin list changed")
+        return false
+    end
+
+    for name, plugin in pairs(old) do
+        eq = core.table.deep_eq(plugin.attr, plugin_attr(name))
+        if not eq then
+            core.log.info("plugin_attr of ", name, " changed")
+            return false
+        end
+    end
+
+    return true
+end
+
+
 local function load(plugin_names)
     local processed = {}
     for _, name in ipairs(plugin_names) do
@@ -122,7 +150,7 @@ local function load(plugin_names)
     end
 
     -- the same configure may be synchronized more than one
-    if core.table.set_eq(local_plugins_hash, processed) then
+    if plugins_eq(local_plugins_hash, processed) then
         core.log.info("plugins not changed")
         return true
     end
@@ -170,7 +198,7 @@ local function load_stream(plugin_names)
     end
 
     -- the same configure may be synchronized more than one
-    if core.table.set_eq(stream_local_plugins_hash, processed) then
+    if plugins_eq(stream_local_plugins_hash, processed) then
         core.log.info("plugins not changed")
         return true
     end
@@ -216,10 +244,12 @@ function _M.load(config)
     local stream_plugin_names
 
     if not config then
+        -- called during starting or hot reload in admin
         local_conf = core.config.local_conf(true)
         http_plugin_names = local_conf.plugins
         stream_plugin_names = local_conf.stream_plugins
     else
+        -- called during synchronizing plugin data
         http_plugin_names = {}
         stream_plugin_names = {}
         for _, conf_value in config_util.iterate_values(config.values) do
diff --git a/apisix/plugins/example-plugin.lua 
b/apisix/plugins/example-plugin.lua
index a590f43..0a58efd 100644
--- a/apisix/plugins/example-plugin.lua
+++ b/apisix/plugins/example-plugin.lua
@@ -15,6 +15,7 @@
 -- limitations under the License.
 --
 local core = require("apisix.core")
+local plugin = require("apisix.plugin")
 local upstream = require("apisix.upstream")
 
 local schema = {
@@ -63,6 +64,10 @@ end
 
 function _M.init()
     -- call this function when plugin is loaded
+    local attr = plugin.plugin_attr(plugin_name)
+    if attr then
+        core.log.info(plugin_name, " get plugin attr val: ", attr.val)
+    end
 end
 
 
diff --git a/t/admin/plugins-reload.t b/t/admin/plugins-reload.t
index d675306..294369d 100644
--- a/t/admin/plugins-reload.t
+++ b/t/admin/plugins-reload.t
@@ -123,3 +123,75 @@ reload plugins on node
 reload plugins on node
 --- error_log
 filter(): [{"name":"jwt-auth"},{"name":"mqtt-proxy","stream":true}]
+
+
+
+=== TEST 3: reload plugins when attributes changed
+--- yaml_config
+apisix:
+  node_listen: 1984
+  admin_key: null
+plugins:
+    - example-plugin
+plugin_attr:
+    example-plugin:
+        val: 0
+--- config
+location /t {
+    content_by_lua_block {
+        local core = require "apisix.core"
+        local data = [[
+apisix:
+  node_listen: 1984
+  admin_key: null
+plugins:
+    - example-plugin
+plugin_attr:
+    example-plugin:
+        val: 1
+        ]]
+        require("lib.test_admin").set_config_yaml(data)
+
+        local t = require("lib.test_admin").test
+        local code, _, org_body = t('/apisix/admin/plugins/reload',
+                                    ngx.HTTP_PUT)
+
+        ngx.status = code
+        ngx.say(org_body)
+        ngx.sleep(0.1)
+
+        local data = [[
+apisix:
+  node_listen: 1984
+  admin_key: null
+plugins:
+    - example-plugin
+plugin_attr:
+    example-plugin:
+        val: 1
+        ]]
+        require("lib.test_admin").set_config_yaml(data)
+
+        local t = require("lib.test_admin").test
+        local code, _, org_body = t('/apisix/admin/plugins/reload',
+                                    ngx.HTTP_PUT)
+        ngx.say(org_body)
+    }
+}
+--- request
+GET /t
+--- response_body
+done
+done
+--- grep_error_log eval
+qr/example-plugin get plugin attr val: \d+/
+--- grep_error_log_out
+example-plugin get plugin attr val: 0
+example-plugin get plugin attr val: 0
+example-plugin get plugin attr val: 0
+example-plugin get plugin attr val: 1
+example-plugin get plugin attr val: 1
+example-plugin get plugin attr val: 1
+--- error_log
+plugin_attr of example-plugin changed
+plugins not changed
diff --git a/t/core/table.t b/t/core/table.t
index a466f62..fc5ea29 100644
--- a/t/core/table.t
+++ b/t/core/table.t
@@ -136,3 +136,38 @@ ok
 GET /t
 --- no_error_log
 [error]
+
+
+
+=== TEST 5: deep_eq
+--- config
+    location /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            local cases = {
+                {expect = true, a = {}, b = {}},
+                {expect = true, a = nil, b = nil},
+                {expect = false, a = nil, b = {}},
+                {expect = false, a = {}, b = nil},
+                {expect = true, a = {a = {b = 1}}, b = {a = {b = 1}}},
+                {expect = false, a = {a = {b = 1}}, b = {a = {b = 1, c = 2}}},
+                {expect = false, a = {a = {b = 1}}, b = {a = {b = 2}}},
+                {expect = true, a = {{a = {b = 1}}}, b = {{a = {b = 1}}}},
+            }
+            for _, t in ipairs(cases) do
+                local actual = core.table.deep_eq(t.a, t.b)
+                local expect = t.expect
+                if actual ~= expect then
+                    ngx.say("expect ", expect, ", actual ", actual)
+                    return
+                end
+            end
+            ngx.say("ok")
+        }
+    }
+--- response_body
+ok
+--- request
+GET /t
+--- no_error_log
+[error]

Reply via email to