Files
kong--kong/spec/02-integration/04-admin_api/22-debug_spec.lua
T
2026-07-13 12:32:21 +08:00

656 lines
21 KiB
Lua

local helpers = require("spec.helpers")
local cjson = require("cjson")
local fmt = string.format
local strategies = {}
for _, strategy in helpers.each_strategy() do
table.insert(strategies, strategy)
end
table.insert(strategies, "off")
for _, strategy in pairs(strategies) do
describe("Admin API - Kong debug route with strategy #" .. strategy, function()
local https_server
lazy_setup(function()
local bp = helpers.get_db_utils(nil, {}) -- runs migrations
local mock_https_server_port = helpers.get_available_port()
local service_mockbin = assert(bp.services:insert {
name = "service-mockbin",
url = fmt("https://127.0.0.1:%s/request", mock_https_server_port)
})
assert(bp.routes:insert {
protocols = { "http" },
hosts = { "mockbin.test" },
paths = { "/" },
service = service_mockbin,
})
assert(bp.plugins:insert {
name = "datadog",
service = service_mockbin,
})
https_server = helpers.https_server.new(mock_https_server_port, nil, "https")
https_server:start()
assert(helpers.start_kong {
database = strategy,
trusted_ips = "127.0.0.1",
nginx_http_proxy_ssl_verify = "on",
-- Mocking https_server is using kong_spec key/cert pairs but the pairs does not
-- have domain defined, so ssl verify will still fail with domain mismatch
nginx_http_proxy_ssl_trusted_certificate = "../spec/fixtures/kong_spec.crt",
nginx_http_proxy_ssl_verify_depth = "5",
})
assert(helpers.start_kong{
database = strategy,
prefix = "node2",
admin_listen = "127.0.0.1:9110",
admin_gui_listen = "off",
proxy_listen = "off",
log_level = "debug",
})
if strategy ~= "off" then
assert(helpers.start_kong({
role = "control_plane",
cluster_cert = "spec/fixtures/kong_clustering.crt",
cluster_cert_key = "spec/fixtures/kong_clustering.key",
database = strategy,
db_update_frequency = 0.1,
admin_listen = "127.0.0.1:9113",
cluster_listen = "127.0.0.1:9005",
admin_gui_listen = "off",
prefix = "cp",
log_level = "debug",
}))
assert(helpers.start_kong({
role = "control_plane",
cluster_cert = "spec/fixtures/kong_clustering.crt",
cluster_cert_key = "spec/fixtures/kong_clustering.key",
database = strategy,
db_update_frequency = 0.1,
admin_listen = "127.0.0.1:9114",
cluster_listen = "127.0.0.1:9006",
admin_gui_listen = "off",
prefix = "cp2",
cluster_telemetry_listen = "localhost:9008",
log_level = "debug",
}))
end
end)
lazy_teardown(function()
https_server:shutdown()
helpers.stop_kong()
helpers.stop_kong("node2")
if strategy ~= "off" then
helpers.stop_kong("cp")
helpers.stop_kong("cp2")
end
end)
describe("/debug/{node, cluster}/log-level", function()
it("gets current log level for traditional and dbless", function()
local res = assert(helpers.admin_client():send {
method = "GET",
path = "/debug/node/log-level",
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
local message = "log level: debug"
assert(json.message == message)
end)
if strategy == "off" then
it("cannot change the log level for dbless", function()
local res = assert(helpers.admin_client():send {
method = "PUT",
path = "/debug/node/log-level/notice",
})
local body = assert.res_status(405, res)
local json = cjson.decode(body)
local message = "cannot change log level when not using a database"
assert(json.message == message)
end)
return
end
it("e2e test - check if dynamic set log level works", function()
local res = assert(helpers.admin_client():send {
method = "PUT",
path = "/debug/node/log-level/alert",
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
local message = "log level changed"
assert(json.message == message)
-- make sure we changed to alert
helpers.wait_until(function()
res = assert(helpers.admin_client():send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: alert"
return json.message == message
end, 30)
-- e2e test: we are not printing lower than alert
helpers.clean_logfile()
res = assert(helpers.proxy_client():send {
method = "GET",
path = "/",
headers = {
Host = "mockbin.test",
},
})
body = assert.res_status(502, res)
assert.matches("An invalid response was received from the upstream server", body)
assert.logfile().has.no.line([[upstream SSL certificate does not match]] ..
[[ "127.0.0.1" while SSL handshaking to upstream]], true, 2)
-- e2e test: we are not printing lower than alert
helpers.clean_logfile()
res = assert(helpers.proxy_client():send {
method = "GET",
path = "/",
headers = {
Host = "mockbin.test",
},
})
assert.res_status(502, res)
-- from timers pre-created by timer-ng (datadog plugin)
assert.logfile().has.no.line("failed to send data to", true, 2)
-- go back to default (debug)
res = assert(helpers.admin_client():send {
method = "PUT",
path = "/debug/node/log-level/debug",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level changed"
assert(json.message == message)
-- make sure we changed to debug
helpers.wait_until(function()
res = assert(helpers.admin_client():send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: debug"
return json.message == message
end, 30)
assert.logfile().has.line(fmt("log level changed to %s", ngx.DEBUG), true, 2)
-- e2e test: we are printing higher than debug
helpers.clean_logfile()
res = assert(helpers.proxy_client():send {
method = "GET",
path = "/",
headers = {
Host = "mockbin.test",
},
})
body = assert.res_status(502, res)
assert.matches("An invalid response was received from the upstream server", body)
assert.logfile().has.line([[upstream SSL certificate does not match]] ..
[[ "127.0.0.1" while SSL handshaking to upstream]], true, 2)
-- e2e test: we are printing higher than debug
helpers.clean_logfile()
res = assert(helpers.proxy_client():send {
method = "GET",
path = "/",
headers = {
Host = "mockbin.test",
},
})
assert.res_status(502, res)
-- from timers pre-created by timer-ng (datadog plugin)
assert.logfile().has.line("failed to send data to", true, 30)
end)
it("changes log level for traditional mode", function()
local res = assert(helpers.admin_client():send {
method = "PUT",
path = "/debug/node/log-level/notice",
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
local message = "log level changed"
assert(json.message == message)
-- make sure we changed to notice
helpers.wait_until(function()
res = assert(helpers.admin_client():send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: notice"
return json.message == message
end, 30)
-- go back to default (debug)
res = assert(helpers.admin_client():send {
method = "PUT",
path = "/debug/node/log-level/debug",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level changed"
assert(json.message == message)
-- make sure we changed to debug
helpers.wait_until(function()
res = assert(helpers.admin_client():send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: debug"
return json.message == message
end, 30)
end)
it("handles unknown log level for traditional mode", function()
local res = assert(helpers.admin_client():send {
method = "PUT",
path = "/debug/node/log-level/stderr",
})
local body = assert.res_status(400, res)
local json = cjson.decode(body)
local message = "unknown log level: stderr"
assert(json.message == message)
end)
it("current log level is equal to configured log level", function()
local res = assert(helpers.admin_client():send {
method = "PUT",
path = "/debug/node/log-level/debug",
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
local message = "log level is already debug"
assert(json.message == message)
end)
it("broadcasts to all traditional nodes", function()
local res = assert(helpers.admin_client():send {
method = "PUT",
path = "/debug/cluster/log-level/emerg"
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
local message = "log level changed"
assert(json.message == message)
-- make sure we changed to emerg on NODE 1
helpers.wait_until(function()
res = assert(helpers.admin_client():send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: emerg"
return json.message == message
end, 30)
-- make sure we changed to emerg on NODE 2
helpers.wait_until(function()
res = assert(helpers.admin_client(nil, 9110):send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: emerg"
return json.message == message
end, 30)
-- decrease log level to debug on both NODE 1 and NODE 2
res = assert(helpers.admin_client():send {
method = "PUT",
path = "/debug/cluster/log-level/debug"
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level changed"
assert(json.message == message)
-- make sure we changed to debug on NODE 1
helpers.wait_until(function()
res = assert(helpers.admin_client():send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: debug"
return json.message == message
end, 30)
-- make sure we changed to debug on NODE 2
helpers.wait_until(function()
res = assert(helpers.admin_client(nil, 9110):send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: debug"
return json.message == message
end, 30)
end)
it("gets current log level for CP", function()
local res = assert(helpers.admin_client(nil, 9113):send {
method = "GET",
path = "/debug/node/log-level",
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
local message = "log level: debug"
assert(json.message == message)
end)
it("changes CP log level", function()
local res = assert(helpers.admin_client(nil, 9113):send {
method = "PUT",
path = "/debug/node/log-level/notice",
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
local message = "log level changed"
assert(json.message == message)
-- make sure we changed to notice
helpers.wait_until(function()
res = assert(helpers.admin_client(nil, 9113):send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: notice"
return json.message == message
end, 30)
-- go back to default (debug)
res = assert(helpers.admin_client(nil, 9113):send {
method = "PUT",
path = "/debug/node/log-level/debug",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level changed"
assert(json.message == message)
-- make sure we changed to debug
helpers.wait_until(function()
res = assert(helpers.admin_client(nil, 9113):send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: debug"
return json.message == message
end, 30)
end)
it("broadcasts to all CP nodes", function()
local res = assert(helpers.admin_client(nil, 9113):send {
method = "PUT",
path = "/debug/cluster/control-planes-nodes/log-level/emerg"
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
local message = "log level changed"
assert(json.message == message)
-- make sure we changed to emerg on CP 1
helpers.wait_until(function()
res = assert(helpers.admin_client(nil, 9113):send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: emerg"
return json.message == message
end, 30)
-- make sure we changed to emerg on CP 2
helpers.wait_until(function()
res = assert(helpers.admin_client(nil, 9114):send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: emerg"
return json.message == message
end, 30)
-- decrease log level to debug on both CP 1 and CP 2
res = assert(helpers.admin_client(nil, 9113):send {
method = "PUT",
path = "/debug/cluster/control-planes-nodes/log-level/debug"
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level changed"
assert(json.message == message)
-- make sure we changed to debug on CP 1
helpers.wait_until(function()
res = assert(helpers.admin_client(nil, 9113):send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: debug"
return json.message == message
end, 30)
-- Wait for CP 2 to check for cluster events
helpers.wait_until(function()
-- make sure we changed to debug on CP 2
res = assert(helpers.admin_client(nil, 9114):send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: debug"
return json.message == message
end, 30)
end)
it("common cluster endpoint not accepted in hybrid mode", function()
local res = assert(helpers.admin_client(nil, 9113):send {
method = "PUT",
path = "/debug/cluster/log-level/notice"
})
assert.res_status(404, res)
end)
it("newly spawned workers can update their log levels", function()
local res = assert(helpers.admin_client():send {
method = "PUT",
path = "/debug/node/log-level/crit?timeout=10",
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
local message = "log level changed"
assert(json.message == message)
-- make sure we changed to crit
helpers.pwait_until(function()
res = assert(helpers.admin_client():send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: crit"
assert.same(message, json.message)
end, 3)
local prefix = helpers.test_conf.prefix
assert(helpers.reload_kong("reload --prefix " .. prefix))
-- Wait for new workers to spawn
helpers.pwait_until(function()
-- make sure new workers' log level is crit
res = assert(helpers.admin_client():send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: crit"
assert.same(message, json.message)
end, 7)
-- wait for the expriration of the previous log level changes
helpers.pwait_until(function()
res = assert(helpers.admin_client():send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: debug"
assert.same(message, json.message)
end, 16)
end)
it("change log_level with timeout", function()
local res = assert(helpers.admin_client():send {
method = "PUT",
path = "/debug/node/log-level/alert?timeout=5",
})
local body = assert.res_status(200, res)
local json = cjson.decode(body)
local message = "log level changed"
assert(json.message == message)
-- make sure we changed to alert
helpers.wait_until(function()
res = assert(helpers.admin_client():send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: alert"
return json.message == message
end, 3)
-- e2e test: we are not printing lower than alert
helpers.clean_logfile()
res = assert(helpers.proxy_client():send {
method = "GET",
path = "/",
headers = {
Host = "mockbin.test",
},
})
body = assert.res_status(502, res)
assert.matches("An invalid response was received from the upstream server", body)
assert.logfile().has.no.line([[upstream SSL certificate does not match]] ..
[[ "127.0.0.1" while SSL handshaking to upstream]], true, 2)
-- e2e test: we are not printing lower than alert
helpers.clean_logfile()
res = assert(helpers.proxy_client():send {
method = "GET",
path = "/",
headers = {
Host = "mockbin.test",
},
})
assert.res_status(502, res)
-- from timers pre-created by timer-ng (datadog plugin)
assert.logfile().has.no.line("failed to send data to", true, 2)
-- wait for the dynamic log level timeout
helpers.wait_until(function()
res = assert(helpers.admin_client():send {
method = "GET",
path = "/debug/node/log-level",
})
body = assert.res_status(200, res)
json = cjson.decode(body)
message = "log level: debug"
return json.message == message
end, 6)
-- e2e test: we are printing higher than debug
helpers.clean_logfile()
res = assert(helpers.proxy_client():send {
method = "GET",
path = "/",
headers = {
Host = "mockbin.test",
},
})
body = assert.res_status(502, res)
assert.matches("An invalid response was received from the upstream server", body)
assert.logfile().has.line([[upstream SSL certificate does not match]] ..
[[ "127.0.0.1" while SSL handshaking to upstream]], true, 2)
-- e2e test: we are printing higher than debug
helpers.clean_logfile()
res = assert(helpers.proxy_client():send {
method = "GET",
path = "/",
headers = {
Host = "mockbin.test",
},
})
assert.res_status(502, res)
-- from timers pre-created by timer-ng (datadog plugin)
assert.logfile().has.line("failed to send data to", true, 30)
end)
it("should refuse invalid timeout", function()
local res = assert(helpers.admin_client():send {
method = "PUT",
path = "/debug/node/log-level/notice?timeout=-1",
})
local body = assert.res_status(400, res)
local json = cjson.decode(body)
assert.same("timeout must be greater than or equal to 0", json.message)
end)
end)
end)
end