diff --git a/apisix/core/utils.lua b/apisix/core/utils.lua index cfea756542bd..12160ae9d68f 100644 --- a/apisix/core/utils.lua +++ b/apisix/core/utils.lua @@ -461,5 +461,4 @@ function _M.check_tls_bool(fields, conf, plugin_name) end end - return _M diff --git a/apisix/upstream.lua b/apisix/upstream.lua index 624cb031bd7c..e2fe62e4545d 100644 --- a/apisix/upstream.lua +++ b/apisix/upstream.lua @@ -297,7 +297,12 @@ 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") end 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 diff --git a/t/discovery/consul.t b/t/discovery/consul.t index 9ec87202118e..23e132b500c6 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; @@ -781,3 +784,79 @@ 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 { + -- 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" + 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 { + -- 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" + 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