Skip to content

Commit

Permalink
feat: add plugin config
Browse files Browse the repository at this point in the history
fix #3532
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
  • Loading branch information
spacewander committed Feb 10, 2021
1 parent 37a7fae commit b3a6f79
Show file tree
Hide file tree
Showing 19 changed files with 1,262 additions and 7 deletions.
1 change: 1 addition & 0 deletions apisix/admin/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ local resources = {
global_rules = require("apisix.admin.global_rules"),
stream_routes = require("apisix.admin.stream_routes"),
plugin_metadata = require("apisix.admin.plugin_metadata"),
plugin_configs = require("apisix.admin.plugin_config"),
}


Expand Down
173 changes: 173 additions & 0 deletions apisix/admin/plugin_config.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
--
-- 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 utils = require("apisix.admin.utils")
local schema_plugin = require("apisix.admin.plugins").check_schema
local type = type
local tostring = tostring


local _M = {
}


local function check_conf(id, conf, need_id)
if not conf then
return nil, {error_msg = "missing configurations"}
end

id = id or conf.id
if need_id and not id then
return nil, {error_msg = "missing id"}
end

if not need_id and id then
return nil, {error_msg = "wrong id, do not need it"}
end

if need_id and conf.id and tostring(conf.id) ~= tostring(id) then
return nil, {error_msg = "wrong id"}
end

conf.id = id

core.log.info("conf: ", core.json.delay_encode(conf))
local ok, err = core.schema.check(core.schema.global_rule, conf)
if not ok then
return nil, {error_msg = "invalid configuration: " .. err}
end

local ok, err = schema_plugin(conf.plugins)
if not ok then
return nil, {error_msg = err}
end

return true
end


function _M.put(id, conf)
local ok, err = check_conf(id, conf, true)
if not ok then
return 400, err
end

local key = "/plugin_configs/" .. id

local ok, err = utils.inject_conf_with_prev_conf("route", key, conf)
if not ok then
return 500, {error_msg = err}
end

local res, err = core.etcd.set(key, conf)
if not res then
core.log.error("failed to put global rule[", key, "]: ", err)
return 500, {error_msg = err}
end

return res.status, res.body
end


function _M.get(id)
local key = "/plugin_configs"
if id then
key = key .. "/" .. id
end
local res, err = core.etcd.get(key, not id)
if not res then
core.log.error("failed to get global rule[", key, "]: ", err)
return 500, {error_msg = err}
end

return res.status, res.body
end


function _M.delete(id)
local key = "/plugin_configs/" .. id
local res, err = core.etcd.delete(key)
if not res then
core.log.error("failed to delete global rule[", key, "]: ", err)
return 500, {error_msg = err}
end

return res.status, res.body
end


function _M.patch(id, conf, sub_path)
if not id then
return 400, {error_msg = "missing global rule id"}
end

if not conf then
return 400, {error_msg = "missing new configuration"}
end

if not sub_path or sub_path == "" then
if type(conf) ~= "table" then
return 400, {error_msg = "invalid configuration"}
end
end

local key = "/plugin_configs/" .. id
local res_old, err = core.etcd.get(key)
if not res_old then
core.log.error("failed to get plugin config [", key, "]: ", err)
return 500, {error_msg = err}
end

if res_old.status ~= 200 then
return res_old.status, res_old.body
end
core.log.info("key: ", key, " old value: ",
core.json.delay_encode(res_old, true))

local node_value = res_old.body.node.value
local modified_index = res_old.body.node.modifiedIndex

if sub_path and sub_path ~= "" then
local code, err, node_val = core.table.patch(node_value, sub_path, conf)
node_value = node_val
if code then
return code, err
end
else
node_value = core.table.merge(node_value, conf);
end

core.log.info("new conf: ", core.json.delay_encode(node_value, true))

utils.inject_timestamp(node_value, nil, conf)

local ok, err = check_conf(id, node_value, true)
if not ok then
return 400, err
end

local res, err = core.etcd.atomic_set(key, node_value, nil, modified_index)
if not res then
core.log.error("failed to set new plugin config[", key, "]: ", err)
return 500, {error_msg = err}
end

return res.status, res.body
end


return _M
17 changes: 17 additions & 0 deletions apisix/admin/routes.lua
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,23 @@ local function check_conf(id, conf, need_id)
end
end

local plugin_config_id = conf.plugin_config_id
if plugin_config_id then
local key = "/plugin_configs/" .. plugin_config_id
local res, err = core.etcd.get(key)
if not res then
return nil, {error_msg = "failed to fetch plugin config info by "
.. "plugin config id [" .. plugin_config_id .. "]: "
.. err}
end

if res.status ~= 200 then
return nil, {error_msg = "failed to fetch plugin config info by "
.. "plugin config id [" .. plugin_config_id .. "], "
.. "response code: " .. res.status}
end
end

if conf.plugins then
local ok, err = schema_plugin(conf.plugins)
if not ok then
Expand Down
2 changes: 1 addition & 1 deletion apisix/cli/etcd.lua
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ function _M.init(env, args)
for _, dir_name in ipairs({"/routes", "/upstreams", "/services",
"/plugins", "/consumers", "/node_status",
"/ssl", "/global_rules", "/stream_routes",
"/proto", "/plugin_metadata"}) do
"/proto", "/plugin_metadata", "/plugin_configs"}) do

local key = (etcd_conf.prefix or "") .. dir_name .. "/"

Expand Down
14 changes: 14 additions & 0 deletions apisix/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ local require = require
local core = require("apisix.core")
local config_util = require("apisix.core.config_util")
local plugin = require("apisix.plugin")
local plugin_config = require("apisix.plugin_config")
local script = require("apisix.script")
local service_fetch = require("apisix.http.service").get
local admin_init = require("apisix.admin.init")
Expand Down Expand Up @@ -111,6 +112,7 @@ function _M.http_init_worker()
router.http_init_worker()
require("apisix.http.service").init_worker()
plugin.init_worker()
plugin_config.init_worker()
require("apisix.consumer").init_worker()

if core.config == require("apisix.core.config_yaml") then
Expand Down Expand Up @@ -381,6 +383,18 @@ function _M.http_access_phase()
core.json.delay_encode(api_ctx.matched_route, true))

local enable_websocket = route.value.enable_websocket

if route.value.plugin_config_id then
local pc = plugin_config.get(route.value.plugin_config_id)
if not pc then
core.log.error("failed to fetch plugin config by ",
"id: ", route.value.plugin_config_id)
return core.response.exit(503)
end

route = plugin_config.merge(route, pc)
end

if route.value.service_id then
local service = service_fetch(route.value.service_id)
if not service then
Expand Down
69 changes: 69 additions & 0 deletions apisix/plugin_config.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
--
-- 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 plugin_checker = require("apisix.plugin").plugin_checker
local pairs = pairs
local error = error


local plugin_configs


local _M = {
}


function _M.init_worker()
local err
plugin_configs, err = core.config.new("/plugin_configs", {
automatic = true,
checker = plugin_checker,
})
if not plugin_configs then
error("failed to sync /plugin_configs: " .. err)
end
end


function _M.get(id)
return plugin_configs:get(id)
end


function _M.merge(route_conf, plugin_config)
if route_conf.prev_plugin_config_ver ~= plugin_config.modifiedIndex then
if not route_conf.value.plugins then
route_conf.value.plugins = {}
elseif not route_conf.orig_plugins then
route_conf.orig_plugins = route_conf.value.plugins
route_conf.value.plugins = core.table.clone(route_conf.value.plugins)
end

for name, value in pairs(plugin_config.value.plugins) do
route_conf.value.plugins[name] = value
end

route_conf.update_count = route_conf.update_count + 1
route_conf.modifiedIndex = route_conf.orig_modifiedIndex .. "#" .. route_conf.update_count
route_conf.prev_plugin_config_ver = plugin_config.modifiedIndex
end

return route_conf
end


return _M
4 changes: 3 additions & 1 deletion apisix/plugins/example-plugin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ end

function _M.rewrite(conf, ctx)
core.log.warn("plugin rewrite phase, conf: ", core.json.encode(conf))
-- core.log.warn(" ctx: ", core.json.encode(ctx, true))
core.log.warn("conf_type: ", ctx.conf_type)
core.log.warn("conf_id: ", ctx.conf_id)
core.log.warn("conf_version: ", ctx.conf_version)
end


Expand Down
3 changes: 3 additions & 0 deletions apisix/router.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ local _M = {version = 0.3}


local function filter(route)
route.orig_modifiedIndex = route.modifiedIndex
route.update_count = 0

route.has_domain = false
if not route.value then
return
Expand Down
19 changes: 18 additions & 1 deletion apisix/schema_def.lua
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,8 @@ _M.route = {
script_id = id_schema,

plugins = plugins_schema,
plugin_config_id = id_schema,

upstream = upstream_schema,

labels = {
Expand Down Expand Up @@ -544,7 +546,8 @@ _M.route = {
},
["not"] = {
anyOf = {
{required = {"script", "plugins"}}
{required = {"script", "plugins"}},
{required = {"script", "plugin_config_id"}},
}
},
additionalProperties = false,
Expand Down Expand Up @@ -746,6 +749,20 @@ _M.plugins = {
}


_M.plugin_config = {
type = "object",
properties = {
id = id_schema,
desc = {type = "string", maxLength = 256},
plugins = plugins_schema,
create_time = timestamp_def,
update_time = timestamp_def
},
required = {"id", "plugins"},
additionalProperties = false,
}


_M.id_schema = id_schema


Expand Down
Loading

0 comments on commit b3a6f79

Please sign in to comment.