diff --git a/kong/init.lua b/kong/init.lua index 6708989a6a6b..97e9798a9fdd 100644 --- a/kong/init.lua +++ b/kong/init.lua @@ -93,6 +93,7 @@ local constants = require "kong.constants" local get_ctx_table = require("resty.core.ctx").get_ctx_table local admin_gui = require "kong.admin_gui" local wasm = require "kong.runloop.wasm" +local reports = require "kong.reports" local kong = kong @@ -221,6 +222,7 @@ do "events:requests:ws", "events:requests:wss", "events:requests:go_plugins", + "events:km:visit", "events:streams", "events:streams:tcp", "events:streams:tls", @@ -1695,6 +1697,12 @@ function Kong.admin_gui_kconfig_content() end end +function Kong.admin_gui_log() + if kong.configuration.anonymous_reports then + reports.admin_gui_log(ngx.ctx) + end +end + function Kong.status_content() return serve_content("kong.status") end diff --git a/kong/reports.lua b/kong/reports.lua index 58a3ac3e55a9..302fb0054a19 100644 --- a/kong/reports.lua +++ b/kong/reports.lua @@ -45,6 +45,9 @@ local TLS_STREAM_COUNT_KEY = "events:streams:tls" local UDP_STREAM_COUNT_KEY = "events:streams:udp" +local KM_VISIT_COUNT_KEY = "events:km:visit" + + local GO_PLUGINS_REQUEST_COUNT_KEY = "events:requests:go_plugins" @@ -336,6 +339,7 @@ local function send_ping(host, port) _ping_infos.grpcs_reqs = get_counter(GRPCS_REQUEST_COUNT_KEY) _ping_infos.ws_reqs = get_counter(WS_REQUEST_COUNT_KEY) _ping_infos.wss_reqs = get_counter(WSS_REQUEST_COUNT_KEY) + _ping_infos.km_visits = get_counter(KM_VISIT_COUNT_KEY) _ping_infos.go_plugin_reqs = get_counter(GO_PLUGINS_REQUEST_COUNT_KEY) _ping_infos.request_route_cache_hit_pos = get_counter(REQUEST_ROUTE_CACHE_HITS_KEY_POS) @@ -352,6 +356,7 @@ local function send_ping(host, port) reset_counter(GRPCS_REQUEST_COUNT_KEY, _ping_infos.grpcs_reqs) reset_counter(WS_REQUEST_COUNT_KEY, _ping_infos.ws_reqs) reset_counter(WSS_REQUEST_COUNT_KEY, _ping_infos.wss_reqs) + reset_counter(KM_VISIT_COUNT_KEY, _ping_infos.km_visits) reset_counter(GO_PLUGINS_REQUEST_COUNT_KEY, _ping_infos.go_plugin_reqs) reset_counter(REQUEST_ROUTE_CACHE_HITS_KEY_POS, _ping_infos.request_route_cache_hit_pos) reset_counter(REQUEST_ROUTE_CACHE_HITS_KEY_NEG, _ping_infos.request_route_cache_hit_neg) @@ -398,6 +403,7 @@ local function configure_ping(kong_conf) add_immutable_value("role", kong_conf.role) add_immutable_value("kic", kong_conf.kic) add_immutable_value("_admin", #kong_conf.admin_listeners > 0 and 1 or 0) + add_immutable_value("_admin_gui", #kong_conf.admin_gui_listeners > 0 and 1 or 0) add_immutable_value("_proxy", #kong_conf.proxy_listeners > 0 and 1 or 0) add_immutable_value("_stream", #kong_conf.stream_listeners > 0 and 1 or 0) end @@ -485,6 +491,13 @@ return { incr_counter(count_key .. ":" .. ROUTE_CACHE_HITS_KEY .. ":" .. route_match_cached) end end, + admin_gui_log = function(ctx) + if not _enabled then + return + end + + incr_counter(KM_VISIT_COUNT_KEY) + end, -- custom methods toggle = function(enable) diff --git a/kong/templates/nginx_kong_gui_include.lua b/kong/templates/nginx_kong_gui_include.lua index 922eb3f20602..28f44a5fc626 100644 --- a/kong/templates/nginx_kong_gui_include.lua +++ b/kong/templates/nginx_kong_gui_include.lua @@ -93,5 +93,9 @@ location ~* ^$(admin_gui_path_prefix)(?/.*)?$ { > end sub_filter_once off; sub_filter_types *; + + log_by_lua_block { + Kong.admin_gui_log() + } } ]] diff --git a/spec/01-unit/11-reports_spec.lua b/spec/01-unit/11-reports_spec.lua index 639b3e40f970..708e1891150e 100644 --- a/spec/01-unit/11-reports_spec.lua +++ b/spec/01-unit/11-reports_spec.lua @@ -260,6 +260,34 @@ describe("reports", function() end) end) + describe("sends '_admin_gui' for 'admin_gui_listen'", function() + it("off", function() + local conf = assert(conf_loader(nil, { + admin_gui_listen = "off", + })) + reports.configure_ping(conf) + + local thread = helpers.tcp_server(port, opts) + reports.send_ping("127.0.0.1", port) + + local _, res = assert(thread:join()) + assert.matches("_admin_gui=0", res, nil, true) + end) + + it("on", function() + local conf = assert(conf_loader(nil, { + admin_gui_listen = "127.0.0.1:8001", + })) + reports.configure_ping(conf) + + local thread = helpers.tcp_server(port, opts) + reports.send_ping("127.0.0.1", port) + + local _, res = assert(thread:join()) + assert.matches("_admin_gui=1", res, nil, true) + end) + end) + describe("sends '_proxy' for 'proxy_listen'", function() it("off", function() local conf = assert(conf_loader(nil, { diff --git a/spec/02-integration/17-admin_gui/03-reports_spec.lua b/spec/02-integration/17-admin_gui/03-reports_spec.lua new file mode 100644 index 000000000000..c575668454b8 --- /dev/null +++ b/spec/02-integration/17-admin_gui/03-reports_spec.lua @@ -0,0 +1,157 @@ +local cjson = require "cjson" +local lfs = require "lfs" +local pl_path = require "pl.path" + +local helpers = require "spec.helpers" +local constants = require "kong.constants" + +describe("anonymous reports for kong manager", function () + local reports_send_ping = function() + ngx.sleep(0.2) -- hand over the CPU so other threads can do work (processing the sent data) + local admin_client = helpers.admin_client() + local res = admin_client:post("/reports/send-ping?port=" .. constants.REPORTS.STATS_TLS_PORT) + assert.response(res).has_status(200) + admin_client:close() + end + + local assert_report = function (value) + local reports_server = helpers.tcp_server(constants.REPORTS.STATS_TLS_PORT, {tls=true}) + reports_send_ping() + local _, reports_data = assert(reports_server:join()) + reports_data = cjson.encode(reports_data) + + assert.match(value, reports_data) + end + + local prepare_gui_dir = function () + local err, gui_dir_path + gui_dir_path = pl_path.join(helpers.test_conf.prefix, "gui") + os.execute("rm -rf " .. gui_dir_path) + err = select(2, lfs.mkdir(gui_dir_path)) + assert.is_nil(err) + return gui_dir_path + end + + local create_gui_file = function (path) + local fd = assert(io.open(path, "w")) + assert.is_not_nil(fd) + assert(fd:write("TEST")) + assert(fd:close()) + end + + local dns_hostsfile + + lazy_setup(function () + dns_hostsfile = assert(os.tmpname() .. ".hosts") + local fd = assert(io.open(dns_hostsfile, "w")) + assert(fd:write("127.0.0.1 " .. constants.REPORTS.ADDRESS)) + assert(fd:close()) + + local bp = assert(helpers.get_db_utils(nil, {}, { "reports-api" })) + + bp.plugins:insert({ + name = "reports-api", + config = {} + }) + end) + + lazy_teardown(function () + os.remove(dns_hostsfile) + end) + + describe("availability status", function () + it("should be correct when admin_gui_listen is set", function () + assert(helpers.start_kong({ + admin_gui_listen = "127.0.0.1:9012", + anonymous_reports = true, + plugins = "bundled,reports-api", + dns_hostsfile = dns_hostsfile, + })) + + finally(function() + helpers.stop_kong() + end) + + assert_report("_admin_gui=1") + end) + + it("should be correct when admin_gui_listen is off", function () + assert(helpers.start_kong({ + admin_gui_listen = "off", + anonymous_reports = true, + plugins = "bundled,reports-api", + dns_hostsfile = dns_hostsfile, + })) + + finally(function() + helpers.stop_kong() + end) + + assert_report("_admin_gui=0") + end) + end) + + describe("visit", function() + lazy_setup(function() + assert(helpers.start_kong({ + admin_gui_listen = "127.0.0.1:9012", + anonymous_reports = true, + plugins = "bundled,reports-api", + dns_hostsfile = dns_hostsfile, + })) + + local gui_dir_path = prepare_gui_dir() + create_gui_file(pl_path.join(gui_dir_path, "index.html")) + create_gui_file(pl_path.join(gui_dir_path, "robots.txt")) + create_gui_file(pl_path.join(gui_dir_path, "favicon.ico")) + create_gui_file(pl_path.join(gui_dir_path, "test.js")) + create_gui_file(pl_path.join(gui_dir_path, "test.css")) + create_gui_file(pl_path.join(gui_dir_path, "test.png")) + end) + + lazy_teardown(function() + os.remove(dns_hostsfile) + + helpers.stop_kong() + end) + + it("should have value 0 when no kong mananger visit occurs", function () + assert_report("km_visits=0") + end) + + it("should increase counter by 1 for each kong mananger visit", function () + local admin_gui_client = helpers.admin_gui_client(nil, 9012) + assert.res_status(200, admin_gui_client:send({ method = "GET", path = "/" })) + assert.res_status(200, admin_gui_client:send({ method = "GET", path = "/services" })) + admin_gui_client:close() + assert_report("km_visits=2") + end) + + it("should reset the counter after report", function () + local admin_gui_client = helpers.admin_gui_client(nil, 9012) + assert.res_status(200, admin_gui_client:send({ method = "GET", path = "/" })) + admin_gui_client:close() + assert_report("km_visits=1") + + admin_gui_client = helpers.admin_gui_client(nil, 9012) + assert.res_status(200, admin_gui_client:send({ method = "GET", path = "/" })) + assert.res_status(200, admin_gui_client:send({ method = "GET", path = "/" })) + assert_report("km_visits=2") + admin_gui_client:close() + end) + + it("should not increase the counter for GUI assets", function () + local admin_gui_client = helpers.admin_gui_client(nil, 9012) + assert.res_status(200, admin_gui_client:send({ method = "GET", path = "/kconfig.js" })) + assert.res_status(200, admin_gui_client:send({ method = "GET", path = "/robots.txt" })) + assert.res_status(200, admin_gui_client:send({ method = "GET", path = "/favicon.ico" })) + assert.res_status(200, admin_gui_client:send({ method = "GET", path = "/test.js" })) + assert.res_status(200, admin_gui_client:send({ method = "GET", path = "/test.css" })) + assert.res_status(200, admin_gui_client:send({ method = "GET", path = "/test.png" })) + assert.res_status(404, admin_gui_client:send({ method = "GET", path = "/not-exist.png" })) + admin_gui_client:close() + + assert_report("km_visits=0") + end) + end) +end) diff --git a/spec/fixtures/custom_nginx.template b/spec/fixtures/custom_nginx.template index 15f4261363d4..d2a302b77038 100644 --- a/spec/fixtures/custom_nginx.template +++ b/spec/fixtures/custom_nginx.template @@ -574,6 +574,10 @@ server { > end sub_filter_once off; sub_filter_types *; + + log_by_lua_block { + Kong.admin_gui_log() + } } } > end -- of (role == "control_plane" or role == "traditional") and #admin_listen > 0 and #admin_gui_listeners > 0 diff --git a/spec/fixtures/default_nginx.template b/spec/fixtures/default_nginx.template index 355da1589d6d..d17378f3b671 100644 --- a/spec/fixtures/default_nginx.template +++ b/spec/fixtures/default_nginx.template @@ -530,6 +530,10 @@ server { > end sub_filter_once off; sub_filter_types *; + + log_by_lua_block { + Kong.admin_gui_log() + } } } > end -- of (role == "control_plane" or role == "traditional") and #admin_listen > 0 and #admin_gui_listeners > 0