diff --git a/lua/apisix/admin/init.lua b/lua/apisix/admin/init.lua index 1162791a4001..70a85b73e0a7 100644 --- a/lua/apisix/admin/init.lua +++ b/lua/apisix/admin/init.lua @@ -16,7 +16,7 @@ local resources = { } -local _M = {version = 0.1} +local _M = {version = 0.2} local router @@ -46,7 +46,7 @@ local function run(params) req_body = data end - local code, data = resource[method](params.id, req_body) + local code, data = resource[method](params.id, req_body, params.sub_path) if code then core.response.exit(code, data) end @@ -60,25 +60,36 @@ end local uri_route = { { path = [[/apisix/admin/{res:routes|services|upstreams|consumers|ssl}]], - handler = run + handler = run, + method = {"GET", "PUT", "POST", "DELETE"}, }, { path = [[/apisix/admin/{res:routes|services|upstreams|consumers|ssl}]] - .. [[/{id:[\d\w_]+}]], - handler = run + .. [[/{id}]], + handler = run, + method = {"GET", "PUT", "POST", "DELETE"}, }, { - path = [[/apisix/admin/schema/{res:plugins}/{id:[\d\w-]+}]], - handler = run + path = [[/apisix/admin/schema/{res:plugins}/{id}]], + handler = run, + method = {"GET", "PUT", "POST", "DELETE"}, }, { path = [[/apisix/admin/{res:schema}/]] .. [[{id:route|service|upstream|consumer|ssl}]], - handler = run + handler = run, + method = {"GET", "PUT", "POST", "DELETE"}, }, { path = [[/apisix/admin/plugins/list]], - handler = get_plugins_list + handler = get_plugins_list, + method = {"GET", "PUT", "POST", "DELETE"}, + }, + { + path = [[/apisix/admin/{res:routes|services|upstreams|consumers|ssl}]] + .. [[/{id}/{sub_path:.*}]], + handler = run, + method = {"PATCH"}, }, } @@ -89,7 +100,6 @@ function _M.init_worker() end router = route.new(uri_route) - router:compile() end diff --git a/lua/apisix/admin/routes.lua b/lua/apisix/admin/routes.lua index 268fa3a2f12e..d67212c95954 100644 --- a/lua/apisix/admin/routes.lua +++ b/lua/apisix/admin/routes.lua @@ -1,10 +1,11 @@ local core = require("apisix.core") local schema_plugin = require("apisix.admin.plugins").check_schema local tostring = tostring +local type = type local _M = { - version = 0.1, + version = 0.2, } @@ -146,4 +147,79 @@ function _M.delete(id) end +function _M.patch(id, conf, sub_path) + if not id then + return 400, {error_msg = "missing route 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 = "/routes" + if id then + key = key .. "/" .. id + end + + local res_old, err = core.etcd.get(key) + if not res_old then + core.log.error("failed to delete route[", 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 id, err = check_conf(id, node_value, true) + if not id 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 route[", key, "]: ", err) + return 500, {error_msg = err} + end + + return res.status, res.body +end + + return _M diff --git a/t/admin/routes.t b/t/admin/routes.t index b0e198b4fb3a..da5f6c2df9fa 100644 --- a/t/admin/routes.t +++ b/t/admin/routes.t @@ -16,17 +16,17 @@ __DATA__ content_by_lua_block { local t = require("lib.test_admin").test local code, body = t('/apisix/admin/routes/1', - ngx.HTTP_PUT, - [[{ - "methods": ["GET"], - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" + ngx.HTTP_PUT, + [[{ + "methods": ["GET"], + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 }, - "desc": "new route", - "uri": "/index.html" + "type": "roundrobin" + }, + "desc": "new route", + "uri": "/index.html" }]], [[{ "node": { @@ -970,3 +970,121 @@ GET /t passed --- no_error_log [error] + + + +=== TEST 28: patch route(new methods) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1/methods', + ngx.HTTP_PATCH, + '["GET"]', + [[{ + "node": { + "value": { + "methods": [ + "GET" + ] + }, + "key": "/apisix/routes/1" + }, + "action": "set" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 29: patch route(new uri) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1/uri', + ngx.HTTP_PATCH, + '"/patch_test"', + [[{ + "node": { + "value": { + "uri": "/patch_test" + }, + "key": "/apisix/routes/1" + }, + "action": "set" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 30: patch route(whole) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1/', + ngx.HTTP_PATCH, + [[{ + "methods": ["GET"], + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" + }, + "desc": "new route", + "uri": "/index.html" + }]], + [[{ + "node": { + "value": { + "methods": [ + "GET" + ], + "uri": "/index.html", + "desc": "new route", + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" + } + }, + "key": "/apisix/routes/1" + }, + "action": "set" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error]