From 90b3c13e373f56c24d8de81bbe67ebecfde14069 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Mon, 28 Apr 2025 18:49:54 +0300 Subject: [PATCH 01/27] adds support of variables into upstream module --- apisix/core/utils.lua | 30 ++++++++++++++++++++++++++++++ apisix/upstream.lua | 8 +++++++- t/core/utils.t | 30 ++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/apisix/core/utils.lua b/apisix/core/utils.lua index cfea756542bd..805d3425d68e 100644 --- a/apisix/core/utils.lua +++ b/apisix/core/utils.lua @@ -461,5 +461,35 @@ function _M.check_tls_bool(fields, conf, plugin_name) end end +--- +-- Checks if a string is an nginx variable. +-- An nginx variable starts with the '$' character. +-- +-- @function core.utils.is_nginx_variable +-- @tparam string str The string to check +-- @treturn boolean true if the string starts with '$', false otherwise +-- @usage +-- local utils = require("apisix.core.utils") +-- +-- -- Usage examples: +-- local is_var = utils.is_nginx_variable("$host") -- true +-- local is_var = utils.is_nginx_variable("host") -- false +-- local is_var = utils.is_nginx_variable("${host}") -- true +-- local is_var = utils.is_nginx_variable("\\$host") -- false +-- +-- -- Usage in APISIX context: +-- if utils.is_nginx_variable(up_conf.service_name) then +-- -- Handle as nginx variable +-- else +-- -- Handle as regular service name +-- end +function _M.is_nginx_variable(str) + if not str or type(str) ~= "string" then + return false + end + + -- Check if the string starts with '$' and it's not escaped + return str:sub(1, 1) == "$" and not (str:sub(1, 2) == "\\$") +end return _M diff --git a/apisix/upstream.lua b/apisix/upstream.lua index f51c1712fd99..29286c31450d 100644 --- a/apisix/upstream.lua +++ b/apisix/upstream.lua @@ -291,7 +291,13 @@ function _M.set_by_route(route, api_ctx) return 503, err end - local new_nodes, err = dis.nodes(up_conf.service_name, up_conf.discovery_args) + if core.utils.is_nginx_variable(up_conf.service_name) then + local service_name = core.utils.resolve_nginx_variable(up_conf.service_name) + else + local service_name = up_conf.service_name + end + + local new_nodes, err = dis.nodes(service_name, up_conf.discovery_args) if not new_nodes then return HTTP_CODE_UPSTREAM_UNAVAILABLE, "no valid upstream node: " .. (err or "nil") end diff --git a/t/core/utils.t b/t/core/utils.t index 9faa545e17e1..c011afa34d25 100644 --- a/t/core/utils.t +++ b/t/core/utils.t @@ -393,3 +393,33 @@ res:nil res:5 res:12 res:7 + + + +=== TEST X: is_nginx_variable +--- config + location /t { + content_by_lua_block { + local is_nginx_variable = require("apisix.core.utils").is_nginx_variable + + local cases = { + {str = "$host", expected = true}, + {str = "host", expected = false}, + {str = "${host}", expected = true}, + {str = "\\$host", expected = false}, + {str = nil, expected = false}, + {str = 123, expected = false}, + {str = "", expected = false}, + } + + for _, case in ipairs(cases) do + local res = is_nginx_variable(case.str) + assert(res == case.expected, + string.format("case %s failed: got %s, expected %s", + case.str, res, case.expected)) + end + } + } +--- request +GET /t +--- response_body From 9def4fb5aef3703acc641f9a96192c970aa14e58 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Tue, 29 Apr 2025 17:15:04 +0300 Subject: [PATCH 02/27] move local upper from if --- apisix/upstream.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apisix/upstream.lua b/apisix/upstream.lua index 29286c31450d..27aaed2125bb 100644 --- a/apisix/upstream.lua +++ b/apisix/upstream.lua @@ -291,10 +291,9 @@ function _M.set_by_route(route, api_ctx) return 503, err end + local service_name = up_conf.service_name if core.utils.is_nginx_variable(up_conf.service_name) then - local service_name = core.utils.resolve_nginx_variable(up_conf.service_name) - else - local service_name = up_conf.service_name + service_name = core.utils.resolve_var(up_conf.service_name, api_ctx.var) end local new_nodes, err = dis.nodes(service_name, up_conf.discovery_args) From cfea8d886a8555ceee0d48ec3311bd743bbdc319 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Thu, 15 May 2025 15:20:18 +0300 Subject: [PATCH 03/27] linter fixes --- apisix/core/utils.lua | 2 +- apisix/upstream.lua | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apisix/core/utils.lua b/apisix/core/utils.lua index 805d3425d68e..3c5698d2d96f 100644 --- a/apisix/core/utils.lua +++ b/apisix/core/utils.lua @@ -489,7 +489,7 @@ function _M.is_nginx_variable(str) end -- Check if the string starts with '$' and it's not escaped - return str:sub(1, 1) == "$" and not (str:sub(1, 2) == "\\$") + return str:sub(1, 1) == "$" and (str:sub(1, 2) ~= "\\$") end return _M diff --git a/apisix/upstream.lua b/apisix/upstream.lua index 27aaed2125bb..1f809e489bcc 100644 --- a/apisix/upstream.lua +++ b/apisix/upstream.lua @@ -303,7 +303,8 @@ function _M.set_by_route(route, api_ctx) local same = upstream_util.compare_upstream_node(up_conf, new_nodes) if not same then - local pass, err = core.schema.check(core.schema.discovery_nodes, new_nodes) + local pass + pass, err = core.schema.check(core.schema.discovery_nodes, new_nodes) if not pass then return HTTP_CODE_UPSTREAM_UNAVAILABLE, "invalid nodes format: " .. err end From ac76dd7eb54ba6dba015dad8124ec6cadce0c395 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Tue, 27 May 2025 13:19:02 +0300 Subject: [PATCH 04/27] bump build --- t/core/utils.t | 1 - 1 file changed, 1 deletion(-) diff --git a/t/core/utils.t b/t/core/utils.t index c011afa34d25..88a291eb51c6 100644 --- a/t/core/utils.t +++ b/t/core/utils.t @@ -395,7 +395,6 @@ res:12 res:7 - === TEST X: is_nginx_variable --- config location /t { From 2160c51f49f8a154f383213217aa8b61f3b3e1ca Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Wed, 28 May 2025 11:00:30 +0300 Subject: [PATCH 05/27] new line --- t/core/utils.t | 1 + 1 file changed, 1 insertion(+) diff --git a/t/core/utils.t b/t/core/utils.t index 88a291eb51c6..c011afa34d25 100644 --- a/t/core/utils.t +++ b/t/core/utils.t @@ -395,6 +395,7 @@ res:12 res:7 + === TEST X: is_nginx_variable --- config location /t { From 5a907fae92bff36ad805d9dacf3dd40d3a3b2615 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Mon, 2 Jun 2025 15:16:43 +0300 Subject: [PATCH 06/27] removes is_nginx_variable * use resolve_var directly * adds resolve_var documentation * removes is_nginx_variable function and tests --- apisix/core/utils.lua | 49 ++++++++++++++++--------------------------- apisix/upstream.lua | 6 +++--- t/core/utils.t | 28 ------------------------- 3 files changed, 21 insertions(+), 62 deletions(-) diff --git a/apisix/core/utils.lua b/apisix/core/utils.lua index 3c5698d2d96f..0373fcad051b 100644 --- a/apisix/core/utils.lua +++ b/apisix/core/utils.lua @@ -337,6 +337,24 @@ do end end -- Resolve ngx.var in the given string +-- @function core.utils.resolve_var +-- @tparam string tpl The template string to resolve variables in +-- @tparam table ctx The context table containing variables +-- @tparam function escaper Optional function to escape resolved values +-- @treturn string The resolved string +-- @treturn string|nil Error message if any +-- @treturn number Number of variables replaced +-- @usage +-- local utils = require("apisix.core.utils") +-- +-- -- Usage examples: +-- local res = utils.resolve_var("$host", ctx.var) -- "example.com" +-- local res = utils.resolve_var("${host}", ctx.var) -- "example.com" +-- local res = utils.resolve_var("TMP_${VAR1}_${VAR2}", ctx.var) -- "TMP_value1_value2" +-- local res = utils.resolve_var("\\$host", ctx.var) -- "$host" +-- +-- -- Usage in APISIX context: +-- local service_name = utils.resolve_var(up_conf.service_name, api_ctx.var) _M.resolve_var = resolve_var @@ -461,35 +479,4 @@ function _M.check_tls_bool(fields, conf, plugin_name) end end ---- --- Checks if a string is an nginx variable. --- An nginx variable starts with the '$' character. --- --- @function core.utils.is_nginx_variable --- @tparam string str The string to check --- @treturn boolean true if the string starts with '$', false otherwise --- @usage --- local utils = require("apisix.core.utils") --- --- -- Usage examples: --- local is_var = utils.is_nginx_variable("$host") -- true --- local is_var = utils.is_nginx_variable("host") -- false --- local is_var = utils.is_nginx_variable("${host}") -- true --- local is_var = utils.is_nginx_variable("\\$host") -- false --- --- -- Usage in APISIX context: --- if utils.is_nginx_variable(up_conf.service_name) then --- -- Handle as nginx variable --- else --- -- Handle as regular service name --- end -function _M.is_nginx_variable(str) - if not str or type(str) ~= "string" then - return false - end - - -- Check if the string starts with '$' and it's not escaped - return str:sub(1, 1) == "$" and (str:sub(1, 2) ~= "\\$") -end - return _M diff --git a/apisix/upstream.lua b/apisix/upstream.lua index f971bb27c2a5..ca428a9ffbde 100644 --- a/apisix/upstream.lua +++ b/apisix/upstream.lua @@ -295,9 +295,9 @@ function _M.set_by_route(route, api_ctx) return 503, err end - local service_name = up_conf.service_name - if core.utils.is_nginx_variable(up_conf.service_name) then - service_name = core.utils.resolve_var(up_conf.service_name, api_ctx.var) + local service_name, err = core.utils.resolve_var(up_conf.service_name, api_ctx.var) + if not service_name then + return 503, "service_name is empty: " .. (err or "nil") end local new_nodes, err = dis.nodes(service_name, up_conf.discovery_args) diff --git a/t/core/utils.t b/t/core/utils.t index c011afa34d25..7c46a8caed21 100644 --- a/t/core/utils.t +++ b/t/core/utils.t @@ -395,31 +395,3 @@ res:12 res:7 - -=== TEST X: is_nginx_variable ---- config - location /t { - content_by_lua_block { - local is_nginx_variable = require("apisix.core.utils").is_nginx_variable - - local cases = { - {str = "$host", expected = true}, - {str = "host", expected = false}, - {str = "${host}", expected = true}, - {str = "\\$host", expected = false}, - {str = nil, expected = false}, - {str = 123, expected = false}, - {str = "", expected = false}, - } - - for _, case in ipairs(cases) do - local res = is_nginx_variable(case.str) - assert(res == case.expected, - string.format("case %s failed: got %s, expected %s", - case.str, res, case.expected)) - end - } - } ---- request -GET /t ---- response_body From 3a623bccd6f76b524c2bc33e8a5ff6b1e0c61e76 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Tue, 3 Jun 2025 10:39:01 +0300 Subject: [PATCH 07/27] new line --- t/core/utils.t | 1 + 1 file changed, 1 insertion(+) diff --git a/t/core/utils.t b/t/core/utils.t index 7c46a8caed21..794d1f46a129 100644 --- a/t/core/utils.t +++ b/t/core/utils.t @@ -395,3 +395,4 @@ res:12 res:7 + From 5b457df81233cca2be5521c4af663ca2f707650f Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Tue, 3 Jun 2025 14:05:31 +0300 Subject: [PATCH 08/27] up --- t/core/utils.t | 1 - 1 file changed, 1 deletion(-) diff --git a/t/core/utils.t b/t/core/utils.t index 794d1f46a129..7c46a8caed21 100644 --- a/t/core/utils.t +++ b/t/core/utils.t @@ -395,4 +395,3 @@ res:12 res:7 - From b66151772735a68c31efc690d47af85e23a06458 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Tue, 3 Jun 2025 19:17:22 +0300 Subject: [PATCH 09/27] check for empty string in service name --- apisix/upstream.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apisix/upstream.lua b/apisix/upstream.lua index 9bf7bbb3784f..2eff416dcc33 100644 --- a/apisix/upstream.lua +++ b/apisix/upstream.lua @@ -298,7 +298,7 @@ function _M.set_by_route(route, api_ctx) end local service_name, err = core.utils.resolve_var(up_conf.service_name, api_ctx.var) - if not service_name then + if not service_name or service_name == "" then return 503, "service_name is empty: " .. (err or "nil") end From 5f19d54b648e77d4ad6dcc20d6c2457d3613898c Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Wed, 4 Jun 2025 16:13:14 +0300 Subject: [PATCH 10/27] newline --- t/core/utils.t | 1 + 1 file changed, 1 insertion(+) diff --git a/t/core/utils.t b/t/core/utils.t index 7c46a8caed21..794d1f46a129 100644 --- a/t/core/utils.t +++ b/t/core/utils.t @@ -395,3 +395,4 @@ res:12 res:7 + From 7548cf8c4601b9ed2f0854794564adf7e57dbe4a Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Wed, 4 Jun 2025 16:16:07 +0300 Subject: [PATCH 11/27] use resolve_var in error message --- apisix/upstream.lua | 2 +- t/core/utils.t | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/apisix/upstream.lua b/apisix/upstream.lua index 2eff416dcc33..e2fe62e4545d 100644 --- a/apisix/upstream.lua +++ b/apisix/upstream.lua @@ -299,7 +299,7 @@ function _M.set_by_route(route, api_ctx) local service_name, err = core.utils.resolve_var(up_conf.service_name, api_ctx.var) if not service_name or service_name == "" then - return 503, "service_name is empty: " .. (err or "nil") + return 503, "resolve_var resolves to empty string: " .. (err or "nil") end local new_nodes, err = dis.nodes(service_name, up_conf.discovery_args) diff --git a/t/core/utils.t b/t/core/utils.t index 794d1f46a129..9faa545e17e1 100644 --- a/t/core/utils.t +++ b/t/core/utils.t @@ -393,6 +393,3 @@ res:nil res:5 res:12 res:7 - - - From b669e78f6bb3e05218844f46ee5026372269a1b2 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Mon, 9 Jun 2025 15:10:14 +0300 Subject: [PATCH 12/27] removes function documentation --- apisix/core/utils.lua | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/apisix/core/utils.lua b/apisix/core/utils.lua index 0373fcad051b..1fbe8d295f3e 100644 --- a/apisix/core/utils.lua +++ b/apisix/core/utils.lua @@ -336,25 +336,6 @@ do return res, nil, n_resolved end end --- Resolve ngx.var in the given string --- @function core.utils.resolve_var --- @tparam string tpl The template string to resolve variables in --- @tparam table ctx The context table containing variables --- @tparam function escaper Optional function to escape resolved values --- @treturn string The resolved string --- @treturn string|nil Error message if any --- @treturn number Number of variables replaced --- @usage --- local utils = require("apisix.core.utils") --- --- -- Usage examples: --- local res = utils.resolve_var("$host", ctx.var) -- "example.com" --- local res = utils.resolve_var("${host}", ctx.var) -- "example.com" --- local res = utils.resolve_var("TMP_${VAR1}_${VAR2}", ctx.var) -- "TMP_value1_value2" --- local res = utils.resolve_var("\\$host", ctx.var) -- "$host" --- --- -- Usage in APISIX context: --- local service_name = utils.resolve_var(up_conf.service_name, api_ctx.var) _M.resolve_var = resolve_var From e8b18c1f88abee3366c524b805ba5fad45fa4b81 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Mon, 9 Jun 2025 15:25:49 +0300 Subject: [PATCH 13/27] adds documentation for service_name as variable --- docs/en/latest/discovery.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/en/latest/discovery.md b/docs/en/latest/discovery.md index 6844ede9f87a..7f891b4e0a49 100644 --- a/docs/en/latest/discovery.md +++ b/docs/en/latest/discovery.md @@ -216,6 +216,35 @@ Server: APISIX web server {"node":{"value":{"uri":"\/user\/*","upstream": {"service_name": "USER-SERVICE", "type": "roundrobin", "discovery_type": "eureka"}},"createdIndex":61925,"key":"\/apisix\/routes\/1","modifiedIndex":61925}} ``` +You can also use variables in the service name. For example, to route requests based on the host header using nginx map: + +First, configure the map in your nginx configuration: + +```nginx +map $http_host $backend { + hostnames; + default service_a; + x.domain.local service_x; + y.domain.local service_y; +} +``` + +Then use the mapped variable in your route configuration: + +```shell +$ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X PUT -i -d ' +{ + "uri": "/*", + "upstream": { + "service_name": "${backend}", + "type": "roundrobin", + "discovery_type": "eureka" + } +}' +``` + +In this example, requests to `x.domain.local` will be routed to the service named "service_a", while requests to `y.domain.local` will be routed to "service_b". + Because the upstream interface URL may have conflict, usually in the gateway by prefix to distinguish: ```shell From 7d411d30d3907ce5a9050c00e6f857cc9a676ade Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Mon, 9 Jun 2025 15:42:31 +0300 Subject: [PATCH 14/27] adds tests for service_name as variable --- t/discovery/consul.t | 118 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/t/discovery/consul.t b/t/discovery/consul.t index 9ec87202118e..7cb974feb85b 100644 --- a/t/discovery/consul.t +++ b/t/discovery/consul.t @@ -781,3 +781,121 @@ location /sleep { qr// ] --- ignore_error_log + + + +=== TEST 16: test service_name as variable in route configuration +--- yaml_config eval: $::yaml_config +--- apisix_yaml +routes: + - + uri: /hello + upstream: + service_name: "${backend}" + discovery_type: consul + type: roundrobin +#END +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + -- Set nginx map variable + ngx.var.backend = "service_a" + + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "upstream": { + "service_name": "${backend}", + "discovery_type": "consul", + "type": "roundrobin" + } + }]] + ) + + if code >= 300 then + ngx.status = code + ngx.say(body) + return + end + + ngx.sleep(1.5) + + local http = require "resty.http" + local httpc = http.new() + local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" + local res, err = httpc:request_uri(uri, { method = "GET"}) + if err then + ngx.log(ngx.ERR, err) + ngx.status = res.status + return + end + ngx.say(res.body) + } + } +--- request +GET /t +--- response_body_like eval +qr/server [1-2]\n/ +--- no_error_log +[error] + +=== TEST 17: test empty variable in service_name +--- yaml_config eval: $::yaml_config +--- apisix_yaml +routes: + - + uri: /hello + upstream: + service_name: "${backend}" + discovery_type: consul + type: roundrobin +#END +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + -- Set empty nginx map variable + ngx.var.backend = "" + + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "upstream": { + "service_name": "${backend}", + "discovery_type": "consul", + "type": "roundrobin" + } + }]] + ) + + if code >= 300 then + ngx.status = code + ngx.say(body) + return + end + + ngx.sleep(1.5) + + local http = require "resty.http" + local httpc = http.new() + local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" + local res, err = httpc:request_uri(uri, { method = "GET"}) + if err then + ngx.log(ngx.ERR, err) + ngx.status = res.status + return + end + ngx.say(res.status) + } + } +--- request +GET /t +--- response_body +503 +--- error_log +resolve_var resolves to empty string \ No newline at end of file From 8a14a9e5b395ee3377be55bb036163d147faf165 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Tue, 10 Jun 2025 11:07:03 +0300 Subject: [PATCH 15/27] adds nginx configuration --- t/discovery/consul.t | 52 +++++--------------------------------------- 1 file changed, 5 insertions(+), 47 deletions(-) diff --git a/t/discovery/consul.t b/t/discovery/consul.t index 7cb974feb85b..b5df2f10cb32 100644 --- a/t/discovery/consul.t +++ b/t/discovery/consul.t @@ -110,6 +110,11 @@ discovery: fail_timeout: 1 weight: 1 max_fails: 1 +nginx_config: + http_configuration_snippet: | + map $http_host $backend { + default service_a; + } _EOC_ our $yaml_config_with_acl = <<_EOC_; @@ -798,31 +803,6 @@ routes: --- config location /t { content_by_lua_block { - local t = require("lib.test_admin").test - - -- Set nginx map variable - ngx.var.backend = "service_a" - - local code, body = t('/apisix/admin/routes/1', - ngx.HTTP_PUT, - [[{ - "uri": "/hello", - "upstream": { - "service_name": "${backend}", - "discovery_type": "consul", - "type": "roundrobin" - } - }]] - ) - - if code >= 300 then - ngx.status = code - ngx.say(body) - return - end - - ngx.sleep(1.5) - local http = require "resty.http" local httpc = http.new() local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" @@ -856,31 +836,9 @@ routes: --- config location /t { content_by_lua_block { - local t = require("lib.test_admin").test - -- Set empty nginx map variable ngx.var.backend = "" - local code, body = t('/apisix/admin/routes/1', - ngx.HTTP_PUT, - [[{ - "uri": "/hello", - "upstream": { - "service_name": "${backend}", - "discovery_type": "consul", - "type": "roundrobin" - } - }]] - ) - - if code >= 300 then - ngx.status = code - ngx.say(body) - return - end - - ngx.sleep(1.5) - local http = require "resty.http" local httpc = http.new() local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" From 9c5880590e6e47016e80f69b70be915a1f294821 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Tue, 10 Jun 2025 19:49:08 +0300 Subject: [PATCH 16/27] returns the previous comment --- apisix/core/utils.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/apisix/core/utils.lua b/apisix/core/utils.lua index 1fbe8d295f3e..12160ae9d68f 100644 --- a/apisix/core/utils.lua +++ b/apisix/core/utils.lua @@ -336,6 +336,7 @@ do return res, nil, n_resolved end end +-- Resolve ngx.var in the given string _M.resolve_var = resolve_var From dbb795a77f74d7a4e2aea21b4f9b87d7ee9ad89f Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Wed, 11 Jun 2025 11:12:29 +0300 Subject: [PATCH 17/27] fix tests lint --- t/discovery/consul.t | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/t/discovery/consul.t b/t/discovery/consul.t index b5df2f10cb32..a800c55c5635 100644 --- a/t/discovery/consul.t +++ b/t/discovery/consul.t @@ -822,6 +822,8 @@ qr/server [1-2]\n/ --- no_error_log [error] + + === TEST 17: test empty variable in service_name --- yaml_config eval: $::yaml_config --- apisix_yaml @@ -856,4 +858,4 @@ GET /t --- response_body 503 --- error_log -resolve_var resolves to empty string \ No newline at end of file +resolve_var resolves to empty string From 54d12c9c85aa7084ad912de0e3939dbee548468f Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Wed, 11 Jun 2025 11:17:13 +0300 Subject: [PATCH 18/27] escape variables --- t/discovery/consul.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/discovery/consul.t b/t/discovery/consul.t index a800c55c5635..83156376cdc8 100644 --- a/t/discovery/consul.t +++ b/t/discovery/consul.t @@ -112,7 +112,7 @@ discovery: max_fails: 1 nginx_config: http_configuration_snippet: | - map $http_host $backend { + map \${http_host} \${backend} { default service_a; } _EOC_ From 3b5782d4bc27837e01833c0234ba73c6931d40c7 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Thu, 12 Jun 2025 13:59:16 +0300 Subject: [PATCH 19/27] move map into http_config --- t/discovery/consul.t | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/t/discovery/consul.t b/t/discovery/consul.t index 83156376cdc8..ea4484404891 100644 --- a/t/discovery/consul.t +++ b/t/discovery/consul.t @@ -26,6 +26,9 @@ add_block_preprocessor(sub { my ($block) = @_; my $http_config = $block->http_config // <<_EOC_; + map \${http_host} \${backend} { + default service_a; + } server { listen 20999; @@ -110,11 +113,6 @@ discovery: fail_timeout: 1 weight: 1 max_fails: 1 -nginx_config: - http_configuration_snippet: | - map \${http_host} \${backend} { - default service_a; - } _EOC_ our $yaml_config_with_acl = <<_EOC_; From c6da5d16825a2d7c13ec532078a899d91f6ffa68 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Fri, 13 Jun 2025 11:06:27 +0300 Subject: [PATCH 20/27] fix nil value for $backend --- t/discovery/consul.t | 3 +++ 1 file changed, 3 insertions(+) diff --git a/t/discovery/consul.t b/t/discovery/consul.t index ea4484404891..23e132b500c6 100644 --- a/t/discovery/consul.t +++ b/t/discovery/consul.t @@ -801,6 +801,9 @@ routes: --- config location /t { content_by_lua_block { + -- Set nginx map variable + ngx.var.backend = "service_a" + local http = require "resty.http" local httpc = http.new() local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" From 100fa5562052116f019fdc53cda499e3e1865421 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Mon, 16 Jun 2025 11:22:48 +0300 Subject: [PATCH 21/27] move set backend directly into the test --- t/discovery/consul.t | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/t/discovery/consul.t b/t/discovery/consul.t index 23e132b500c6..b07a43c23461 100644 --- a/t/discovery/consul.t +++ b/t/discovery/consul.t @@ -26,10 +26,6 @@ add_block_preprocessor(sub { my ($block) = @_; my $http_config = $block->http_config // <<_EOC_; - map \${http_host} \${backend} { - default service_a; - } - server { listen 20999; @@ -800,10 +796,8 @@ routes: #END --- config location /t { + set $backend "service_a"; content_by_lua_block { - -- Set nginx map variable - ngx.var.backend = "service_a" - local http = require "resty.http" local httpc = http.new() local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" @@ -838,6 +832,7 @@ routes: #END --- config location /t { + set $backend "service_a"; content_by_lua_block { -- Set empty nginx map variable ngx.var.backend = "" From b688b39bebcc1d9f74c74a46dfd5bce1f554dd44 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Thu, 17 Jul 2025 18:14:58 +0300 Subject: [PATCH 22/27] move variable into http section --- t/discovery/consul.t | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/t/discovery/consul.t b/t/discovery/consul.t index b07a43c23461..3d72ff678235 100644 --- a/t/discovery/consul.t +++ b/t/discovery/consul.t @@ -794,9 +794,12 @@ routes: discovery_type: consul type: roundrobin #END +--- http_config + map $http_host $backend { + default service_a; + } --- config location /t { - set $backend "service_a"; content_by_lua_block { local http = require "resty.http" local httpc = http.new() @@ -830,13 +833,13 @@ routes: discovery_type: consul type: roundrobin #END +--- http_config + map $http_host $backend { + default ""; + } --- config location /t { - set $backend "service_a"; content_by_lua_block { - -- Set empty nginx map variable - ngx.var.backend = "" - local http = require "resty.http" local httpc = http.new() local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" From 2c3d085edf933146c2a47d14107c483bb17d87c8 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Fri, 18 Jul 2025 12:53:15 +0300 Subject: [PATCH 23/27] register services --- t/discovery/consul.t | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/t/discovery/consul.t b/t/discovery/consul.t index 3d72ff678235..57d6891ac4fa 100644 --- a/t/discovery/consul.t +++ b/t/discovery/consul.t @@ -799,24 +799,32 @@ routes: default service_a; } --- config - location /t { - content_by_lua_block { - local http = require "resty.http" - local httpc = http.new() - local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" - local res, err = httpc:request_uri(uri, { method = "GET"}) - if err then - ngx.log(ngx.ERR, err) - ngx.status = res.status - return - end - ngx.say(res.body) - } +location /v1/agent { + proxy_pass http://127.0.0.1:8500; +} +location /sleep { + content_by_lua_block { + local args = ngx.req.get_uri_args() + local sec = args.sec or "2" + ngx.sleep(tonumber(sec)) + ngx.say("ok") } ---- request -GET /t +} +--- timeout: 6 +--- request eval +[ + "PUT /v1/agent/service/register\n" . "{\"ID\":\"service_a1\",\"Name\":\"service_a\",\"Tags\":[\"primary\",\"v1\"],\"Address\":\"127.0.0.1\",\"Port\":30511,\"Meta\":{\"service_a_version\":\"4.0\"},\"EnableTagOverride\":false,\"Weights\":{\"Passing\":10,\"Warning\":1}}", + "PUT /v1/agent/service/register\n" . "{\"ID\":\"service_a2\",\"Name\":\"service_a\",\"Tags\":[\"primary\",\"v1\"],\"Address\":\"127.0.0.1\",\"Port\":30512,\"Meta\":{\"service_a_version\":\"4.0\"},\"EnableTagOverride\":false,\"Weights\":{\"Passing\":10,\"Warning\":1}}", + "GET /sleep?sec=2", + "GET /hello", +] --- response_body_like eval -qr/server [1-2]\n/ +[ + qr//, + qr//, + qr/ok\n/, + qr/server [1-2]\n/, +] --- no_error_log [error] From 6fdd1ec2a15681da8a7af4592c6a8fac651fa884 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Mon, 21 Jul 2025 18:03:34 +0300 Subject: [PATCH 24/27] add upstreams for tests --- t/discovery/consul.t | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/t/discovery/consul.t b/t/discovery/consul.t index 57d6891ac4fa..b0193a857fda 100644 --- a/t/discovery/consul.t +++ b/t/discovery/consul.t @@ -798,6 +798,25 @@ routes: map $http_host $backend { default service_a; } + + server { + listen 30511; + + location /hello { + content_by_lua_block { + ngx.say("server 1") + } + } + } + server { + listen 30512; + + location /hello { + content_by_lua_block { + ngx.say("server 2") + } + } + } --- config location /v1/agent { proxy_pass http://127.0.0.1:8500; From 641fe78b36ef584c4d82a016b942ce0cc565b313 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Tue, 22 Jul 2025 19:26:57 +0300 Subject: [PATCH 25/27] fix newline and description --- apisix/core/utils.lua | 1 + docs/en/latest/discovery.md | 2 +- t/discovery/consul.t | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apisix/core/utils.lua b/apisix/core/utils.lua index 12160ae9d68f..cfea756542bd 100644 --- a/apisix/core/utils.lua +++ b/apisix/core/utils.lua @@ -461,4 +461,5 @@ function _M.check_tls_bool(fields, conf, plugin_name) end end + return _M diff --git a/docs/en/latest/discovery.md b/docs/en/latest/discovery.md index 7f891b4e0a49..04125b1396e2 100644 --- a/docs/en/latest/discovery.md +++ b/docs/en/latest/discovery.md @@ -243,7 +243,7 @@ $ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X }' ``` -In this example, requests to `x.domain.local` will be routed to the service named "service_a", while requests to `y.domain.local` will be routed to "service_b". +In this example, requests to `x.domain.local` will be routed to the service named "service_x", while requests to `y.domain.local` will be routed to "service_y". Because the upstream interface URL may have conflict, usually in the gateway by prefix to distinguish: diff --git a/t/discovery/consul.t b/t/discovery/consul.t index b0193a857fda..e1a63dbc91c9 100644 --- a/t/discovery/consul.t +++ b/t/discovery/consul.t @@ -26,6 +26,7 @@ add_block_preprocessor(sub { my ($block) = @_; my $http_config = $block->http_config // <<_EOC_; + server { listen 20999; From e54929ad364b9f52a8a2fcff50bf55cf28ccb01b Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Tue, 22 Jul 2025 19:31:06 +0300 Subject: [PATCH 26/27] adds details into the Chinese documentation --- docs/zh/latest/discovery.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/zh/latest/discovery.md b/docs/zh/latest/discovery.md index 3aaaaaf039ff..66cf1f5ff953 100644 --- a/docs/zh/latest/discovery.md +++ b/docs/zh/latest/discovery.md @@ -221,6 +221,35 @@ Server: APISIX web server {"node":{"value":{"uri":"\/user\/*","upstream": {"service_name": "USER-SERVICE", "type": "roundrobin", "discovery_type": "eureka"}},"createdIndex":61925,"key":"\/apisix\/routes\/1","modifiedIndex":61925}} ``` +您也可以在服务名称中使用变量。例如,使用 nginx map 根据 host 头进行请求路由: + +首先,在您的 nginx 配置中配置 map: + +```nginx +map $http_host $backend { + hostnames; + default service_a; + x.domain.local service_x; + y.domain.local service_y; +} +``` + +然后在路由配置中使用映射的变量: + +```shell +$ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X PUT -i -d ' +{ + "uri": "/*", + "upstream": { + "service_name": "${backend}", + "type": "roundrobin", + "discovery_type": "eureka" + } +}' +``` + +在这个例子中,对 `x.domain.local` 的请求将被路由到名为 "service_x" 的服务,而对 `y.domain.local` 的请求将被路由到 "service_y"。 + 因为上游的接口 URL 可能会有冲突,通常会在网关通过前缀来进行区分: ```shell From e7f886cfacf245db2adf0b114277e38d2a7ba702 Mon Sep 17 00:00:00 2001 From: Dzianis Bazhok Date: Fri, 1 Aug 2025 14:24:55 +0300 Subject: [PATCH 27/27] adds service_name logging --- apisix/upstream.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apisix/upstream.lua b/apisix/upstream.lua index 14508b2742c9..a1f1cf11fb66 100644 --- a/apisix/upstream.lua +++ b/apisix/upstream.lua @@ -312,7 +312,7 @@ function _M.set_by_route(route, api_ctx) local new_nodes, err = dis.nodes(service_name, up_conf.discovery_args) if not new_nodes then - return HTTP_CODE_UPSTREAM_UNAVAILABLE, "no valid upstream node: " .. (err or "nil") + return HTTP_CODE_UPSTREAM_UNAVAILABLE, "no valid upstream node for service '" .. service_name .. "': " .. (err or "nil") end local same = upstream_util.compare_upstream_node(up_conf, new_nodes)