Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(pdk): add service.set_proxy_address function #13775

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
37 changes: 37 additions & 0 deletions kong/pdk/service.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
25 changes: 25 additions & 0 deletions t/01-pdk/09-service/00-phase_checks.t
Original file line number Diff line number Diff line change
Expand Up @@ -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, },
Expand Down Expand Up @@ -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, },
Expand Down
182 changes: 182 additions & 0 deletions t/01-pdk/09-service/06-set-proxy-address.t
Original file line number Diff line number Diff line change
@@ -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]
Loading