You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by mo...@apache.org on 2024/02/19 07:13:42 UTC

(apisix) branch master updated: feat: add plugins/reload to control api (#10905)

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

monkeydluffy 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 21d9ceeac feat: add plugins/reload to control api (#10905)
21d9ceeac is described below

commit 21d9ceeac9715dcdad37335fe402e325ee74a43d
Author: Shehar Yaar <sh...@gmail.com>
AuthorDate: Mon Feb 19 12:43:36 2024 +0530

    feat: add plugins/reload to control api (#10905)
---
 apisix/control/router.lua     |  12 +-
 apisix/control/v1.lua         |  18 +++
 apisix/init.lua               |   1 +
 docs/en/latest/control-api.md |  10 ++
 t/control/plugins-reload.t    | 341 ++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 381 insertions(+), 1 deletion(-)

diff --git a/apisix/control/router.lua b/apisix/control/router.lua
index 851dbc872..e6e5ff9b3 100644
--- a/apisix/control/router.lua
+++ b/apisix/control/router.lua
@@ -27,7 +27,7 @@ local pairs = pairs
 local type = type
 local ngx = ngx
 local get_method = ngx.req.get_method
-
+local events = require("apisix.events")
 
 local _M = {}
 
@@ -198,5 +198,15 @@ end
 
 end -- do
 
+local function reload_plugins()
+    core.log.info("start to hot reload plugins")
+    plugin_mod.load()
+end
+
+
+function _M.init_worker()
+    -- register reload plugin handler
+    events:register(reload_plugins, builtin_v1_routes.reload_event, "PUT")
+end
 
 return _M
diff --git a/apisix/control/v1.lua b/apisix/control/v1.lua
index 6838ee4cb..213ed0ac4 100644
--- a/apisix/control/v1.lua
+++ b/apisix/control/v1.lua
@@ -25,11 +25,14 @@ local collectgarbage = collectgarbage
 local ipairs = ipairs
 local pcall = pcall
 local str_format = string.format
+local ngx = ngx
 local ngx_var = ngx.var
+local events = require("apisix.events")
 
 
 local _M = {}
 
+_M.RELOAD_EVENT = 'control-api-plugin-reload'
 
 function _M.schema()
     local http_plugins, stream_plugins = plugin.get_all({
@@ -400,6 +403,14 @@ function _M.dump_plugin_metadata()
     return 200, metadata.value
 end
 
+function _M.post_reload_plugins()
+    local success, err = events:post(_M.RELOAD_EVENT, ngx.req.get_method(), ngx.time())
+    if not success then
+        core.response.exit(503, err)
+    end
+
+    core.response.exit(200, "done")
+end
 
 return {
     -- /v1/schema
@@ -474,5 +485,12 @@ return {
         uris = {"/plugin_metadata/*"},
         handler = _M.dump_plugin_metadata,
     },
+    -- /v1/plugins/reload
+    {
+        methods = {"PUT"},
+        uris = {"/plugins/reload"},
+        handler = _M.post_reload_plugins,
+    },
     get_health_checkers = _get_health_checkers,
+    reload_event = _M.RELOAD_EVENT,
 }
diff --git a/apisix/init.lua b/apisix/init.lua
index 9a13fae8e..91dc83f0b 100644
--- a/apisix/init.lua
+++ b/apisix/init.lua
@@ -155,6 +155,7 @@ function _M.http_init_worker()
     apisix_upstream.init_worker()
     require("apisix.plugins.ext-plugin.init").init_worker()
 
+    control_api_router.init_worker()
     local_conf = core.config.local_conf()
 
     if local_conf.apisix and local_conf.apisix.enable_server_tokens == false then
diff --git a/docs/en/latest/control-api.md b/docs/en/latest/control-api.md
index e21ca86d5..8bd147869 100644
--- a/docs/en/latest/control-api.md
+++ b/docs/en/latest/control-api.md
@@ -475,3 +475,13 @@ Dumps the metadata with the specified `plugin_name`:
     "id": "file-logger"
 }
 ```
+
+### PUT /v1/plugins/reload
+
+Introduced in [v3.9.0](https://github.com/apache/apisix/releases/tag/3.9.0)
+
+Triggers a hot reload of the plugins.
+
+```shell
+curl "http://127.0.0.1:9090/v1/plugins/reload" -X PUT
+```
diff --git a/t/control/plugins-reload.t b/t/control/plugins-reload.t
new file mode 100644
index 000000000..cb9e18623
--- /dev/null
+++ b/t/control/plugins-reload.t
@@ -0,0 +1,341 @@
+#
+# 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';
+
+repeat_each(1);
+no_long_string();
+no_root_location();
+no_shuffle();
+log_level("info");
+workers(2);
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    $block;
+});
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: reload plugins
+--- yaml_config
+apisix:
+    node_listen: 1984
+    enable_control: true
+    control:
+      ip: "127.0.0.1"
+      port: 9090
+--- config
+location /t {
+    content_by_lua_block {
+        local t = require("lib.test_admin")
+
+        local code, body, res = t.test('/v1/plugins/reload',
+            ngx.HTTP_PUT)
+        ngx.say(res)
+        ngx.sleep(1)
+    }
+}
+--- request
+GET /t
+--- response_body
+done
+--- error_log
+load plugin times: 2
+load plugin times: 2
+start to hot reload plugins
+start to hot reload plugins
+
+
+
+=== TEST 2: reload plugins when attributes changed
+--- yaml_config
+apisix:
+  node_listen: 1984
+  enable_admin: true
+  node_listen: 1984
+plugins:
+    - example-plugin
+plugin_attr:
+    example-plugin:
+        val: 0
+--- config
+location /t {
+    content_by_lua_block {
+        local core = require "apisix.core"
+        ngx.sleep(0.1)
+        local data = [[
+deployment:
+  role: traditional
+  role_traditional:
+    config_provider: etcd
+  admin:
+    admin_key: null
+apisix:
+  node_listen: 1984
+  enable_control: true
+  control:
+    ip: "127.0.0.1"
+    port: 9090
+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('/v1/plugins/reload',
+                                    ngx.HTTP_PUT)
+
+        ngx.status = code
+        ngx.say(org_body)
+        ngx.sleep(0.1)
+
+        local data = [[
+deployment:
+  role: traditional
+  role_traditional:
+    config_provider: etcd
+  admin:
+    admin_key: null
+apisix:
+  node_listen: 1984
+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('/v1/plugins/reload',
+                                    ngx.HTTP_PUT)
+        ngx.say(org_body)
+        ngx.sleep(0.1)
+    }
+}
+--- 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
+example-plugin get plugin attr val: 1
+example-plugin get plugin attr val: 1
+example-plugin get plugin attr val: 1
+
+
+
+=== TEST 3: reload plugins to change prometheus' export uri
+--- yaml_config
+apisix:
+  node_listen: 1984
+plugins:
+  - public-api
+  - prometheus
+plugin_attr:
+  prometheus:
+    export_uri: /metrics
+--- config
+location /t {
+    content_by_lua_block {
+        local core = require "apisix.core"
+        ngx.sleep(0.1)
+        local t = require("lib.test_admin").test
+
+        -- setup public API route
+        local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "plugins": {
+                            "public-api": {}
+                        },
+                        "uri": "/apisix/metrics"
+                 }]]
+                )
+        ngx.say(code)
+
+        local code, _, org_body = t('/apisix/metrics',
+                                    ngx.HTTP_GET)
+        ngx.say(code)
+
+        local data = [[
+deployment:
+  role: traditional
+  role_traditional:
+    config_provider: etcd
+  admin:
+    admin_key: null
+apisix:
+  node_listen: 1984
+  enable_control: true
+  control:
+    ip: "127.0.0.1"
+    port: 9090
+plugins:
+  - public-api
+  - prometheus
+plugin_attr:
+  prometheus:
+    export_uri: /apisix/metrics
+        ]]
+        require("lib.test_admin").set_config_yaml(data)
+
+        local code, _, org_body = t('/v1/plugins/reload',
+                                    ngx.HTTP_PUT)
+
+        ngx.say(org_body)
+
+        ngx.sleep(0.1)
+        local code, _, org_body = t('/apisix/metrics',
+                                    ngx.HTTP_GET)
+        ngx.say(code)
+    }
+}
+--- request
+GET /t
+--- response_body
+201
+404
+done
+200
+
+
+
+=== TEST 4: reload plugins to disable skywalking
+--- yaml_config
+apisix:
+  node_listen: 1984
+  enable_control: true
+  control:
+    ip: "127.0.0.1"
+    port: 9090
+plugins:
+  - skywalking
+plugin_attr:
+  skywalking:
+    service_name: APISIX
+    service_instance_name: "APISIX Instance Name"
+    endpoint_addr: http://127.0.0.1:12801
+    report_interval: 1
+--- config
+location /t {
+    content_by_lua_block {
+        local core = require "apisix.core"
+        ngx.sleep(1.2)
+        local t = require("lib.test_admin").test
+
+        local data = [[
+deployment:
+  role: traditional
+  role_traditional:
+    config_provider: etcd
+  admin:
+    admin_key: null
+apisix:
+  node_listen: 1984
+plugins:
+  - prometheus
+        ]]
+        require("lib.test_admin").set_config_yaml(data)
+
+        local code, _, org_body = t('/v1/plugins/reload',
+                                    ngx.HTTP_PUT)
+
+        ngx.say(org_body)
+
+        ngx.sleep(2)
+    }
+}
+--- request
+GET /t
+--- response_body
+done
+--- no_error_log
+[alert]
+--- grep_error_log eval
+qr/Instance report fails/
+--- grep_error_log_out
+Instance report fails
+
+
+
+=== TEST 5: wrong method to reload plugins
+--- request
+GET /v1/plugins/reload
+--- error_code: 404
+
+
+
+=== TEST 6: wrong method to reload plugins
+--- request
+POST /v1/plugins/reload
+--- error_code: 404
+
+
+
+=== TEST 7: reload plugin with data_plane deployment
+--- yaml_config
+apisix:
+    node_listen: 1984
+    enable_admin: false
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: yaml
+--- apisix_yaml
+routes:
+  -
+    uri: /hello
+    upstream:
+      nodes:
+        "127.0.0.1:1980": 1
+      type: roundrobin
+#END
+--- config
+location /t {
+    content_by_lua_block {
+        local t = require("lib.test_admin")
+
+        local code, body, res = t.test('/v1/plugins/reload',
+            ngx.HTTP_PUT)
+        ngx.say(res)
+        ngx.sleep(1)
+    }
+}
+--- request
+GET /t
+--- response_body
+done
+--- error_log
+load plugin times: 2
+load plugin times: 2
+start to hot reload plugins
+start to hot reload plugins