From 4b5b3a58fa6572fdc6eda145915ed45dda415ad7 Mon Sep 17 00:00:00 2001 From: Vinicius Mignot Date: Fri, 18 Oct 2024 11:14:50 -0300 Subject: [PATCH] feat(pdk): add service.set_proxy_address function --- ...eat-add-set_proxy_address-pdk-function.yml | 5 + kong/pdk/service.lua | 37 ++++ t/01-pdk/09-service/00-phase_checks.t | 25 +++ t/01-pdk/09-service/06-set-proxy-address.t | 182 ++++++++++++++++++ 4 files changed, 249 insertions(+) create mode 100644 changelog/unreleased/kong/feat-add-set_proxy_address-pdk-function.yml create mode 100644 t/01-pdk/09-service/06-set-proxy-address.t diff --git a/changelog/unreleased/kong/feat-add-set_proxy_address-pdk-function.yml b/changelog/unreleased/kong/feat-add-set_proxy_address-pdk-function.yml new file mode 100644 index 000000000000..9c9db76ef01c --- /dev/null +++ b/changelog/unreleased/kong/feat-add-set_proxy_address-pdk-function.yml @@ -0,0 +1,5 @@ +message: | + Added `service.set_proxy_address` PDK function to set IP and port to proxy the + request and bypass the load-balancer. +type: feature +scope: PDK diff --git a/kong/pdk/service.lua b/kong/pdk/service.lua index c936cfc14cb8..45f500afd070 100644 --- a/kong/pdk/service.lua +++ b/kong/pdk/service.lua @@ -18,6 +18,7 @@ local floor = math.floor local ngx = ngx local check_phase = phase_checker.check +local hostname_type = require("kong.tools.ip").hostname_type local PHASES = phase_checker.phases @@ -108,6 +109,42 @@ local function new() end +--- + -- Sets the IP and port on which to connect to for proxying the request. + -- Using this method is equivalent to ask Kong to not run the load-balancing + -- phase for this request, and consider it manually overridden. + -- Load-balancing components such as retries and health-checks will also be + -- ignored for this request. Use `kong.service.set_retries` to overwrite + -- retries count. + -- + -- The `ip` argument expects the IP address of the upstream server, and the + -- `port` expects a port number. + -- + -- @function kong.service.set_proxy_address + -- @phases access + -- @tparam string ip + -- @tparam number port + -- @usage + -- kong.service.set_proxy_address("192.168.130.1", 443) + function service.set_proxy_address(ip, port) + check_phase(access_and_rewrite_and_balancer_preread) + + if type(ip) ~= "string" or hostname_type(ip) == "name" then + return error("ip must be an IPv4 or IPv6 address") + end + if type(port) ~= "number" or floor(port) ~= port then + error("port must be an integer", 2) + end + if port < 0 or port > 65535 then + error("port must be an integer between 0 and 65535: given " .. port, 2) + end + + local ctx = ngx.ctx + ctx.balancer_data.ip = ip + ctx.balancer_data.port = port + end + + -- Sets the retry callback function when the target set by `service.set_target` -- failed to connect. The callback function will be called with no argument and -- must return `host`, `port` and `err` if any. diff --git a/t/01-pdk/09-service/00-phase_checks.t b/t/01-pdk/09-service/00-phase_checks.t index c19d9a824b70..c8fa3402b24b 100644 --- a/t/01-pdk/09-service/00-phase_checks.t +++ b/t/01-pdk/09-service/00-phase_checks.t @@ -76,6 +76,18 @@ qq{ body_filter = "forced false", log = "forced false", admin_api = "forced false", + }, { + method = "set_proxy_address", + args = { "192.168.0.47", 8000 }, + init_worker = "forced false", + certificate = "pending", + rewrite = true, + access = true, + response = "forced false", + header_filter = "forced false", + body_filter = "forced false", + log = "forced false", + admin_api = "forced false", }, { method = "set_retries", args = { 3, }, @@ -263,6 +275,19 @@ qq{ log = "pending", admin_api = "forced false", preread = "pending", + }, { + method = "set_proxy_address", + args = { "192.168.0.47", 8000 }, + init_worker = "forced false", + certificate = "pending", + rewrite = true, + access = true, + response = "forced false", + header_filter = "forced false", + body_filter = "forced false", + log = "pending", + admin_api = "forced false", + preread = "pending", }, { method = "set_retries", args = { 3, }, diff --git a/t/01-pdk/09-service/06-set-proxy-address.t b/t/01-pdk/09-service/06-set-proxy-address.t new file mode 100644 index 000000000000..dcf997b08a55 --- /dev/null +++ b/t/01-pdk/09-service/06-set-proxy-address.t @@ -0,0 +1,182 @@ +use strict; +use warnings FATAL => 'all'; +use Test::Nginx::Socket::Lua; +do "./t/Util.pm"; + +plan tests => repeat_each() * (blocks() * 3); + +run_tests(); + +__DATA__ + +=== TEST 1: service.set_proxy_address() errors if ip is not a string +--- http_config eval: $t::Util::HttpConfig +--- config + location = /t { + content_by_lua_block { + local PDK = require "kong.pdk" + local pdk = PDK.new() + + local pok, err = pcall(pdk.service.set_proxy_address, 127001, 123) + ngx.say(err) + } + } +--- request +GET /t +--- response_body +ip must be an IPv4 or IPv6 address +--- no_error_log +[error] + + + +=== TEST 2: service.set_proxy_address() sets ngx.ctx.balancer_data.ip and port +--- http_config eval: $t::Util::HttpConfig +--- config + location = /t { + + set $upstream_host ''; + + content_by_lua_block { + local PDK = require "kong.pdk" + local pdk = PDK.new() + + ngx.ctx.balancer_data = { + host = "foo.xyz" + } + + local ok = pdk.service.set_proxy_address("10.0.0.11", 123) + + ngx.say(tostring(ok)) + ngx.say("host: ", ngx.ctx.balancer_data.host) + ngx.say("ip: ", ngx.ctx.balancer_data.ip) + ngx.say("port: ", ngx.ctx.balancer_data.port) + } + } +--- request +GET /t +--- response_body +nil +host: foo.xyz +ip: 10.0.0.11 +port: 123 +--- no_error_log +[error] + + + +=== TEST 3: service.set_proxy_address() errors if port is not a number +--- http_config eval: $t::Util::HttpConfig +--- config + location = /t { + content_by_lua_block { + local PDK = require "kong.pdk" + local pdk = PDK.new() + + local pok, err = pcall(pdk.service.set_proxy_address, "1.2.3.4", "foo") + ngx.say(err) + } + } +--- request +GET /t +--- response_body +port must be an integer +--- no_error_log +[error] + + + +=== TEST 4: service.set_proxy_address() errors if port is not an integer +--- http_config eval: $t::Util::HttpConfig +--- config + location = /t { + content_by_lua_block { + local PDK = require "kong.pdk" + local pdk = PDK.new() + + local pok, err = pcall(pdk.service.set_proxy_address, "5.6.7.8", 123.4) + + ngx.say(err) + } + } +--- request +GET /t +--- response_body +port must be an integer +--- no_error_log +[error] + + + +=== TEST 5: service.set_proxy_address() errors if port is out of range +--- http_config eval: $t::Util::HttpConfig +--- config + location = /t { + content_by_lua_block { + local PDK = require "kong.pdk" + local pdk = PDK.new() + + local pok, err = pcall(pdk.service.set_proxy_address, "9.10.11.12", -1) + ngx.say(err) + local pok, err = pcall(pdk.service.set_proxy_address, "13.14.15.16", 70000) + ngx.say(err) + } + } +--- request +GET /t +--- response_body +port must be an integer between 0 and 65535: given -1 +port must be an integer between 0 and 65535: given 70000 +--- no_error_log +[error] + + + +=== TEST 6: service.set_proxy_address() sets the balancer port +--- http_config eval: $t::Util::HttpConfig +--- config + location = /t { + + set $upstream_host ''; + + content_by_lua_block { + local PDK = require "kong.pdk" + local pdk = PDK.new() + + ngx.ctx.balancer_data = { + port = 8000 + } + + local ok = pdk.service.set_proxy_address("17.18.19.20", 1234) + + ngx.say(tostring(ok)) + ngx.say("port: ", ngx.ctx.balancer_data.port) + } + } +--- request +GET /t +--- response_body +nil +port: 1234 +--- no_error_log +[error] + + +=== TEST 7: service.set_proxy_address() errors if IP is not an IPv4 or IPv6 address +--- http_config eval: $t::Util::HttpConfig +--- config + location = /t { + content_by_lua_block { + local PDK = require "kong.pdk" + local pdk = PDK.new() + + local pok, err = pcall(pdk.service.set_proxy_address, "konghq.test", 443) + ngx.say(err) + } + } +--- request +GET /t +--- response_body +ip must be an IPv4 or IPv6 address +--- no_error_log +[error]