Skip to content

feat: adds support for nginx variables in service_name param #12187

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

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
90b3c13
adds support of variables into upstream module
undying Apr 28, 2025
9def4fb
move local upper from if
undying Apr 29, 2025
cfea8d8
linter fixes
undying May 15, 2025
3b90fe2
Merge branch 'master' into upstreams_variable_support
undying May 26, 2025
ac76dd7
bump build
undying May 27, 2025
2160c51
new line
undying May 28, 2025
e8e086c
Merge branch 'master' into upstreams_variable_support
undying May 29, 2025
5a907fa
removes is_nginx_variable
undying Jun 2, 2025
ab7bc5d
Merge branch 'upstreams_variable_support' of github.com:undying/apisi…
undying Jun 2, 2025
3a623bc
new line
undying Jun 3, 2025
5b457df
up
undying Jun 3, 2025
b661517
check for empty string in service name
undying Jun 3, 2025
5f19d54
newline
undying Jun 4, 2025
7548cf8
use resolve_var in error message
undying Jun 4, 2025
b669e78
removes function documentation
undying Jun 9, 2025
e8b18c1
adds documentation for service_name as variable
undying Jun 9, 2025
7d411d3
adds tests for service_name as variable
undying Jun 9, 2025
8a14a9e
adds nginx configuration
undying Jun 10, 2025
9c58805
returns the previous comment
undying Jun 10, 2025
dbb795a
fix tests lint
undying Jun 11, 2025
54d12c9
escape variables
undying Jun 11, 2025
3b5782d
move map into http_config
undying Jun 12, 2025
c6da5d1
fix nil value for $backend
undying Jun 13, 2025
100fa55
move set backend directly into the test
undying Jun 16, 2025
b688b39
move variable into http section
undying Jul 17, 2025
2c3d085
register services
undying Jul 18, 2025
6fdd1ec
add upstreams for tests
undying Jul 21, 2025
641fe78
fix newline and description
undying Jul 22, 2025
e54929a
adds details into the Chinese documentation
undying Jul 22, 2025
6f21de0
Merge branch 'master' into upstreams_variable_support
undying Jul 23, 2025
94920a4
Merge branch 'master' into upstreams_variable_support
undying Jul 25, 2025
e7f886c
adds service_name logging
undying Aug 1, 2025
fdcd511
Merge branch 'master' into upstreams_variable_support
undying Aug 4, 2025
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
9 changes: 7 additions & 2 deletions apisix/upstream.lua
Original file line number Diff line number Diff line change
Expand Up @@ -305,9 +305,14 @@ 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)
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, "resolve_var resolves to empty string: " .. (err or "nil")
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")
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)
Expand Down
29 changes: 29 additions & 0 deletions docs/en/latest/discovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -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_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:

```shell
Expand Down
29 changes: 29 additions & 0 deletions docs/zh/latest/discovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
105 changes: 105 additions & 0 deletions t/discovery/consul.t
Original file line number Diff line number Diff line change
Expand Up @@ -781,3 +781,108 @@ 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
--- http_config
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;
}
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")
}
}
--- 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//,
qr//,
qr/ok\n/,
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
--- http_config
map $http_host $backend {
default "";
}
--- 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.status)
}
}
--- request
GET /t
--- response_body
503
--- error_log
resolve_var resolves to empty string
Loading