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

shreemaan-abhishek 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 a508d83de4 fix(plugin): align unavailable plugin handling (#13928)
a508d83de4 is described below

commit a508d83de4a6aed4ab31dce728f9d3578d80f58c
Author: Shreemaan Abhishek <[email protected]>
AuthorDate: Fri Sep 11 18:36:45 2026 +0800

    fix(plugin): align unavailable plugin handling (#13928)
---
 apisix/core/config_etcd.lua |   2 +-
 apisix/plugin.lua           |  15 +++-
 t/core/plugin-checker.t     | 181 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 194 insertions(+), 4 deletions(-)

diff --git a/apisix/core/config_etcd.lua b/apisix/core/config_etcd.lua
index c6b4697c8b..7f41dd7a0a 100644
--- a/apisix/core/config_etcd.lua
+++ b/apisix/core/config_etcd.lua
@@ -563,7 +563,7 @@ local function load_full_data(self, dir_res, headers, 
prev_values, prev_values_h
         end
 
         if data_valid and self.checker then
-            data_valid, err = self.checker(item.value)
+            data_valid, err = self.checker(item.value, item.key)
             if not data_valid then
                 log.error("failed to check item data of [", self.key,
                           "] err:", err, " ,val: ", 
json.delay_encode(item.value))
diff --git a/apisix/plugin.lua b/apisix/plugin.lua
index 5a7a405a09..632e25c74c 100644
--- a/apisix/plugin.lua
+++ b/apisix/plugin.lua
@@ -162,6 +162,13 @@ local function check_disable(plugin_conf)
 
     return plugin_conf._meta.disable
 end
+
+
+local function warn_unavailable_plugin(name, plugin_conf)
+    if check_disable(plugin_conf) ~= true then
+        core.log.warn("plugin [", name, "] is not enabled and will be skipped")
+    end
+end
 -- exposed for callers that must not act on a plugin config which never runs,
 -- such as the control API reporting the health checkers a plugin owns
 _M.check_disable = check_disable
@@ -1016,8 +1023,7 @@ local function check_single_plugin_schema(name, 
plugin_conf, schema_type, skip_d
         end
 
         if skip_disabled_plugin then
-            core.log.warn("skipping check schema for disabled or unknown 
plugin [",
-                                    name, "]. Enable the plugin or modify 
configuration")
+            warn_unavailable_plugin(name, plugin_conf)
             return true
         else
             return false, "unknown plugin [" .. name .. "]"
@@ -1271,6 +1277,7 @@ local function stream_check_schema(plugins_conf, 
schema_type, skip_disabled_plug
         local plugin_obj = stream_local_plugins_hash[name]
         if not plugin_obj then
             if skip_disabled_plugin then
+                warn_unavailable_plugin(name, plugin_conf)
                 goto CONTINUE
             else
                 return false, "unknown plugin [" .. name .. "]"
@@ -1318,7 +1325,9 @@ end
 
 function _M.stream_plugin_checker(item, in_cp)
     if item.plugins then
-        local skip_disabled_plugins = not in_cp
+        -- config_etcd passes the key as the second checker argument, so only
+        -- an explicit boolean marks validation on the control plane.
+        local skip_disabled_plugins = in_cp ~= true
         if core.config.type == "yaml" or core.config.type == "json" then
             skip_disabled_plugins = false
         end
diff --git a/t/core/plugin-checker.t b/t/core/plugin-checker.t
new file mode 100644
index 0000000000..8e44ce5c85
--- /dev/null
+++ b/t/core/plugin-checker.t
@@ -0,0 +1,181 @@
+#
+# 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.
+#
+use t::APISIX 'no_plan';
+
+no_root_location();
+no_long_string();
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: control-plane stream validation rejects an unavailable plugin
+--- config
+    location /t {
+        content_by_lua_block {
+            local checker = require("apisix.plugin").stream_plugin_checker
+            local ok, err = checker({plugins = {missing = {}}}, true)
+            ngx.say(ok or false, ": ", err)
+        }
+    }
+--- request
+GET /t
+--- response_body
+false: unknown plugin [missing]
+
+
+
+=== TEST 2: an etcd key identifies tolerant data-plane stream validation
+--- config
+    location /t {
+        content_by_lua_block {
+            local checker = require("apisix.plugin").stream_plugin_checker
+            local ok, err = checker({plugins = {missing = {}}},
+                                    "/apisix/stream_routes/1")
+            ngx.say(ok, ": ", err)
+        }
+    }
+--- request
+GET /t
+--- response_body
+true: nil
+--- error_log
+plugin [missing] is not enabled and will be skipped
+
+
+
+=== TEST 3: an explicitly disabled unavailable stream plugin is silent
+--- config
+    location /t {
+        content_by_lua_block {
+            local checker = require("apisix.plugin").stream_plugin_checker
+            local ok, err = checker({
+                plugins = {missing = {_meta = {disable = true}}}
+            }, "/apisix/stream_routes/1")
+            ngx.say(ok, ": ", err)
+        }
+    }
+--- request
+GET /t
+--- response_body
+true: nil
+--- no_error_log
+plugin [missing] is not enabled and will be skipped
+
+
+
+=== TEST 4: tolerant HTTP validation warns about an unavailable plugin
+--- config
+    location /t {
+        content_by_lua_block {
+            local checker = require("apisix.plugin").check_schema
+            local ok, err = checker({missing = {}}, nil, true)
+            ngx.say(ok, ": ", err)
+        }
+    }
+--- request
+GET /t
+--- response_body
+true: nil
+--- error_log
+plugin [missing] is not enabled and will be skipped
+
+
+
+=== TEST 5: an explicitly disabled unavailable HTTP plugin is silent
+--- config
+    location /t {
+        content_by_lua_block {
+            local checker = require("apisix.plugin").check_schema
+            local ok, err = checker({
+                missing = {_meta = {disable = true}}
+            }, nil, true)
+            ngx.say(ok, ": ", err)
+        }
+    }
+--- request
+GET /t
+--- response_body
+true: nil
+--- no_error_log
+plugin [missing] is not enabled and will be skipped
+
+
+
+=== TEST 6: single-item full loading passes the etcd key to its checker
+--- config
+    location /t {
+        content_by_lua_block {
+            local config_etcd = require("apisix.core.config_etcd")
+            local etcd = require("apisix.core.etcd")
+            local original_get_format = etcd.get_format
+            local etcd_cli = {}
+
+            function etcd_cli.readdir()
+                return {
+                    status = 200,
+                    headers = {},
+                    body = {header = {revision = 1}, kvs = {}},
+                }
+            end
+
+            etcd.get_format = function(res)
+                res.body = {
+                    node = {
+                        key = "/apisix/plugins",
+                        value = {{name = "jwt-auth"}},
+                        modifiedIndex = 1,
+                    },
+                }
+                return res
+            end
+
+            config_etcd.test_sync_data({
+                etcd_cli = etcd_cli,
+                key = "/apisix/plugins",
+                single_item = true,
+                need_reload = true,
+                checker = function(_, key)
+                    ngx.say(key)
+                    return true
+                end,
+                upgrade_version = function() end,
+                conf_version = 1,
+            })
+            etcd.get_format = original_get_format
+        }
+    }
+--- request
+GET /t
+--- response_body
+/apisix/plugins
+
+
+
+=== TEST 7: strict HTTP validation rejects an unavailable plugin
+--- config
+    location /t {
+        content_by_lua_block {
+            local checker = require("apisix.plugin").check_schema
+            local ok, err = checker({missing = {}}, nil, false)
+            ngx.say(ok or false, ": ", err)
+        }
+    }
+--- request
+GET /t
+--- response_body
+false: unknown plugin [missing]

Reply via email to