Skip to content

Commit e68e03f

Browse files
authored
feat(control): expose services(#5271)
1 parent 50fed63 commit e68e03f

File tree

3 files changed

+319
-2
lines changed

3 files changed

+319
-2
lines changed

apisix/control/v1.lua

+51-2
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ local function iter_add_get_routes_info(values, route_id)
165165
new_route.value.upstream.parent = nil
166166
end
167167
core.table.insert(infos, new_route)
168-
-- check the roude id
168+
-- check the route id
169169
if route_id and route.value.id == route_id then
170170
return new_route
171171
end
@@ -240,6 +240,43 @@ function _M.trigger_gc()
240240
end
241241

242242

243+
local function iter_add_get_services_info(values, svc_id)
244+
local infos = {}
245+
for _, svc in core.config_util.iterate_values(values) do
246+
local new_svc = core.table.deepcopy(svc)
247+
if new_svc.value.upstream and new_svc.value.upstream.parent then
248+
new_svc.value.upstream.parent = nil
249+
end
250+
core.table.insert(infos, new_svc)
251+
-- check the service id
252+
if svc_id and svc.value.id == svc_id then
253+
return new_svc
254+
end
255+
end
256+
if not svc_id then
257+
return infos
258+
end
259+
return nil
260+
end
261+
262+
function _M.dump_all_services_info()
263+
local services = get_services()
264+
local infos = iter_add_get_services_info(services, nil)
265+
return 200, infos
266+
end
267+
268+
function _M.dump_service_info()
269+
local services = get_services()
270+
local uri_segs = core.utils.split_uri(ngx_var.uri)
271+
local svc_id = uri_segs[4]
272+
local info = iter_add_get_services_info(services, svc_id)
273+
if not info then
274+
return 404, {error_msg = str_format("service[%s] not found", svc_id)}
275+
end
276+
return 200, info
277+
end
278+
279+
243280
return {
244281
-- /v1/schema
245282
{
@@ -271,12 +308,24 @@ return {
271308
uris = {"/routes"},
272309
handler = _M.dump_all_routes_info,
273310
},
274-
--- /v1/route/*
311+
-- /v1/route/*
275312
{
276313
methods = {"GET"},
277314
uris = {"/route/*"},
278315
handler = _M.dump_route_info,
279316
},
317+
-- /v1/services
318+
{
319+
methods = {"GET"},
320+
uris = {"/services"},
321+
handler = _M.dump_all_services_info
322+
},
323+
-- /v1/service/*
324+
{
325+
methods = {"GET"},
326+
uris = {"/service/*"},
327+
handler = _M.dump_service_info
328+
},
280329
-- /v1/upstreams
281330
{
282331
methods = {"GET"},

docs/en/latest/control-api.md

+82
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,88 @@ Return specific route info with **route_id** in the format below:
284284
}
285285
```
286286

287+
### Get /v1/services
288+
289+
Introduced since `v2.11`.
290+
291+
Return all services info in the format below:
292+
293+
```json
294+
[
295+
{
296+
"has_domain": false,
297+
"clean_handlers": {},
298+
"modifiedIndex": 671,
299+
"key": "/apisix/services/200",
300+
"createdIndex": 671,
301+
"value": {
302+
"upstream": {
303+
"scheme": "http",
304+
"hash_on": "vars",
305+
"pass_host": "pass",
306+
"type": "roundrobin",
307+
"nodes": [
308+
{
309+
"port": 80,
310+
"weight": 1,
311+
"host": "39.97.63.215"
312+
}
313+
]
314+
},
315+
"create_time": 1634552648,
316+
"id": "200",
317+
"plugins": {
318+
"limit-count": {
319+
"key": "remote_addr",
320+
"time_window": 60,
321+
"redis_timeout": 1000,
322+
"allow_degradation": false,
323+
"show_limit_quota_header": true,
324+
"policy": "local",
325+
"count": 2,
326+
"rejected_code": 503
327+
}
328+
},
329+
"update_time": 1634552648
330+
}
331+
}
332+
]
333+
```
334+
335+
### Get /v1/service/{service_id}
336+
337+
Introduced since `v2.11`.
338+
339+
Return specific service info with **service_id** in the format below:
340+
341+
```json
342+
{
343+
"has_domain": false,
344+
"clean_handlers": {},
345+
"modifiedIndex": 728,
346+
"key": "/apisix/services/5",
347+
"createdIndex": 728,
348+
"value": {
349+
"create_time": 1634554563,
350+
"id": "5",
351+
"upstream": {
352+
"scheme": "http",
353+
"hash_on": "vars",
354+
"pass_host": "pass",
355+
"type": "roundrobin",
356+
"nodes": [
357+
{
358+
"port": 80,
359+
"weight": 1,
360+
"host": "39.97.63.215"
361+
}
362+
]
363+
},
364+
"update_time": 1634554563
365+
}
366+
}
367+
```
368+
287369
### Get /v1/upstreams
288370

289371
Introduced since `v2.11.0`.

t/control/services.t

+186
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
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+
use t::APISIX 'no_plan';
18+
19+
repeat_each(1);
20+
no_long_string();
21+
no_root_location();
22+
no_shuffle();
23+
log_level("info");
24+
25+
add_block_preprocessor(sub {
26+
my ($block) = @_;
27+
28+
if (!$block->yaml_config) {
29+
my $yaml_config = <<_EOC_;
30+
apisix:
31+
node_listen: 1984
32+
config_center: yaml
33+
enable_admin: false
34+
_EOC_
35+
36+
$block->set_value("yaml_config", $yaml_config);
37+
}
38+
39+
if (!$block->request) {
40+
$block->set_value("request", "GET /t");
41+
}
42+
});
43+
44+
run_tests;
45+
46+
__DATA__
47+
48+
=== TEST 1: services
49+
--- apisix_yaml
50+
services:
51+
-
52+
id: 200
53+
upstream:
54+
nodes:
55+
"127.0.0.1:1980": 1
56+
type: roundrobin
57+
#END
58+
--- config
59+
location /t {
60+
content_by_lua_block {
61+
local json = require("toolkit.json")
62+
local t = require("lib.test_admin")
63+
local code, body, res = t.test('/v1/services',
64+
ngx.HTTP_GET)
65+
res = json.decode(res)
66+
if res[1] then
67+
local data = {}
68+
data.id = res[1].value.id
69+
data.plugins = res[1].value.plugins
70+
data.upstream = res[1].value.upstream
71+
ngx.say(json.encode(data))
72+
end
73+
return
74+
}
75+
}
76+
--- response_body
77+
{"id":"200","upstream":{"hash_on":"vars","nodes":[{"host":"127.0.0.1","port":1980,"weight":1}],"pass_host":"pass","scheme":"http","type":"roundrobin"}}
78+
79+
80+
81+
=== TEST 2: multiple services
82+
--- apisix_yaml
83+
services:
84+
-
85+
id: 200
86+
upstream:
87+
nodes:
88+
"127.0.0.1:1980": 1
89+
type: roundrobin
90+
-
91+
id: 201
92+
upstream:
93+
nodes:
94+
"127.0.0.2:1980": 1
95+
type: roundrobin
96+
#END
97+
--- config
98+
location /t {
99+
content_by_lua_block {
100+
local json = require("toolkit.json")
101+
local t = require("lib.test_admin")
102+
local core = require("apisix.core")
103+
local code, body, res = t.test('/v1/services',
104+
ngx.HTTP_GET)
105+
res = json.decode(res)
106+
local g_data = {}
107+
for _, r in core.config_util.iterate_values(res) do
108+
local data = {}
109+
data.id = r.value.id
110+
data.plugins = r.value.plugins
111+
data.upstream = r.value.upstream
112+
core.table.insert(g_data, data)
113+
end
114+
ngx.say(json.encode(g_data))
115+
return
116+
}
117+
}
118+
--- response_body
119+
[{"id":"200","upstream":{"hash_on":"vars","nodes":[{"host":"127.0.0.1","port":1980,"weight":1}],"pass_host":"pass","scheme":"http","type":"roundrobin"}},{"id":"201","upstream":{"hash_on":"vars","nodes":[{"host":"127.0.0.2","port":1980,"weight":1}],"pass_host":"pass","scheme":"http","type":"roundrobin"}}]
120+
121+
122+
123+
=== TEST 3: get service with id 5
124+
--- apisix_yaml
125+
services:
126+
-
127+
id: 5
128+
plugins:
129+
limit-count:
130+
count: 2
131+
time_window: 60
132+
rejected_code: 503
133+
key: remote_addr
134+
upstream:
135+
nodes:
136+
"127.0.0.1:1980": 1
137+
type: roundrobin
138+
#END
139+
--- config
140+
location /t {
141+
content_by_lua_block {
142+
local json = require("toolkit.json")
143+
local t = require("lib.test_admin")
144+
local code, body, res = t.test('/v1/service/5',
145+
ngx.HTTP_GET)
146+
res = json.decode(res)
147+
if res then
148+
local data = {}
149+
data.id = res.value.id
150+
data.plugins = res.value.plugins
151+
data.upstream = res.value.upstream
152+
ngx.say(json.encode(data))
153+
end
154+
return
155+
}
156+
}
157+
--- response_body
158+
{"id":"5","plugins":{"limit-count":{"allow_degradation":false,"count":2,"key":"remote_addr","policy":"local","rejected_code":503,"show_limit_quota_header":true,"time_window":60}},"upstream":{"hash_on":"vars","nodes":[{"host":"127.0.0.1","port":1980,"weight":1}],"pass_host":"pass","scheme":"http","type":"roundrobin"}}
159+
160+
161+
162+
=== TEST 4: services with invalid id
163+
--- apisix_yaml
164+
services:
165+
-
166+
id: 1
167+
upstream:
168+
nodes:
169+
"127.0.0.1:1980": 1
170+
type: roundrobin
171+
#END
172+
--- config
173+
location /t {
174+
content_by_lua_block {
175+
local json = require("toolkit.json")
176+
local t = require("lib.test_admin")
177+
local code, body, res = t.test('/v1/service/2',
178+
ngx.HTTP_GET)
179+
local data = {}
180+
data.status = code
181+
ngx.say(json.encode(data))
182+
return
183+
}
184+
}
185+
--- response_body
186+
{"status":404}

0 commit comments

Comments
 (0)