Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(control): add plugins/reload to control api #10905

Merged
merged 10 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 1 addition & 76 deletions apisix/admin/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ local reload_event = "/apisix/admin/plugins/reload"
local ipairs = ipairs
local error = error
local type = type
local sync_local_conf_to_etcd = require("apisix.utils.config_etcd").sync_local_conf_to_etcd


local events
Expand Down Expand Up @@ -290,82 +291,6 @@ local function post_reload_plugins()
end


local function plugins_eq(old, new)
local old_set = {}
for _, p in ipairs(old) do
old_set[p.name] = p
end

local new_set = {}
for _, p in ipairs(new) do
new_set[p.name] = p
end

return core.table.set_eq(old_set, new_set)
end


local function sync_local_conf_to_etcd(reset)
local local_conf = core.config.local_conf()

local plugins = {}
for _, name in ipairs(local_conf.plugins) do
core.table.insert(plugins, {
name = name,
})
end

for _, name in ipairs(local_conf.stream_plugins) do
core.table.insert(plugins, {
name = name,
stream = true,
})
end

if reset then
local res, err = core.etcd.get("/plugins")
if not res then
core.log.error("failed to get current plugins: ", err)
return
end

if res.status == 404 then
-- nothing need to be reset
return
end

if res.status ~= 200 then
core.log.error("failed to get current plugins, status: ", res.status)
return
end

local stored_plugins = res.body.node.value
local revision = res.body.node.modifiedIndex
if plugins_eq(stored_plugins, plugins) then
core.log.info("plugins not changed, don't need to reset")
return
end

core.log.warn("sync local conf to etcd")

local res, err = core.etcd.atomic_set("/plugins", plugins, nil, revision)
if not res then
core.log.error("failed to set plugins: ", err)
end

return
end

core.log.warn("sync local conf to etcd")

-- need to store all plugins name into one key so that it can be updated atomically
local res, err = core.etcd.set("/plugins", plugins)
if not res then
core.log.error("failed to set plugins: ", err)
end
end


local function reload_plugins(data, event, source, pid)
core.log.info("start to hot reload plugins")
plugin.load()
Expand Down
26 changes: 25 additions & 1 deletion apisix/control/router.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ local pairs = pairs
local type = type
local ngx = ngx
local get_method = ngx.req.get_method

local ngx_worker_id = ngx.worker.id
local events = require("apisix.events")
local sync_local_conf_to_etcd = require("apisix.utils.config_etcd").sync_local_conf_to_etcd

local _M = {}

Expand All @@ -46,6 +48,7 @@ local function format_dismod_uri(mod_name, uri)
return core.table.concat(tmp, "")
end


-- we do not hardcode the discovery module's control api uri
local function format_dismod_control_api_uris(mod_name, api_route)
if not api_route or #api_route == 0 then
Expand Down Expand Up @@ -198,5 +201,26 @@ end

end -- do

local function reload_plugins()
core.log.info("start to hot reload plugins")
plugin_mod.load()

local local_conf = core.config.local_conf()
local deployment_role = core.table.try_read_attr(
local_conf, "deployment", "role")
if deployment_role ~= "data_plane" then
-- data_plane should not write to etcd
if ngx_worker_id() == 0 then
sync_local_conf_to_etcd()
end
end
sheharyaar marked this conversation as resolved.
Show resolved Hide resolved

end


function _M.init_worker()
-- register reload plugin handler
events:register(reload_plugins, builtin_v1_routes.reload_event, "PUT")
end

sheharyaar marked this conversation as resolved.
Show resolved Hide resolved
return _M
18 changes: 18 additions & 0 deletions apisix/control/v1.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
}
1 change: 1 addition & 0 deletions apisix/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
97 changes: 97 additions & 0 deletions apisix/utils/config_etcd.lua
sheharyaar marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
--
-- 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 ipairs = ipairs

local _M = {}

local function plugins_eq(old, new)
local old_set = {}
for _, p in ipairs(old) do
old_set[p.name] = p
end

local new_set = {}
for _, p in ipairs(new) do
new_set[p.name] = p
end

return core.table.set_eq(old_set, new_set)
end


function _M.sync_local_conf_to_etcd(reset)
local local_conf = core.config.local_conf()

local plugins = {}
for _, name in ipairs(local_conf.plugins) do
core.table.insert(plugins, {
name = name,
})
end

for _, name in ipairs(local_conf.stream_plugins) do
core.table.insert(plugins, {
name = name,
stream = true,
})
end

if reset then
local res, err = core.etcd.get("/plugins")
if not res then
core.log.error("failed to get current plugins: ", err)
return
end

if res.status == 404 then
-- nothing need to be reset
return
end

if res.status ~= 200 then
core.log.error("failed to get current plugins, status: ", res.status)
return
end

local stored_plugins = res.body.node.value
local revision = res.body.node.modifiedIndex
if plugins_eq(stored_plugins, plugins) then
core.log.info("plugins not changed, don't need to reset")
return
end

core.log.warn("sync local conf to etcd")

local res, err = core.etcd.atomic_set("/plugins", plugins, nil, revision)
if not res then
core.log.error("failed to set plugins: ", err)
end

return
end

core.log.warn("sync local conf to etcd")

-- need to store all plugins name into one key so that it can be updated atomically
local res, err = core.etcd.set("/plugins", plugins)
if not res then
core.log.error("failed to set plugins: ", err)
end
end

return _M
10 changes: 10 additions & 0 deletions docs/en/latest/control-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
starsz marked this conversation as resolved.
Show resolved Hide resolved

```shell
curl "http://127.0.0.1:9090/v1/plugins/reload" -X PUT
```
Loading
Loading