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

feature: supported to save/delete/change global rule. #492

Merged
merged 7 commits into from
Sep 10, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ For more detailed information, see the [White Paper](https://www.iresty.com/down
- **IP whitelist/blacklist**
- **IdP**: Support external authentication services, such as Auth0, okta, etc., users can use this to connect to Oauth2.0 and other authentication methods.
- **[Stand-alone mode](doc/stand-alone.md)**: Supports to load route rules from local yaml file, it is more friendly such as under the kubernetes(k8s).
- **Global rule**: Allows to run any plugin for all request, eg: limit rate, IP filter etc.
- **ACL**: TODO.
- **Bot detection**: TODO.

Expand Down
1 change: 1 addition & 0 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ APISIX 通过插件机制,提供动态负载平衡、身份验证、限流限
- **IP 黑名单**
- **IdP 支持**: 支持外部的身份认证服务,比如 Auth0,okta 等,用户可以借此来对接 Oauth2.0 等认证方式。
- **[单机模式](doc/stand-alone-cn.md)**: 支持从本地配置文件中加载路由规则,在 kubernetes(k8s) 等环境下更友好。
- **全局规则**:允许对所有请求执行插件,比如黑白名单、限流限速等。
- **ACL**: TODO。
- **Bot detection**: TODO。

Expand Down
2 changes: 1 addition & 1 deletion bin/apisix
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ local function init_etcd(show_output)

for _, dir_name in ipairs({"/routes", "/upstreams", "/services",
"/plugins", "/consumers", "/node_status",
"/ssl"}) do
"/ssl", "/global_rules"}) do
local cmd = "curl " .. uri .. dir_name
.. "?prev_exist=false -X PUT -d dir=true "
.. "--connect-timeout " .. timeout
Expand Down
28 changes: 27 additions & 1 deletion lua/apisix.lua
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ function _M.http_ssl_phase()
end


do
local upstream_vars = {
uri = "upstream_uri",
scheme = "upstream_scheme",
Expand All @@ -155,6 +156,7 @@ end
for name, _ in pairs(upstream_vars) do
core.table.insert(upstream_names, name)
end

function _M.http_access_phase()
local ngx_ctx = ngx.ctx
local api_ctx = ngx_ctx.api_ctx
Expand All @@ -166,6 +168,26 @@ function _M.http_access_phase()

core.ctx.set_vars_meta(api_ctx)

-- load and run global rule
if router.global_rules.values and #router.global_rules.values > 0 then
local plugins = core.tablepool.fetch("plugins", 32, 0)
for _, global_rule in ipairs(router.global_rules.values) do
api_ctx.conf_type = "global_rule"
api_ctx.conf_version = global_rule.modifiedIndex
api_ctx.conf_id = global_rule.value.id

core.table.clear(plugins)
api_ctx.plugins = plugin.filter(global_rule, plugins)
run_plugin("rewrite", plugins, api_ctx)
end

core.tablepool.release("plugins", plugins)
api_ctx.plugins = nil
api_ctx.conf_type = nil
api_ctx.conf_version = nil
api_ctx.conf_id = nil
end

router.router_http.match(api_ctx)

core.log.info("matched route: ",
Expand Down Expand Up @@ -233,6 +255,9 @@ function _M.http_access_phase()
run_plugin("access", plugins, api_ctx)
end

end -- do


function _M.grpc_access_phase()
local ngx_ctx = ngx.ctx
local api_ctx = ngx_ctx.api_ctx
Expand Down Expand Up @@ -293,15 +318,16 @@ function _M.grpc_access_phase()
end



function _M.http_header_filter_phase()
run_plugin("header_filter")
end


function _M.http_body_filter_phase()
run_plugin("body_filter")
end


function _M.http_log_phase()
local api_ctx = run_plugin("log")
if api_ctx then
Expand Down
159 changes: 159 additions & 0 deletions lua/apisix/admin/global_rules.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
local core = require("apisix.core")
local schema_plugin = require("apisix.admin.plugins").check_schema
local type = type
local tostring = tostring


local _M = {
version = 0.1,
}


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 route id"}
end

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

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

core.log.info("schema: ", core.json.delay_encode(core.schema.global_rule))
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 = "/global_rules/" .. id
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 = "/global_rules/" .. id
local res, err = core.etcd.get(key)
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 = "/global_rules/" .. id
-- core.log.info("key: ", key)
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 sub_path then
return 400, {error_msg = "missing sub-path"}
end

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

local key = "/global_rules/" .. id
local res_old, err = core.etcd.get(key)
if not res_old then
core.log.error("failed to get global rule [", 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 sub_value = node_value
local sub_paths = core.utils.split_uri(sub_path)
for i = 1, #sub_paths - 1 do
local sub_name = sub_paths[i]
if sub_value[sub_name] == nil then
sub_value[sub_name] = {}
end

sub_value = sub_value[sub_name]

if type(sub_value) ~= "table" then
return 400, "invalid sub-path: /"
.. core.table.concat(sub_paths, 1, i)
end
end

if type(sub_value) ~= "table" then
return 400, "invalid sub-path: /" .. sub_path
end

local sub_name = sub_paths[#sub_paths]
if sub_name and sub_name ~= "" then
sub_value[sub_name] = conf
else
node_value = conf
end
core.log.info("new conf: ", core.json.delay_encode(node_value, true))

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

-- TODO: this is not safe, we need to use compare-set
local res, err = core.etcd.set(key, node_value)
if not res then
core.log.error("failed to set new global rule[", key, "]: ", err)
return 500, {error_msg = err}
end

return res.status, res.body
end


return _M
1 change: 1 addition & 0 deletions lua/apisix/admin/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ local resources = {
ssl = require("apisix.admin.ssl"),
plugins = require("apisix.admin.plugins"),
proto = require("apisix.admin.proto"),
global_rules= require("apisix.admin.global_rules"),
}


Expand Down
10 changes: 10 additions & 0 deletions lua/apisix/core/schema.lua
Original file line number Diff line number Diff line change
Expand Up @@ -401,4 +401,14 @@ _M.proto = {
}


_M.global_rule = {
type = "object",
properties = {
plugins = plugins_schema
},
required = {"plugins"},
additionalProperties = false,
}


return _M
13 changes: 12 additions & 1 deletion lua/apisix/http/router.lua
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
local require = require
local core = require("apisix.core")
local local_conf = core.config.local_conf
local error = error


local _M = {version = 0.1}
local _M = {version = 0.2}


function _M.init_worker()
Expand All @@ -23,6 +24,16 @@ function _M.init_worker()
local router_ssl = require("apisix.http.router." .. router_ssl_name)
router_ssl:init_worker()
_M.router_ssl = router_ssl

local global_rules, err = core.config.new("/global_rules", {
automatic = true,
item_schema = core.schema.global_rule
})
if not global_rules then
error("failed to create etcd instance for fetching /global_rules : "
.. err)
end
_M.global_rules = global_rules
end


Expand Down
2 changes: 1 addition & 1 deletion lua/apisix/plugins/limit-count.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ local function create_limit_obj(conf)
end


function _M.access(conf, ctx)
function _M.rewrite(conf, ctx)
core.log.info("ver: ", ctx.conf_version)
local lim, err = core.lrucache.plugin_ctx(plugin_name, ctx,
create_limit_obj, conf)
Expand Down
Loading