Skip to content

Commit dd1fcfd

Browse files
committed
feat(consul): filter nodes in upstream with metadata
docs keep same with #12445, but use metadata_match and array values
1 parent b61a897 commit dd1fcfd

File tree

7 files changed

+602
-34
lines changed

7 files changed

+602
-34
lines changed

apisix/discovery/consul/init.lua

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ local core_sleep = require("apisix.core.utils").sleep
2121
local resty_consul = require('resty.consul')
2222
local http = require('resty.http')
2323
local util = require("apisix.cli.util")
24+
local discovery_utils = require("apisix.utils.discovery")
2425
local ipairs = ipairs
2526
local error = error
2627
local ngx = ngx
@@ -78,36 +79,6 @@ function _M.all_nodes()
7879
return all_services
7980
end
8081

81-
local function match_metadata_filters(inst, filters)
82-
local metadata = inst.metadata or {}
83-
for _, f in ipairs(filters) do
84-
local key = f.key
85-
local allowed_vals = f.value
86-
local val = metadata[key]
87-
local matched = false
88-
for _, allowed in ipairs(allowed_vals) do
89-
if val == allowed then
90-
matched = true
91-
break
92-
end
93-
end
94-
if not matched then
95-
return false
96-
end
97-
end
98-
return true
99-
end
100-
101-
local function match_nodes_by_metadata(nodes, filters)
102-
local result = {}
103-
for _, node in ipairs(nodes) do
104-
if match_metadata_filters(node, filters) then
105-
core.table.insert(result, node)
106-
end
107-
end
108-
return result
109-
end
110-
11182
function _M.nodes(service_name, discovery_args)
11283
if not all_services then
11384
log.error("all_services is nil, failed to fetch nodes for : ", service_name)
@@ -117,7 +88,7 @@ function _M.nodes(service_name, discovery_args)
11788
local resp_list = all_services[service_name]
11889

11990
if discovery_args and discovery_args.metadata_match then
120-
resp_list = match_nodes_by_metadata(resp_list, discovery_args.metadata_match)
91+
resp_list = discovery_utils.nodes_metadata_match(resp_list, discovery_args.metadata_match)
12192
end
12293

12394
if not resp_list then

apisix/schema_def.lua

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,18 @@ local upstream_schema = {
488488
description = "group name",
489489
type = "string",
490490
},
491+
metadata_match = {
492+
description = "metadata for filtering service instances",
493+
type = "object",
494+
additionalProperties = {
495+
type = "array",
496+
items = {
497+
description = "candidate metadata value",
498+
type = "string",
499+
},
500+
uniqueItems = true,
501+
}
502+
},
491503
}
492504
},
493505
pass_host = {

apisix/utils/discovery.lua

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
--
2+
-- Licensed to the Apache Software Foundation (ASF) under one or more
3+
-- contributor license agreements. See the NOTICE file distributed with
4+
-- this work for additional information regarding copyright ownership.
5+
-- The ASF licenses this file to You under the Apache License, Version 2.0
6+
-- (the "License"); you may not use this file except in compliance with
7+
-- the License. You may obtain a copy of the License at
8+
--
9+
-- http://www.apache.org/licenses/LICENSE-2.0
10+
--
11+
-- Unless required by applicable law or agreed to in writing, software
12+
-- distributed under the License is distributed on an "AS IS" BASIS,
13+
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
-- See the License for the specific language governing permissions and
15+
-- limitations under the License.
16+
--
17+
local ipairs = ipairs
18+
local pairs = pairs
19+
20+
local _M = {}
21+
22+
local function do_metadata_match(inst, filters)
23+
local metadata = inst.metadata or {}
24+
for key, values in pairs(filters) do
25+
local matched = false
26+
for _, value in ipairs(values) do
27+
if metadata[key] == value then
28+
matched = true
29+
break
30+
end
31+
end
32+
if not matched then
33+
return false
34+
end
35+
end
36+
return true
37+
end
38+
39+
local function nodes_metadata_match(nodes, metadata_match)
40+
local result = {}
41+
for _, node in ipairs(nodes) do
42+
if do_metadata_match(node, metadata_match) then
43+
core.table.insert(result, node)
44+
end
45+
end
46+
return result
47+
end
48+
_M.nodes_metadata_match = nodes_metadata_match
49+
50+
return _M

docs/en/latest/discovery/consul.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,61 @@ $ curl http://127.0.0.1:9180/apisix/admin/stream_routes/1 -H "X-API-KEY: $admin_
232232
}'
233233
```
234234

235+
### discovery_args
236+
237+
| Name | Type | Requirement | Default | Valid | Description |
238+
|----------------| ------ | ----------- | ------- | ----- | ------------------------------------------------------------ |
239+
| metadata_match | object | optional | {} | | Filter service instances by metadata using containment matching |
240+
241+
#### Metadata filtering
242+
243+
APISIX supports filtering service instances based on metadata. When a route is configured with metadata conditions, only service instances whose metadata matched with roles specified in the route's `metadata_match` configuration will be selected.
244+
245+
Example: If a service instance has metadata `{lane: "a", env: "prod", version: "1.0"}`, it will match routes configured with metadata `{lane: ["a"]}` or `{lane: ["a", "b"], env: "prod"}`, but not routes configured with `{lane: ["c"]}` or `{lane: "a", region: "us"}`.
246+
247+
Example of routing a request with metadata filtering:
248+
249+
```shell
250+
$ curl http://127.0.0.1:9180/apisix/admin/routes/5 -H "X-API-KEY: $admin_key" -X PUT -i -d '
251+
{
252+
"uri": "/nacosWithMetadata/*",
253+
"upstream": {
254+
"service_name": "APISIX-NACOS",
255+
"type": "roundrobin",
256+
"discovery_type": "nacos",
257+
"discovery_args": {
258+
"metadata": {
259+
"version": ["v1", "v2"]
260+
}
261+
}
262+
}
263+
}'
264+
```
265+
266+
This route will only route traffic to service instances that have the metadata field `version` set to `v1` or `v2`.
267+
268+
For multiple metadata criteria:
269+
270+
```shell
271+
$ curl http://127.0.0.1:9180/apisix/admin/routes/6 -H "X-API-KEY: $admin_key" -X PUT -i -d '
272+
{
273+
"uri": "/nacosWithMultipleMetadata/*",
274+
"upstream": {
275+
"service_name": "APISIX-NACOS",
276+
"type": "roundrobin",
277+
"discovery_type": "nacos",
278+
"discovery_args": {
279+
"metadata": {
280+
"lane": ["a"],
281+
"env": ["prod"]
282+
}
283+
}
284+
}
285+
}'
286+
```
287+
288+
This route will only route traffic to service instances that have both `lane: "a"` and `env: "prod"` in their metadata.
289+
235290
You could find more usage in the `apisix/t/discovery/stream/consul.t` file.
236291

237292
## Debugging API

0 commit comments

Comments
 (0)