127 lines
4.2 KiB
Lua
127 lines
4.2 KiB
Lua
--- This plugin serves as a bridge for debugging RPC calls, allowing to incercept and manipulate the calls
|
|
-- debugging is supported by a set of RPC calls,
|
|
-- CP side:
|
|
-- 1. kong.rpc.debug.register: registers self as the debugger of the CP
|
|
-- params: nil
|
|
-- returns: true
|
|
-- 2. kong.rpc.debug.call: let CP to call a method on a node
|
|
-- returns: { result = "result", error = "error" }
|
|
-- params: { node_id = "node_id", method = "method", args = { ... } }
|
|
-- 3. kong.rpc.debug.lua_code: let CP to execute lua code on a node
|
|
-- params: "lua code"
|
|
-- returns: the return value of the lua code
|
|
--
|
|
--- debugger side:
|
|
-- 1. kong.rpc.debug.call_handler: the debugger will receive a call from the CP when a hooked API is called
|
|
-- params: { call_seq = "call_seq", method = "method", node_id = "node_id", payload = { ... } }
|
|
-- the debugger can return 2 types of responses:
|
|
-- a. { mock = true, result = "result", error = "error" }, if the API is mocked
|
|
-- b. { prehook = true/false, posthook = true/false, args = { ... }/nil }, boolean to be true if a prehook/posthook is present, and args to be the manipulated args
|
|
-- 2. kong.rpc.debug.call_handler_post: the debugger will receive a call from the CP when a posthook is called
|
|
-- params: { call_seq = "call_seq", method = "method", node_id = "node_id", payload = { result = "result", error = "error" } }
|
|
-- return: { result = "result", error = "error" }
|
|
|
|
local kong_meta = require("kong.meta")
|
|
local shallow_copy = require("kong.tools.table").shallow_copy
|
|
local debugger_prefix = "kong.rpc.debug."
|
|
|
|
local _M = {
|
|
PRIORITY = 1000,
|
|
VERSION = kong_meta.version,
|
|
}
|
|
|
|
|
|
local function hook(debugger_node_id)
|
|
local original_callbacks = shallow_copy(kong.rpc.callbacks.callbacks)
|
|
local next_call_seq = 0
|
|
for api, cb in pairs(original_callbacks) do
|
|
if api:sub(1, #debugger_prefix) == "kong.rpc.debug." then
|
|
goto skip
|
|
end
|
|
|
|
kong.log.info("hooking registering RPC proxy API: ", api)
|
|
-- re-register
|
|
kong.rpc.callbacks.callbacks[api] = nil
|
|
kong.rpc.callbacks:register(api, function(node_id, payload)
|
|
local call_seq = next_call_seq
|
|
next_call_seq = next_call_seq + 1
|
|
kong.log.info("hooked proxy API ", api, " called by node: ", node_id)
|
|
kong.log.info("forwarding to node: ", node_id)
|
|
local res, err = kong.rpc:call(debugger_node_id, "kong.rpc.debug.call_handler", { call_seq = call_seq, method = api, node_id = node_id, payload = payload })
|
|
if not res then
|
|
return nil, "Failed to call debugger(" .. debugger_node_id .. "): " .. err
|
|
end
|
|
|
|
if res.error then
|
|
return nil, res.error
|
|
end
|
|
|
|
-- no prehook/posthook, directly return mock result
|
|
if res.mock then
|
|
return res.result, res.error
|
|
end
|
|
|
|
if res.prehook then
|
|
payload = res.args
|
|
end
|
|
|
|
local call_res, call_err = cb(node_id, payload)
|
|
|
|
if res.posthook then
|
|
res, err = kong.rpc:call(debugger_node_id, "kong.rpc.debug.call_handler_post",
|
|
{ call_seq = call_seq, method = api, node_id = node_id, payload = { result = call_res, error = call_err } })
|
|
if not res then
|
|
return nil, "Failed to call debugger post hook(" .. debugger_node_id .. "): " .. err
|
|
end
|
|
|
|
call_res, call_err = res.result, res.error
|
|
end
|
|
|
|
return call_res, call_err
|
|
end)
|
|
|
|
::skip::
|
|
end
|
|
end
|
|
|
|
|
|
|
|
function _M.init_worker()
|
|
local registered
|
|
kong.rpc.callbacks:register("kong.rpc.debug.register", function(node_id, register_payload)
|
|
if registered then
|
|
return nil, "already registered: " .. registered
|
|
|
|
else
|
|
registered = node_id
|
|
end
|
|
|
|
hook(node_id)
|
|
|
|
return true
|
|
end)
|
|
|
|
kong.rpc.callbacks:register("kong.rpc.debug.call", function(node_id, payload)
|
|
if node_id ~= registered then
|
|
return nil, "not authorized"
|
|
end
|
|
|
|
local res, err = kong.rpc:call(payload.node_id, payload.method, payload.args)
|
|
return {
|
|
result = res,
|
|
error = err,
|
|
}
|
|
end)
|
|
|
|
kong.rpc.callbacks:register("kong.rpc.debug.lua_code", function(node_id, payload)
|
|
if node_id ~= registered then
|
|
return nil, "not authorized"
|
|
end
|
|
|
|
local code = assert(loadstring(payload))
|
|
return code()
|
|
end)
|
|
end
|
|
|
|
return _M
|