diff --git a/DEVELOPER.md b/DEVELOPER.md index e797f934faa9..b847a0f00739 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -473,7 +473,7 @@ The [EmmyLuaDebugger](https://github.com/EmmyLua/EmmyLuaDebugger) is a standalon that runs on the same machine as Kong Gateway and that mediates between the IDE's debugger and the Lua code running in Kong Gateway. It can be downloaded from [GitHub](https://github.com/EmmyLua/EmmyLuaDebugger/releases). The release -ZIP file contains a single share library named emmy_core.so (Linux) or emmy_core.dylib (macOS). +ZIP file contains a single shared library named emmy_core.so (Linux) or emmy_core.dylib (macOS). Place this file in a directory that is convenient for you and remember the path. Depending on your Linux version, you may need to compile @@ -486,7 +486,7 @@ recent version of GLIBC to be present. To enable the EmmyLua debugger, the `KONG_EMMY_DEBUGGER` environment variable must be set to the absolute path of the debugger shared library file when Kong Gateway is started. It is also advisable to start Kong Gateway with only one worker process, as debugging multiple worker -processes is not supported. For example: +processes requires special care. For example: ```shell KONG_EMMY_DEBUGGER=/path/to/emmy_core.so KONG_NGINX_WORKER_PROCESSES=1 kong start @@ -515,6 +515,33 @@ a breakpoint in the global `access` function that is defined `runloop/handler.lu a proxy request to the Gateway. The debugger should stop at the breakpoint and you can inspect the variables in the request context. +### Debugging `busted` tests + +To debug `busted` tests, you can set the `BUSTED_EMMY_DEBUGGER` environment variable to the path +to the EmmyLua debugger shared library. When debugging is enabled, `busted` will always wait for +the IDE to connect during startup. + +### Debugging environment variables + +The following environment variables can be set to control the behavior of the EmmyLua debugger +integration: + +- `KONG_EMMY_DEBUGGER`: The path to the EmmyLua debugger shared library. +- `KONG_EMMY_DEBUGGER_HOST`: The IP address that the EmmyLua debugger will listen on. The default + is `localhost`. +- `KONG_EMMY_DEBUGGER_PORT`: The port that the EmmyLua debugger will listen on. The default is + `9966`. +- `KONG_EMMY_DEBUGGER_WAIT`: If set, Kong Gateway will wait for the debugger to connect + before starting continuing to start. +- `KONG_EMMY_DEBUGGER_SOURCE_PATH`: The path to the source code that the EmmyLua debugger will + use to resolve source code locations. The default is the current working directory. +- `KONG_EMMY_DEBUGGER_MULTI_WORKER`: If set, a debugger will be started for each worker process, using + incrementing port numbers starting at `KONG_EMMY_DEBUGGER_PORT`. The default is to start + only one debugger for worker zero. + +To control debugger behavior while running `busted` tests, a similar set of environment variables +prefixed with `BUSTED_` instead of `KONG_` can be used. + ## What's next - Refer to the [Kong Gateway Docs](https://docs.konghq.com/gateway/) for more information. diff --git a/bin/busted b/bin/busted index 348eff4e1584..021e13a79241 100755 --- a/bin/busted +++ b/bin/busted @@ -8,6 +8,8 @@ local pl_file = require("pl.file") local tools_system = require("kong.tools.system") +local emmy_debugger = require("kong.tools.emmy_debugger") + local cert_path do local busted_cert_file = pl_path.tmpname() local busted_cert_content = pl_file.read("spec/fixtures/kong_spec.crt") @@ -72,6 +74,16 @@ end pcall(require, "luarocks.loader") +if os.getenv("BUSTED_EMMY_DEBUGGER") then + emmy_debugger.init({ + debugger = os.getenv("BUSTED_EMMY_DEBUGGER"), + host = os.getenv("BUSTED_EMMY_DEBUGGER_HOST"), + port = os.getenv("BUSTED_EMMY_DEBUGGER_PORT"), + wait = true, + source_path = os.getenv("BUSTED_EMMY_DEBUGGER_SOURCE_PATH"), + }) +end + require("kong.globalpatches")({ cli = true, rbusted = true @@ -88,4 +100,5 @@ _G.require = require "spec.require".require -- Busted command-line runner require 'busted.runner'({ standalone = false }) + -- vim: set ft=lua ts=2 sw=2 sts=2 et : diff --git a/kong/tools/emmy_debugger.lua b/kong/tools/emmy_debugger.lua index 9053a041f16c..25793ae85591 100644 --- a/kong/tools/emmy_debugger.lua +++ b/kong/tools/emmy_debugger.lua @@ -1,11 +1,17 @@ local pl_path = require "pl.path" local utils = require "kong.tools.utils" -local debugger = os.getenv("KONG_EMMY_DEBUGGER") -local emmy_debugger_host = os.getenv("KONG_EMMY_DEBUGGER_HOST") or "localhost" -local emmy_debugger_port = os.getenv("KONG_EMMY_DEBUGGER_PORT") or 9966 -local emmy_debugger_wait = os.getenv("KONG_EMMY_DEBUGGER_WAIT") -local emmy_debugger_source_path = utils.split(os.getenv("KONG_EMMY_DEBUGGER_SOURCE_PATH") or "", ":") +local env_config = { + debugger = os.getenv("KONG_EMMY_DEBUGGER"), + host = os.getenv("KONG_EMMY_DEBUGGER_HOST"), + port = os.getenv("KONG_EMMY_DEBUGGER_PORT"), + wait = os.getenv("KONG_EMMY_DEBUGGER_WAIT"), + source_path = os.getenv("KONG_EMMY_DEBUGGER_SOURCE_PATH"), + multi_worker = os.getenv("KONG_EMMY_DEBUGGER_MULTI_WORKER"), +} + +local source_path +local env_prefix local function find_source(path) if pl_path.exists(path) then @@ -17,60 +23,80 @@ local function find_source(path) return path end - for _, source_path in ipairs(emmy_debugger_source_path) do - local full_path = pl_path.join(source_path, path) + if path:match("^jsonschema:") then + -- code is executing from jsonschema, don't attempt to map + return path + end + + for _, p in ipairs(source_path) do + local full_path = pl_path.join(p, path) if pl_path.exists(full_path) then return full_path end end - ngx.log(ngx.ERR, "source file " .. path .. " not found in KONG_EMMY_DEBUGGER_SOURCE_PATH") + ngx.log(ngx.ERR, "source file " .. path .. " not found in " .. env_prefix .. "_EMMY_DEBUGGER_SOURCE_PATH") return path end -local function init() +local function load_debugger(path) + _G.emmy = { + fixPath = find_source + } + + local ext = pl_path.extension(path) + local name = pl_path.basename(path):sub(1, -#ext - 1) + + local save_cpath = package.cpath + package.cpath = pl_path.dirname(path) .. '/?' .. ext + local dbg = require(name) + package.cpath = save_cpath + return dbg +end + +local function init(config_) + local config = config_ or {} + local debugger = config.debugger or env_config.debugger + local host = config.host or env_config.host or "localhost" + local port = config.port or env_config.port or 9966 + local wait = config.wait or env_config.wait + local multi_worker = env_config.multi_worker or env_config.multi_worker + + env_prefix = config.env_prefix or "KONG" + source_path = utils.split(config.source_path or env_config.source_path or "", ":") + if not debugger then return end if not pl_path.isabs(debugger) then - ngx.log(ngx.ERR, "KONG_EMMY_DEBUGGER (" .. debugger .. ") must be an absolute path") + ngx.log(ngx.ERR, env_prefix .. "_EMMY_DEBUGGER (" .. debugger .. ") must be an absolute path") return end if not pl_path.exists(debugger) then - ngx.log(ngx.ERR, "KONG_EMMY_DEBUGGER (" .. debugger .. ") file not found") + ngx.log(ngx.ERR, env_prefix .. "_EMMY_DEBUGGER (" .. debugger .. ") file not found") return end local ext = pl_path.extension(debugger) if ext ~= ".so" and ext ~= ".dylib" then - ngx.log(ngx.ERR, "KONG_EMMY_DEBUGGER (" .. debugger .. ") must be a .so (Linux) or .dylib (macOS) file") + ngx.log(ngx.ERR, env_prefix .. "_EMMY_DEBUGGER (" .. debugger .. ") must be a .so (Linux) or .dylib (macOS) file") return end - if ngx.worker.id() ~= 0 then - ngx.log(ngx.ERR, "KONG_EMMY_DEBUGGER is only supported in the first worker process, suggest setting KONG_NGINX_WORKER_PROCESSES to 1") + if ngx.worker.id() > 0 and not multi_worker then + ngx.log(ngx.ERR, env_prefix .. "_EMMY_DEBUGGER is only supported in the first worker process, suggest setting KONG_NGINX_WORKER_PROCESSES to 1") return end ngx.log(ngx.NOTICE, "loading EmmyLua debugger " .. debugger) ngx.log(ngx.WARN, "The EmmyLua integration for Kong is a feature solely for your convenience during development. Kong assumes no liability as a result of using the integration and does not endorse it’s usage. Issues related to usage of EmmyLua integration should be directed to the respective project instead.") - _G.emmy = { - fixPath = find_source - } - - local name = pl_path.basename(debugger):sub(1, -#ext - 1) - - local save_cpath = package.cpath - package.cpath = pl_path.dirname(debugger) .. '/?' .. ext - local dbg = require(name) - package.cpath = save_cpath - - dbg.tcpListen(emmy_debugger_host, emmy_debugger_port) + local dbg = load_debugger(debugger) + dbg.tcpListen(host, port + (ngx.worker.id() or 0)) - ngx.log(ngx.NOTICE, "EmmyLua debugger loaded, listening on port ", emmy_debugger_port) + ngx.log(ngx.NOTICE, "EmmyLua debugger loaded, listening on port ", port) - if emmy_debugger_wait then + if wait then -- Wait for IDE connection ngx.log(ngx.NOTICE, "waiting for IDE to connect") dbg.waitIDE() @@ -79,5 +105,6 @@ local function init() end return { - init = init + init = init, + load_debugger = load_debugger }