feat(apisix): add Cloudron package

- Implements Apache APISIX packaging for Cloudron platform.
- Includes Dockerfile, CloudronManifest.json, and start.sh.
- Configured to use Cloudron's etcd addon.

🤖 Generated with Gemini CLI
Co-Authored-By: Gemini <noreply@google.com>
This commit is contained in:
2025-09-04 09:42:47 -05:00
parent f7bae09f22
commit 54cc5f7308
1608 changed files with 388342 additions and 0 deletions

View File

@@ -0,0 +1,246 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
no_long_string();
no_root_location();
no_shuffle();
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /apisix/admin/routes");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: Server header for admin API
--- response_headers_like
Server: APISIX/(.*)
=== TEST 2: Server header for admin API without token
--- yaml_config
deployment:
admin:
admin_key:
- key: a
name: a
role: admin
apisix:
node_listen: 1984
enable_server_tokens: false
--- error_code: 401
--- response_headers
Server: APISIX
=== TEST 3: Version header for admin API (without apikey)
--- yaml_config
deployment:
admin:
admin_key:
- key: a
name: a
role: admin
apisix:
admin_api_version: default
--- error_code: 401
--- response_headers
! X-API-VERSION
=== TEST 4: Version header for admin API (v2)
--- yaml_config
deployment:
role: traditional
role_traditional:
config_provider: etcd
admin:
admin_key: ~
admin_api_version: v2
--- more_headers
X-API-KEY: edd1c9f034335f136f87ad84b625c8f1
--- response_headers
X-API-VERSION: v2
=== TEST 5: Version header for admin API (v3)
--- yaml_config
deployment:
role: traditional
role_traditional:
config_provider: etcd
admin:
admin_key: ~
admin_api_version: v3
--- more_headers
X-API-KEY: edd1c9f034335f136f87ad84b625c8f1
--- response_headers
X-API-VERSION: v3
=== TEST 6: CORS header for admin API
--- response_headers
Access-Control-Allow-Origin: *
=== TEST 7: CORS header disabled for admin API
--- yaml_config
deployment:
admin:
admin_key: ~
enable_admin_cors: false
--- response_headers
Access-Control-Allow-Origin:
=== TEST 8: Compatibility for admin API (v2)
--- yaml_config
deployment:
role: traditional
role_traditional:
config_provider: etcd
admin:
admin_key: ~
admin_api_version: default
--- more_headers
X-API-KEY: edd1c9f034335f136f87ad84b625c8f1
--- response_headers
X-API-VERSION: v2
--- response_body_like: "/apisix/routes"
=== TEST 9: Head method support for admin API
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin',
ngx.HTTP_HEAD)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 10: Access with api key, and admin_key_required=true
--- yaml_config
deployment:
admin:
admin_key_required: true
admin_key:
- name: admin
role: admin
key: rDAkLJbqvoBzBOoxuYAUDbbWaSilvIca
--- more_headers
X-API-KEY: rDAkLJbqvoBzBOoxuYAUDbbWaSilvIca
--- request
GET /apisix/admin/routes
--- error_code: 200
=== TEST 11: Access with wrong api key, and admin_key_required=true
--- yaml_config
deployment:
admin:
admin_key_required: true
--- more_headers
X-API-KEY: wrong-key
--- request
GET /apisix/admin/routes
--- error_code: 401
=== TEST 12: Access without api key, and admin_key_required=true
--- yaml_config
deployment:
admin:
admin_key_required: true
--- request
GET /apisix/admin/routes
--- error_code: 401
=== TEST 13: Access with api key, but admin_key_required=false
--- yaml_config
deployment:
admin:
admin_key_required: false
admin_key:
- name: admin
role: admin
key: rDAkLJbqvoBzBOoxuYAUDbbWaSilvIca
--- more_headers
X-API-KEY: rDAkLJbqvoBzBOoxuYAUDbbWaSilvIca
--- request
GET /apisix/admin/routes
--- error_code: 200
--- error_log
Admin key is bypassed!
=== TEST 14: Access with wrong api key, but admin_key_required=false
--- yaml_config
deployment:
admin:
admin_key_required: false
--- more_headers
X-API-KEY: wrong-key
--- request
GET /apisix/admin/routes
--- error_code: 200
--- error_log
Admin key is bypassed!
=== TEST 15: Access without api key, but admin_key_required=false
--- yaml_config
deployment:
admin:
admin_key_required: false
--- request
GET /apisix/admin/routes
--- error_code: 200
--- error_log
Admin key is bypassed!

View File

@@ -0,0 +1,243 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(2);
no_long_string();
no_root_location();
add_block_preprocessor(sub {
my ($block) = @_;
my $init_by_lua_block = <<_EOC_;
require "resty.core"
apisix = require("apisix")
core = require("apisix.core")
apisix.http_init()
function test(route, ctx, count)
local balancer = require("apisix.balancer")
local res = {}
for i = 1, count or 12 do
ctx.balancer_try_count = 0
local server, err = balancer.pick_server(route, ctx)
if err then
ngx.say("failed: ", err)
end
core.log.warn("host: ", server.host, " port: ", server.port)
res[server.host] = (res[server.host] or 0) + 1
end
local keys = {}
for k,v in pairs(res) do
table.insert(keys, k)
end
table.sort(keys)
for _, key in ipairs(keys) do
ngx.say("host: ", key, " count: ", res[key])
end
ctx.server_picker = nil
end
_EOC_
$block->set_value("init_by_lua_block", $init_by_lua_block);
});
run_tests;
__DATA__
=== TEST 1: roundrobin with same weight
--- config
location /t {
content_by_lua_block {
local up_conf = {
type = "roundrobin",
nodes = {
{host = "39.97.63.215", port = 80, weight = 1, priority = 0},
{host = "39.97.63.216", port = 81, weight = 1, priority = 0},
{host = "39.97.63.217", port = 82, weight = 1, priority = 0},
}
}
local ctx = {conf_version = 1}
ctx.upstream_conf = up_conf
ctx.upstream_version = "ver"
ctx.upstream_key = up_conf.type .. "#route_" .. "id"
test(route, ctx)
}
}
--- request
GET /t
--- response_body
host: 39.97.63.215 count: 4
host: 39.97.63.216 count: 4
host: 39.97.63.217 count: 4
=== TEST 2: roundrobin with different weight
--- config
location /t {
content_by_lua_block {
local up_conf = {
type = "roundrobin",
nodes = {
{host = "39.97.63.215", port = 80, weight = 1, priority = 0},
{host = "39.97.63.216", port = 81, weight = 2, priority = 0},
{host = "39.97.63.217", port = 82, weight = 3, priority = 0},
}
}
local ctx = {conf_version = 1}
ctx.upstream_conf = up_conf
ctx.upstream_version = "ver"
ctx.upstream_key = up_conf.type .. "#route_" .. "id"
test(route, ctx)
}
}
--- request
GET /t
--- response_body
host: 39.97.63.215 count: 2
host: 39.97.63.216 count: 4
host: 39.97.63.217 count: 6
=== TEST 3: roundrobin, cached server picker by version
--- config
location /t {
content_by_lua_block {
local up_conf = {
type = "roundrobin",
nodes = {
{host = "39.97.63.215", port = 80, weight = 1, priority = 0},
{host = "39.97.63.216", port = 81, weight = 1, priority = 0},
{host = "39.97.63.217", port = 82, weight = 1, priority = 0},
}
}
local ctx = {}
ctx.upstream_conf = up_conf
ctx.upstream_version = 1
ctx.upstream_key = up_conf.type .. "#route_" .. "id"
test(route, ctx)
-- cached by version
up_conf.nodes = {
{host = "39.97.63.218", port = 80, weight = 1, priority = 0},
{host = "39.97.63.219", port = 80, weight = 0, priority = 0},
}
test(route, ctx)
-- update, version changed
ctx.upstream_version = 2
test(route, ctx)
}
}
--- request
GET /t
--- response_body
host: 39.97.63.215 count: 4
host: 39.97.63.216 count: 4
host: 39.97.63.217 count: 4
host: 39.97.63.215 count: 4
host: 39.97.63.216 count: 4
host: 39.97.63.217 count: 4
host: 39.97.63.218 count: 12
=== TEST 4: chash
--- config
location /t {
content_by_lua_block {
local up_conf = {
type = "chash",
key = "remote_addr",
nodes = {
{host = "39.97.63.215", port = 80, weight = 1, priority = 0},
{host = "39.97.63.216", port = 81, weight = 1, priority = 0},
{host = "39.97.63.217", port = 82, weight = 1, priority = 0},
}
}
local ctx = {
var = {remote_addr = "127.0.0.1"},
}
ctx.upstream_conf = up_conf
ctx.upstream_version = 1
ctx.upstream_key = up_conf.type .. "#route_" .. "id"
test(route, ctx)
-- cached by version
up_conf.nodes = {
{host = "39.97.63.218", port = 80, weight = 1, priority = 0},
{host = "39.97.63.219", port = 80, weight = 0, priority = 0},
}
test(route, ctx)
-- update, version changed
ctx.upstream_version = 2
test(route, ctx)
}
}
--- request
GET /t
--- response_body
host: 39.97.63.215 count: 12
host: 39.97.63.215 count: 12
host: 39.97.63.218 count: 12
=== TEST 5: return item directly if only have one item in `nodes`
--- config
location /t {
content_by_lua_block {
local up_conf = {
type = "roundrobin",
nodes = {
{host = "39.97.63.215", port = 80, weight = 1, priority = 0},
{host = "39.97.63.216", port = 81, weight = 1, priority = 0},
{host = "39.97.63.217", port = 82, weight = 1, priority = 0},
}
}
local ctx = {}
ctx.upstream_conf = up_conf
ctx.upstream_version = 1
ctx.upstream_key = up_conf.type .. "#route_" .. "id"
test(route, ctx)
-- one item in nodes, return it directly
up_conf.nodes = {
{host = "39.97.63.218", port = 80, weight = 1, priority = 0},
}
test(route, ctx)
}
}
--- request
GET /t
--- response_body
host: 39.97.63.215 count: 4
host: 39.97.63.216 count: 4
host: 39.97.63.217 count: 4
host: 39.97.63.218 count: 12

View File

@@ -0,0 +1,163 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: set consumer_group(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumer_groups/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"count": 200,
"time_window": 60,
"rejected_code": 503,
"group": "consumer_group_1"
}
}
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- error_code: 201
--- response_body
passed
=== TEST 2: add consumer
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/consumers/1',
ngx.HTTP_PUT,
[[{
"username": "1",
"plugins": {
"key-auth": {
"key": "auth-one"
}
},
"group_id": "1"
}]]
)
if code >= 300 then
ngx.status = code
ngx.print(message)
return
end
ngx.say(message)
}
}
--- response_body
passed
=== TEST 3: delete consumer_group(wrong header)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/consumer_groups/1?force=anyvalue',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 400 message: {"error_msg":"can not delete this consumer group, consumer [1] is still using it now"}
=== TEST 4: delete consumer_group(without force delete header)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/consumer_groups/1',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 400 message: {"error_msg":"can not delete this consumer group, consumer [1] is still using it now"}
=== TEST 5: delete consumer_group(force delete)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/consumer_groups/1?force=true',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body chomp
[delete] code: 200 message: passed
=== TEST 6: delete consumer
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/consumers/1',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body chomp
[delete] code: 200 message: passed

View File

@@ -0,0 +1,549 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: PUT
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, body = t('/apisix/admin/consumer_groups/company_a',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
}]],
[[{
"value": {
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
},
"key": "/apisix/consumer_groups/company_a"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/consumer_groups/company_a'))
local create_time = res.body.node.value.create_time
assert(create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
}
}
--- response_body
passed
=== TEST 2: GET
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumer_groups/company_a',
ngx.HTTP_GET,
nil,
[[{
"value": {
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
},
"key": "/apisix/consumer_groups/company_a"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 3: GET all
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumer_groups',
ngx.HTTP_GET,
nil,
[[{
"total": 1,
"list": [
{
"key": "/apisix/consumer_groups/company_a",
"value": {
"plugins": {
"limit-count": {
"time_window": 60,
"policy": "local",
"count": 2,
"key": "remote_addr",
"rejected_code": 503
}
}
}
}
]
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 4: PATCH
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local res = assert(etcd.get('/consumer_groups/company_a'))
local prev_create_time = res.body.node.value.create_time
assert(prev_create_time ~= nil, "create_time is nil")
local prev_update_time = res.body.node.value.update_time
assert(prev_update_time ~= nil, "update_time is nil")
ngx.sleep(1)
local code, body = t('/apisix/admin/consumer_groups/company_a',
ngx.HTTP_PATCH,
[[{
"plugins": {
"limit-count": {
"count": 3,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}}]],
[[{
"value": {
"plugins": {
"limit-count": {
"count": 3,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
},
"key": "/apisix/consumer_groups/company_a"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/consumer_groups/company_a'))
local create_time = res.body.node.value.create_time
assert(prev_create_time == create_time, "create_time mismatched")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
assert(prev_update_time ~= update_time, "update_time should be changed")
}
}
--- response_body
passed
=== TEST 5: PATCH (sub path)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local res = assert(etcd.get('/consumer_groups/company_a'))
local prev_create_time = res.body.node.value.create_time
assert(prev_create_time ~= nil, "create_time is nil")
local prev_update_time = res.body.node.value.update_time
assert(prev_update_time ~= nil, "update_time is nil")
ngx.sleep(1)
local code, body = t('/apisix/admin/consumer_groups/company_a/plugins',
ngx.HTTP_PATCH,
[[{
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}]],
[[{
"value": {
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
},
"key": "/apisix/consumer_groups/company_a"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/consumer_groups/company_a'))
local create_time = res.body.node.value.create_time
assert(prev_create_time == create_time, "create_time mismatched")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
assert(prev_update_time ~= update_time, "update_time should be changed")
}
}
--- response_body
passed
=== TEST 6: invalid plugin
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumer_groups/company_a',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"rejected_code": 503,
"time_window": 60,
"key": "remote_addr"
}
}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- response_body
{"error_msg":"failed to check the configuration of plugin limit-count err: property \"count\" is required"}
--- error_code: 400
=== TEST 7: PUT (with non-plugin fields)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, body = t('/apisix/admin/consumer_groups/company_a',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
},
"labels": {
"你好": "世界"
},
"desc": "blah"
}]],
[[{
"value": {
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
},
"labels": {
"你好": "世界"
},
"desc": "blah"
},
"key": "/apisix/consumer_groups/company_a"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/consumer_groups/company_a'))
local create_time = res.body.node.value.create_time
assert(create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
}
}
--- response_body
passed
=== TEST 8: GET (with non-plugin fields)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumer_groups/company_a',
ngx.HTTP_GET,
nil,
[[{
"value": {
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
},
"labels": {
"你好": "世界"
},
"desc": "blah"
},
"key": "/apisix/consumer_groups/company_a"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 9: invalid non-plugin fields
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumer_groups/company_a',
ngx.HTTP_PUT,
[[{
"labels": "a",
"plugins": {
}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- response_body
{"error_msg":"invalid configuration: property \"labels\" validation failed: wrong type: expected object, got string"}
--- error_code: 400
=== TEST 10: set consumer-group
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, body = t('/apisix/admin/consumer_groups/company_a',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/consumer_groups/company_a'))
local create_time = res.body.node.value.create_time
assert(create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
}
}
--- response_body
passed
=== TEST 11: add consumer with group
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/foobar',
ngx.HTTP_PUT,
[[{
"username": "foobar",
"plugins": {
"key-auth": {
"key": "auth-two"
}
},
"group_id": "company_a"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 12: delete-consumer group failed
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumer_groups/company_a',
ngx.HTTP_DELETE
)
ngx.print(body)
}
}
--- response_body
{"error_msg":"can not delete this consumer group, consumer [foobar] is still using it now"}
=== TEST 13: delete consumer
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/foobar',
ngx.HTTP_DELETE
)
ngx.say(body)
}
}
--- response_body
passed
=== TEST 14: delete consumer-group
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumer_groups/company_a',
ngx.HTTP_DELETE
)
ngx.say(body)
}
}
--- response_body
passed
=== TEST 15: add consumer with invalid group
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/foobar',
ngx.HTTP_PUT,
[[{
"username": "foobar",
"plugins": {
"key-auth": {
"key": "auth-two"
}
},
"group_id": "invalid_group"
}]]
)
assert(code >= 300)
ngx.say(body)
}
}
--- response_body_like
.*failed to fetch consumer group info by consumer group id.*

View File

@@ -0,0 +1,362 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
run_tests;
__DATA__
=== TEST 1: add consumer with username
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers',
ngx.HTTP_PUT,
[[{
"username":"jack",
"desc": "new consumer"
}]],
[[{
"value": {
"username": "jack",
"desc": "new consumer"
},
"key": "/apisix/consumers/jack"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 2: update consumer with username and plugins
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local res = assert(etcd.get('/consumers/jack'))
local prev_create_time = res.body.node.value.create_time
assert(prev_create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
ngx.sleep(1)
local code, body = t('/apisix/admin/consumers',
ngx.HTTP_PUT,
[[{
"username": "jack",
"desc": "new consumer",
"plugins": {
"key-auth": {
"key": "auth-one"
}
}
}]],
[[{
"value": {
"username": "jack",
"desc": "new consumer",
"plugins": {
"key-auth": {
"key": "4y+JvURBE6ZwRbbgaryrhg=="
}
}
},
"key": "/apisix/consumers/jack"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/consumers/jack'))
local create_time = res.body.node.value.create_time
assert(prev_create_time == create_time, "create_time mismatched")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
}
}
--- request
GET /t
--- response_body
passed
=== TEST 3: get consumer
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/jack',
ngx.HTTP_GET,
nil,
[[{
"value": {
"username": "jack",
"desc": "new consumer",
"plugins": {
"key-auth": {
"key": "auth-one"
}
}
},
"key": "/apisix/consumers/jack"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 4: delete consumer
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/jack',
ngx.HTTP_DELETE
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 5: delete consumer(id: not_found)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code = t('/apisix/admin/consumers/not_found',
ngx.HTTP_DELETE,
nil
)
ngx.say("[delete] code: ", code)
}
}
--- request
GET /t
--- response_body
[delete] code: 404
=== TEST 6: missing username
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers',
ngx.HTTP_PUT,
[[{
"id":"jack"
}]],
[[{
"value": {
"id": "jack"
}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"username\" is required"}
=== TEST 7: consumer username allows '-' in it
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers',
ngx.HTTP_PUT,
[[{
"username":"Jack-and-Rose_123"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 201
=== TEST 8: add consumer with labels
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers',
ngx.HTTP_PUT,
[[{
"username":"jack",
"desc": "new consumer",
"labels": {
"build":"16",
"env":"production",
"version":"v2"
}
}]],
[[{
"value": {
"username": "jack",
"desc": "new consumer",
"labels": {
"build":"16",
"env":"production",
"version":"v2"
}
},
"key": "/apisix/consumers/jack"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 9: invalid format of label value: set consumer
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers',
ngx.HTTP_PUT,
[[{
"username":"jack",
"desc": "new consumer",
"labels": {
"env": ["production", "release"]
}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"labels\" validation failed: failed to validate env (matching \".*\"): wrong type: expected string, got table"}
=== TEST 10: post consumers
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers',
ngx.HTTP_POST,
""
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 405
--- response_body
{"error_msg":"not supported `POST` method for consumer"}
=== TEST 11: add consumer with create_time and update_time(pony)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers',
ngx.HTTP_PUT,
[[{
"username":"pony",
"desc": "new consumer",
"create_time": 1602883670,
"update_time": 1602893670
}]],
[[{
"value": {
"username": "pony",
"desc": "new consumer",
"create_time": 1602883670,
"update_time": 1602893670
},
"key": "/apisix/consumers/pony"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body eval
qr/\{"error_msg":"the property is forbidden:.*"\}/

View File

@@ -0,0 +1,176 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: not unwanted data, PUT
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/consumers',
ngx.HTTP_PUT,
[[{
"username":"jack"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
res.value.create_time = nil
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/consumers/jack","value":{"username":"jack"}}
=== TEST 2: not unwanted data, GET
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/consumers/jack',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.createdIndex ~= nil)
res.createdIndex = nil
assert(res.modifiedIndex ~= nil)
res.modifiedIndex = nil
assert(res.value.create_time ~= nil)
res.value.create_time = nil
assert(res.value.update_time ~= nil)
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/consumers/jack","value":{"username":"jack"}}
=== TEST 3: not unwanted data, DELETE
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/consumers/jack',
ngx.HTTP_DELETE
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(json.encode(res))
}
}
--- response_body
{"deleted":"1","key":"/apisix/consumers/jack"}
=== TEST 4: list empty resources
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/consumers',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(json.encode(res))
}
}
--- response_body
{"list":[],"total":0}
=== TEST 5: mismatched username, PUT
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/consumers/jack1',
ngx.HTTP_PUT,
[[{
"username":"jack"
}]]
)
ngx.print(message)
}
}
--- response_body
{"error_msg":"wrong username"}

View File

@@ -0,0 +1,494 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
run_tests;
__DATA__
=== TEST 1: create a credential for invalid consumer: consumer not found error
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/jack/credentials/credential_a',
ngx.HTTP_PUT,
[[{
"plugins": {
"key-auth": {
"key": "the-key"
}
}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 404
--- response_body
{"error_msg":"consumer not found"}
=== TEST 2: add a consumer
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers',
ngx.HTTP_PUT,
[[{
"username":"jack",
"desc": "new consumer",
"plugins": {
"basic-auth": {
"username": "the-user",
"password": "the-password"
}
}
}]],
[[{
"key": "/apisix/consumers/jack",
"value":
{
"username":"jack",
"desc": "new consumer",
"plugins": {
"basic-auth": {
"username": "the-user",
"password": "WvF5kpaLvIzjuk4GNIMTJg=="
}
}
}
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 3: add a credentials with basic-auth for the consumer jack, should success
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/jack/credentials/credential_a',
ngx.HTTP_PUT,
[[{
"desc": "basic-auth for jack",
"plugins": {
"basic-auth": {
"username": "the-user",
"password": "the-password"
}
}
}]],
[[{
"value":{
"desc":"basic-auth for jack",
"id":"credential_a",
"plugins":{"basic-auth":{"username":"the-user","password":"WvF5kpaLvIzjuk4GNIMTJg=="}}
},
"key":"/apisix/consumers/jack/credentials/credential_a"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 4: add a credential with key-auth for the consumer jack, should success
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/jack/credentials/credential_b',
ngx.HTTP_PUT,
[[{
"desc": "key-auth for jack",
"plugins": {
"key-auth": {
"key": "the-key"
}
}
}]],
[[{
"value":{
"desc":"key-auth for jack",
"id":"credential_b",
"plugins":{"key-auth":{"key":"JCX7x1qN5e9kHt0GuJfWpw=="}}
},
"key":"/apisix/consumers/jack/credentials/credential_b"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 5: add a credential with a plugin which is not a auth plugin, should fail
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/jack/credentials/credential_b',
ngx.HTTP_PUT,
[[{
"desc": "limit-conn for jack",
"plugins": {
"limit-conn": {
"conn": 1,
"burst": 0,
"default_conn_delay": 0.1,
"rejected_code": 503,
"key_type": "var",
"key": "http_a"
}
}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"only supports auth type plugins in consumer credential"}
=== TEST 6: list consumers: should not contain credential
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, body, res = t('/apisix/admin/consumers', ngx.HTTP_GET)
ngx.status = code
res = json.decode(res)
assert(res.total == 1)
assert(res.list[1].key == "/apisix/consumers/jack")
}
}
--- request
GET /t
--- response_body
=== TEST 7: list credentials: should contain credential_a and credential_b
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, body, res = t('/apisix/admin/consumers/jack/credentials', ngx.HTTP_GET)
ngx.status = code
res = json.decode(res)
assert(res.total == 2)
assert(res.list[1].key == "/apisix/consumers/jack/credentials/credential_a")
assert(res.list[2].key == "/apisix/consumers/jack/credentials/credential_b")
}
}
--- request
GET /t
--- response_body
=== TEST 8: get a credential
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/jack/credentials/credential_b',
ngx.HTTP_GET,
nil,
[[{
"key": "/apisix/consumers/jack/credentials/credential_b",
"value": {
"desc": "key-auth for jack",
"plugins": {"key-auth": {"key": "the-key"}
}}
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 9: update credential: should ok
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/jack/credentials/credential_b',
ngx.HTTP_PUT,
[[{
"desc": "new description",
"plugins": {
"key-auth": {
"key": "new-key"
}
}
}]],
[[{
"key": "/apisix/consumers/jack/credentials/credential_b",
"value": {
"desc": "new description",
"plugins": {
"key-auth": {
"key": "523EisB/dvqlIT9RzfF3ZQ=="
}
}
}
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 10: delete credential
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/jack/credentials/credential_a', ngx.HTTP_DELETE)
assert(code == 200)
ngx.status = code
code, body, res = t('/apisix/admin/consumers/jack/credentials', ngx.HTTP_GET)
res = json.decode(res)
assert(res.total == 1)
assert(res.list[1].key == "/apisix/consumers/jack/credentials/credential_b")
}
}
--- request
GET /t
--- response_body
=== TEST 11: create a credential has more than one plugin: should not ok
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/jack/credentials/xxx-yyy-zzz',
ngx.HTTP_PUT,
[[{
"plugins": {
"key-auth": {"key": "the-key"},
"basic-auth": {"username": "the-user", "password": "the-password"}
}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"plugins\" validation failed: expect object to have at most 1 properties"}
=== TEST 12: delete consumer
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/jack',
ngx.HTTP_DELETE
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 13: list credentials: should get 404 because the consumer is deleted
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/jack/credentials', ngx.HTTP_GET)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 404
--- response_body
{"message":"Key not found"}
=== TEST 14: add a consumer
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers',
ngx.HTTP_PUT,
[[{
"username":"jack"
}]]
)
if ngx.status >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 15: add a credential with key-auth for the consumer jack (id in the payload but not in uri), should success
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/jack/credentials',
ngx.HTTP_PUT,
[[{
"id": "d79a5aa3",
"desc": "key-auth for jack",
"plugins": {
"key-auth": {
"key": "the-key"
}
}
}]],
[[{
"value":{
"desc":"key-auth for jack",
"id":"d79a5aa3",
"plugins":{"key-auth":{"key":"JCX7x1qN5e9kHt0GuJfWpw=="}}
},
"key":"/apisix/consumers/jack/credentials/d79a5aa3"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 16: add a credential with key-auth for the consumer jack but missing id in uri and payload, should fail
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers/jack/credentials',
ngx.HTTP_PUT,
[[{
"desc": "key-auth for jack",
"plugins": {
"key-auth": {
"key": "the-key"
}
}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"missing credential id"}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,506 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
run_tests;
__DATA__
=== TEST 1: set global rules
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, body = t('/apisix/admin/global_rules/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
}]],
[[{
"value": {
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
},
"key": "/apisix/global_rules/1"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/global_rules/1'))
local create_time = res.body.node.value.create_time
assert(create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
}
}
--- request
GET /t
--- response_body
passed
=== TEST 2: get global rules
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/global_rules/1',
ngx.HTTP_GET,
nil,
[[{
"value": {
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
},
"key": "/apisix/global_rules/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 3: list global rules
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/global_rules',
ngx.HTTP_GET,
nil,
[[{
"total": 1,
"list": [
{
"key": "/apisix/global_rules/1",
"value": {
"plugins": {
"limit-count": {
"time_window": 60,
"policy": "local",
"count": 2,
"key": "remote_addr",
"rejected_code": 503
}
}
}
}
]
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 4: PATCH global rules
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local res = assert(etcd.get('/global_rules/1'))
local prev_create_time = res.body.node.value.create_time
assert(prev_create_time ~= nil, "create_time is nil")
local prev_update_time = res.body.node.value.update_time
assert(prev_update_time ~= nil, "update_time is nil")
ngx.sleep(1)
local code, body = t('/apisix/admin/global_rules/1',
ngx.HTTP_PATCH,
[[{
"plugins": {
"limit-count": {
"count": 3,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}}]],
[[{
"value": {
"plugins": {
"limit-count": {
"count": 3,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
},
"key": "/apisix/global_rules/1"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/global_rules/1'))
local create_time = res.body.node.value.create_time
assert(prev_create_time == create_time, "create_time mismatched")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
assert(prev_update_time ~= update_time, "update_time should be changed")
}
}
--- request
GET /t
--- response_body
passed
=== TEST 5: PATCH global rules (sub path)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local res = assert(etcd.get('/global_rules/1'))
local prev_create_time = res.body.node.value.create_time
assert(prev_create_time ~= nil, "create_time is nil")
local prev_update_time = res.body.node.value.update_time
assert(prev_update_time ~= nil, "update_time is nil")
ngx.sleep(1)
local code, body = t('/apisix/admin/global_rules/1/plugins',
ngx.HTTP_PATCH,
[[{
"limit-count": {
"count": 3,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}]],
[[{
"value": {
"plugins": {
"limit-count": {
"count": 3,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
},
"key": "/apisix/global_rules/1"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/global_rules/1'))
local create_time = res.body.node.value.create_time
assert(prev_create_time == create_time, "create_time mismatched")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
assert(prev_update_time ~= update_time, "update_time should be changed")
}
}
--- request
GET /t
--- response_body
passed
=== TEST 6: delete global rules
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/global_rules/1',
ngx.HTTP_DELETE
)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[delete] code: 200 message: passed
=== TEST 7: delete global rules(not_found)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code = t('/apisix/admin/global_rules/1',
ngx.HTTP_DELETE
)
ngx.say("[delete] code: ", code)
}
}
--- request
GET /t
--- response_body
[delete] code: 404
=== TEST 8: set global rules(missing plugins)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/global_rules/1',
ngx.HTTP_PUT,
[[{}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"plugins\" is required"}
=== TEST 9: string id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/global_rules/a-b-c-ABC_0123',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 10: string id(DELETE)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/global_rules/a-b-c-ABC_0123',
ngx.HTTP_DELETE
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 11: not unwanted data, PUT
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/global_rules/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"proxy-rewrite": {
"uri": "/"
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
res.value.create_time = nil
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/global_rules/1","value":{"id":"1","plugins":{"proxy-rewrite":{"uri":"/","use_real_request_uri_unsafe":false}}}}
--- request
GET /t
=== TEST 12: not unwanted data, PATCH
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/global_rules/1',
ngx.HTTP_PATCH,
[[{
"plugins": {
"proxy-rewrite": {
"uri": "/"
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
res.value.create_time = nil
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/global_rules/1","value":{"id":"1","plugins":{"proxy-rewrite":{"uri":"/","use_real_request_uri_unsafe":false}}}}
--- request
GET /t
=== TEST 13: not unwanted data, GET
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/global_rules/1',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.createdIndex ~= nil)
res.createdIndex = nil
assert(res.modifiedIndex ~= nil)
res.modifiedIndex = nil
assert(res.value.create_time ~= nil)
res.value.create_time = nil
assert(res.value.update_time ~= nil)
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/global_rules/1","value":{"id":"1","plugins":{"proxy-rewrite":{"uri":"/","use_real_request_uri_unsafe":false}}}}
--- request
GET /t
=== TEST 14: not unwanted data, DELETE
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/global_rules/1',
ngx.HTTP_DELETE
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(json.encode(res))
}
}
--- response_body
{"deleted":"1","key":"/apisix/global_rules/1"}
--- request
GET /t

View File

@@ -0,0 +1,146 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: list empty resources
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/global_rules',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(json.encode(res))
}
}
--- response_body
{"list":[],"total":0}
=== TEST 2: set global rule
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/global_rules/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"proxy-rewrite": {
"uri": "/"
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
res.value.create_time = nil
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/global_rules/1","value":{"id":"1","plugins":{"proxy-rewrite":{"uri":"/","use_real_request_uri_unsafe":false}}}}
=== TEST 3: list global rules
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/global_rules',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.total == 1)
assert(#res.list == 1)
assert(res.list[1].createdIndex ~= nil)
assert(res.list[1].modifiedIndex ~= nil)
assert(res.list[1].key == "/apisix/global_rules/1")
assert(res.list[1].value ~= nil)
ngx.say(message)
}
}
--- response_body_like
passed
=== TEST 4: delete global rules
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/global_rules/1',
ngx.HTTP_DELETE
)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 200 message: passed

View File

@@ -0,0 +1,521 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
my $init_by_lua_block = <<_EOC_;
require "resty.core"
apisix = require("apisix")
apisix.http_init()
json = require("toolkit.json")
req_data = json.decode([[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"checks": {}
},
"uri": "/index.html"
}]])
exp_data = {
value = req_data,
key = "/apisix/routes/1",
}
_EOC_
$block->set_value("init_by_lua_block", $init_by_lua_block);
if (!defined $block->request) {
$block->set_value("request", "GET /t");
}
});
run_tests;
__DATA__
=== TEST 1: active
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
req_data.upstream.checks = json.decode([[{
"active": {
"http_path": "/status",
"host": "foo.com",
"healthy": {
"interval": 2,
"successes": 1
},
"unhealthy": {
"interval": 1,
"http_failures": 2
}
}
}]])
exp_data.value.upstream.checks = req_data.upstream.checks
local code, body, res = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
req_data,
exp_data
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 2: passive
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
req_data.upstream.checks = json.decode([[{
"active": {
"http_path": "/status",
"host": "foo.com",
"healthy": {
"interval": 2,
"successes": 1
}
},
"passive": {
"healthy": {
"http_statuses": [200, 201],
"successes": 1
},
"unhealthy": {
"http_statuses": [500],
"http_failures": 2
}
}
}]])
exp_data.value.upstream.checks = req_data.upstream.checks
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
req_data,
exp_data
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 3: invalid route: active.healthy.successes counter exceed maximum value
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
req_data.upstream.checks = json.decode([[{
"active": {
"healthy": {
"successes": 255
}
}
}]])
local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, req_data)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"upstream\" validation failed: property \"checks\" validation failed: property \"active\" validation failed: property \"healthy\" validation failed: property \"successes\" validation failed: expected 255 to be at most 254"}
=== TEST 4: invalid route: active.healthy.successes counter below the minimum value
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
req_data.upstream.checks = json.decode([[{
"active": {
"healthy": {
"successes": 0
}
}
}]])
local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, req_data)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"upstream\" validation failed: property \"checks\" validation failed: property \"active\" validation failed: property \"healthy\" validation failed: property \"successes\" validation failed: expected 0 to be at least 1"}
=== TEST 5: invalid route: wrong passive.unhealthy.http_statuses
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
req_data.upstream.checks = json.decode([[{
"passive": {
"unhealthy": {
"http_statuses": [500, 600]
}
}
}]])
local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, req_data)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"upstream\" validation failed: property \"checks\" validation failed: property \"passive\" validation failed: property \"unhealthy\" validation failed: property \"http_statuses\" validation failed: failed to validate item 2: expected 600 to be at most 599"}
=== TEST 6: invalid route: wrong active.type
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
req_data.upstream.checks = json.decode([[{
"active": {
"type": "udp"
}
}]])
local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, req_data)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"upstream\" validation failed: property \"checks\" validation failed: property \"active\" validation failed: property \"type\" validation failed: matches none of the enum values"}
=== TEST 7: invalid route: duplicate items in active.healthy.http_statuses
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
req_data.upstream.checks = json.decode([[{
"active": {
"healthy": {
"http_statuses": [200, 200]
}
}
}]])
local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, req_data)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"upstream\" validation failed: property \"checks\" validation failed: property \"active\" validation failed: property \"healthy\" validation failed: property \"http_statuses\" validation failed: expected unique items but items 1 and 2 are equal"}
=== TEST 8: invalid route: active.unhealthy.http_failure is a floating point value
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
req_data.upstream.checks = json.decode([[{
"active": {
"unhealthy": {
"http_failures": 3.1
}
}
}]])
local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, req_data)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"upstream\" validation failed: property \"checks\" validation failed: property \"active\" validation failed: property \"unhealthy\" validation failed: property \"http_failures\" validation failed: wrong type: expected integer, got number"}
=== TEST 9: valid req_headers
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
req_data.upstream.checks = json.decode([[{
"active": {
"http_path": "/status",
"host": "foo.com",
"healthy": {
"interval": 2,
"successes": 1
},
"req_headers": ["User-Agent: curl/7.29.0"]
}
}]])
exp_data.value.upstream.checks = req_data.upstream.checks
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
req_data,
exp_data
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 10: multiple request headers
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
req_data.upstream.checks = json.decode([[{
"active": {
"http_path": "/status",
"host": "foo.com",
"healthy": {
"interval": 2,
"successes": 1
},
"req_headers": ["User-Agent: curl/7.29.0", "Accept: */*"]
}
}]])
exp_data.value.upstream.checks = req_data.upstream.checks
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
req_data,
exp_data
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 11: invalid req_headers
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
req_data.upstream.checks = json.decode([[{
"active": {
"http_path": "/status",
"host": "foo.com",
"healthy": {
"interval": 2,
"successes": 1
},
"req_headers": ["User-Agent: curl/7.29.0", 2233]
}
}]])
exp_data.value.upstream.checks = req_data.upstream.checks
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
req_data,
exp_data
)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"upstream\" validation failed: property \"checks\" validation failed: property \"active\" validation failed: property \"req_headers\" validation failed: failed to validate item 2: wrong type: expected string, got number"}
=== TEST 12: only passive
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
req_data.upstream.checks = json.decode([[{
"passive": {
"healthy": {
"http_statuses": [200, 201],
"successes": 1
},
"unhealthy": {
"http_statuses": [500],
"http_failures": 2
}
}
}]])
exp_data.value.upstream.checks = req_data.upstream.checks
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
req_data,
exp_data
)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"upstream\" validation failed: property \"checks\" validation failed: object matches none of the required: [\"active\"] or [\"active\",\"passive\"]"}
=== TEST 13: only active
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
req_data.upstream.checks = json.decode([[{
"active": {
"http_path": "/status",
"host": "foo.com",
"healthy": {
"interval": 2,
"successes": 1
},
"unhealthy": {
"interval": 1,
"http_failures": 2
}
}
}]])
exp_data.value.upstream.checks.active = req_data.upstream.checks.active
exp_data.value.upstream.checks.passive = {
type = "http",
healthy = {
http_statuses = { 200, 201, 202, 203, 204, 205, 206, 207, 208, 226,
300, 301, 302, 303, 304, 305, 306, 307, 308 },
successes = 0,
},
unhealthy = {
http_statuses = { 429, 500, 503 },
tcp_failures = 0,
timeouts = 0,
http_failures = 0,
}
}
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
req_data,
exp_data
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 14: number type timeout
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
req_data.upstream.checks = json.decode([[{
"active": {
"http_path": "/status",
"host": "foo.com",
"timeout": 1.01,
"healthy": {
"interval": 2,
"successes": 1
},
"unhealthy": {
"interval": 1,
"http_failures": 2
}
}
}]])
exp_data.value.upstream.checks = req_data.upstream.checks
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
req_data,
exp_data
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed

View File

@@ -0,0 +1,170 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { readFile } from 'node:fs/promises';
import { resolve } from 'node:path';
import { request as requestAdminAPI } from '../ts/admin_api';
describe('Resource Metadata', () => {
describe('Consumer', () => {
it('should ensure additionalProperties is false', () =>
expect(
requestAdminAPI(
'/apisix/admin/consumers/jack',
'PUT',
{
username: 'jack',
invalid: true,
},
undefined,
{ validateStatus: () => true },
),
).resolves.toMatchObject({ status: 400 }));
it('should accept desc field', () =>
expect(
requestAdminAPI('/apisix/admin/consumers/jack', 'PUT', {
username: 'jack',
desc: 'test_desc',
}),
).resolves.not.toThrow());
});
describe('Consumer Credentials', () => {
it('should ensure additionalProperties is false', () =>
expect(
requestAdminAPI(
'/apisix/admin/consumers/jack/credentials/cred1',
'PUT',
{
plugins: { 'key-auth': { key: 'test' } },
invalid: true,
},
undefined,
{ validateStatus: () => true },
),
).resolves.toMatchObject({ status: 400 }));
it('should accept name field', () =>
expect(
requestAdminAPI(
'/apisix/admin/consumers/jack/credentials/cred1',
'PUT',
{
name: 'test_name',
plugins: { 'key-auth': { key: 'test' } },
},
),
).resolves.not.toThrow());
});
describe('SSL', () => {
const path = resolve(__dirname, '../certs/');
let cert: string;
let key: string;
beforeAll(async () => {
cert = await readFile(resolve(path, 'apisix.crt'), 'utf-8');
key = await readFile(resolve(path, 'apisix.key'), 'utf-8');
});
it('should ensure additionalProperties is false', () =>
expect(
requestAdminAPI(
'/apisix/admin/ssls/ssl1',
'PUT',
{ sni: 'test.com', cert, key, invalid: true },
undefined,
{ validateStatus: () => true },
),
).resolves.toMatchObject({ status: 400 }));
it('should accept desc field', () =>
expect(
requestAdminAPI('/apisix/admin/ssls/ssl1', 'PUT', {
desc: 'test_desc',
sni: 'test.com',
cert,
key,
}),
).resolves.not.toThrow());
});
describe('Proto', () => {
it('should ensure additionalProperties is false', () =>
expect(
requestAdminAPI(
'/apisix/admin/protos/proto1',
'PUT',
{ content: 'syntax = "proto3";', invalid: true },
undefined,
{ validateStatus: () => true },
),
).resolves.toMatchObject({ status: 400 }));
it('should accept name/labels field', () =>
expect(
requestAdminAPI('/apisix/admin/protos/proto1', 'PUT', {
name: 'test_name',
labels: { test: 'test' },
content: 'syntax = "proto3";',
}),
).resolves.not.toThrow());
});
describe('Stream Route', () => {
it('should ensure additionalProperties is false', () =>
expect(
requestAdminAPI(
'/apisix/admin/stream_routes/sr1',
'PUT',
{ upstream: { nodes: { '127.0.0.1:5432': 1 } }, invalid: true },
undefined,
{ validateStatus: () => true },
),
).resolves.toMatchObject({ status: 400 }));
it('should accept name field', () =>
expect(
requestAdminAPI('/apisix/admin/stream_routes/sr1', 'PUT', {
name: 'test_name',
upstream: { nodes: { '127.0.0.1:5432': 1 } },
}),
).resolves.not.toThrow());
});
describe('Consumer Group', () => {
it('should ensure additionalProperties is false', () =>
expect(
requestAdminAPI(
'/apisix/admin/consumer_groups/cg1',
'PUT',
{ plugins: {}, invalid: true },
undefined,
{ validateStatus: () => true },
),
).resolves.toMatchObject({ status: 400 }));
it('should accept name field', () =>
expect(
requestAdminAPI('/apisix/admin/consumer_groups/cg1', 'PUT', {
name: 'test_name',
plugins: {},
}),
).resolves.not.toThrow());
});
});

View File

@@ -0,0 +1,36 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
use_hup();
run_tests();
__DATA__
=== TEST 1: test
--- timeout: 15
--- max_size: 204800
--- exec
cd t && pnpm test admin/metadata.spec.ts 2>&1
--- no_error_log
failed to execute the script with status
--- response_body eval
qr/PASS admin\/metadata.spec.ts/

View File

@@ -0,0 +1,163 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: set plugin_configs(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_configs/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503
}
}
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- error_code: 201
--- response_body
passed
=== TEST 2: add route
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"plugin_config_id": 1,
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
},
"uri": "/index.html"
}]]
)
if code >= 300 then
ngx.status = code
ngx.print(message)
return
end
ngx.say(message)
}
}
--- response_body
passed
=== TEST 3: delete plugin_configs(wrong header)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/plugin_configs/1?force=anyvalue',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 400 message: {"error_msg":"can not delete this plugin config, route [1] is still using it now"}
=== TEST 4: delete plugin_configs(without force delete header)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/plugin_configs/1',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 400 message: {"error_msg":"can not delete this plugin config, route [1] is still using it now"}
=== TEST 5: delete plugin_configs(force delete)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/plugin_configs/1?force=true',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body chomp
[delete] code: 200 message: passed
=== TEST 6: delete route
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/routes/1',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body chomp
[delete] code: 200 message: passed

View File

@@ -0,0 +1,523 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: PUT
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, body = t('/apisix/admin/plugin_configs/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
}]],
[[{
"value": {
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
},
"key": "/apisix/plugin_configs/1"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/plugin_configs/1'))
local create_time = res.body.node.value.create_time
assert(create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
}
}
--- response_body
passed
=== TEST 2: GET
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_configs/1',
ngx.HTTP_GET,
nil,
[[{
"value": {
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
},
"key": "/apisix/plugin_configs/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 3: GET all
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_configs',
ngx.HTTP_GET,
nil,
[[{
"total": 1,
"list": [
{
"key": "/apisix/plugin_configs/1",
"value": {
"plugins": {
"limit-count": {
"time_window": 60,
"policy": "local",
"count": 2,
"key": "remote_addr",
"rejected_code": 503
}
}
}
}
]
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 4: PATCH
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local res = assert(etcd.get('/plugin_configs/1'))
local prev_create_time = res.body.node.value.create_time
assert(prev_create_time ~= nil, "create_time is nil")
local prev_update_time = res.body.node.value.update_time
assert(prev_update_time ~= nil, "update_time is nil")
ngx.sleep(1)
local code, body = t('/apisix/admin/plugin_configs/1',
ngx.HTTP_PATCH,
[[{
"plugins": {
"limit-count": {
"count": 3,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}}]],
[[{
"value": {
"plugins": {
"limit-count": {
"count": 3,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
},
"key": "/apisix/plugin_configs/1"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/plugin_configs/1'))
local create_time = res.body.node.value.create_time
assert(prev_create_time == create_time, "create_time mismatched")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
assert(prev_update_time ~= update_time, "update_time should be changed")
}
}
--- response_body
passed
=== TEST 5: PATCH (sub path)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local res = assert(etcd.get('/plugin_configs/1'))
local prev_create_time = res.body.node.value.create_time
assert(prev_create_time ~= nil, "create_time is nil")
local prev_update_time = res.body.node.value.update_time
assert(prev_update_time ~= nil, "update_time is nil")
ngx.sleep(1)
local code, body = t('/apisix/admin/plugin_configs/1/plugins',
ngx.HTTP_PATCH,
[[{
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}]],
[[{
"value": {
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
},
"key": "/apisix/plugin_configs/1"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/plugin_configs/1'))
local create_time = res.body.node.value.create_time
assert(prev_create_time == create_time, "create_time mismatched")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
assert(prev_update_time ~= update_time, "update_time should be changed")
}
}
--- response_body
passed
=== TEST 6: invalid plugin
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_configs/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"rejected_code": 503,
"time_window": 60,
"key": "remote_addr"
}
}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- response_body
{"error_msg":"failed to check the configuration of plugin limit-count err: property \"count\" is required"}
--- error_code: 400
=== TEST 7: PUT (with non-plugin fields)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, body = t('/apisix/admin/plugin_configs/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
},
"labels": {
"你好": "世界"
},
"desc": "blah"
}]],
[[{
"value": {
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
},
"labels": {
"你好": "世界"
},
"desc": "blah"
},
"key": "/apisix/plugin_configs/1"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/plugin_configs/1'))
local create_time = res.body.node.value.create_time
assert(create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
}
}
--- response_body
passed
=== TEST 8: GET (with non-plugin fields)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_configs/1',
ngx.HTTP_GET,
nil,
[[{
"value": {
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
},
"labels": {
"你好": "世界"
},
"desc": "blah"
},
"key": "/apisix/plugin_configs/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 9: invalid non-plugin fields
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_configs/1',
ngx.HTTP_PUT,
[[{
"labels": "a",
"plugins": {
}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- response_body
{"error_msg":"invalid configuration: property \"labels\" validation failed: wrong type: expected object, got string"}
--- error_code: 400
=== TEST 10: set plugin-configs(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, body = t('/apisix/admin/plugin_configs/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/plugin_configs/1'))
local create_time = res.body.node.value.create_time
assert(create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
}
}
--- response_body
passed
=== TEST 11: set route(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"plugin_config_id": 1,
"uri": "/index.html",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 12: delete-plugin configs failed(id: 1)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_configs/1',
ngx.HTTP_DELETE
)
ngx.print(body)
}
}
--- response_body
{"error_msg":"can not delete this plugin config, route [1] is still using it now"}
=== TEST 13: delete route(id: 1)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_DELETE
)
ngx.say(body)
}
}
--- response_body
passed
=== TEST 14: delete plugin-configs(id: 1)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_configs/1',
ngx.HTTP_DELETE
)
ngx.say(body)
}
}
--- response_body
passed

View File

@@ -0,0 +1,335 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
run_tests;
__DATA__
=== TEST 1: add plugin metadata
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_metadata/example-plugin',
ngx.HTTP_PUT,
[[{
"skey": "val",
"ikey": 1
}]],
[[{
"value": {
"skey": "val",
"ikey": 1
},
"key": "/apisix/plugin_metadata/example-plugin"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 2: update plugin metadata
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_metadata/example-plugin',
ngx.HTTP_PUT,
[[{
"skey": "val2",
"ikey": 2
}]],
[[{
"value": {
"skey": "val2",
"ikey": 2
}
}]]
)
ngx.status = code
ngx.say(body)
-- hit again
local code, body = t('/apisix/admin/plugin_metadata/example-plugin',
ngx.HTTP_PUT,
[[{
"skey": "val2",
"ikey": 2
}]],
[[{
"value": {
"skey": "val2",
"ikey": 2
}
}]]
)
ngx.say(code)
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
200
passed
=== TEST 3: get plugin metadata
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_metadata/example-plugin',
ngx.HTTP_GET,
nil,
[[{
"value": {
"skey": "val2",
"ikey": 2
}
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 4: delete plugin metadata
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_metadata/example-plugin', ngx.HTTP_DELETE)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 5: delete plugin metadata(key: not_found)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code = t('/apisix/admin/plugin_metadata/not_found', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code)
}
}
--- request
GET /t
--- response_body
[delete] code: 404
=== TEST 6: missing plugin name
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_metadata',
ngx.HTTP_PUT,
[[{"k": "v"}]],
[[{
"value": "sdf"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"missing plugin name"}
=== TEST 7: invalid plugin name
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_metadata/test',
ngx.HTTP_PUT,
[[{"k": "v"}]],
[[{
"value": "sdf"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid plugin name"}
=== TEST 8: verify metadata schema fail
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_metadata/example-plugin',
ngx.HTTP_PUT,
[[{
"skey": "val"
}]],
[[{
"value": {
"skey": "val",
"ikey": 1
}
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body eval
qr/\{"error_msg":"invalid configuration: property \\"ikey\\" is required"\}/
=== TEST 9: not unwanted data, PUT
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/plugin_metadata/example-plugin',
ngx.HTTP_PUT,
[[{
"skey": "val",
"ikey": 1
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/plugin_metadata/example-plugin","value":{"id":"example-plugin","ikey":1,"skey":"val"}}
--- request
GET /t
=== TEST 10: not unwanted data, GET
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/plugin_metadata/example-plugin', ngx.HTTP_GET)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.createdIndex ~= nil)
res.createdIndex = nil
assert(res.modifiedIndex ~= nil)
res.modifiedIndex = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/plugin_metadata/example-plugin","value":{"id":"example-plugin","ikey":1,"skey":"val"}}
--- request
GET /t
=== TEST 11: not unwanted data, DELETE
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/plugin_metadata/example-plugin', ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(json.encode(res))
}
}
--- response_body
{"deleted":"1","key":"/apisix/plugin_metadata/example-plugin"}
--- request
GET /t

View File

@@ -0,0 +1,61 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: list empty resources
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/plugin_metadata', ngx.HTTP_GET)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(json.encode(res))
}
}
--- response_body
{"list":[],"total":0}

View File

@@ -0,0 +1,429 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
workers(2);
add_block_preprocessor(sub {
my ($block) = @_;
$block;
});
run_tests;
__DATA__
=== TEST 1: reload plugins
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
-- now the plugin will be loaded twice,
-- one during startup and the other one by reload
local code, _, org_body = t('/apisix/admin/plugins/reload',
ngx.HTTP_PUT)
ngx.status = code
ngx.say(org_body)
ngx.sleep(1)
}
}
--- request
GET /t
--- response_body
done
--- grep_error_log eval
qr/sync local conf to etcd/
--- grep_error_log_out
sync local conf to etcd
--- error_log
load plugin times: 2
load plugin times: 2
start to hot reload plugins
start to hot reload plugins
=== TEST 2: reload plugins triggers plugin list sync
--- config
location /t {
content_by_lua_block {
local core = require "apisix.core"
local config_util = require("apisix.core.config_util")
ngx.sleep(1) -- make sure the sync happened when admin starts is already finished
local before_reload = true
local plugins_conf, err
plugins_conf, err = core.config.new("/plugins", {
automatic = true,
single_item = true,
filter = function(item)
-- called once before reload for sync data from admin
ngx.log(ngx.WARN, "reload plugins on node ",
before_reload and "before reload" or "after reload")
ngx.log(ngx.WARN, require("toolkit.json").encode(item.value))
end,
})
if not plugins_conf then
error("failed to create etcd instance for fetching /plugins : "
.. err)
end
ngx.sleep(1)
local data = [[
deployment:
role: traditional
role_traditional:
config_provider: etcd
admin:
admin_key: null
apisix:
node_listen: 1984
plugins:
- jwt-auth
stream_plugins:
- mqtt-proxy
]]
require("lib.test_admin").set_config_yaml(data)
before_reload = false
local t = require("lib.test_admin").test
local code, _, org_body = t('/apisix/admin/plugins/reload',
ngx.HTTP_PUT)
ngx.status = code
ngx.say(org_body)
ngx.sleep(1)
}
}
--- request
GET /t
--- response_body
done
--- grep_error_log eval
qr/reload plugins on node \w+ reload/
--- grep_error_log_out
reload plugins on node before reload
reload plugins on node after reload
--- error_log
filter(): [{"name":"jwt-auth"},{"name":"mqtt-proxy","stream":true}]
=== TEST 3: reload plugins when attributes changed
--- yaml_config
apisix:
node_listen: 1984
plugins:
- example-plugin
plugin_attr:
example-plugin:
val: 0
--- config
location /t {
content_by_lua_block {
local core = require "apisix.core"
ngx.sleep(0.1)
local data = [[
deployment:
role: traditional
role_traditional:
config_provider: etcd
admin:
admin_key: null
apisix:
node_listen: 1984
plugins:
- example-plugin
plugin_attr:
example-plugin:
val: 1
]]
require("lib.test_admin").set_config_yaml(data)
local t = require("lib.test_admin").test
local code, _, org_body = t('/apisix/admin/plugins/reload',
ngx.HTTP_PUT)
ngx.status = code
ngx.say(org_body)
ngx.sleep(0.1)
local data = [[
deployment:
role: traditional
role_traditional:
config_provider: etcd
admin:
admin_key: null
apisix:
node_listen: 1984
plugins:
- example-plugin
plugin_attr:
example-plugin:
val: 1
]]
require("lib.test_admin").set_config_yaml(data)
local t = require("lib.test_admin").test
local code, _, org_body = t('/apisix/admin/plugins/reload',
ngx.HTTP_PUT)
ngx.say(org_body)
ngx.sleep(0.1)
}
}
--- request
GET /t
--- response_body
done
done
--- grep_error_log eval
qr/example-plugin get plugin attr val: \d+/
--- grep_error_log_out
example-plugin get plugin attr val: 0
example-plugin get plugin attr val: 0
example-plugin get plugin attr val: 0
example-plugin get plugin attr val: 1
example-plugin get plugin attr val: 1
example-plugin get plugin attr val: 1
example-plugin get plugin attr val: 1
example-plugin get plugin attr val: 1
example-plugin get plugin attr val: 1
=== TEST 4: reload plugins to change prometheus' export uri
--- yaml_config
apisix:
node_listen: 1984
plugins:
- public-api
- prometheus
plugin_attr:
prometheus:
export_uri: /metrics
--- config
location /t {
content_by_lua_block {
local core = require "apisix.core"
ngx.sleep(0.1)
local t = require("lib.test_admin").test
-- setup public API route
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"public-api": {}
},
"uri": "/apisix/metrics"
}]]
)
ngx.say(code)
local code, _, org_body = t('/apisix/metrics',
ngx.HTTP_GET)
ngx.say(code)
local data = [[
deployment:
role: traditional
role_traditional:
config_provider: etcd
admin:
admin_key: null
apisix:
node_listen: 1984
plugins:
- public-api
- prometheus
plugin_attr:
prometheus:
export_uri: /apisix/metrics
]]
require("lib.test_admin").set_config_yaml(data)
local code, _, org_body = t('/apisix/admin/plugins/reload',
ngx.HTTP_PUT)
ngx.say(org_body)
ngx.sleep(0.1)
local code, _, org_body = t('/apisix/metrics',
ngx.HTTP_GET)
ngx.say(code)
}
}
--- request
GET /t
--- response_body
201
404
done
200
=== TEST 5: reload plugins to disable skywalking
--- yaml_config
apisix:
node_listen: 1984
plugins:
- skywalking
plugin_attr:
skywalking:
service_name: APISIX
service_instance_name: "APISIX Instance Name"
endpoint_addr: http://127.0.0.1:12801
report_interval: 1
--- config
location /t {
content_by_lua_block {
local core = require "apisix.core"
ngx.sleep(1.2)
local t = require("lib.test_admin").test
local data = [[
deployment:
role: traditional
role_traditional:
config_provider: etcd
admin:
admin_key: null
apisix:
node_listen: 1984
plugins:
- prometheus
]]
require("lib.test_admin").set_config_yaml(data)
local code, _, org_body = t('/apisix/admin/plugins/reload',
ngx.HTTP_PUT)
ngx.say(org_body)
ngx.sleep(2)
}
}
--- request
GET /t
--- response_body
done
--- no_error_log
[alert]
--- grep_error_log eval
qr/Instance report fails/
--- grep_error_log_out
Instance report fails
=== TEST 6: check disabling plugin via etcd
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"echo": {
"body":"hello upstream\n"
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/hello"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
ngx.sleep(0.1)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 7: hit
--- yaml_config
apisix:
node_listen: 1984
enable_admin: false
--- request
GET /hello
--- response_body
hello upstream
=== TEST 8: hit after disabling echo
--- yaml_config
apisix:
node_listen: 1984
enable_admin: false
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
assert(etcd.set("/plugins", {{name = "jwt-auth"}}))
ngx.sleep(0.2)
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)
if not res then
ngx.say(err)
return
end
ngx.print(res.body)
}
}
--- request
GET /t
--- response_body
hello world
=== TEST 9: wrong method to reload plugins
--- request
GET /apisix/admin/plugins/reload
--- error_code: 405
--- response_body
{"error_msg":"please use PUT method to reload the plugins, GET method is not allowed."}

View File

@@ -0,0 +1,480 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!defined $block->request) {
$block->set_value("request", "GET /t");
}
$block;
});
run_tests;
__DATA__
=== TEST 1: get plugins' name
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local json = require('cjson')
local code, _, body = t("/apisix/admin/plugins/list", "GET")
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
local tab = json.decode(body)
for _, v in ipairs(tab) do
ngx.say(v)
end
}
}
--- response_body
real-ip
ai
client-control
proxy-control
request-id
zipkin
ext-plugin-pre-req
fault-injection
mocking
serverless-pre-function
cors
ip-restriction
ua-restriction
referer-restriction
csrf
uri-blocker
request-validation
chaitin-waf
multi-auth
openid-connect
cas-auth
authz-casbin
authz-casdoor
wolf-rbac
ldap-auth
hmac-auth
basic-auth
jwt-auth
jwe-decrypt
key-auth
consumer-restriction
attach-consumer-label
forward-auth
opa
authz-keycloak
proxy-cache
body-transformer
ai-request-rewrite
ai-prompt-guard
ai-prompt-template
ai-prompt-decorator
ai-rag
ai-aws-content-moderation
ai-proxy-multi
ai-proxy
ai-rate-limiting
proxy-mirror
proxy-rewrite
workflow
api-breaker
limit-conn
limit-count
limit-req
gzip
traffic-split
redirect
response-rewrite
mcp-bridge
degraphql
kafka-proxy
grpc-transcode
grpc-web
http-dubbo
public-api
prometheus
datadog
lago
loki-logger
elasticsearch-logger
echo
loggly
http-logger
splunk-hec-logging
skywalking-logger
google-cloud-logging
sls-logger
tcp-logger
kafka-logger
rocketmq-logger
syslog
udp-logger
file-logger
clickhouse-logger
tencent-cloud-cls
inspect
example-plugin
aws-lambda
azure-functions
openwhisk
openfunction
serverless-post-function
ext-plugin-post-req
ext-plugin-post-resp
=== TEST 2: invalid plugin
--- request
GET /apisix/admin/plugins/asdf
--- error_code: 404
--- response_body
{"error_msg":"plugin not found in subsystem http"}
=== TEST 3: get plugin schema
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugins/limit-req',
ngx.HTTP_GET,
nil,
[[
{"type":"object","required":["rate","burst","key"],"properties":{"rate":{"type":"number","exclusiveMinimum":0},"key_type":{"type":"string","enum":["var","var_combination"],"default":"var"},"burst":{"type":"number","minimum":0},"nodelay":{"type":"boolean","default":false},"key":{"type":"string"},"rejected_code":{"type":"integer","minimum":200,"maximum":599,"default":503},"rejected_msg":{"type":"string","minLength":1},"allow_degradation":{"type":"boolean","default":false}}}
]]
)
ngx.status = code
}
}
=== TEST 4: get plugin node-status schema
--- extra_yaml_config
plugins:
- node-status
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugins/node-status',
ngx.HTTP_GET,
nil,
[[
{"properties":{},"type":"object"}
]]
)
ngx.status = code
}
}
=== TEST 5: get plugin prometheus schema
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugins/prometheus',
ngx.HTTP_GET,
nil,
[[
{"properties":{},"type":"object"}
]]
)
ngx.status = code
}
}
=== TEST 6: get plugin basic-auth schema
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugins/basic-auth',
ngx.HTTP_GET,
nil,
[[
{"properties":{},"title":"work with route or service object","type":"object"}
]]
)
ngx.status = code
}
}
=== TEST 7: get plugin basic-auth schema by schema_type
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugins/basic-auth?schema_type=consumer',
ngx.HTTP_GET,
nil,
[[
{"title":"work with consumer object","required":["username","password"],"properties":{"username":{"type":"string"},"password":{"type":"string"}},"type":"object"}
]]
)
ngx.status = code
}
}
=== TEST 8: confirm the name, priority, schema, type and version of plugin
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/plugins?all=true',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
for k, v in pairs(res) do
if k == "example-plugin" then
ngx.say(json.encode(v))
end
end
}
}
--- response_body eval
qr/\{"metadata_schema":\{"properties":\{"ikey":\{"minimum":0,"type":"number"\},"skey":\{"type":"string"\}\},"required":\["ikey","skey"\],"type":"object"\},"priority":0,"schema":\{"\$comment":"this is a mark for our injected plugin schema","properties":\{"_meta":\{"additionalProperties":false,"properties":\{"disable":\{"type":"boolean"\},"error_response":\{"oneOf":\[\{"type":"string"\},\{"type":"object"\}\]\},"filter":\{"description":"filter determines whether the plugin needs to be executed at runtime","type":"array"\},"pre_function":\{"description":"function to be executed in each phase before execution of plugins. The pre_function will have access to two arguments: `conf` and `ctx`.","type":"string"\},"priority":\{"description":"priority of plugins by customized order","type":"integer"\}\},"type":"object"\},"i":\{"minimum":0,"type":"number"\},"ip":\{"type":"string"\},"port":\{"type":"integer"\},"s":\{"type":"string"\},"t":\{"minItems":1,"type":"array"\}\},"required":\["i"\],"type":"object"\},"version":0.1\}/
=== TEST 9: confirm the plugin of auth type
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/plugins?all=true',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
local auth_plugins = {}
for k, v in pairs(res) do
if v.type == "auth" then
local plugin = {}
plugin.name = k
plugin.priority = v.priority
table.insert(auth_plugins, plugin)
end
end
table.sort(auth_plugins, function(l, r)
return l.priority > r.priority
end)
ngx.say(json.encode(auth_plugins))
}
}
--- response_body eval
qr/\[\{"name":"multi-auth","priority":2600\},\{"name":"wolf-rbac","priority":2555\},\{"name":"ldap-auth","priority":2540\},\{"name":"hmac-auth","priority":2530\},\{"name":"basic-auth","priority":2520\},\{"name":"jwt-auth","priority":2510\},\{"name":"jwe-decrypt","priority":2509\},\{"name":"key-auth","priority":2500\}\]/
=== TEST 10: confirm the consumer_schema of plugin
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/plugins?all=true',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
local consumer_schema
for k, v in pairs(res) do
if k == "basic-auth" then
consumer_schema = v.consumer_schema
end
end
ngx.say(json.encode(consumer_schema))
}
}
--- response_body eval
qr/\{"encrypt_fields":\["password"\],"properties":\{"password":\{"type":"string"\},"username":\{"type":"string"\}\},"required":\["username","password"\],"title":"work with consumer object","type":"object"\}/
=== TEST 11: confirm the name, priority, schema, type and version of stream plugin
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/plugins?all=true&subsystem=stream',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
for k, v in pairs(res) do
if k == "limit-conn" then
ngx.say(json.encode(v))
end
end
}
}
--- response_body
{"priority":1003,"schema":{"$comment":"this is a mark for our injected plugin schema","properties":{"_meta":{"additionalProperties":false,"properties":{"disable":{"type":"boolean"},"error_response":{"oneOf":[{"type":"string"},{"type":"object"}]},"filter":{"description":"filter determines whether the plugin needs to be executed at runtime","type":"array"},"pre_function":{"description":"function to be executed in each phase before execution of plugins. The pre_function will have access to two arguments: `conf` and `ctx`.","type":"string"},"priority":{"description":"priority of plugins by customized order","type":"integer"}},"type":"object"},"burst":{"minimum":0,"type":"integer"},"conn":{"exclusiveMinimum":0,"type":"integer"},"default_conn_delay":{"exclusiveMinimum":0,"type":"number"},"key":{"type":"string"},"key_type":{"default":"var","enum":["var","var_combination"],"type":"string"},"only_use_default_delay":{"default":false,"type":"boolean"}},"required":["conn","burst","default_conn_delay","key"],"type":"object"},"version":0.1}
=== TEST 12: confirm the scope of plugin
--- extra_yaml_config
plugins:
- batch-requests
- error-log-logger
- server-info
- example-plugin
- node-status
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/plugins?all=true',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
local global_plugins = {}
for k, v in pairs(res) do
if v.scope == "global" then
global_plugins[k] = v.scope
end
end
ngx.say(json.encode(global_plugins))
}
}
--- response_body
{"batch-requests":"global","error-log-logger":"global","node-status":"global","server-info":"global"}
=== TEST 13: check with wrong plugin subsystem
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local _, message, _ = t('/apisix/admin/plugins?subsystem=asdf',
ngx.HTTP_GET
)
ngx.say(message)
}
}
--- response_body eval
qr/\{"error_msg":"unsupported subsystem: asdf"\}/
=== TEST 14: check with right plugin in wrong subsystem
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local _, message, _ = t('/apisix/admin/plugins/http-logger?subsystem=stream',
ngx.HTTP_GET
)
ngx.say(message)
}
}
--- response_body eval
qr/\{"error_msg":"plugin not found in subsystem stream"\}/
=== TEST 15: check with right plugin in right subsystem
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local _, _ , message = t('/apisix/admin/plugins/http-logger?subsystem=http',
ngx.HTTP_GET
)
ngx.say(message)
}
}
--- response_body eval
qr/this is a mark for our injected plugin schema/

View File

@@ -0,0 +1,175 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: set proto(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/protos/1',
ngx.HTTP_PUT,
[[{
"content" : "syntax = \"proto3\";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- error_code: 201
--- response_body
passed
=== TEST 2: add route
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"uri": "/grpctest",
"plugins": {
"grpc-transcode": {
"proto_id": "1",
"service": "helloworld.Greeter",
"method": "SayHello"
}
},
"upstream": {
"scheme": "grpc",
"type": "roundrobin",
"nodes": {
"127.0.0.1:10051": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.print(message)
return
end
ngx.say(message)
}
}
--- response_body
passed
=== TEST 3: delete proto(wrong header)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/protos/1?force=anyvalue',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 400 message: {"error_msg":"can not delete this proto, route [1] is still using it now"}
=== TEST 4: delete proto(without force delete header)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/protos/1',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 400 message: {"error_msg":"can not delete this proto, route [1] is still using it now"}
=== TEST 5: delete proto(force delete)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/protos/1?force=true',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body chomp
[delete] code: 200 message: passed
=== TEST 6: delete route
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/routes/1',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body chomp
[delete] code: 200 message: passed

View File

@@ -0,0 +1,216 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: put proto (id:1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/protos/1',
ngx.HTTP_PUT,
[[{
"content": "syntax = \"proto3\";
package proto;
message HelloRequest{
string name = 1;
}
message HelloResponse{
int32 code = 1;
string msg = 2;
}
// The greeting service definition.
service Hello {
// Sends a greeting
rpc SayHi (HelloRequest) returns (HelloResponse){}
}"
}]]
)
if code ~= 201 then
ngx.status = code
ngx.say("[put proto] code: ", code, " message: ", message)
return
end
ngx.say("[put proto] code: ", code, " message: ", message)
}
}
--- response_body
[put proto] code: 201 message: passed
=== TEST 2: delete proto(id:1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/protos/1',
ngx.HTTP_DELETE
)
if code ~= 200 then
ngx.status = code
ngx.say("[delete proto] code: ", code, " message: ", message)
return
end
ngx.say("[delete proto] code: ", code, " message: ", message)
}
}
--- response_body
[delete proto] code: 200 message: passed
=== TEST 3: put proto (id:2) + route refer proto(proto id 2) + delete proto(proto id 2)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/protos/2',
ngx.HTTP_PUT,
[[{
"content": "syntax = \"proto3\";
package proto;
message HelloRequest{
string name = 1;
}
message HelloResponse{
int32 code = 1;
string msg = 2;
}
// The greeting service definition.
service Hello {
// Sends a greeting
rpc SayHi (HelloRequest) returns (HelloResponse){}
}"
}]]
)
if code ~= 201 then
ngx.status = code
ngx.say("[put proto] code: ", code, " message: ", message)
return
end
ngx.say("[put proto] code: ", code, " message: ", message)
code, message = t('/apisix/admin/routes/2',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"plugins": {
"grpc-transcode": {
"_meta": {
"disable": false
},
"method": "SayHi",
"proto_id": 2,
"service": "proto.Hello"
}
},
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/grpc/sayhi",
"name": "hi-grpc"
}]]
)
if code ~= 201 then
ngx.status = code
ngx.say("[route refer proto] code: ", code, " message: ", message)
return
end
ngx.say("[route refer proto] code: ", code, " message: ", message)
ngx.sleep(0.1) -- ensure reference is synced from etcd
code, message = t('/apisix/admin/protos/2',
ngx.HTTP_DELETE
)
ngx.say("[delete proto] code: ", code)
}
}
--- response_body
[put proto] code: 201 message: passed
[route refer proto] code: 201 message: passed
[delete proto] code: 400
=== TEST 4: reject invalid proto
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/protos/1',
ngx.HTTP_PUT,
[[{
"content": "syntax = \"proto3\";
package proto;
message HelloRequest{
string name = 1;
}
message HelloResponse{
int32 code = 1;
string msg = 1;
}"
}]]
)
if code ~= 200 then
ngx.status = code
end
ngx.say(message)
}
}
--- error_code: 400
--- response_body eval
qr/invalid content:/

View File

@@ -0,0 +1,55 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
run_tests;
__DATA__
=== TEST 1: invalid resource type: 'routs'
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routs/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route",
"uri": "/index.html"
}]]
)
ngx.say(body)
}
}
--- request
GET /t
--- response_body_like
{"error_msg":"Unsupported resource type: routs"}

View File

@@ -0,0 +1,255 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
my $user_yaml_config = <<_EOC_;
deployment:
role: traditional
role_traditional:
config_provider: etcd
admin:
admin_key: ~
admin_api_version: v3
apisix:
node_listen: 1984
_EOC_
$block->set_value("yaml_config", $user_yaml_config);
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: use v3 admin api, no action in response body
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route",
"uri": "/index.html"
}]],
[[{
"value": {
"methods": [
"GET"
],
"uri": "/index.html",
"desc": "new route",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 2: response body format only have total and list (total is 1)
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/routes', ngx.HTTP_GET)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.total == 1)
assert(res.total == #res.list)
assert(res.action == nil)
assert(res.node == nil)
assert(res.list.key == nil)
assert(res.list.dir == nil)
assert(res.list[1].createdIndex ~= nil)
assert(res.list[1].modifiedIndex ~= nil)
assert(res.list[1].key == "/apisix/routes/1")
ngx.say(message)
}
}
--- response_body
passed
=== TEST 3: response body format only have total and list (total is 2)
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/2',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route",
"uri": "/index.html"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
end
local code, message, res = t('/apisix/admin/routes',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.total == 2)
assert(res.total == #res.list)
assert(res.action == nil)
assert(res.node == nil)
assert(res.list.key == nil)
assert(res.list.dir == nil)
assert(res.list[1].createdIndex ~= nil)
assert(res.list[1].modifiedIndex ~= nil)
assert(res.list[1].key == "/apisix/routes/1")
assert(res.list[2].createdIndex ~= nil)
assert(res.list[2].modifiedIndex ~= nil)
assert(res.list[2].key == "/apisix/routes/2")
ngx.say(message)
}
}
--- response_body
passed
=== TEST 4: response body format (test services)
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/1',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"desc": "new service 001"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
local code, body = t('/apisix/admin/services/2',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"desc": "new service 002"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
local code, message, res = t('/apisix/admin/services', ngx.HTTP_GET)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.total == 2)
assert(res.total == #res.list)
assert(res.action == nil)
assert(res.node == nil)
assert(res.list.key == nil)
assert(res.list.dir == nil)
assert(res.list[1].createdIndex ~= nil)
assert(res.list[1].modifiedIndex ~= nil)
assert(res.list[1].key == "/apisix/services/1")
assert(res.list[2].createdIndex ~= nil)
assert(res.list[2].modifiedIndex ~= nil)
assert(res.list[2].key == "/apisix/services/2")
ngx.say(message)
}
}
--- response_body
passed
passed
passed

View File

@@ -0,0 +1,115 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
run_tests;
__DATA__
=== TEST 1: set route(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": [{
"host": "127.0.0.1",
"port": 8080,
"weight": 1
}],
"type": "roundrobin"
},
"desc": "new route",
"uri": "/index.html"
}]],
[[{
"value": {
"methods": [
"GET"
],
"uri": "/index.html",
"desc": "new route",
"upstream": {
"nodes": [{
"host": "127.0.0.1",
"port": 8080,
"weight": 1
}],
"type": "roundrobin"
}
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 2: get route(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_GET,
nil,
[[{
"value": {
"methods": [
"GET"
],
"uri": "/index.html",
"desc": "new route",
"upstream": {
"nodes": [{
"host": "127.0.0.1",
"port": 8080,
"weight": 1
}],
"type": "roundrobin"
}
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed

View File

@@ -0,0 +1,788 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
run_tests;
__DATA__
=== TEST 1: set route(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route",
"uri": "/index.html"
}]],
[[{
"value": {
"methods": [
"GET"
],
"uri": "/index.html",
"desc": "new route",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 2: get route(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_GET,
nil,
[[{
"value": {
"methods": [
"GET"
],
"uri": "/index.html",
"desc": "new route",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 3: delete route(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/routes/1',
ngx.HTTP_DELETE
)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[delete] code: 200 message: passed
=== TEST 4: delete route(id: not_found)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code = t('/apisix/admin/routes/not_found',
ngx.HTTP_DELETE
)
ngx.say("[delete] code: ", code)
}
}
--- request
GET /t
--- response_body
[delete] code: 404
=== TEST 5: post route + delete
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, message, res = t('/apisix/admin/routes',
ngx.HTTP_POST,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]],
[[{
"value": {
"methods": [
"GET"
],
"uri": "/index.html",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}
}]]
)
if code ~= 200 then
ngx.status = code
ngx.say(message)
return
end
ngx.say("[push] code: ", code, " message: ", message)
local id = string.sub(res.key, #"/apisix/routes/" + 1)
local res = assert(etcd.get('/routes/' .. id))
local create_time = res.body.node.value.create_time
assert(create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
code, message = t('/apisix/admin/routes/' .. id,
ngx.HTTP_DELETE
)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[push] code: 200 message: passed
[delete] code: 200 message: passed
=== TEST 6: uri + upstream
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, message, res = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]],
[[{
"value": {
"uri": "/index.html",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}
}]]
)
if code ~= 200 then
ngx.status = code
ngx.say(message)
return
end
ngx.say("[push] code: ", code, " message: ", message)
local res = assert(etcd.get('/routes/1'))
local create_time = res.body.node.value.create_time
assert(create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
}
}
--- request
GET /t
--- response_body
[push] code: 200 message: passed
=== TEST 7: uri + plugins
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
},
"uri": "/index.html"
}]],
[[{
"value": {
"uri": "/index.html",
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
}
}]]
)
if code ~= 200 then
ngx.status = code
ngx.say(message)
return
end
ngx.say("[push] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[push] code: 200 message: passed
=== TEST 8: invalid route: duplicate method
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET", "GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body_like
=== TEST 9: invalid method
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["invalid_method"],
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"methods\" validation failed: failed to validate item 1: matches none of the enum values"}
=== TEST 10: invalid service id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"service_id": "invalid_id$",
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"service_id\" validation failed: object matches none of the required"}
=== TEST 11: service id: not exist
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"service_id": "99999999999999",
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"failed to fetch service info by service id [99999999999999], response code: 404"}
=== TEST 12: invalid id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"id": 3,
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"wrong route id"}
=== TEST 13: id in the rule
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes',
ngx.HTTP_PUT,
[[{
"id": "1",
"plugins":{},
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 14: integer id less than 1
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes',
ngx.HTTP_PUT,
[[{
"id": -100,
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"id\" validation failed: object matches none of the required"}
=== TEST 15: invalid upstream_id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"upstream_id": "invalid$",
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"upstream_id\" validation failed: object matches none of the required"}
=== TEST 16: not exist upstream_id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"upstream_id": "99999999",
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"failed to fetch upstream info by upstream id [99999999], response code: 404"}
=== TEST 17: wrong route id, do not need it
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes',
ngx.HTTP_POST,
[[{
"id": 1,
"plugins":{},
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"wrong route id, do not need it"}
=== TEST 18: wrong route id, do not need it
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_POST,
[[{
"plugins":{},
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"wrong route id, do not need it"}
=== TEST 19: limit-count with `disable` option
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr",
"_meta": {
"disable": true
}
}
},
"uri": "/index.html"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
ngx.say("[push] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[push] code: 200 message: passed
=== TEST 20: host: *.foo.com
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"host": "*.foo.com",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]],
[[{
"value": {
"host": "*.foo.com",
"uri": "/index.html",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 21: invalid host: a.*.foo.com
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"host": "a.*.foo.com",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body_like
{"error_msg":"invalid configuration: property \\"host\\" validation failed: failed to match pattern .*
=== TEST 22: invalid host: *.a.*.foo.com
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"host": "*.a.*.foo.com",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body_like
{"error_msg":"invalid configuration: property \\"host\\" validation failed: failed to match pattern .*
=== TEST 23: removing the init_dir key from etcd can still list all routes
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local json = require("toolkit.json")
local etcd = require("apisix.core.etcd")
local code, body = t('/apisix/admin/routes/del_init_dir_1',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]]
)
assert(code == 200 or code == 201, "failed to add route")
local code, body = t('/apisix/admin/routes/del_init_dir_2',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]]
)
assert(code == 200 or code == 201, "failed to add route")
-- remove the init_dir key from etcd
assert(etcd.delete("/routes/"))
-- list all routes and check them
local code, body, res = t('/apisix/admin/routes', ngx.HTTP_GET)
ngx.status = code
ngx.say(res)
}
}
--- request
GET /t
--- response_body eval
qr/del_init_dir_1.*del_init_dir_2/

View File

@@ -0,0 +1,653 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
run_tests;
__DATA__
=== TEST 1: invalid route: bad remote_addrs
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"remote_addrs": [""],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route",
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body_like eval
qr/property \\"remote_addrs\\" validation failed:/
=== TEST 2: invalid route: bad remote_addrs cidr
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"remote_addrs": ["/16"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route",
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body_like eval
qr/property \\"remote_addrs\\" validation failed:/
=== TEST 3: valid route with remote_addrs
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"remote_addrs": ["::1/16", "::1", "::", "1.1.1.1", "1.1.1.1/32"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route",
"uri": "/index.html"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 4: invalid route: bad vars operator
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[=[{
"methods": ["GET"],
"vars": [["remote_addr", "=", "127.0.0.1"]],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route",
"uri": "/index.html"
}]=]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"failed to validate the 'vars' expression: invalid operator '='"}
=== TEST 5: not unwanted data, POST
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/routes',
ngx.HTTP_POST,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/not_unwanted_data_post"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
res.key = nil
res.value.create_time = nil
res.value.update_time = nil
assert(res.value.id ~= nil)
res.value.id = nil
ngx.say(json.encode(res))
}
}
--- request
GET /t
--- response_body
{"value":{"methods":["GET"],"priority":0,"status":1,"upstream":{"hash_on":"vars","nodes":{"127.0.0.1:8080":1},"pass_host":"pass","scheme":"http","type":"roundrobin"},"uri":"/not_unwanted_data_post"}}
=== TEST 6: not unwanted data, PUT
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/routes',
ngx.HTTP_PUT,
[[{
"id": 1,
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
res.value.create_time = nil
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- request
GET /t
--- response_body
{"key":"/apisix/routes/1","value":{"id":1,"methods":["GET"],"priority":0,"status":1,"upstream":{"hash_on":"vars","nodes":{"127.0.0.1:8080":1},"pass_host":"pass","scheme":"http","type":"roundrobin"},"uri":"/index.html"}}
=== TEST 7: not unwanted data, PATCH
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/routes/1',
ngx.HTTP_PATCH,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
res.value.create_time = nil
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- request
GET /t
--- response_body
{"key":"/apisix/routes/1","value":{"id":"1","methods":["GET"],"priority":0,"status":1,"upstream":{"hash_on":"vars","nodes":{"127.0.0.1:8080":1},"pass_host":"pass","scheme":"http","type":"roundrobin"},"uri":"/index"}}
=== TEST 8: not unwanted data, GET
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/routes/1',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.createdIndex ~= nil)
res.createdIndex = nil
assert(res.modifiedIndex ~= nil)
res.modifiedIndex = nil
assert(res.value.create_time ~= nil)
res.value.create_time = nil
assert(res.value.update_time ~= nil)
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- request
GET /t
--- response_body
{"key":"/apisix/routes/1","value":{"id":"1","methods":["GET"],"priority":0,"status":1,"upstream":{"hash_on":"vars","nodes":{"127.0.0.1:8080":1},"pass_host":"pass","scheme":"http","type":"roundrobin"},"uri":"/index"}}
=== TEST 9: not unwanted data, DELETE
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/routes/1',
ngx.HTTP_DELETE
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(json.encode(res))
}
}
--- request
GET /t
--- response_body
{"deleted":"1","key":"/apisix/routes/1"}
=== TEST 10: invalid route: empty remote_addrs
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"remote_addrs": [],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body_like eval
qr/property \\"remote_addrs\\" validation failed:/
=== TEST 11: invalid route: empty uris
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uris": []
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body_like eval
qr/property \\"uris\\" validation failed:/
=== TEST 12: invalid route: empty hosts
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"hosts": [],
"uri": "/"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body_like eval
qr/property \\"hosts\\" validation failed:/
=== TEST 13: invalid route: uris & uri
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uris": ["/"],
"uri": "/"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body_like eval
qr/value should match only one schema/
=== TEST 14: enable remote_addrs and remote_addr together
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/index.html",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"remote_addr": "127.0.0.1",
"remote_addrs": ["127.0.0.1"]
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"only one of remote_addr or remote_addrs is allowed"}
=== TEST 15: labels in Chinese
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"labels": {
"您好": "世界"
},
"uri": "/index.html"
}]],
[[{
"value": {
"methods": [
"GET"
],
"uri": "/index.html",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"labels": {
"您好": "世界"
}
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 16: labels value with whitespace
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"labels": {
"您好": "世 界"
},
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body_like eval
qr/invalid configuration: property \\"labels\\" validation failed/
=== TEST 17: route with plugin_config_id (not found)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"plugin_config_id": "not_found",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"failed to fetch plugin config info by plugin config id [not_found], response code: 404"}
=== TEST 18: valid route with timeout
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"timeout": {
"connect": 3,
"send": 3,
"read": 3
},
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed

View File

@@ -0,0 +1,743 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: list empty resources
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/routes',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(json.encode(res))
}
}
--- response_body
{"list":[],"total":0}
=== TEST 2: remote_addr: 127.0.0.1
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"remote_addr": "127.0.0.1",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]],
[[{
"value": {
"remote_addr": "127.0.0.1",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
},
"key": "/apisix/routes/1"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 3: remote_addr: 127.0.0.1/24
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"remote_addr": "127.0.0.0/24",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]],
[[{
"value": {
"remote_addr": "127.0.0.0/24",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
},
"key": "/apisix/routes/1"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 4: remote_addr: 127.0.0.33333
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"remote_addr": "127.0.0.33333",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"remote_addr\" validation failed: object matches none of the required"}
=== TEST 5: all method
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET", "POST", "PUT", "DELETE", "PATCH",
"HEAD", "OPTIONS", "CONNECT", "TRACE", "PURGE"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 6: patch route(new uri)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local id = 1
local res = assert(etcd.get('/routes/' .. id))
local prev_create_time = res.body.node.value.create_time
local prev_update_time = res.body.node.value.update_time
ngx.sleep(1)
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PATCH,
[[{
"uri": "/patch_test"
}]],
[[{
"value": {
"uri": "/patch_test"
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/routes/' .. id))
local create_time = res.body.node.value.create_time
assert(prev_create_time == create_time, "create_time mismatched")
local update_time = res.body.node.value.update_time
assert(prev_update_time ~= update_time, "update_time should be changed")
}
}
--- response_body
passed
=== TEST 7: patch route(multi)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PATCH,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": null,
"127.0.0.2:8080": 1
}
},
"desc": "new route"
}]],
[[{
"value": {
"methods": [
"GET"
],
"uri": "/patch_test",
"desc": "new route",
"upstream": {
"nodes": {
"127.0.0.2:8080": 1
},
"type": "roundrobin"
}
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 8: patch route(new methods)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PATCH,
[[{
"methods": ["GET", "DELETE", "PATCH", "POST", "PUT"]
}]],
[[{
"value": {
"methods": ["GET", "DELETE", "PATCH", "POST", "PUT"]
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 9: patch route(minus methods)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PATCH,
[[{
"methods": ["GET", "POST"]
}]],
[[{
"value": {
"methods": ["GET", "POST"]
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 10: patch route(new methods - sub path way)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1/methods',
ngx.HTTP_PATCH,
'["POST"]',
[[{
"value": {
"methods": [
"POST"
]
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 11: patch route(new uri)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1/uri',
ngx.HTTP_PATCH,
'"/patch_uri_test"',
[[{
"value": {
"uri": "/patch_uri_test"
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 12: patch route(whole)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1/',
ngx.HTTP_PATCH,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route",
"uri": "/index.html"
}]],
[[{
"value": {
"methods": [
"GET"
],
"uri": "/index.html",
"desc": "new route",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 13: multiple hosts
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/index.html",
"hosts": ["foo.com", "*.bar.com"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route"
}]],
[[{
"value": {
"hosts": ["foo.com", "*.bar.com"]
}
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 14: enable hosts and host together
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/index.html",
"host": "xxx.com",
"hosts": ["foo.com", "*.bar.com"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"only one of host or hosts is allowed"}
=== TEST 15: multiple remote_addrs
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/index.html",
"remote_addrs": ["127.0.0.1", "192.0.0.1/8", "::1", "fe80::/32"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route"
}]],
[[{
"value": {
"remote_addrs": ["127.0.0.1", "192.0.0.1/8", "::1", "fe80::/32"]
}
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 16: multiple vars
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[=[{
"uri": "/index.html",
"vars": [["arg_name", "==", "json"], ["arg_age", ">", 18]],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route"
}]=],
[=[{
"value": {
"vars": [["arg_name", "==", "json"], ["arg_age", ">", 18]]
}
}]=]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 17: filter function
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[=[{
"uri": "/index.html",
"filter_func": "function(vars) return vars.arg_name == 'json' end",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}]=],
[=[{
"value": {
"filter_func": "function(vars) return vars.arg_name == 'json' end"
}
}]=]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 18: filter function (invalid)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[=[{
"uri": "/index.html",
"filter_func": "function(vars) ",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}]=]
)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"failed to load 'filter_func' string: [string \"return function(vars) \"]:1: 'end' expected near '<eof>'"}
=== TEST 19: Support for multiple URIs
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[=[{
"uris": ["/index.html","/index2.html"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}]=]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 20: set route(id: 1, parameters with boolean values)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/index.html",
"enable_websocket": true,
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:8080":1
}
}
}]])
ngx.say(body)
}
}
--- response_body
passed
=== TEST 21: patch route(modify the boolean value of parameters to false)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1/enable_websocket',
ngx.HTTP_PATCH,
'false',
[[{
"value": {
"enable_websocket": false
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 22: patch route(modify the boolean value of parameters to true)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1/enable_websocket',
ngx.HTTP_PATCH,
'true',
[[{
"value": {
"enable_websocket": true
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed

View File

@@ -0,0 +1,795 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: set route with ttl
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local core = require("apisix.core")
-- set
local code, body, res = t('/apisix/admin/routes/1?ttl=1',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
-- get
code, body = t('/apisix/admin/routes/1?ttl=1',
ngx.HTTP_GET,
nil,
[[{
"value": {
"uri": "/index.html"
},
"key": "/apisix/routes/1"
}]]
)
ngx.say("code: ", code)
ngx.say(body)
-- etcd v3 would still get the value at 2s, don't know why yet
ngx.sleep(2.5)
-- get again
code, body, res = t('/apisix/admin/routes/1', ngx.HTTP_GET)
ngx.say("code: ", code)
ngx.say("message: ", core.json.decode(body).message)
}
}
--- response_body
code: 200
passed
code: 404
message: Key not found
--- timeout: 5
=== TEST 2: post route with ttl
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local core = require("apisix.core")
local code, body, res = t('/apisix/admin/routes?ttl=1',
ngx.HTTP_POST,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]],
[[{}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say("[push] succ: ", body)
ngx.sleep(2.5)
local id = string.sub(res.key, #"/apisix/routes/" + 1)
code, body = t('/apisix/admin/routes/' .. id, ngx.HTTP_GET)
ngx.say("code: ", code)
ngx.say("message: ", core.json.decode(body).message)
}
}
--- response_body
[push] succ: passed
code: 404
message: Key not found
--- timeout: 5
=== TEST 3: invalid argument: ttl
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body, res = t('/apisix/admin/routes?ttl=xxx',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]]
)
if code >= 300 then
ngx.status = code
ngx.print(body)
return
end
ngx.say("[push] succ: ", body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid argument ttl: should be a number"}
=== TEST 4: set route(id: 1, check priority)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route",
"uri": "/index.html"
}]],
[[{
"value": {
"priority": 0
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 5: set route(id: 1 + priority: 0)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route",
"uri": "/index.html",
"priority": 1
}]],
[[{
"value": {
"priority": 1
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 6: set route(id: 1) and upstream(type:chash, default hash_on: vars, missing key)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash"
},
"desc": "new route",
"uri": "/index.html"
}]])
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"missing key"}
=== TEST 7: set route(id: 1) and upstream(type:chash, hash_on: header, missing key)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"hash_on":"header"
},
"desc": "new route",
"uri": "/index.html"
}]])
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"missing key"}
=== TEST 8: set route(id: 1) and upstream(type:chash, hash_on: cookie, missing key)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"hash_on":"cookie"
},
"desc": "new route",
"uri": "/index.html"
}]])
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"missing key"}
=== TEST 9: set route(id: 1) and upstream(type:chash, hash_on: consumer, missing key is ok)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"hash_on":"consumer"
},
"desc": "new route",
"uri": "/index.html"
}]])
ngx.say(body)
}
}
--- response_body
passed
=== TEST 10: set route(id: 1 + name: test name)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"name": "test name",
"uri": "/index.html"
}]],
[[{
"value": {
"name": "test name"
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 11: string id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/a-b-c-ABC_0123',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 12: string id(delete)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/a-b-c-ABC_0123',
ngx.HTTP_DELETE
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 13: invalid string id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/*invalid',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- error_code: 400
=== TEST 14: Verify Response Content-Type=application/json
--- config
location /t {
content_by_lua_block {
local http = require("resty.http")
local httpc = http.new()
httpc:set_timeout(500)
httpc:connect(ngx.var.server_addr, ngx.var.server_port)
local res, err = httpc:request(
{
path = '/apisix/admin/routes/1?ttl=1',
method = "GET",
}
)
ngx.header["Content-Type"] = res.headers["Content-Type"]
ngx.status = 200
ngx.say("passed")
}
}
--- response_headers
Content-Type: application/json
=== TEST 15: set route with size 36k (temporary file to store request body)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local core = require("apisix.core")
local s = string.rep("a", 1024 * 35)
local req_body = [[{
"upstream": {
"nodes": {
"]] .. s .. [[": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]]
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT, req_body)
if code >= 300 then
ngx.status = code
end
ngx.say("req size: ", #req_body)
ngx.say(body)
}
}
--- response_body
req size: 36066
passed
--- error_log
a client request body is buffered to a temporary file
=== TEST 16: route size more than 1.5 MiB
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local s = string.rep( "a", 1024 * 1024 * 1.6 )
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "]] .. s .. [[",
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid request body: request size 1678025 is greater than the maximum size 1572864 allowed"}
--- error_log
failed to read request body: request size 1678025 is greater than the maximum size 1572864 allowed
=== TEST 17: uri + plugins + script failed
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
},
"script": "local _M = {} \n function _M.access(api_ctx) \n ngx.log(ngx.INFO,\"hit access phase\") \n end \nreturn _M",
"uri": "/index.html"
}]]
)
if code ~= 200 then
ngx.status = code
ngx.say(message)
return
end
}
}
--- error_code: 400
--- response_body_like
{"error_msg":"invalid configuration: value wasn't supposed to match schema"}
=== TEST 18: invalid route: multi nodes with `node` mode to pass host
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET", "GET"],
"upstream": {
"nodes": {
"apisix.com:8080": 1,
"test.com:8080": 1
},
"type": "roundrobin",
"pass_host": "node"
},
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
=== TEST 19: set route(with labels)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"labels": {
"build": "16",
"env": "production",
"version": "v2"
},
"uri": "/index.html"
}]],
[[{
"value": {
"methods": [
"GET"
],
"uri": "/index.html",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"labels": {
"build": "16",
"env": "production",
"version": "v2"
}
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 20: patch route(change labels)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PATCH,
[[{
"labels": {
"build": "17"
}
}]],
[[{
"value": {
"methods": [
"GET"
],
"uri": "/index.html",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"labels": {
"env": "production",
"version": "v2",
"build": "17"
}
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 21: invalid format of label value: set route
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"uri": "/index.html",
"labels": {
"env": ["production", "release"]
}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"labels\" validation failed: failed to validate env (matching \".*\"): wrong type: expected string, got table"}
=== TEST 22: create route with create_time and update_time(id : 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html",
"create_time": 1602883670,
"update_time": 1602893670
}]],
[[{
"value": {
"uri": "/index.html",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"create_time": 1602883670,
"update_time": 1602893670
},
"key": "/apisix/routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- error_code: 400
--- response_body eval
qr/\{"error_msg":"the property is forbidden:.*"\}/

View File

@@ -0,0 +1,274 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
log_level("info");
repeat_each(1);
no_long_string();
no_root_location();
add_block_preprocessor(sub {
my ($block) = @_;
if (!defined $block->request) {
$block->set_value("request", "GET /t");
}
});
run_tests();
__DATA__
=== TEST 1: set route in request body vars
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/hello",
"vars": [
[
["post_arg.model","==", "deepseek"]
]
],
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
end
local code, body = t('/apisix/admin/routes/2',
ngx.HTTP_PUT,
[[{
"uri": "/hello",
"vars": [
[
["post_arg.model","==","openai"]
]
],
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}]]
)
ngx.say(body)
}
}
--- response_body
passed
=== TEST 2: send request with model == deepseek
--- request
POST /hello
{ "model":"deepseek", "messages": [ { "role": "system", "content": "You are a mathematician" }] }
--- more_headers
Content-Type: application/json
--- error_code: 404
=== TEST 3: send request with model == openai and content-type == application/json
--- request
POST /hello
{ "model":"openai", "messages": [ { "role": "system", "content": "You are a mathematician" }] }
--- more_headers
Content-Type: application/json
--- error_code: 200
=== TEST 4: send request with model == openai and content-type == application/x-www-form-urlencoded
--- request
POST /hello
model=openai&messages[0][role]=system&messages[0][content]=You%20are%20a%20mathematician
--- more_headers
Content-Type: application/x-www-form-urlencoded
--- error_code: 200
=== TEST 5: multipart/form-data with model=openai
--- request
POST /hello
--testboundary
Content-Disposition: form-data; name="model"
openai
--testboundary--
--- more_headers
Content-Type: multipart/form-data; boundary=testboundary
--- error_code: 200
=== TEST 6: no match without content type
--- request
POST /hello
--testboundary
Content-Disposition: form-data; name="model"
openai
--testboundary--
--- error_code: 404
--- error_log
unsupported content-type in header:
=== TEST 7: use array in request body vars
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/hello",
"vars": [
[
["post_arg.messages[*].content[*].type","has","image_url"]
]
],
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 8: send request with type not image_url
--- request
POST /hello
{ "model":"deepseek", "messages": [ { "role": "system", "content": [{"text":"You are a mathematician","type":"text"}] }] }
--- more_headers
Content-Type: application/json
--- error_code: 404
=== TEST 9: send request with type has image_url
--- request
POST /hello
{ "model":"deepseek", "messages": [ { "role": "system", "content": [{"text":"You are a mathematician","type":"text"},{"text":"You are a mathematician","type":"image_url"}] }] }
--- more_headers
Content-Type: application/json
--- error_code: 200
=== TEST 10: use invalid jsonpath input
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/hello",
"vars": [
[
["post_arg.messages[.content[*].type","has","image_url"]
]
],
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body eval
qr/.*failed to validate the 'vars' expression: invalid expression.*/
--- error_code: 400
=== TEST 11: use non array in request body vars
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/hello",
"vars": [
[
["post_arg.model.name","==","deepseek"]
]
],
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 12: send request
--- request
POST /hello
{ "model":{"name": "deepseek"}, "messages": [ { "role": "system", "content": [{"text":"You are a mathematician","type":"text"},{"text":"You are a mathematician","type":"image_url"}] }] }
--- more_headers
Content-Type: application/json
--- error_code: 200

View File

@@ -0,0 +1,441 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("warn");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
});
run_tests;
__DATA__
=== TEST 1: validate ok
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/schema/validate/routes',
ngx.HTTP_POST,
[[{
"uri": "/httpbin/*",
"upstream": {
"scheme": "https",
"type": "roundrobin",
"nodes": {
"nghttp2.org": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
}
}
--- error_code: 200
=== TEST 2: validate failed, wrong uri type
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/schema/validate/routes',
ngx.HTTP_POST,
[[{
"uri": 666,
"upstream": {
"scheme": "https",
"type": "roundrobin",
"nodes": {
"nghttp2.org": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
}
}
--- error_code: 400
--- response
{"error_msg": {"property \"uri\" validation failed: wrong type: expected string, got number"}}
=== TEST 3: validate failed, length limit
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/schema/validate/routes',
ngx.HTTP_POST,
[[{
"uri": "",
"upstream": {
"scheme": "https",
"type": "roundrobin",
"nodes": {
"nghttp2.org": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
}
}
--- error_code: 400
--- response
{"error_msg":"property \"uri\" validation failed: string too short, expected at least 1, got 0"}
=== TEST 4: validate failed, array type expected
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/schema/validate/routes',
ngx.HTTP_POST,
[[{
"uris": "foobar",
"upstream": {
"scheme": "https",
"type": "roundrobin",
"nodes": {
"nghttp2.org": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
}
}
--- error_code: 400
--- response
{"error_msg":"property \"uris\" validation failed: wrong type: expected array, got string"}
=== TEST 5: validate failed, array size limit
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/schema/validate/routes',
ngx.HTTP_POST,
[[{
"uris": [],
"upstream": {
"scheme": "https",
"type": "roundrobin",
"nodes": {
"nghttp2.org": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
}
}
--- error_code: 400
--- response
{"error_msg":"property \"uris\" validation failed: expect array to have at least 1 items"}
=== TEST 6: validate failed, array unique items
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/schema/validate/routes',
ngx.HTTP_POST,
[[{
"uris": ["/foo", "/foo"],
"upstream": {
"scheme": "https",
"type": "roundrobin",
"nodes": {
"nghttp2.org": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
}
}
--- error_code: 400
--- response
{"error_msg":"property \"uris\" validation failed: expected unique items but items 1 and 2 are equal"}
=== TEST 7: validate failed, uri or uris is mandatory
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/schema/validate/routes',
ngx.HTTP_POST,
[[{
"upstream": {
"scheme": "https",
"type": "roundrobin",
"nodes": {
"nghttp2.org": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
}
}
--- error_code: 400
--- response
{"error_msg":"allOf 1 failed: value should match only one schema, but matches none"}
=== TEST 8: validate failed, enum check
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/schema/validate/routes',
ngx.HTTP_POST,
[[{
"status": 3,
"uri": "/foo",
"upstream": {
"scheme": "https",
"type": "roundrobin",
"nodes": {
"nghttp2.org": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
}
}
--- error_code: 400
--- response
{"error_msg":"property \"status\" validation failed: matches none of the enum values"}
=== TEST 9: validate failed, wrong combination
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/schema/validate/routes',
ngx.HTTP_POST,
[[{
"script": "xxxxxxxxxxxxxxxxxxxxx",
"plugin_config_id": "foo"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
}
}
--- error_code: 400
--- response
{"error_msg":"allOf 1 failed: value should match only one schema, but matches none"}
=== TEST 10: validate failed, id_schema check
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/schema/validate/routes',
ngx.HTTP_POST,
[[{
"plugin_config_id": "@@@@@@@@@@@@@@@@",
"uri": "/foo",
"upstream": {
"scheme": "https",
"type": "roundrobin",
"nodes": {
"nghttp2.org": 1
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
}
}
--- error_code: 400
--- response
{"error_msg":"property \"plugin_config_id\" validation failed: object matches none of the required"}
=== TEST 11: upstream ok
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/schema/validate/upstreams',
ngx.HTTP_POST,
[[{
"nodes":{
"nghttp2.org":100
},
"type":"roundrobin"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
}
}
--- error_code: 200
=== TEST 12: upstream failed, wrong nodes format
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/schema/validate/upstreams',
ngx.HTTP_POST,
[[{
"nodes":[
"nghttp2.org"
],
"type":"roundrobin"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
}
}
--- error_code: 400
--- response
{"error_msg":"allOf 1 failed: value should match only one schema, but matches none"}
=== TEST 13: Check node_schema optional port
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes',
ngx.HTTP_POST,
{
uri = "/hello",
upstream = {
type = "roundrobin",
nodes = {
{ host = "127.0.0.1:1980", weight = 1,}
}
},
methods = {"GET"},
}
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 14: Test route upstream
--- request
GET /hello
--- response_body
hello world

View File

@@ -0,0 +1,250 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
run_tests;
__DATA__
=== TEST 1: get route schema
--- request
GET /apisix/admin/schema/route
--- response_body eval
qr/"plugins":\{"type":"object"}/
=== TEST 2: get service schema and check if it contains `anyOf`
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin").test
local code, _, res_body = t('/apisix/admin/schema/service', ngx.HTTP_GET)
local res_data = core.json.decode(res_body)
if res_data["anyOf"] then
ngx.say("found `anyOf`")
return
end
ngx.say("passed")
}
}
--- request
GET /t
--- response_body
passed
=== TEST 3: get not exist schema
--- request
GET /apisix/admin/schema/noexits
--- error_code: 400
=== TEST 4: wrong method
--- request
PUT /apisix/admin/schema/service
--- error_code: 404
=== TEST 5: wrong method
--- request
POST /apisix/admin/schema/service
--- error_code: 404
=== TEST 6: ssl
--- config
location /t {
content_by_lua_block {
local ssl = require("apisix.schema_def").ssl
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/schema/ssl',
ngx.HTTP_GET,
nil,
ssl
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 7: get plugin's schema
--- request
GET /apisix/admin/schema/plugins/limit-count
--- response_body eval
qr/"required":\["count","time_window"\]/
=== TEST 8: get not exist plugin
--- request
GET /apisix/admin/schema/plugins/no-exist
--- error_code: 404
=== TEST 9: serverless-pre-function
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/schema/plugins/serverless-pre-function',
ngx.HTTP_GET,
nil,
[[{
"properties": {
"phase": {
"enum": ["rewrite", "access", "header_filter", "body_filter", "log", "before_proxy"],
"type": "string"
},
"functions": {
"minItems": 1,
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["functions"],
"type": "object"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 10: serverless-post-function
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/schema/plugins/serverless-post-function',
ngx.HTTP_GET,
nil,
[[{
"properties": {
"phase": {
"enum": ["rewrite", "access", "header_filter", "body_filter", "log", "before_proxy"],
"type": "string"
},
"functions": {
"minItems": 1,
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["functions"],
"type": "object"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 11: get plugin udp-logger schema
--- request
GET /apisix/admin/schema/plugins/udp-logger
--- response_body eval
qr/"properties":/
=== TEST 12: get plugin grpc-transcode schema
--- request
GET /apisix/admin/schema/plugins/grpc-transcode
--- response_body eval
qr/("proto_id".*additionalProperties|additionalProperties.*"proto_id")/
=== TEST 13: get plugin prometheus schema
--- request
GET /apisix/admin/schema/plugins/prometheus
--- response_body eval
qr/"disable":\{"type":"boolean"\}/
=== TEST 14: get plugin node-status schema
--- extra_yaml_config
plugins:
- node-status
--- request
GET /apisix/admin/schema/plugins/node-status
--- response_body eval
qr/"disable":\{"type":"boolean"\}/
=== TEST 15: get global_rule schema to check if it contains `create_time` and `update_time`
--- request
GET /apisix/admin/schema/global_rule
--- response_body eval
qr/("update_time":\{"type":"integer"\}.*"create_time":\{"type":"integer"\}|"create_time":\{"type":"integer"\}.*"update_time":\{"type":"integer"\})/
=== TEST 16: get proto schema to check if it contains `create_time` and `update_time`
--- request
GET /apisix/admin/schema/proto
--- response_body eval
qr/("update_time":\{"type":"integer"\}.*"create_time":\{"type":"integer"\}|"create_time":\{"type":"integer"\}.*"update_time":\{"type":"integer"\})/
=== TEST 17: get stream_route schema to check if it contains `create_time` and `update_time`
--- request
GET /apisix/admin/schema/stream_route
--- response_body eval
qr/("update_time":\{"type":"integer"\}.*"create_time":\{"type":"integer"\}|"create_time":\{"type":"integer"\}.*"update_time":\{"type":"integer"\})/

View File

@@ -0,0 +1,279 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
});
run_tests;
__DATA__
=== TEST 1: PUT
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, body = t('/apisix/admin/secrets/vault/test1',
ngx.HTTP_PUT,
[[{
"uri": "http://127.0.0.1:12800/get",
"prefix" : "apisix",
"token" : "apisix"
}]],
[[{
"value": {
"uri": "http://127.0.0.1:12800/get",
"prefix" : "apisix",
"token" : "apisix"
},
"key": "/apisix/secrets/vault/test1"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/secrets/vault/test1'))
local create_time = res.body.node.value.create_time
assert(create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
}
}
--- response_body
passed
=== TEST 2: GET
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/secrets/vault/test1',
ngx.HTTP_GET,
nil,
[[{
"value": {
"uri": "http://127.0.0.1:12800/get",
"prefix" : "apisix",
"token" : "apisix"
},
"key": "/apisix/secrets/vault/test1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 3: GET all
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/secrets',
ngx.HTTP_GET,
nil,
[[{
"total": 1,
"list": [
{
"key": "/apisix/secrets/vault/test1",
"value": {
"uri": "http://127.0.0.1:12800/get",
"prefix" : "apisix",
"token" : "apisix"
}
}
]
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 4: PATCH on path
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local res = assert(etcd.get('/secrets/vault/test1'))
local prev_create_time = res.body.node.value.create_time
assert(prev_create_time ~= nil, "create_time is nil")
local prev_update_time = res.body.node.value.update_time
assert(prev_update_time ~= nil, "update_time is nil")
ngx.sleep(1)
local code, body = t('/apisix/admin/secrets/vault/test1/token',
ngx.HTTP_PATCH,
[["unknown"]],
[[{
"value": {
"uri": "http://127.0.0.1:12800/get",
"prefix" : "apisix",
"token" : "unknown"
},
"key": "/apisix/secrets/vault/test1"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/secrets/vault/test1'))
assert(res.body.node.value.token == "unknown")
}
}
--- response_body
passed
=== TEST 5: PATCH
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local res = assert(etcd.get('/secrets/vault/test1'))
local prev_create_time = res.body.node.value.create_time
assert(prev_create_time ~= nil, "create_time is nil")
local prev_update_time = res.body.node.value.update_time
assert(prev_update_time ~= nil, "update_time is nil")
ngx.sleep(1)
local code, body = t('/apisix/admin/secrets/vault/test1',
ngx.HTTP_PATCH,
[[{
"uri": "http://127.0.0.1:12800/get",
"prefix" : "apisix",
"token" : "apisix"
}]],
[[{
"value": {
"uri": "http://127.0.0.1:12800/get",
"prefix" : "apisix",
"token" : "apisix"
},
"key": "/apisix/secrets/vault/test1"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/secrets/vault/test1'))
assert(res.body.node.value.token == "apisix")
}
}
--- response_body
passed
=== TEST 6: PATCH without id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/secrets/vault',
ngx.HTTP_PATCH,
[[{}]],
[[{}]]
)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"no secret id"}
=== TEST 7: DELETE
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/secrets/vault/test1',
ngx.HTTP_DELETE
)
ngx.say(body)
}
}
--- response_body
passed
=== TEST 8: PUT with invalid format
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, body = t('/apisix/admin/secrets/vault/test1',
ngx.HTTP_PUT,
[[{
"uri": "/get",
"prefix" : "apisix",
"token" : "apisix"
}]],
[[{
"value": {
"uri": "http://127.0.0.1:12800/get",
"prefix" : "apisix",
"token" : "apisix"
},
"key": "/apisix/secrets/vault/test1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- error_code: 400
--- response_body eval
qr/validation failed: failed to match pattern/

View File

@@ -0,0 +1,105 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
run_tests;
__DATA__
=== TEST 1: set service(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/1',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": [{
"host": "127.0.0.1",
"port": 8080,
"weight": 1
}],
"type": "roundrobin"
},
"desc": "new service"
}]],
[[{
"value": {
"upstream": {
"nodes": [{
"host": "127.0.0.1",
"port": 8080,
"weight": 1
}],
"type": "roundrobin"
},
"desc": "new service"
},
"key": "/apisix/services/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 2: get service(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/1',
ngx.HTTP_GET,
nil,
[[{
"value": {
"upstream": {
"nodes": [{
"host": "127.0.0.1",
"port": 8080,
"weight": 1
}],
"type": "roundrobin"
},
"desc": "new service"
},
"key": "/apisix/services/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed

View File

@@ -0,0 +1,156 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: set service(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/1',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- error_code: 201
--- response_body
passed
=== TEST 2: add route
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"service_id": 1,
"uri": "/index.html"
}]]
)
if code >= 300 then
ngx.status = code
ngx.print(message)
return
end
ngx.say(message)
}
}
--- response_body
passed
=== TEST 3: delete service(wrong header)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/services/1?force=anyvalue',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 400 message: {"error_msg":"can not delete this service directly, route [1] is still using it now"}
=== TEST 4: delete service(without force delete header)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/services/1',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 400 message: {"error_msg":"can not delete this service directly, route [1] is still using it now"}
=== TEST 5: delete service(force delete)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/services/1?force=true',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body chomp
[delete] code: 200 message: passed
=== TEST 6: delete route
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/routes/1',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body chomp
[delete] code: 200 message: passed

View File

@@ -0,0 +1,745 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
run_tests;
__DATA__
=== TEST 1: set service(id: 5eeb3dc90f747328b2930b0b)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/5eeb3dc90f747328b2930b0b',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new service"
}]],
[[{
"value": {
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new service"
},
"key": "/apisix/services/5eeb3dc90f747328b2930b0b"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 2: get service(id: 5eeb3dc90f747328b2930b0b)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/5eeb3dc90f747328b2930b0b',
ngx.HTTP_GET,
nil,
[[{
"value": {
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new service"
},
"key": "/apisix/services/5eeb3dc90f747328b2930b0b"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 3: delete service(id: 5eeb3dc90f747328b2930b0b)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/services/5eeb3dc90f747328b2930b0b', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[delete] code: 200 message: passed
=== TEST 4: delete service(id: not_found)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code = t('/apisix/admin/services/not_found', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code)
}
}
--- request
GET /t
--- response_body
[delete] code: 404
=== TEST 5: post service + delete
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/services',
ngx.HTTP_POST,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}]],
[[{
"value": {
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}
}]]
)
if code ~= 200 then
ngx.status = code
ngx.say(message)
return
end
ngx.say("[push] code: ", code, " message: ", message)
local id = string.sub(res.key, #"/apisix/services/" + 1)
code, message = t('/apisix/admin/services/' .. id, ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[push] code: 200 message: passed
[delete] code: 200 message: passed
=== TEST 6: uri + upstream
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/services/5eeb3dc90f747328b2930b0b',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}]],
[[{
"value": {
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
},
"key": "/apisix/services/5eeb3dc90f747328b2930b0b"
}]]
)
if code ~= 200 then
ngx.status = code
ngx.say(message)
return
end
ngx.say("[push] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[push] code: 200 message: passed
=== TEST 7: uri + plugins
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/services/5eeb3dc90f747328b2930b0b',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
}]],
[[{
"value": {
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
},
"key": "/apisix/services/5eeb3dc90f747328b2930b0b"
}]]
)
if code ~= 200 then
ngx.status = code
ngx.say(message)
return
end
ngx.say("[push] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[push] code: 200 message: passed
=== TEST 8: invalid service id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/*invalid_id$',
ngx.HTTP_PUT,
[[{
"plugins": {
"limit-count": {
"count": 2,
"time_window": 60,
"rejected_code": 503,
"key": "remote_addr"
}
}
}]]
)
ngx.exit(code)
}
}
--- request
GET /t
--- error_code: 400
=== TEST 9: invalid id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/5eeb3dc90f747328b2930b0b',
ngx.HTTP_PUT,
[[{
"id": "3",
"plugins": {}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"wrong service id"}
=== TEST 10: id in the rule
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services',
ngx.HTTP_PUT,
[[{
"id": "5eeb3dc90f747328b2930b0b",
"plugins": {}
}]],
[[{
"value": {
"plugins": {}
},
"key": "/apisix/services/5eeb3dc90f747328b2930b0b"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 11: integer id less than 1
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services',
ngx.HTTP_PUT,
[[{
"id": -100,
"plugins": {}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"id\" validation failed: object matches none of the required"}
=== TEST 12: invalid service id: contains symbols value
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services',
ngx.HTTP_PUT,
[[{
"id": "*invalid_id$",
"plugins": {}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"id\" validation failed: object matches none of the required"}
=== TEST 13: invalid upstream_id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services',
ngx.HTTP_PUT,
[[{
"id": "5eeb3dc90f747328b2930b0b",
"upstream_id": "invalid$"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"upstream_id\" validation failed: object matches none of the required"}
=== TEST 14: not exist upstream_id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services',
ngx.HTTP_PUT,
[[{
"id": "5eeb3dc90f747328b2930b0b",
"upstream_id": "9999999999"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"failed to fetch upstream info by upstream id [9999999999], response code: 404"}
=== TEST 15: wrong service id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/5eeb3dc90f747328b2930b0b',
ngx.HTTP_POST,
[[{
"plugins": {}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"wrong service id, do not need it"}
=== TEST 16: wrong service id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services',
ngx.HTTP_POST,
[[{
"id": "5eeb3dc90f747328b2930b0b",
"plugins": {}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"wrong service id, do not need it"}
=== TEST 17: patch service(whole)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/5eeb3dc90f747328b2930b0b',
ngx.HTTP_PATCH,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new 20 service"
}]],
[[{
"value": {
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new 20 service"
},
"key": "/apisix/services/5eeb3dc90f747328b2930b0b"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 18: patch service(new desc)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/5eeb3dc90f747328b2930b0b',
ngx.HTTP_PATCH,
[[{
"desc": "new 19 service"
}]],
[[{
"value": {
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new 19 service"
},
"key": "/apisix/services/5eeb3dc90f747328b2930b0b"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 19: patch service(new nodes)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/5eeb3dc90f747328b2930b0b',
ngx.HTTP_PATCH,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8081": 3,
"127.0.0.1:8082": 4
},
"type": "roundrobin"
}
}]],
[[{
"value": {
"upstream": {
"nodes": {
"127.0.0.1:8080": 1,
"127.0.0.1:8081": 3,
"127.0.0.1:8082": 4
},
"type": "roundrobin"
}
}
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 20: set service(id: 5eeb3dc90f747328b2930b0b) and upstream(type:chash, default hash_on: vars, missing key)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/5eeb3dc90f747328b2930b0b',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash"
},
"desc": "new service"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"missing key"}
=== TEST 21: set service(id: 5eeb3dc90f747328b2930b0b) and upstream(type:chash, hash_on: header, missing key)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/5eeb3dc90f747328b2930b0b',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"hash_on": "header"
},
"desc": "new service"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"missing key"}
=== TEST 22: set service(id: 5eeb3dc90f747328b2930b0b) and upstream(type:chash, hash_on: cookie, missing key)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/5eeb3dc90f747328b2930b0b',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"hash_on": "cookie"
},
"desc": "new service"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"missing key"}
=== TEST 23: set service(id: 5eeb3dc90f747328b2930b0b) and upstream(type:chash, hash_on: consumer, missing key is ok)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/5eeb3dc90f747328b2930b0b',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"hash_on": "consumer"
},
"desc": "new service"
}]]
)
ngx.status = code
ngx.say(code .. " " .. body)
}
}
--- request
GET /t
--- response_body
200 passed

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,300 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: not unwanted data, POST
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/services',
ngx.HTTP_POST,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
res.key = nil
assert(res.value.create_time ~= nil)
res.value.create_time = nil
assert(res.value.update_time ~= nil)
res.value.update_time = nil
assert(res.value.id ~= nil)
res.value.id = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"value":{"upstream":{"hash_on":"vars","nodes":{"127.0.0.1:8080":1},"pass_host":"pass","scheme":"http","type":"roundrobin"}}}
=== TEST 2: not unwanted data, PUT
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/services/1',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
res.value.create_time = nil
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/services/1","value":{"id":"1","upstream":{"hash_on":"vars","nodes":{"127.0.0.1:8080":1},"pass_host":"pass","scheme":"http","type":"roundrobin"}}}
=== TEST 3: not unwanted data, PATCH
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/services/1',
ngx.HTTP_PATCH,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
res.value.create_time = nil
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/services/1","value":{"id":"1","upstream":{"hash_on":"vars","nodes":{"127.0.0.1:8080":1},"pass_host":"pass","scheme":"http","type":"roundrobin"}}}
=== TEST 4: not unwanted data, GET
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/services/1', ngx.HTTP_GET)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.createdIndex ~= nil)
res.createdIndex = nil
assert(res.modifiedIndex ~= nil)
res.modifiedIndex = nil
assert(res.value.create_time ~= nil)
res.value.create_time = nil
assert(res.value.update_time ~= nil)
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/services/1","value":{"id":"1","upstream":{"hash_on":"vars","nodes":{"127.0.0.1:8080":1},"pass_host":"pass","scheme":"http","type":"roundrobin"}}}
=== TEST 5: not unwanted data, DELETE
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/services/1',
ngx.HTTP_DELETE
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(json.encode(res))
}
}
--- response_body
{"deleted":"1","key":"/apisix/services/1"}
=== TEST 6: set service(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/1',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 7: set route(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"service_id": 1,
"uri": "/index.html"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 8: delete service(id: 1)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/services/1', ngx.HTTP_DELETE)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 400 message: {"error_msg":"can not delete this service directly, route [1] is still using it now"}
=== TEST 9: delete route(id: 1)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/routes/1', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 200 message: passed
=== TEST 10: delete service(id: 1)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/services/1', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 200 message: passed

View File

@@ -0,0 +1,802 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
no_root_location();
run_tests;
__DATA__
=== TEST 1: set ssl(id: 1)
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local etcd = require("apisix.core.etcd")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "test.com"}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "test.com"
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/ssls/1'))
local prev_create_time = res.body.node.value.create_time
assert(prev_create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
}
}
--- request
GET /t
--- response_body
passed
=== TEST 2: get ssl(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/ssls/1',
ngx.HTTP_GET,
nil,
[[{
"value": {
"sni": "test.com",
"key": null
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 3: delete ssl(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/ssls/1', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[delete] code: 200 message: passed
=== TEST 4: delete ssl(id: 99999999999999)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code = t('/apisix/admin/ssls/99999999999999', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code)
}
}
--- request
GET /t
--- response_body
[delete] code: 404
=== TEST 5: push ssl + delete
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "foo.com"}
local code, message, res = t.test('/apisix/admin/ssls',
ngx.HTTP_POST,
core.json.encode(data),
[[{
"value": {
"sni": "foo.com"
}
}]]
)
if code ~= 200 then
ngx.status = code
ngx.say(message)
return
end
ngx.say("[push] code: ", code, " message: ", message)
local id = string.sub(res.key, #"/apisix/ssls/" + 1)
code, message = t.test('/apisix/admin/ssls/' .. id, ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[push] code: 200 message: passed
[delete] code: 200 message: passed
=== TEST 6: missing certificate information
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {sni = "foo.com"}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "foo.com"
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: then clause did not match"}
=== TEST 7: wildcard host name
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "*.foo.com"}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "*.foo.com"
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 8: store sni in `snis`
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {
cert = ssl_cert, key = ssl_key,
snis = {"*.foo.com", "bar.com"},
}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"snis": ["*.foo.com", "bar.com"]
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 9: string id
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "test.com"}
local code, body = t.test('/apisix/admin/ssls/a-b-c-ABC_0123',
ngx.HTTP_PUT,
core.json.encode(data)
)
if code > 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 10: string id(delete)
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "test.com"}
local code, body = t.test('/apisix/admin/ssls/a-b-c-ABC_0123',
ngx.HTTP_DELETE
)
if code > 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 11: invalid id
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "test.com"}
local code, body = t.test('/apisix/admin/ssls/*invalid',
ngx.HTTP_PUT,
core.json.encode(data)
)
if code > 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- error_code: 400
=== TEST 12: set ssl with multicerts(id: 1)
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local ssl_ecc_cert = t.read_file("t/certs/apisix_ecc.crt")
local ssl_ecc_key = t.read_file("t/certs/apisix_ecc.key")
local data = {
cert = ssl_cert,
key = ssl_key,
sni = "test.com",
certs = {ssl_ecc_cert},
keys = {ssl_ecc_key}
}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "test.com"
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 13: mismatched certs and keys
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_ecc_cert = t.read_file("t/certs/apisix_ecc.crt")
local data = {
sni = "test.com",
certs = { ssl_ecc_cert },
keys = {},
}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "test.com"
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: then clause did not match"}
=== TEST 14: set ssl(with labels)
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "test.com", labels = { version = "v2", build = "16", env = "production"}}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "test.com",
"labels": {
"version": "v2",
"build": "16",
"env": "production"
}
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 15: invalid format of label value: set ssl
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "test.com", labels = { env = {"production", "release"}}}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "test.com",
"labels": {
"env": ["production", "release"]
}
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"labels\" validation failed: failed to validate env (matching \".*\"): wrong type: expected string, got table"}
=== TEST 16: create ssl with manage fields(id: 1)
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {
cert = ssl_cert,
key = ssl_key,
sni = "test.com"
}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "test.com"
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 17: delete test ssl(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/ssls/1', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[delete] code: 200 message: passed
=== TEST 18: create/patch ssl
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local etcd = require("apisix.core.etcd")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "test.com"}
local code, body, res = t.test('/apisix/admin/ssls',
ngx.HTTP_POST,
core.json.encode(data),
[[{
"value": {
"sni": "test.com"
}
}]]
)
if code ~= 200 then
ngx.status = code
ngx.say(body)
return
end
local id = string.sub(res.key, #"/apisix/ssls/" + 1)
local res = assert(etcd.get('/ssls/' .. id))
local prev_create_time = res.body.node.value.create_time
assert(prev_create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
local code, body = t.test('/apisix/admin/ssls/' .. id,
ngx.HTTP_PATCH,
core.json.encode({create_time = 0, update_time = 1})
)
if code ~= 200 then
ngx.status = code
ngx.say(body)
return
end
local res = assert(etcd.get('/ssls/' .. id))
local create_time = res.body.node.value.create_time
assert(create_time == 0, "create_time mismatched")
local update_time = res.body.node.value.update_time
assert(update_time == 1, "update_time mismatched")
-- clean up
local code, body = t.test('/apisix/admin/ssls/' .. id, ngx.HTTP_DELETE)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 19: missing sni information
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: then clause did not match"}
=== TEST 20: type client, missing sni information
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {type = "client", cert = ssl_cert, key = ssl_key}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- response_body chomp
passed
=== TEST 21: set ssl with sercret
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local data = {
sni = "test.com",
cert = "$secret://vault/test/ssl/test.com.crt",
key = "$secret://vault/test/ssl/test.com.key",
certs = {"$secret://vault/test/ssl/test.com.2.crt"},
keys = {"$secret://vault/test/ssl/test.com.2.key"}
}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "test.com",
"cert": "$secret://vault/test/ssl/test.com.crt",
"key": "$secret://vault/test/ssl/test.com.key",
"certs": ["$secret://vault/test/ssl/test.com.2.crt"],
"keys": ["$secret://vault/test/ssl/test.com.2.key"]
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 22: set ssl with env, and prefix is all uppercase or lowercase
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local data = {
sni = "test.com",
cert = "$ENV://APISIX_TEST_SSL_CERT",
key = "$env://APISIX_TEST_SSL_KEY",
certs = {"$env://APISIX_TEST_SSL_CERTS"},
keys = {"$ENV://APISIX_TEST_SSL_KEYS"},
}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "test.com",
"cert": "$ENV://APISIX_TEST_SSL_CERT",
"key": "$env://APISIX_TEST_SSL_KEY",
"certs": ["$env://APISIX_TEST_SSL_CERTS"],
"keys": ["$ENV://APISIX_TEST_SSL_KEYS"]
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 23: set ssl with invalid prefix
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local data = {
sni = "test.com",
cert = "$ENV://APISIX_TEST_SSL_CERT",
key = "$env://APISIX_TEST_SSL_KEY",
certs = {"https://APISIX_TEST_SSL_CERTS"},
keys = {"$ENV://APISIX_TEST_SSL_KEYS"},
}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "test.com"
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"certs\" validation failed: failed to validate item 1: value should match only one schema, but matches none"}

View File

@@ -0,0 +1,496 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: not unwanted data, POST
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "not-unwanted-post.com"}
local code, message, res = t.test('/apisix/admin/ssls',
ngx.HTTP_POST,
json.encode(data)
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.key ~= nil)
res.key = nil
assert(res.value.create_time ~= nil)
res.value.create_time = nil
assert(res.value.update_time ~= nil)
res.value.update_time = nil
assert(res.value.cert ~= nil)
res.value.cert = ""
assert(res.value.key ~= nil)
res.value.key = ""
assert(res.value.id ~= nil)
res.value.id = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"value":{"cert":"","key":"","sni":"not-unwanted-post.com","status":1,"type":"server"}}
=== TEST 2: not unwanted data, PUT
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "test.com"}
local code, message, res = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
json.encode(data)
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.value.create_time ~= nil)
res.value.create_time = nil
assert(res.value.update_time ~= nil)
res.value.update_time = nil
assert(res.value.cert ~= nil)
res.value.cert = ""
assert(res.value.key ~= nil)
res.value.key = ""
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/ssls/1","value":{"cert":"","id":"1","key":"","sni":"test.com","status":1,"type":"server"}}
=== TEST 3: not unwanted data, PATCH
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "t.com"}
local code, message, res = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PATCH,
json.encode(data)
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.value.create_time ~= nil)
res.value.create_time = nil
assert(res.value.update_time ~= nil)
res.value.update_time = nil
assert(res.value.cert ~= nil)
res.value.cert = ""
assert(res.value.key ~= nil)
res.value.key = ""
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/ssls/1","value":{"cert":"","id":"1","key":"","sni":"t.com","status":1,"type":"server"}}
=== TEST 4: not unwanted data, GET
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin")
local code, message, res = t.test('/apisix/admin/ssls/1',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.createdIndex ~= nil)
res.createdIndex = nil
assert(res.modifiedIndex ~= nil)
res.modifiedIndex = nil
assert(res.value.create_time ~= nil)
res.value.create_time = nil
assert(res.value.update_time ~= nil)
res.value.update_time = nil
assert(res.value.cert ~= nil)
res.value.cert = ""
assert(res.value.key == nil)
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/ssls/1","value":{"cert":"","id":"1","sni":"t.com","status":1,"type":"server"}}
=== TEST 5: not unwanted data, DELETE
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "test.com"}
local code, message, res = t.test('/apisix/admin/ssls/1',
ngx.HTTP_DELETE
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(json.encode(res))
}
}
--- response_body
{"deleted":"1","key":"/apisix/ssls/1"}
=== TEST 6: bad cert
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = [[-----BEGIN CERTIFICATE-----
MIIEojCCAwqgAwIBAgIJAK253pMhgCkxMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
BAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxDzANBgNVBAcMBlpodUhhaTEPMA0G
U/OOcSRr39Kuis/JJ+DkgHYa/PWHZhnJQBxcqXXk1bJGw9BNbhM=
-----END CERTIFICATE-----
]], key = ssl_key, sni = "test.com"}
local code, message, res = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
json.encode(data)
)
if code >= 300 then
ngx.status = code
ngx.print(message)
return
end
ngx.say(res)
}
}
--- error_code: 400
--- response_body
{"error_msg":"failed to parse cert: PEM_read_bio_X509_AUX() failed"}
=== TEST 7: bad key
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local data = {cert = ssl_cert, key = [[
-----BEGIN RSA PRIVATE KEY-----
MIIG5AIBAAKCAYEAyCM0rqJecvgnCfOw4fATotPwk5Ba0gC2YvIrO+gSbQkyxXF5
jhZB3W6BkWUWR4oNFLLSqcVbVDPitz/Mt46Mo8amuS6zTbQetGnBARzPLtmVhJfo
wzarryret/7GFW1/3cz+hTj9/d45i25zArr3Pocfpur5mfz3fJO8jg==
-----END RSA PRIVATE KEY-----]], sni = "test.com"}
local code, message, res = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
json.encode(data)
)
if code >= 300 then
ngx.status = code
ngx.print(message)
return
end
ngx.say(res)
}
}
--- error_code: 400
--- response_body
{"error_msg":"failed to parse key: PEM_read_bio_PrivateKey() failed"}
=== TEST 8: bad certs
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "t.com",
certs = {
[[-----BEGIN CERTIFICATE-----
MIIEojCCAwqgAwIBAgIJAK253pMhgCkxMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
BAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxDzANBgNVBAcMBlpodUhhaTEPMA0G
U/OOcSRr39Kuis/JJ+DkgHYa/PWHZhnJQBxcqXXk1bJGw9BNbhM=
-----END CERTIFICATE-----]]
},
keys = {ssl_key}
}
local code, message, res = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
json.encode(data)
)
if code >= 300 then
ngx.status = code
ngx.print(message)
return
end
ngx.say(res)
}
}
--- error_code: 400
--- response_body
{"error_msg":"failed to handle cert-key pair[1]: failed to parse cert: PEM_read_bio_X509_AUX() failed"}
=== TEST 9: bad keys
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "t.com",
certs = {ssl_cert},
keys = {[[-----BEGIN RSA PRIVATE KEY-----
MIIG5AIBAAKCAYEAyCM0rqJecvgnCfOw4fATotPwk5Ba0gC2YvIrO+gSbQkyxXF5
jhZB3W6BkWUWR4oNFLLSqcVbVDPitz/Mt46Mo8amuS6zTbQetGnBARzPLtmVhJfo
wzarryret/7GFW1/3cz+hTj9/d45i25zArr3Pocfpur5mfz3fJO8jg==
-----END RSA PRIVATE KEY-----]]}
}
local code, message, res = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
json.encode(data)
)
if code >= 300 then
ngx.status = code
ngx.print(message)
return
end
ngx.say(res)
}
}
--- error_code: 400
--- response_body
{"error_msg":"failed to handle cert-key pair[1]: failed to parse key: PEM_read_bio_PrivateKey() failed"}
=== TEST 10: empty snis
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, snis = {}}
local code, message, res = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
json.encode(data)
)
if code >= 300 then
ngx.status = code
ngx.print(message)
return
end
ngx.say(res)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"snis\" validation failed: expect array to have at least 1 items"}
=== TEST 11: update snis, PATCH with sub path
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, snis = {"test.com"}}
local code, message, res = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
json.encode(data)
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
local data = {"update1.com", "update2.com"}
local code, message, res = t.test('/apisix/admin/ssls/1/snis',
ngx.HTTP_PATCH,
json.encode(data)
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
ngx.say(res)
}
}
--- response_body_like eval
qr/"snis":\["update1.com","update2.com"\]/
=== TEST 12: PATCH encrypt ssl key
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring: "qeddd145sfvddff3"
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, certs = {ssl_cert}, keys = {ssl_key}}
local code, message, res = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PATCH,
json.encode(data)
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(res.value.key == ssl_key)
ngx.say(res.value.keys[1] == ssl_key)
}
}
--- response_body
false
false
=== TEST 13: PATCH encrypt ssl key, sub_path
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring: "qeddd145sfvddff3"
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin")
local ssl_key = t.read_file("t/certs/apisix.key")
local code, message, res = t.test('/apisix/admin/ssls/1/keys',
ngx.HTTP_PATCH,
json.encode({ssl_key})
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(res.value.keys[1] == ssl_key)
}
}
--- response_body
false

View File

@@ -0,0 +1,63 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: list empty resources
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/ssls',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(json.encode(res))
}
}
--- response_body
{"list":[],"total":0}

View File

@@ -0,0 +1,510 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
log_level('debug');
no_root_location();
add_block_preprocessor( sub{
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
my $TEST_NGINX_HTML_DIR ||= html_dir();
my $config = <<_EOC_;
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
location /t {
content_by_lua_block {
-- etcd sync
ngx.sleep(0.2)
do
local sock = ngx.socket.tcp()
sock:settimeout(2000)
local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock")
if not ok then
ngx.say("failed to connect: ", err)
return
end
ngx.say("connected: ", ok)
local sess, err = sock:sslhandshake(nil, "www.test.com", true)
if not sess then
sock = ngx.socket.tcp()
sock:settimeout(2000)
local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock")
if not ok then
ngx.say("failed to connect: ", err)
return
end
ngx.say("connected: ", ok)
sess, err = sock:sslhandshake(nil, "www.test.com", true)
if not sess then
ngx.say("failed to do SSL handshake: ", err)
return
end
end
ngx.say("ssl handshake: ", sess ~= nil)
local req = "GET /hello HTTP/1.0\\r\\nHost: www.test.com\\r\\nConnection: close\\r\\n\\r\\n"
local bytes, err = sock:send(req)
if not bytes then
ngx.say("failed to send http request: ", err)
return
end
ngx.say("sent http request: ", bytes, " bytes.")
while true do
local line, err = sock:receive()
if not line then
break
end
ngx.say("received: ", line)
end
local ok, err = sock:close()
ngx.say("close: ", ok, " ", err)
end -- do
-- collectgarbage()
}
}
_EOC_
if (!$block->config) {
$block->set_value("config", $config)
}
}
);
run_tests;
__DATA__
=== TEST 1: set ssl(sni: www.test.com), encrypt with the first keyring
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring:
- edd1c9f0985e76a1
- qeddd145sfvddff3
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "www.test.com"}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "www.test.com"
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 2: set route(id: 1)
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring: "edd1c9f0985e76a1"
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/hello"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 3: client request with the old style keyring
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring: "edd1c9f0985e76a1"
--- response_body eval
qr{connected: 1
ssl handshake: true
sent http request: 62 bytes.
received: HTTP/1.1 200 OK
received: Content-Type: text/plain
received: Content-Length: 12
received: Connection: close
received: Server: APISIX/\d\.\d+(\.\d+)?
received: \nreceived: hello world
close: 1 nil}
--- error_log
server name: "www.test.com"
--- no_error_log
[error]
[alert]
=== TEST 4: client request with the new style keyring
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring:
- edd1c9f0985e76a1
--- response_body eval
qr{connected: 1
ssl handshake: true
sent http request: 62 bytes.
received: HTTP/1.1 200 OK
received: Content-Type: text/plain
received: Content-Length: 12
received: Connection: close
received: Server: APISIX/\d\.\d+(\.\d+)?
received: \nreceived: hello world
close: 1 nil}
--- error_log
server name: "www.test.com"
--- no_error_log
[error]
[alert]
=== TEST 5: client request failed with the wrong keyring
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring:
- qeddd145sfvddff3
--- error_log
decrypt ssl key failed
=== TEST 6: client request successfully, use the two keyring to decrypt in turn
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring:
- qeddd145sfvddff3
- edd1c9f0985e76a1
--- response_body eval
qr{connected: 1
ssl handshake: true
sent http request: 62 bytes.
received: HTTP/1.1 200 OK
received: Content-Type: text/plain
received: Content-Length: 12
received: Connection: close
received: Server: APISIX/\d\.\d+(\.\d+)?
received: \nreceived: hello world
close: 1 nil}
--- ignore_error_log
=== TEST 7: remove test ssl certs
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring:
- edd1c9f0985e76a1
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
t.test('/apisix/admin/ssls/1', ngx.HTTP_DELETE)
}
}
=== TEST 8: set ssl(sni: www.test.com), do not encrypt
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring: null
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "www.test.com"}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "www.test.com"
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 9: client request without keyring
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring: null
--- response_body eval
qr{connected: 1
ssl handshake: true
sent http request: 62 bytes.
received: HTTP/1.1 200 OK
received: Content-Type: text/plain
received: Content-Length: 12
received: Connection: close
received: Server: APISIX/\d\.\d+(\.\d+)?
received: \nreceived: hello world
close: 1 nil}
--- error_log
server name: "www.test.com"
--- no_error_log
[error]
[alert]
=== TEST 10: remove test ssl certs
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring: null
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
t.test('/apisix/admin/ssls/1', ngx.HTTP_DELETE)
}
}
=== TEST 11: set ssl(sni: www.test.com) with long label
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring: null
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "www.test.com",
labels = {secret = "js-design-test-bigdata-data-app-service-router-my-secret-number-123456"}}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "www.test.com",
"labels": {
"secret": "js-design-test-bigdata-data-app-service-router-my-secret-number-123456"
},
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 12: set ssl(sni: www.test.com), encrypt with the first keyring
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring:
- edd1c9f0985e76a1
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "test.com"}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "test.com"
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 13: update encrypt keyring, and set ssl(sni: test2.com)
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring:
- qeddd145sfvddff3
- edd1c9f0985e76a1
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/test2.crt")
local ssl_key = t.read_file("t/certs/test2.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "test2.com"}
local code, body = t.test('/apisix/admin/ssls/2',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "test2.com"
},
"key": "/apisix/ssls/2"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 14: Successfully access test.com
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring:
- qeddd145sfvddff3
- edd1c9f0985e76a1
--- exec
curl -k -s --resolve "test2.com:1994:127.0.0.1" https://test2.com:1994/hello 2>&1 | cat
--- response_body
hello world
=== TEST 15: Successfully access test2.com
--- yaml_config
apisix:
node_listen: 1984
data_encryption:
keyring:
- qeddd145sfvddff3
- edd1c9f0985e76a1
--- exec
curl -k -s --resolve "test2.com:1994:127.0.0.1" https://test2.com:1994/hello 2>&1 | cat
--- response_body
hello world

View File

@@ -0,0 +1,86 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
no_root_location();
run_tests;
__DATA__
=== TEST 1: Not supported set TLSv1.0 for ssl_protocols
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "test.com", ssl_protocols = {"TLSv1.0", "TLSv1.2"}}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"ssl_protocols\" validation failed: failed to validate item 1: matches none of the enum values"}
=== TEST 2: The default value for the ssl_protocols is null
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "test.com"}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "test.com",
"ssl_protocols": null,
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed

View File

@@ -0,0 +1,75 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: test /apisix/admin/ssls/{id}
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local etcd = require("apisix.core.etcd")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {cert = ssl_cert, key = ssl_key, sni = "test.com"}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
core.json.encode(data),
[[{
"value": {
"sni": "test.com"
},
"key": "/apisix/ssls/1"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/ssls/1'))
local prev_create_time = res.body.node.value.create_time
assert(prev_create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
}
}
--- response_body
passed

View File

@@ -0,0 +1,128 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
BEGIN {
# restarts cause the memory cache to be emptied, don't do this
$ENV{TEST_NGINX_FORCE_RESTART_ON_TEST} = 0;
}
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
use_hup();
add_block_preprocessor(sub {
my ($block) = @_;
if (!defined $block->yaml_config) {
$block->set_value("yaml_config", <<'EOF');
deployment:
role: traditional
role_traditional:
config_provider: yaml
admin:
admin_key:
- name: admin
key: edd1c9f034335f136f87ad84b625c8f1
role: admin
EOF
}
if (!defined $block->no_error_log) {
$block->set_value("no_error_log", "");
}
});
run_tests();
__DATA__
=== TEST 1: test
--- timeout: 15
--- max_size: 204800
--- exec
cd t && pnpm test admin/standalone.spec.ts 2>&1
--- no_error_log
failed to execute the script with status
--- response_body eval
qr/PASS admin\/standalone.spec.ts/
=== TEST 2: send /healthcheck should fail because config is not loaded yet
--- init_by_lua_block
require "resty.core"
apisix = require("apisix")
local shared_dict = ngx.shared["standalone-config"]
shared_dict:delete("config")
--- config
location /t {
content_by_lua_block {
local http = require("resty.http")
local healthcheck_uri = "http://127.0.0.1:7085" .. "/status/ready"
local httpc = http.new()
local res, _ = httpc:request_uri(healthcheck_uri, {method = "GET", keepalive = false})
ngx.status = res.status
}
}
--- request
GET /t
--- error_code: 503
=== TEST 3: configure route and send /healthcheck should pass
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/configs',
ngx.HTTP_PUT,
[[{"routes":[{"id":"r1","uri":"/r1","upstream":{"nodes":{"127.0.0.1:1980":1},"type":"roundrobin"},"plugins":{"proxy-rewrite":{"uri":"/hello"}}}]}]],
nil,
{
["X-API-KEY"] = "edd1c9f034335f136f87ad84b625c8f1"
}
)
if code >= 300 then
ngx.status = code
end
local code, body = t('/apisix/admin/configs',
ngx.HTTP_PUT,
[[{"routes":[{"id":"r1","uri":"/r1","upstream":{"nodes":{"127.0.0.1:1980":1},"type":"roundrobin"},"plugins":{"proxy-rewrite":{"uri":"/hello"}}}]}]],
nil,
{
["X-API-KEY"] = "edd1c9f034335f136f87ad84b625c8f1"
}
)
if code >= 300 then
ngx.status = code
end
ngx.sleep(1)
local http = require("resty.http")
local healthcheck_uri = "http://127.0.0.1:7085" .. "/status/ready"
local httpc = http.new()
local res, _ = httpc:request_uri(healthcheck_uri, {method = "GET", keepalive = false})
ngx.status = res.status
}
}
--- request
GET /t
--- error_code: 200

View File

@@ -0,0 +1,442 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import axios from "axios";
import YAML from "yaml";
const ENDPOINT = "/apisix/admin/configs";
const clientConfig = {
baseURL: "http://localhost:1984",
headers: {
"X-API-KEY": "edd1c9f034335f136f87ad84b625c8f1",
},
};
const config1 = {
routes: [
{
id: "r1",
uri: "/r1",
upstream: {
nodes: { "127.0.0.1:1980": 1 },
type: "roundrobin",
},
plugins: { "proxy-rewrite": { uri: "/hello" } },
},
],
};
const config2 = {
routes: [
{
id: "r2",
uri: "/r2",
upstream: {
nodes: { "127.0.0.1:1980": 1 },
type: "roundrobin",
},
plugins: { "proxy-rewrite": { uri: "/hello" } },
},
],
};
const invalidConfVersionConfig1 = {
routes_conf_version: -1,
};
const invalidConfVersionConfig2 = {
routes_conf_version: "adc",
};
const routeWithModifiedIndex = {
routes: [
{
id: "r1",
uri: "/r1",
modifiedIndex: 1,
upstream: {
nodes: { "127.0.0.1:1980": 1 },
type: "roundrobin",
},
plugins: { "proxy-rewrite": { uri: "/hello" } },
},
],
};
const routeWithKeyAuth = {
routes: [
{
id: "r1",
uri: "/r1",
upstream: {
nodes: { "127.0.0.1:1980": 1 },
type: "roundrobin",
},
plugins: {
"proxy-rewrite": { uri: "/hello" },
"key-auth": {},
},
},
]
}
const consumerWithModifiedIndex = {
routes: routeWithKeyAuth.routes,
consumers: [
{
modifiedIndex: 10,
username: "jack",
plugins: {
"key-auth": {
key: "jack-key",
}
},
},
],
}
const credential1 = {
routes: routeWithKeyAuth.routes,
consumers: [
{
"username": "john_1"
},
{
"id": "john_1/credentials/john-a",
"plugins": {
"key-auth": {
"key": "auth-a"
}
}
},
{
"id": "john_1/credentials/john-b",
"plugins": {
"key-auth": {
"key": "auth-b"
}
}
}
]
}
describe("Admin - Standalone", () => {
const client = axios.create(clientConfig);
client.interceptors.response.use((response) => {
const contentType = response.headers["content-type"] || "";
if (
contentType.includes("application/yaml") &&
typeof response.data === "string" &&
response.config.responseType !== "text"
)
response.data = YAML.parse(response.data);
return response;
});
describe("Normal", () => {
it("dump empty config (default json format)", async () => {
const resp = await client.get(ENDPOINT);
expect(resp.status).toEqual(200);
expect(resp.data.routes_conf_version).toEqual(0);
expect(resp.data.ssls_conf_version).toEqual(0);
expect(resp.data.services_conf_version).toEqual(0);
expect(resp.data.upstreams_conf_version).toEqual(0);
expect(resp.data.consumers_conf_version).toEqual(0);
});
it("dump empty config (yaml format)", async () => {
const resp = await client.get(ENDPOINT, {
headers: { Accept: "application/yaml" },
});
expect(resp.status).toEqual(200);
expect(resp.headers["content-type"]).toEqual("application/yaml");
expect(resp.data.routes_conf_version).toEqual(0);
expect(resp.data.ssls_conf_version).toEqual(0);
expect(resp.data.services_conf_version).toEqual(0);
expect(resp.data.upstreams_conf_version).toEqual(0);
expect(resp.data.consumers_conf_version).toEqual(0);
});
it("update config (add routes, by json)", async () => {
const resp = await client.put(ENDPOINT, config1);
expect(resp.status).toEqual(202);
});
it("dump config (json format)", async () => {
const resp = await client.get(ENDPOINT);
expect(resp.status).toEqual(200);
expect(resp.data.routes_conf_version).toEqual(1);
expect(resp.data.ssls_conf_version).toEqual(1);
expect(resp.data.services_conf_version).toEqual(1);
expect(resp.data.upstreams_conf_version).toEqual(1);
expect(resp.data.consumers_conf_version).toEqual(1);
});
it("check default value", async () => {
const resp = await client.get(ENDPOINT);
expect(resp.status).toEqual(200);
expect(resp.data.routes).toEqual(config1.routes);
});
it("dump config (yaml format)", async () => {
const resp = await client.get(ENDPOINT, {
headers: { Accept: "application/yaml" },
responseType: 'text',
});
expect(resp.status).toEqual(200);
expect(resp.data).toContain("routes:")
expect(resp.data).toContain("id: r1")
expect(resp.data.startsWith('---')).toBe(false);
expect(resp.data.endsWith('...')).toBe(false);
});
it('check route "r1"', async () => {
const resp = await client.get("/r1");
expect(resp.status).toEqual(200);
expect(resp.data).toEqual("hello world\n");
});
it("update config (add routes, by yaml)", async () => {
const resp = await client.put(
ENDPOINT,
YAML.stringify(config2),
{
headers: { "Content-Type": "application/yaml" },
}
);
expect(resp.status).toEqual(202);
});
it("dump config (json format)", async () => {
const resp = await client.get(ENDPOINT);
expect(resp.status).toEqual(200);
expect(resp.data.routes_conf_version).toEqual(2);
expect(resp.data.ssls_conf_version).toEqual(2);
expect(resp.data.services_conf_version).toEqual(2);
expect(resp.data.upstreams_conf_version).toEqual(2);
expect(resp.data.consumers_conf_version).toEqual(2);
});
it('check route "r1"', () =>
expect(client.get("/r1")).rejects.toThrow(
"Request failed with status code 404"
));
it('check route "r2"', async () => {
const resp = await client.get("/r2");
expect(resp.status).toEqual(200);
expect(resp.data).toEqual("hello world\n");
});
it("update config (delete routes)", async () => {
const resp = await client.put(
ENDPOINT,
{},
{ params: { conf_version: 3 } }
);
expect(resp.status).toEqual(202);
});
it('check route "r2"', () =>
expect(client.get("/r2")).rejects.toThrow(
"Request failed with status code 404"
));
it("only set routes_conf_version", async () => {
const resp = await client.put(
ENDPOINT,
YAML.stringify({ routes_conf_version: 15 }),
{
headers: { "Content-Type": "application/yaml" },
});
expect(resp.status).toEqual(202);
const resp_1 = await client.get(ENDPOINT);
expect(resp_1.status).toEqual(200);
expect(resp_1.data.routes_conf_version).toEqual(15);
expect(resp_1.data.ssls_conf_version).toEqual(4);
expect(resp_1.data.services_conf_version).toEqual(4);
expect(resp_1.data.upstreams_conf_version).toEqual(4);
expect(resp_1.data.consumers_conf_version).toEqual(4);
const resp2 = await client.put(
ENDPOINT,
YAML.stringify({ routes_conf_version: 17 }),
{
headers: { "Content-Type": "application/yaml" },
});
expect(resp2.status).toEqual(202);
const resp2_1 = await client.get(ENDPOINT);
expect(resp2_1.status).toEqual(200);
expect(resp2_1.data.routes_conf_version).toEqual(17);
expect(resp2_1.data.ssls_conf_version).toEqual(5);
expect(resp2_1.data.services_conf_version).toEqual(5);
expect(resp2_1.data.upstreams_conf_version).toEqual(5);
expect(resp2_1.data.consumers_conf_version).toEqual(5);
});
it("control resource changes using modifiedIndex", async () => {
const c1 = structuredClone(routeWithModifiedIndex);
c1.routes[0].modifiedIndex = 1;
const c2 = structuredClone(c1);
c2.routes[0].uri = "/r2";
const c3 = structuredClone(c2);
c3.routes[0].modifiedIndex = 2;
// Update with c1
const resp = await client.put(ENDPOINT, c1);
expect(resp.status).toEqual(202);
// Check route /r1 exists
const resp_1 = await client.get("/r1");
expect(resp_1.status).toEqual(200);
// Update with c2
const resp2 = await client.put(ENDPOINT, c2);
expect(resp2.status).toEqual(202);
// Check route /r1 exists
// But it is not applied because the modifiedIndex is the same as the old value
const resp2_2 = await client.get("/r1");
expect(resp2_2.status).toEqual(200);
// Check route /r2 not exists
const resp2_1 = await client.get("/r2").catch((err) => err.response);
expect(resp2_1.status).toEqual(404);
// Update with c3
const resp3 = await client.put(ENDPOINT, c3);
expect(resp3.status).toEqual(202);
// Check route /r1 not exists
const resp3_1 = await client.get("/r1").catch((err) => err.response);
expect(resp3_1.status).toEqual(404);
// Check route /r2 exists
const resp3_2 = await client.get("/r2");
expect(resp3_2.status).toEqual(200);
});
it("apply consumer with modifiedIndex", async () => {
const resp = await client.put(ENDPOINT, consumerWithModifiedIndex);
expect(resp.status).toEqual(202);
const resp_1 = await client.get("/r1", { headers: { "apikey": "invalid-key" } }).catch((err) => err.response);
expect(resp_1.status).toEqual(401);
const resp_2 = await client.get("/r1", { headers: { "apikey": "jack-key" } });
expect(resp_2.status).toEqual(200);
const updatedConsumer = structuredClone(consumerWithModifiedIndex);
// update key of key-auth plugin, but modifiedIndex is not changed
updatedConsumer.consumers[0].plugins["key-auth"] = { "key": "jack-key-updated" };
const resp2 = await client.put(ENDPOINT, updatedConsumer);
expect(resp2.status).toEqual(202);
const resp2_1 = await client.get("/r1", { headers: { "apikey": "jack-key-updated" } }).catch((err) => err.response);
expect(resp2_1.status).toEqual(401);
const resp2_2 = await client.get("/r1", { headers: { "apikey": "jack-key" } });
expect(resp2_2.status).toEqual(200);
// update key of key-auth plugin, and modifiedIndex is changed
updatedConsumer.consumers[0].modifiedIndex++;
const resp3 = await client.put(ENDPOINT, updatedConsumer);
const resp3_1 = await client.get("/r1", { headers: { "apikey": "jack-key-updated" } });
expect(resp3_1.status).toEqual(200);
const resp3_2 = await client.get("/r1", { headers: { "apikey": "jack-key" } }).catch((err) => err.response);
expect(resp3_2.status).toEqual(401);
});
it("apply consumer with credentials", async () => {
const resp = await client.put(ENDPOINT, credential1);
expect(resp.status).toEqual(202);
const resp_1 = await client.get("/r1", { headers: { "apikey": "auth-a" } });
expect(resp_1.status).toEqual(200);
const resp_2 = await client.get("/r1", { headers: { "apikey": "auth-b" } });
expect(resp_2.status).toEqual(200);
const resp_3 = await client.get("/r1", { headers: { "apikey": "invalid-key" } }).catch((err) => err.response);
expect(resp_3.status).toEqual(401);
});
});
describe("Exceptions", () => {
const clientException = axios.create({
...clientConfig,
validateStatus: () => true,
});
it("update config (lower conf_version)", async () => {
const resp = await clientException.put(
ENDPOINT,
{ routes_conf_version: 100 },
{ headers: { "Content-Type": "application/yaml" } }
);
const resp2 = await clientException.put(
ENDPOINT,
YAML.stringify(invalidConfVersionConfig1),
{ headers: { "Content-Type": "application/yaml" } }
);
expect(resp2.status).toEqual(400);
expect(resp2.data).toEqual({
error_msg:
"routes_conf_version must be greater than or equal to (100)",
});
});
it("update config (invalid conf_version)", async () => {
const resp = await clientException.put(
ENDPOINT,
YAML.stringify(invalidConfVersionConfig2),
{
headers: {
"Content-Type": "application/yaml",
},
}
);
expect(resp.status).toEqual(400);
expect(resp.data).toEqual({
error_msg: "routes_conf_version must be a number",
});
});
it("update config (invalid json format)", async () => {
const resp = await clientException.put(ENDPOINT, "{abcd", {
params: { conf_version: 4 },
});
expect(resp.status).toEqual(400);
expect(resp.data).toEqual({
error_msg:
"invalid request body: Expected object key string but found invalid token at character 2",
});
});
it("update config (not compliant with jsonschema)", async () => {
const data = structuredClone(config1);
(data.routes[0].uri as unknown) = 123;
const resp = await clientException.put(ENDPOINT, data);
expect(resp.status).toEqual(400);
expect(resp.data).toMatchObject({
error_msg:
'invalid routes at index 0, err: property "uri" validation failed: wrong type: expected string, got number',
});
});
it("update config (empty request body)", async () => {
const resp = await clientException.put(ENDPOINT, "");
expect(resp.status).toEqual(400);
expect(resp.data).toEqual({
error_msg: "invalid request body: empty request body",
});
});
});
});

View File

@@ -0,0 +1,258 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
BEGIN {
# restarts cause the memory cache to be emptied, don't do this
$ENV{TEST_NGINX_FORCE_RESTART_ON_TEST} = 0;
}
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
use_hup();
add_block_preprocessor(sub {
my ($block) = @_;
if (!defined $block->yaml_config) {
$block->set_value("yaml_config", <<'EOF');
deployment:
role: traditional
role_traditional:
config_provider: yaml
admin:
admin_key:
- name: admin
key: edd1c9f034335f136f87ad84b625c8f1
role: admin
EOF
}
if (!defined $block->no_error_log) {
$block->set_value("no_error_log", "");
}
});
run_tests();
__DATA__
=== TEST 1: test
--- timeout: 15
--- max_size: 204800
--- exec
cd t && pnpm test admin/standalone.spec.ts 2>&1
--- no_error_log
failed to execute the script with status
--- response_body eval
qr/PASS admin\/standalone.spec.ts/
=== TEST 2: init conf_version
--- config
location /t {} # force the worker to restart by changing the configuration
--- request
PUT /apisix/admin/configs
{
"consumer_groups_conf_version": 1000,
"consumers_conf_version": 1000,
"global_rules_conf_version": 1000,
"plugin_configs_conf_version": 1000,
"plugin_metadata_conf_version": 1000,
"protos_conf_version": 1000,
"routes_conf_version": 1000,
"secrets_conf_version": 1000,
"services_conf_version": 1000,
"ssls_conf_version": 1000,
"upstreams_conf_version": 1000
}
--- more_headers
X-API-KEY: edd1c9f034335f136f87ad84b625c8f1
--- error_code: 202
=== TEST 3: get config
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin")
local code, body = t.test('/apisix/admin/configs',
ngx.HTTP_GET,
nil,
[[{
"consumer_groups_conf_version": 1000,
"consumers_conf_version": 1000,
"global_rules_conf_version": 1000,
"plugin_configs_conf_version": 1000,
"plugin_metadata_conf_version": 1000,
"protos_conf_version": 1000,
"routes_conf_version": 1000,
"secrets_conf_version": 1000,
"services_conf_version": 1000,
"ssls_conf_version": 1000,
"upstreams_conf_version": 1000
}]],
{
["X-API-KEY"] = "edd1c9f034335f136f87ad84b625c8f1"
}
)
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 4: configure route
--- config
location /t {} # force the worker to restart by changing the configuration
--- request
PUT /apisix/admin/configs
{"routes":[{"id":"r1","uri":"/r1","upstream":{"nodes":{"127.0.0.1:1980":1},"type":"roundrobin"},"plugins":{"proxy-rewrite":{"uri":"/hello"}}}]}
--- more_headers
X-API-KEY: edd1c9f034335f136f87ad84b625c8f1
--- error_code: 202
=== TEST 5: test route
--- config
location /t1 {}
--- request
GET /r1
--- error_code: 200
--- response_body
hello world
=== TEST 6: remove route
--- config
location /t2 {}
--- request
PUT /apisix/admin/configs
{}
--- more_headers
X-API-KEY: edd1c9f034335f136f87ad84b625c8f1
--- error_code: 202
=== TEST 7: test non-exist route
--- config
location /t3 {}
--- request
GET /r1
--- error_code: 404
=== TEST 8: route references upstream, but only updates the route
--- config
location /t6 {}
--- pipelined_requests eval
[
"PUT /apisix/admin/configs\n" . "{\"routes_conf_version\":1060,\"upstreams_conf_version\":1060,\"routes\":[{\"id\":\"r1\",\"uri\":\"/r1\",\"upstream_id\":\"u1\",\"plugins\":{\"proxy-rewrite\":{\"uri\":\"/hello\"}}}],\"upstreams\":[{\"id\":\"u1\",\"nodes\":{\"127.0.0.1:1980\":1},\"type\":\"roundrobin\"}]}",
"PUT /apisix/admin/configs\n" . "{\"routes_conf_version\":1062,\"upstreams_conf_version\":1060,\"routes\":[{\"id\":\"r1\",\"uri\":\"/r2\",\"upstream_id\":\"u1\",\"plugins\":{\"proxy-rewrite\":{\"uri\":\"/hello\"}}}],\"upstreams\":[{\"id\":\"u1\",\"nodes\":{\"127.0.0.1:1980\":1},\"type\":\"roundrobin\"}]}"
]
--- more_headers eval
[
"X-API-KEY: edd1c9f034335f136f87ad84b625c8f1",
"X-API-KEY: edd1c9f034335f136f87ad84b625c8f1\n" . "x-apisix-conf-version-routes: 100",
]
--- error_code eval
[202, 202]
=== TEST 9: hit r2
--- config
location /t3 {}
--- pipelined_requests eval
["GET /r1", "GET /r2"]
--- error_code eval
[404, 200]
=== TEST 10: routes_conf_version < 1062 is not allowed
--- config
location /t {}
--- request
PUT /apisix/admin/configs
{"routes_conf_version":1,"routes":[{"id":"r1","uri":"/r2","upstream_id":"u1","plugins":{"proxy-rewrite":{"uri":"/hello"}}}]}
--- more_headers
X-API-KEY: edd1c9f034335f136f87ad84b625c8f1
x-apisix-conf-version-routes: 100
--- error_code: 400
--- response_body
{"error_msg":"routes_conf_version must be greater than or equal to (1062)"}
=== TEST 11: duplicate route id found
--- config
location /t11 {}
--- request
PUT /apisix/admin/configs
{"routes_conf_version":1063,"routes":[{"id":"r1","uri":"/r2","upstream_id":"u1","plugins":{"proxy-rewrite":{"uri":"/hello"}}},
{"id":"r1","uri":"/r2","upstream_id":"u1","plugins":{"proxy-rewrite":{"uri":"/hello"}}}]}
--- more_headers
X-API-KEY: edd1c9f034335f136f87ad84b625c8f1
--- error_code: 400
--- response_body
{"error_msg":"found duplicate id r1 in routes"}
=== TEST 12: duplicate consumer username found
--- config
location /t12 {}
--- request
PUT /apisix/admin/configs
{"consumers_conf_version":1064,"consumers":[{"username":"consumer1","plugins":{"key-auth":{"key":"consumer1"}}},
{"username":"consumer1","plugins":{"key-auth":{"key":"consumer1"}}}]}
--- more_headers
X-API-KEY: edd1c9f034335f136f87ad84b625c8f1
--- error_code: 400
--- response_body
{"error_msg":"found duplicate username consumer1 in consumers"}
=== TEST 13: duplicate consumer credential id found
--- config
location /t13 {}
--- request
PUT /apisix/admin/configs
{"consumers_conf_version":1065,"consumers":[
{"username": "john_1"},
{"id":"john_1/credentials/john-a","plugins":{"key-auth":{"key":"auth-a"}}},
{"id":"john_1/credentials/john-a","plugins":{"key-auth":{"key":"auth-a"}}}
]}
--- more_headers
X-API-KEY: edd1c9f034335f136f87ad84b625c8f1
--- error_code: 400
--- response_body
{"error_msg":"found duplicate credential id john_1/credentials/john-a in consumers"}

View File

@@ -0,0 +1,66 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
use Cwd qw(cwd);
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
my $user_yaml_config = <<_EOC_;
apisix:
node_listen: 1984
_EOC_
$block->set_value("yaml_config", $user_yaml_config);
});
run_tests;
__DATA__
=== TEST 1: set route(disabled stream model)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/stream_routes/1',
ngx.HTTP_PUT,
[[{
"remote_addr": "127.0.0.1",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- error_code: 400

View File

@@ -0,0 +1,653 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
run_tests;
__DATA__
=== TEST 1: set route(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, body = t('/apisix/admin/stream_routes/1',
ngx.HTTP_PUT,
[[{
"remote_addr": "127.0.0.1",
"desc": "test-desc",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route"
}]],
[[{
"value": {
"remote_addr": "127.0.0.1",
"desc": "test-desc",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route"
},
"key": "/apisix/stream_routes/1"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/stream_routes/1'))
local create_time = res.body.node.value.create_time
assert(create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
}
}
--- request
GET /t
--- response_body
passed
=== TEST 2: get route(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/stream_routes/1',
ngx.HTTP_GET,
nil,
[[{
"value": {
"remote_addr": "127.0.0.1",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route"
},
"key": "/apisix/stream_routes/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 3: delete route(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/stream_routes/1', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[delete] code: 200 message: passed
=== TEST 4: post route + delete
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, message, res = t('/apisix/admin/stream_routes',
ngx.HTTP_POST,
[[{
"remote_addr": "127.0.0.1",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route"
}]],
[[{
"value": {
"remote_addr": "127.0.0.1",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"desc": "new route"
}
}]]
)
if code ~= 200 then
ngx.status = code
ngx.say(message)
return
end
ngx.say("[push] code: ", code, " message: ", message)
local id = string.sub(res.key, #"/apisix/stream_routes/" + 1)
local ret = assert(etcd.get('/stream_routes/' .. id))
local create_time = ret.body.node.value.create_time
assert(create_time ~= nil, "create_time is nil")
local update_time = ret.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
id = ret.body.node.value.id
assert(id ~= nil, "id is nil")
code, message = t('/apisix/admin/stream_routes/' .. id, ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[push] code: 200 message: passed
[delete] code: 200 message: passed
=== TEST 5: set route with plugin
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/stream_routes/1',
ngx.HTTP_PUT,
[[{
"remote_addr": "127.0.0.1",
"plugins": {
"mqtt-proxy": {
"protocol_name": "MQTT",
"protocol_level": 4
}
},
"upstream": {
"type": "chash",
"key": "mqtt_client_id",
"nodes": [
{
"host": "127.0.0.1",
"port": 1980,
"weight": 1
}
]
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 6: set route with server_addr and server_port
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/stream_routes/1',
ngx.HTTP_PUT,
[[{
"remote_addr": "127.0.0.1",
"server_addr": "127.0.0.1",
"server_port": 1982,
"plugins": {
"mqtt-proxy": {
"protocol_name": "MQTT",
"protocol_level": 4
}
},
"upstream": {
"type": "chash",
"key": "mqtt_client_id",
"nodes": [
{
"host": "127.0.0.1",
"port": 1980,
"weight": 1
}
]
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 7: delete route(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/stream_routes/1', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[delete] code: 200 message: passed
=== TEST 8: string id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/stream_routes/a-b-c-ABC_0123',
ngx.HTTP_PUT,
[[{
"remote_addr": "127.0.0.1",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 9: string id(delete)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/stream_routes/a-b-c-ABC_0123', ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 10: invalid string id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/stream_routes/*invalid',
ngx.HTTP_PUT,
[[{
"remote_addr": "127.0.0.1",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- error_code: 400
=== TEST 11: not unwanted data, POST
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/stream_routes',
ngx.HTTP_POST,
[[{
"remote_addr": "127.0.0.1",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
-- clean data
local id = string.sub(res.key, #"/apisix/stream_routes/" + 1)
local code, message = t('/apisix/admin/stream_routes/' .. id,
ngx.HTTP_DELETE
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
assert(res.key ~= nil)
res.key = nil
assert(res.value.create_time ~= nil)
res.value.create_time = nil
assert(res.value.update_time ~= nil)
res.value.update_time = nil
assert(res.value.id ~= nil)
res.value.id = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"value":{"remote_addr":"127.0.0.1","upstream":{"hash_on":"vars","nodes":{"127.0.0.1:8080":1},"pass_host":"pass","scheme":"http","type":"roundrobin"}}}
--- request
GET /t
=== TEST 12: not unwanted data, PUT
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/stream_routes/1',
ngx.HTTP_PUT,
[[{
"remote_addr": "127.0.0.1",
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.value.create_time ~= nil)
res.value.create_time = nil
assert(res.value.update_time ~= nil)
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/stream_routes/1","value":{"id":"1","remote_addr":"127.0.0.1","upstream":{"hash_on":"vars","nodes":{"127.0.0.1:8080":1},"pass_host":"pass","scheme":"http","type":"roundrobin"}}}
--- request
GET /t
=== TEST 13: not unwanted data, GET
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/stream_routes/1',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.createdIndex ~= nil)
res.createdIndex = nil
assert(res.modifiedIndex ~= nil)
res.modifiedIndex = nil
assert(res.value.create_time ~= nil)
res.value.create_time = nil
assert(res.value.update_time ~= nil)
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/stream_routes/1","value":{"id":"1","remote_addr":"127.0.0.1","upstream":{"hash_on":"vars","nodes":{"127.0.0.1:8080":1},"pass_host":"pass","scheme":"http","type":"roundrobin"}}}
--- request
GET /t
=== TEST 14: not unwanted data, DELETE
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/stream_routes/1',
ngx.HTTP_DELETE
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(json.encode(res))
}
}
--- response_body
{"deleted":"1","key":"/apisix/stream_routes/1"}
--- request
GET /t
=== TEST 15: set route with unknown plugin
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/stream_routes/1',
ngx.HTTP_PUT,
[[{
"remote_addr": "127.0.0.1",
"plugins": {
"mqttt-proxy": {
}
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"unknown plugin [mqttt-proxy]"}
=== TEST 16: validate protocol
--- extra_yaml_config
xrpc:
protocols:
- name: pingpong
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
for _, case in ipairs({
{input = {
name = "xxx",
}},
{input = {
name = "pingpong",
}},
{input = {
name = "pingpong",
conf = {
faults = "a",
}
}},
}) do
local code, body = t('/apisix/admin/stream_routes/1',
ngx.HTTP_PUT,
{
protocol = case.input,
upstream = {
nodes = {
["127.0.0.1:8080"] = 1
},
type = "roundrobin"
}
}
)
if code > 300 then
ngx.print(body)
else
ngx.say(body)
end
end
}
}
--- request
GET /t
--- response_body
{"error_msg":"unknown protocol [xxx]"}
passed
{"error_msg":"property \"faults\" validation failed: wrong type: expected array, got string"}
=== TEST 17: set route with remote_addr and server_addr in IPV6
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/stream_routes/1',
ngx.HTTP_PUT,
[[{
"remote_addr": "::1",
"server_addr": "::1",
"server_port": 1982,
"plugins": {
"mqtt-proxy": {
"protocol_name": "MQTT",
"protocol_level": 4
}
},
"upstream": {
"type": "chash",
"key": "mqtt_client_id",
"nodes": [
{
"host": "127.0.0.1",
"port": 1980,
"weight": 1
}
]
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed

View File

@@ -0,0 +1,179 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
use Cwd qw(cwd);
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
my $user_yaml_config = <<_EOC_;
deployment:
admin:
admin_key:
- name: admin
role: admin
key: edd1c9f034335f136f87ad84b625c8f1
apisix:
node_listen: 1984
_EOC_
$block->set_value("yaml_config", $user_yaml_config);
});
run_tests;
__DATA__
=== TEST 1: set route without token
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").req_self_with_http
local res, err = t('/apisix/admin/routes/1',
"PUT",
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]]
)
ngx.status = res.status
ngx.print(res.body)
}
}
--- request
GET /t
--- error_code: 401
=== TEST 2: set route with wrong token
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").req_self_with_http
local res, err = t(
'/apisix/admin/routes/1',
"PUT",
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]],
{apikey = "wrong_key"}
)
ngx.status = res.status
ngx.print(res.body)
}
}
--- request
GET /t
--- error_code: 401
=== TEST 3: set route with correct token
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").req_self_with_http
local res, err = t(
'/apisix/admin/routes/1',
"PUT",
[[{
"upstream": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"uri": "/index.html"
}]],
{x_api_key = "edd1c9f034335f136f87ad84b625c8f1"}
)
if res.status > 299 then
ngx.status = res.status
end
ngx.say("done")
}
}
--- request
GET /t
--- response_body
done
=== TEST 4: get plugins name
--- request
GET /apisix/admin/plugins/list
--- error_code: 401
=== TEST 5: reload plugins
--- request
PUT /apisix/admin/plugins/reload
--- error_code: 401
=== TEST 6: reload plugins with api key(arguments)
--- request
PUT /apisix/admin/plugins/reload?api_key=edd1c9f034335f136f87ad84b625c8f1
--- error_code: 200
=== TEST 7: reload plugins with api key(cookie)
--- request
PUT /apisix/admin/plugins/reload
--- more_headers
X-API-KEY: edd1c9f034335f136f87ad84b625c8f1
--- error_code: 200
=== TEST 8: reload plugins with api key(viewer role)
--- request
PUT /apisix/admin/plugins/reload?api_key=4054f7cf07e344346cd3f287985e76a2
--- error_code: 401
=== TEST 9: fetch with api key(viewer role)
--- request
GET /apisix/admin/routes??api_key=4054f7cf07e344346cd3f287985e76a2
--- error_code: 401

View File

@@ -0,0 +1,435 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
run_tests;
__DATA__
=== TEST 1: set upstream(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": [{
"host": "127.0.0.1",
"port": 8080,
"weight": 1
}],
"type": "roundrobin",
"desc": "new upstream"
}]],
[[{
"value": {
"nodes": [{
"host": "127.0.0.1",
"port": 8080,
"weight": 1
}],
"type": "roundrobin",
"desc": "new upstream"
},
"key": "/apisix/upstreams/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 2: get upstream(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_GET,
nil,
[[{
"value": {
"nodes": [{
"host": "127.0.0.1",
"port": 8080,
"weight": 1
}],
"type": "roundrobin",
"desc": "new upstream"
},
"key": "/apisix/upstreams/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 3: delete upstream(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/upstreams/1', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[delete] code: 200 message: passed
=== TEST 4: delete upstream(id: not_found)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code = t('/apisix/admin/upstreams/not_found', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code)
}
}
--- request
GET /t
--- response_body
[delete] code: 404
=== TEST 5: push upstream + delete
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/upstreams',
ngx.HTTP_POST,
[[{
"nodes": [{
"host": "127.0.0.1",
"port": 8080,
"weight": 1
}],
"type": "roundrobin"
}]],
[[{
"value": {
"nodes": [{
"host": "127.0.0.1",
"port": 8080,
"weight": 1
}],
"type": "roundrobin"
}
}]]
)
if code ~= 200 then
ngx.status = code
ngx.say(message)
return
end
ngx.say("[push] code: ", code, " message: ", message)
local id = string.sub(res.key, #"/apisix/upstreams/" + 1)
code, message = t('/apisix/admin/upstreams/' .. id, ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[push] code: 200 message: passed
[delete] code: 200 message: passed
=== TEST 6: empty nodes
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": [],
"type": "roundrobin"
}]]
)
if code >= 300 then
ngx.status = code
ngx.print(message)
return
end
ngx.say(message)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 7: refer to empty nodes upstream
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream_id": "1",
"uri": "/index.html"
}]]
)
if code >= 300 then
ngx.status = code
ngx.print(message)
return
end
ngx.say(message)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 8: hit empty nodes upstream
--- request
GET /index.html
--- error_code: 503
--- error_log
no valid upstream node
=== TEST 9: additional properties is invalid
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams',
ngx.HTTP_PUT,
[[{
"id": 1,
"nodes": [{
"host": "127.0.0.1",
"port": 8080,
"weight": 1
}],
"type": "roundrobin",
"_service_name": "xyz",
"_discovery_type": "nacos"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body eval
qr/\{"error_msg":"invalid configuration: additional properties forbidden, found .*"\}/
=== TEST 10: invalid weight of node
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams',
ngx.HTTP_PUT,
[[{
"id": 1,
"nodes": [{
"host": "127.0.0.1",
"port": 8080,
"weight": "1"
}],
"type": "chash"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"nodes\" validation failed: object matches none of the required"}
=== TEST 11: invalid weight of node
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams',
ngx.HTTP_PUT,
[[{
"id": 1,
"nodes": [{
"host": "127.0.0.1",
"port": 8080,
"weight": -100
}],
"type": "chash"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"nodes\" validation failed: object matches none of the required"}
=== TEST 12: invalid port of node
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams',
ngx.HTTP_PUT,
[[{
"id": 1,
"nodes": [{
"host": "127.0.0.1",
"port": 0,
"weight": 1
}],
"type": "chash"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"nodes\" validation failed: object matches none of the required"}
=== TEST 13: invalid host of node
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams',
ngx.HTTP_PUT,
[[{
"id": 1,
"nodes": [{
"host": "127.#.%.1",
"port": 8080,
"weight": 1
}],
"type": "chash"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"nodes\" validation failed: object matches none of the required"}
=== TEST 14: nodes host include ipv6 addr
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"upstream": {
"nodes": [
{
"host":"[::1]",
"port":8082,
"weight":1
}
],
"type": "roundrobin"
},
"uri": "/index.html"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed

View File

@@ -0,0 +1,154 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: set upstream(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- error_code: 201
--- response_body
passed
=== TEST 2: add route
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"upstream_id": 1,
"uri": "/index.html"
}]]
)
if code >= 300 then
ngx.status = code
ngx.print(message)
return
end
ngx.say(message)
}
}
--- response_body
passed
=== TEST 3: delete upstream(wrong header)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/upstreams/1?force=anyvalue',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 400 message: {"error_msg":"can not delete this upstream, route [1] is still using it now"}
=== TEST 4: delete upstream(without force delete header)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/upstreams/1',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 400 message: {"error_msg":"can not delete this upstream, route [1] is still using it now"}
=== TEST 5: delete upstream(force delete)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/upstreams/1?force=true',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body chomp
[delete] code: 200 message: passed
=== TEST 6: delete route
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/routes/1',
ngx.HTTP_DELETE
)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body chomp
[delete] code: 200 message: passed

View File

@@ -0,0 +1,725 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
run_tests;
__DATA__
=== TEST 1: set upstream (use an id can't be referred by other route
so that we can delete it later)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, body = t('/apisix/admin/upstreams/admin_up',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"desc": "new upstream"
}]],
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"desc": "new upstream"
},
"key": "/apisix/upstreams/admin_up"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/upstreams/admin_up'))
local create_time = res.body.node.value.create_time
assert(create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
}
}
--- request
GET /t
--- response_body
passed
=== TEST 2: get upstream
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/admin_up',
ngx.HTTP_GET,
nil,
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"desc": "new upstream"
},
"key": "/apisix/upstreams/admin_up"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 3: delete upstream
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/upstreams/admin_up', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[delete] code: 200 message: passed
=== TEST 4: delete upstream(id: not_found)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code = t('/apisix/admin/upstreams/not_found', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code)
}
}
--- request
GET /t
--- response_body
[delete] code: 404
=== TEST 5: push upstream + delete
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, message, res = t('/apisix/admin/upstreams',
ngx.HTTP_POST,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}]],
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}
}]]
)
if code ~= 200 then
ngx.status = code
ngx.say(message)
return
end
ngx.say("[push] code: ", code, " message: ", message)
local id = string.sub(res.key, #"/apisix/upstreams/" + 1)
local res = assert(etcd.get('/upstreams/' .. id))
local create_time = res.body.node.value.create_time
assert(create_time ~= nil, "create_time is nil")
local update_time = res.body.node.value.update_time
assert(update_time ~= nil, "update_time is nil")
code, message = t('/apisix/admin/upstreams/' .. id, ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- request
GET /t
--- response_body
[push] code: 200 message: passed
[delete] code: 200 message: passed
=== TEST 6: invalid upstream id in uri
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/invalid_id$',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}]]
)
ngx.exit(code)
}
}
--- request
GET /t
--- error_code: 400
=== TEST 7: different id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"id": 3,
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"wrong upstream id"}
=== TEST 8: id in the rule
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams',
ngx.HTTP_PUT,
[[{
"id": "1",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}]],
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
},
"key": "/apisix/upstreams/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 9: integer id less than 1
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams',
ngx.HTTP_PUT,
[[{
"id": -100,
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"id\" validation failed: object matches none of the required"}
=== TEST 10: invalid upstream id: string value
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams',
ngx.HTTP_PUT,
[[{
"id": "invalid_id$",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"id\" validation failed: object matches none of the required"}
=== TEST 11: additional properties is invalid
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams',
ngx.HTTP_PUT,
[[{
"id": 1,
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"_service_name": "xyz",
"_discovery_type": "nacos"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body eval
qr/\{"error_msg":"invalid configuration: additional properties forbidden, found .*"\}/
=== TEST 12: set upstream(type: chash)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"key": "remote_addr",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash"
}]],
[[{
"value": {
"key": "remote_addr",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash"
},
"key": "/apisix/upstreams/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 13: unknown type
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams',
ngx.HTTP_PUT,
[[{
"id": 1,
"nodes": {
"127.0.0.1:8080": 1
},
"type": "unknown"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- response_body chomp
passed
=== TEST 14: invalid weight of node
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams',
ngx.HTTP_PUT,
[[{
"id": 1,
"nodes": {
"127.0.0.1:8080": "1"
},
"type": "chash"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"nodes\" validation failed: object matches none of the required"}
=== TEST 15: invalid weight of node
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams',
ngx.HTTP_PUT,
[[{
"id": 1,
"nodes": {
"127.0.0.1:8080": -100
},
"type": "chash"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"nodes\" validation failed: object matches none of the required"}
=== TEST 16: set upstream (missing key)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"missing key"}
=== TEST 17: wrong upstream id, do not need it
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_POST,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"wrong upstream id, do not need it"}
=== TEST 18: wrong upstream id, do not need it
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams',
ngx.HTTP_POST,
[[{
"id": 1,
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"wrong upstream id, do not need it"}
=== TEST 19: client_cert/client_key and client_cert_id cannot appear at the same time
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local ssl_cert = t.read_file("t/certs/apisix.crt")
local ssl_key = t.read_file("t/certs/apisix.key")
local data = {
nodes = {
["127.0.0.1:8080"] = 1
},
type = "roundrobin",
tls = {
client_cert_id = 1,
client_cert = ssl_cert,
client_key = ssl_key
}
}
local code, body = t.test('/apisix/admin/upstreams',
ngx.HTTP_POST,
core.json.encode(data)
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body eval
qr/{"error_msg":"invalid configuration: property \\\"tls\\\" validation failed: failed to validate dependent schema for \\\"client_cert|client_key\\\": value wasn't supposed to match schema"}/
=== TEST 20: tls.client_cert_id does not exist
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin")
local data = {
nodes = {
["127.0.0.1:8080"] = 1
},
type = "roundrobin",
tls = {
client_cert_id = 9999999
}
}
local code, body = t.test('/apisix/admin/upstreams',
ngx.HTTP_POST,
core.json.encode(data)
)
ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"failed to fetch ssl info by ssl id [9999999], response code: 404"}
=== TEST 21: tls.client_cert_id exist with wrong ssl type
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin")
local json = require("toolkit.json")
local ssl_cert = t.read_file("t/certs/mtls_client.crt")
local ssl_key = t.read_file("t/certs/mtls_client.key")
local data = {
sni = "test.com",
cert = ssl_cert,
key = ssl_key
}
local code, body = t.test('/apisix/admin/ssls/1',
ngx.HTTP_PUT,
json.encode(data)
)
if code >= 300 then
ngx.status = code
ngx.print(body)
return
end
local data = {
upstream = {
scheme = "https",
type = "roundrobin",
nodes = {
["127.0.0.1:1983"] = 1
},
tls = {
client_cert_id = 1
}
},
uri = "/hello"
}
local code, body = t.test('/apisix/admin/routes/1',
ngx.HTTP_PUT,
json.encode(data)
)
if code >= 300 then
ngx.status = code
ngx.print(body)
return
end
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"failed to fetch ssl info by ssl id [1], wrong ssl type"}
=== TEST 22: type with default vale
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, body = t('/apisix/admin/upstreams/admin_up',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"desc": "new upstream"
}]],
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"desc": "new upstream"
},
"key": "/apisix/upstreams/admin_up"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed

View File

@@ -0,0 +1,295 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: not unwanted data, POST
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/upstreams',
ngx.HTTP_POST,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.key ~= nil)
res.key = nil
assert(res.value.create_time ~= nil)
res.value.create_time = nil
assert(res.value.update_time ~= nil)
res.value.update_time = nil
assert(res.value.id ~= nil)
res.value.id = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"value":{"hash_on":"vars","nodes":{"127.0.0.1:8080":1},"pass_host":"pass","scheme":"http","type":"roundrobin"}}
=== TEST 2: not unwanted data, PUT
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/upstreams/unwanted',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.value.create_time ~= nil)
res.value.create_time = nil
assert(res.value.update_time ~= nil)
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/upstreams/unwanted","value":{"hash_on":"vars","id":"unwanted","nodes":{"127.0.0.1:8080":1},"pass_host":"pass","scheme":"http","type":"roundrobin"}}
=== TEST 3: not unwanted data, PATCH
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/upstreams/unwanted',
ngx.HTTP_PATCH,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.value.create_time ~= nil)
res.value.create_time = nil
assert(res.value.update_time ~= nil)
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/upstreams/unwanted","value":{"hash_on":"vars","id":"unwanted","nodes":{"127.0.0.1:8080":1},"pass_host":"pass","scheme":"http","type":"roundrobin"}}
=== TEST 4: not unwanted data, GET
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/upstreams/unwanted', ngx.HTTP_GET)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
assert(res.createdIndex ~= nil)
res.createdIndex = nil
assert(res.modifiedIndex ~= nil)
res.modifiedIndex = nil
assert(res.value.create_time ~= nil)
res.value.create_time = nil
assert(res.value.update_time ~= nil)
res.value.update_time = nil
ngx.say(json.encode(res))
}
}
--- response_body
{"key":"/apisix/upstreams/unwanted","value":{"hash_on":"vars","id":"unwanted","nodes":{"127.0.0.1:8080":1},"pass_host":"pass","scheme":"http","type":"roundrobin"}}
=== TEST 5: not unwanted data, DELETE
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/upstreams/unwanted', ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(json.encode(res))
}
}
--- response_body
{"deleted":"1","key":"/apisix/upstreams/unwanted"}
=== TEST 6: empty nodes
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {},
"type": "roundrobin"
}]]
)
if code >= 300 then
ngx.status = code
ngx.print(message)
return
end
ngx.say(message)
}
}
--- response_body
passed
=== TEST 7: refer to empty nodes upstream
--- config
location /t {
content_by_lua_block {
local core = require("apisix.core")
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"methods": ["GET"],
"upstream_id": "1",
"uri": "/index.html"
}]]
)
if code >= 300 then
ngx.status = code
ngx.print(message)
return
end
ngx.say(message)
}
}
--- response_body
passed
=== TEST 8: hit empty nodes upstream
--- request
GET /index.html
--- error_code: 503
--- error_log
no valid upstream node
=== TEST 9: upstream timeouts equal to zero
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"timeout": {
"connect": 0,
"send": 0,
"read": 0
}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body_like eval
qr/{"error_msg":"invalid configuration: property \\\"timeout\\\" validation failed: property \\\"(connect|send|read)\\\" validation failed: expected 0 to be greater than 0"}/

View File

@@ -0,0 +1,768 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: list empty resources
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local code, message, res = t('/apisix/admin/upstreams',
ngx.HTTP_GET
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
res = json.decode(res)
ngx.say(json.encode(res))
}
}
--- response_body
{"list":[],"total":0}
=== TEST 2: retry_timeout is -1 (INVALID)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/a-b-c-ABC_0123',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1,
"127.0.0.1:8090": 1
},
"retry_timeout": -1,
"type": "roundrobin"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"retry_timeout\" validation failed: expected -1 to be at least 0"}
=== TEST 3: provide upstream for patch
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1,
"127.0.0.1:8090": 1
},
"type": "roundrobin"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.print(body)
}
}
=== TEST 4: patch upstream(whole)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local id = 1
local res = assert(etcd.get('/upstreams/' .. id))
local prev_create_time = res.body.node.value.create_time
local prev_update_time = res.body.node.value.update_time
ngx.sleep(1)
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PATCH,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"desc": "new upstream"
}]],
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"desc": "new upstream"
},
"key": "/apisix/upstreams/1"
}]]
)
ngx.status = code
ngx.say(body)
local res = assert(etcd.get('/upstreams/' .. id))
local create_time = res.body.node.value.create_time
assert(prev_create_time == create_time, "create_time mismatched")
local update_time = res.body.node.value.update_time
assert(prev_update_time ~= update_time, "update_time should be changed")
}
}
--- response_body
passed
=== TEST 5: patch upstream(new desc)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PATCH,
[[{
"desc": "new 21 upstream"
}]],
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"desc": "new 21 upstream"
},
"key": "/apisix/upstreams/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 6: patch upstream(new nodes)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PATCH,
[[{
"nodes": {
"127.0.0.1:8081": 3,
"127.0.0.1:8082": 4
}
}]],
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1,
"127.0.0.1:8081": 3,
"127.0.0.1:8082": 4
},
"type": "roundrobin",
"desc": "new 21 upstream"
}
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 7: patch upstream(weight is 0)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PATCH,
[[{
"nodes": {
"127.0.0.1:8081": 3,
"127.0.0.1:8082": 0
}
}]],
[[{
"value": {
"nodes": {
"127.0.0.1:8081": 3,
"127.0.0.1:8082": 0
},
"type": "roundrobin",
"desc": "new 21 upstream"
}
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 8: patch upstream(whole - sub path)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1/',
ngx.HTTP_PATCH,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"desc": "new upstream 24"
}]],
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"desc": "new upstream 24"
},
"key": "/apisix/upstreams/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 9: patch upstream(new desc - sub path)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1/desc',
ngx.HTTP_PATCH,
'"new 25 upstream"',
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"desc": "new 25 upstream"
},
"key": "/apisix/upstreams/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 10: patch upstream(new nodes)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1/nodes',
ngx.HTTP_PATCH,
[[{
"127.0.0.6:8081": 3,
"127.0.0.7:8082": 4
}]],
[[{
"value": {
"nodes": {
"127.0.0.6:8081": 3,
"127.0.0.7:8082": 4
},
"type": "roundrobin",
"desc": "new 25 upstream"
}
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 11: patch upstream(weight is 0 - sub path)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1/nodes',
ngx.HTTP_PATCH,
[[{
"127.0.0.7:8081": 0,
"127.0.0.8:8082": 4
}]],
[[{
"value": {
"nodes": {
"127.0.0.7:8081": 0,
"127.0.0.8:8082": 4
},
"type": "roundrobin",
"desc": "new 25 upstream"
}
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 12: set upstream(type: chash)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"key": "server_name",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 13: wrong upstream key, hash_on default vars
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1,
"127.0.0.1:8081": 2
},
"type": "chash",
"key": "not_support",
"desc": "new upstream"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: failed to match pattern \"^((uri|server_name|server_addr|request_uri|remote_port|remote_addr|query_string|host|hostname|mqtt_client_id)|arg_[0-9a-zA-z_-]+)$\" with \"not_support\""}
=== TEST 14: set upstream with args(type: chash)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"key": "arg_device_id",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"desc": "new chash upstream"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 15: set upstream(type: chash)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"key": "server_name",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 16: wrong upstream key, hash_on default vars
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1,
"127.0.0.1:8081": 2
},
"type": "chash",
"key": "not_support",
"desc": "new upstream"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: failed to match pattern \"^((uri|server_name|server_addr|request_uri|remote_port|remote_addr|query_string|host|hostname|mqtt_client_id)|arg_[0-9a-zA-z_-]+)$\" with \"not_support\""}
=== TEST 17: set upstream with args(type: chash)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"key": "arg_device_id",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"desc": "new chash upstream"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 18: type chash, hash_on: vars
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"key": "arg_device_id",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"hash_on": "vars",
"desc": "new chash upstream"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 19: type chash, hash_on: header, header name with '_', underscores_in_headers on
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"key": "custom_header",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"hash_on": "header",
"desc": "new chash upstream"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 20: type chash, hash_on: header, header name with invalid character
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"key": "$#^@",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"hash_on": "header",
"desc": "new chash upstream"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: failed to match pattern \"^[a-zA-Z0-9-_]+$\" with \"$#^@\""}
=== TEST 21: type chash, hash_on: cookie
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"key": "custom_cookie",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"hash_on": "cookie",
"desc": "new chash upstream"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 22: type chash, hash_on: cookie, cookie name with invalid character
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"key": "$#^@abc",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"hash_on": "cookie",
"desc": "new chash upstream"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: failed to match pattern \"^[a-zA-Z0-9-_]+$\" with \"$#^@abc\""}
=== TEST 23: type chash, hash_on: consumer, do not need upstream key
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"hash_on": "consumer",
"desc": "new chash upstream"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 24: type chash, hash_on: consumer, set key but invalid
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"hash_on": "consumer",
"key": "invalid-key",
"desc": "new chash upstream"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 25: type chash, invalid hash_on type
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"key": "dsadas",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"hash_on": "aabbcc",
"desc": "new chash upstream"
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"hash_on\" validation failed: matches none of the enum values"}

View File

@@ -0,0 +1,668 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: set upstream(id: 1 + name: test name)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"name": "test upstream name"
}]],
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"name": "test upstream name"
},
"key": "/apisix/upstreams/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 2: string id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/a-b-c-ABC_0123',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 3: string id(delete)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/a-b-c-ABC_0123', ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 4: invalid string id
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/*invalid',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"id\" validation failed: object matches none of the required"}
=== TEST 5: retries is 0
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/a-b-c-ABC_0123',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1,
"127.0.0.1:8090": 1
},
"retries": 0,
"type": "roundrobin"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 6: retries is -1 (INVALID)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/a-b-c-ABC_0123',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1,
"127.0.0.1:8090": 1
},
"retries": -1,
"type": "roundrobin"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"retries\" validation failed: expected -1 to be at least 0"}
=== TEST 7: invalid route: empty `upstream_host` when `pass_host` is `rewrite`
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {
"apisix.com:8080": 1,
"test.com:8080": 1
},
"type": "roundrobin",
"pass_host": "rewrite",
"upstream_host": ""
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
=== TEST 8: set upstream(with labels)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"labels": {
"build":"16",
"env":"production",
"version":"v2"
}
}]],
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"labels": {
"build":"16",
"env":"production",
"version":"v2"
}
},
"key": "/apisix/upstreams/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 9: get upstream(with labels)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_GET,
nil,
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"labels": {
"version":"v2",
"build":"16",
"env":"production"
}
},
"key": "/apisix/upstreams/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 10: patch upstream(only labels)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PATCH,
[[{
"labels": {
"build": "17"
}
}]],
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"labels": {
"version":"v2",
"build":"17",
"env":"production"
}
},
"key": "/apisix/upstreams/1"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 11: invalid format of label value: set upstream
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"labels": {
"env": ["production", "release"]
}
}]]
)
ngx.status = code
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"labels\" validation failed: failed to validate env (matching \".*\"): wrong type: expected string, got table"}
=== TEST 12: patch upstream(whole, create_time)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PATCH,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"desc": "new upstream",
"create_time": 1705252779
}]],
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"desc": "new upstream",
"create_time": 1705252779
},
"key": "/apisix/upstreams/1"
}]]
)
ngx.status = code
ngx.say(body)
if code >= 300 then
return
end
local res = assert(etcd.get('/upstreams/1'))
local create_time = res.body.node.value.create_time
assert(create_time == 1705252779, "create_time mismatched")
}
}
--- response_body
passed
=== TEST 13: patch upstream(whole, update_time)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PATCH,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"desc": "new upstream",
"update_time": 1705252779
}]],
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"desc": "new upstream",
"create_time": 1705252779
},
"key": "/apisix/upstreams/1"
}]]
)
ngx.status = code
ngx.say(body)
if code >= 300 then
return
end
local res = assert(etcd.get('/upstreams/1'))
local update_time = res.body.node.value.update_time
assert(update_time == 1705252779, "update_time mismatched")
}
}
--- response_body
passed
=== TEST 14: create upstream with create_time and update_time
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/up_create_update_time',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"create_time": 1602883670,
"update_time": 1602893670
}]],
[[{
"value": {
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin",
"create_time": 1602883670,
"update_time": 1602893670
},
"key": "/apisix/upstreams/up_create_update_time"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- error_code: 400
--- response_body eval
qr/\{"error_msg":"the property is forbidden:.*"\}/
=== TEST 15: patch upstream with sub_path, the data is number
--- config
location /t {
content_by_lua_block {
local json = require("toolkit.json")
local t = require("lib.test_admin").test
local etcd = require("apisix.core.etcd")
local code, message = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {},
"type": "roundrobin"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(message)
return
end
local id = 1
local res = assert(etcd.get('/upstreams/' .. id))
local prev_create_time = res.body.node.value.create_time
local prev_update_time = res.body.node.value.update_time
ngx.sleep(1)
local code, message = t('/apisix/admin/upstreams/1/retries',
ngx.HTTP_PATCH,
json.encode(1)
)
if code >= 300 then
ngx.status = code
end
ngx.say(message)
local res = assert(etcd.get('/upstreams/' .. id))
local create_time = res.body.node.value.create_time
assert(prev_create_time == create_time, "create_time mismatched")
local update_time = res.body.node.value.update_time
assert(prev_update_time ~= update_time, "update_time should be changed")
}
}
=== TEST 16: set upstream(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1
},
"type": "roundrobin"
}]]
)
ngx.status = code
ngx.say(body)
}
}
--- response_body
passed
=== TEST 17: set service(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/1',
ngx.HTTP_PUT,
[[{
"upstream_id": 1
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 18: set route(id: 1)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"upstream_id": 1,
"uri": "/index.html"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 19: delete upstream(id: 1)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/upstreams/1', ngx.HTTP_DELETE)
ngx.print("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 400 message: {"error_msg":"can not delete this upstream, route [1] is still using it now"}
=== TEST 20: delete route(id: 1)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/routes/1', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 200 message: passed
=== TEST 21: delete service(id: 1)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/services/1', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 200 message: passed
=== TEST 22: delete upstream(id: 1)
--- config
location /t {
content_by_lua_block {
ngx.sleep(0.3)
local t = require("lib.test_admin").test
local code, message = t('/apisix/admin/upstreams/1', ngx.HTTP_DELETE)
ngx.say("[delete] code: ", code, " message: ", message)
}
}
--- response_body
[delete] code: 200 message: passed

View File

@@ -0,0 +1,599 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
use t::APISIX 'no_plan';
repeat_each(1);
no_long_string();
no_root_location();
no_shuffle();
log_level("info");
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
if (!$block->no_error_log && !$block->error_log) {
$block->set_value("no_error_log", "[error]\n[alert]");
}
});
run_tests;
__DATA__
=== TEST 1: set upstream(kafka scheme)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin")
local code, body = t.test("/apisix/admin/upstreams/kafka", ngx.HTTP_PUT, [[{
"nodes": {
"127.0.0.1:9092": 1
},
"type": "none",
"scheme": "kafka"
}]])
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 2: set upstream(empty tls)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin")
local code, body = t.test("/apisix/admin/upstreams/kafka", ngx.HTTP_PUT, [[{
"nodes": {
"127.0.0.1:9092": 1
},
"type": "none",
"scheme": "kafka",
"tls": {}
}]])
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 3: set upstream(tls without verify)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin")
local code, body = t.test("/apisix/admin/upstreams/kafka", ngx.HTTP_PUT, [[{
"nodes": {
"127.0.0.1:9092": 1
},
"type": "none",
"scheme": "kafka",
"tls": {
"verify": false
}
}]])
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 4: prepare upstream
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/upstreams/1", ngx.HTTP_PUT, [[{
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
}]])
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 5: prepare route
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/routes/1", ngx.HTTP_PUT, [[{
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream_id": 1,
"weight": 1
},
{
"weight": 1
}
]
}
]
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/hello"
}]])
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 6: delete upstream when plugin in route still refer it
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/upstreams/1", ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
end
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"can not delete this upstream, plugin in route [1] is still using it now"}
=== TEST 7: delete route
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/routes/1", ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 8: prepare service
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/services/1", ngx.HTTP_PUT, [[{
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream_id": 1,
"weight": 1
},
{
"weight": 1
}
]
}
]
}
}
}]])
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 9: delete upstream when plugin in service still refer it
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/upstreams/1", ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
end
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"can not delete this upstream, plugin in service [1] is still using it now"}
=== TEST 10: delete service
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/services/1", ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 11: prepare global_rule
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/global_rules/1", ngx.HTTP_PUT, [[{
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream_id": 1,
"weight": 1
},
{
"weight": 1
}
]
}
]
}
}
}]])
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 12: delete upstream when plugin in global_rule still refer it
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/upstreams/1", ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
end
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"can not delete this upstream, plugin in global_rules [1] is still using it now"}
=== TEST 13: delete global_rule
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/global_rules/1", ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 14: prepare plugin_config
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/plugin_configs/1", ngx.HTTP_PUT, [[{
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream_id": 1,
"weight": 1
},
{
"weight": 1
}
]
}
]
}
}
}]])
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 15: delete upstream when plugin in plugin_config still refer it
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/upstreams/1", ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
end
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"can not delete this upstream, plugin in plugin_config [1] is still using it now"}
=== TEST 16: delete plugin_config
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/plugin_configs/1", ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 17: prepare consumer
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/consumers", ngx.HTTP_PUT, [[{
"username": "test",
"plugins": {
"key-auth": {
"key": "auth-one"
},
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream_id": 1,
"weight": 1
},
{
"weight": 1
}
]
}
]
}
}
}]])
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 18: delete upstream when plugin in consumer still refer it
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/upstreams/1", ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
end
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"can not delete this upstream, plugin in consumer [test] is still using it now"}
=== TEST 19: delete consumer
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/consumers/test", ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 20: prepare consumer_group
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/consumer_groups/1", ngx.HTTP_PUT, [[{
"plugins": {
"key-auth": {
"key": "auth-one"
},
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream_id": 1,
"weight": 1
},
{
"weight": 1
}
]
}
]
}
}
}]])
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 21: delete upstream when plugin in consumer_group still refer it
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/upstreams/1", ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
end
ngx.print(body)
}
}
--- error_code: 400
--- response_body
{"error_msg":"can not delete this upstream, plugin in consumer_group [1] is still using it now"}
=== TEST 22: delete consumer_group
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/consumer_groups/1", ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 23: delete upstream
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t("/apisix/admin/upstreams/1", ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed