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:
654
CloudronPackages/APISIX/apisix-source/t/plugin/api-breaker.t
Normal file
654
CloudronPackages/APISIX/apisix-source/t/plugin/api-breaker.t
Normal file
@@ -0,0 +1,654 @@
|
||||
#
|
||||
# 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_shuffle();
|
||||
no_root_location();
|
||||
log_level('info');
|
||||
run_tests;
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: sanity
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local plugin = require("apisix.plugins.api-breaker")
|
||||
local ok, err = plugin.check_schema({
|
||||
break_response_code = 502,
|
||||
unhealthy = {
|
||||
http_statuses = {500},
|
||||
failures = 1,
|
||||
},
|
||||
healthy = {
|
||||
http_statuses = {200},
|
||||
successes = 1,
|
||||
},
|
||||
})
|
||||
if not ok then
|
||||
ngx.say(err)
|
||||
end
|
||||
|
||||
ngx.say("done")
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
done
|
||||
|
||||
|
||||
|
||||
=== TEST 2: default configuration
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local plugin = require("apisix.plugins.api-breaker")
|
||||
local conf = {
|
||||
break_response_code = 502
|
||||
}
|
||||
|
||||
local ok, err = plugin.check_schema(conf)
|
||||
if not ok then
|
||||
ngx.say(err)
|
||||
end
|
||||
|
||||
ngx.say(require("toolkit.json").encode(conf))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
{"break_response_code":502,"healthy":{"http_statuses":[200],"successes":3},"max_breaker_sec":300,"unhealthy":{"failures":3,"http_statuses":[500]}}
|
||||
|
||||
|
||||
|
||||
=== TEST 3: default `healthy`
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local plugin = require("apisix.plugins.api-breaker")
|
||||
local conf = {
|
||||
break_response_code = 502,
|
||||
healthy = {}
|
||||
}
|
||||
|
||||
local ok, err = plugin.check_schema(conf)
|
||||
if not ok then
|
||||
ngx.say(err)
|
||||
end
|
||||
|
||||
ngx.say(require("toolkit.json").encode(conf))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
{"break_response_code":502,"healthy":{"http_statuses":[200],"successes":3},"max_breaker_sec":300,"unhealthy":{"failures":3,"http_statuses":[500]}}
|
||||
|
||||
|
||||
|
||||
=== TEST 4: default `unhealthy`
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local plugin = require("apisix.plugins.api-breaker")
|
||||
local conf = {
|
||||
break_response_code = 502,
|
||||
unhealthy = {}
|
||||
}
|
||||
|
||||
local ok, err = plugin.check_schema(conf)
|
||||
if not ok then
|
||||
ngx.say(err)
|
||||
end
|
||||
|
||||
ngx.say(require("toolkit.json").encode(conf))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
{"break_response_code":502,"healthy":{"http_statuses":[200],"successes":3},"max_breaker_sec":300,"unhealthy":{"failures":3,"http_statuses":[500]}}
|
||||
|
||||
|
||||
|
||||
=== TEST 5: bad break_response_code
|
||||
--- 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": {
|
||||
"api-breaker": {
|
||||
"break_response_code": 199,
|
||||
"unhealthy": {
|
||||
"http_statuses": [500, 503],
|
||||
"failures": 3
|
||||
},
|
||||
"healthy": {
|
||||
"http_statuses": [200, 206],
|
||||
"successes": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1980": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/api_breaker"
|
||||
}]]
|
||||
)
|
||||
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.print(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- error_code: 400
|
||||
--- response_body
|
||||
{"error_msg":"failed to check the configuration of plugin api-breaker err: property \"break_response_code\" validation failed: expected 199 to be at least 200"}
|
||||
|
||||
|
||||
|
||||
=== TEST 6: bad max_breaker_sec
|
||||
--- 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": {
|
||||
"api-breaker": {
|
||||
"break_response_code": 200,
|
||||
"max_breaker_sec": -1
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1980": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/api_breaker"
|
||||
}]]
|
||||
)
|
||||
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- error_code: 400
|
||||
|
||||
|
||||
|
||||
=== TEST 7: bad unhealthy.http_statuses
|
||||
--- 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": {
|
||||
"api-breaker": {
|
||||
"break_response_code": 200,
|
||||
"max_breaker_sec": 40,
|
||||
"unhealthy": {
|
||||
"http_statuses": [500, 603],
|
||||
"failures": 3
|
||||
},
|
||||
"healthy": {
|
||||
"http_statuses": [200, 206],
|
||||
"successes": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1980": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/api_breaker"
|
||||
}]]
|
||||
)
|
||||
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- error_code: 400
|
||||
|
||||
|
||||
|
||||
=== TEST 8: same http_statuses in healthy
|
||||
--- 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": {
|
||||
"api-breaker": {
|
||||
"break_response_code": 500,
|
||||
"unhealthy": {
|
||||
"http_statuses": [500, 503],
|
||||
"failures": 3
|
||||
},
|
||||
"healthy": {
|
||||
"http_statuses": [206, 206],
|
||||
"successes": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1980": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/api_breaker"
|
||||
}]]
|
||||
)
|
||||
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.print(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- error_code: 400
|
||||
--- response_body
|
||||
{"error_msg":"failed to check the configuration of plugin api-breaker err: property \"healthy\" validation failed: property \"http_statuses\" validation failed: expected unique items but items 1 and 2 are equal"}
|
||||
|
||||
|
||||
|
||||
=== TEST 9: set route, http_statuses: [500, 503]
|
||||
--- 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": {
|
||||
"api-breaker": {
|
||||
"break_response_code": 599,
|
||||
"unhealthy": {
|
||||
"http_statuses": [500, 503],
|
||||
"failures": 3
|
||||
},
|
||||
"healthy": {
|
||||
"http_statuses": [200, 206],
|
||||
"successes": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1980": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/api_breaker"
|
||||
}]]
|
||||
)
|
||||
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
|
||||
|
||||
|
||||
=== TEST 10: trigger breaker
|
||||
--- request eval
|
||||
[
|
||||
"GET /api_breaker?code=200", "GET /api_breaker?code=500",
|
||||
"GET /api_breaker?code=503", "GET /api_breaker?code=500",
|
||||
"GET /api_breaker?code=500", "GET /api_breaker?code=500"
|
||||
]
|
||||
--- error_code eval
|
||||
[200, 500, 503, 500, 599, 599]
|
||||
|
||||
|
||||
|
||||
=== TEST 11: trigger reset status
|
||||
--- request eval
|
||||
[
|
||||
"GET /api_breaker?code=500", "GET /api_breaker?code=500",
|
||||
|
||||
"GET /api_breaker?code=200", "GET /api_breaker?code=200",
|
||||
"GET /api_breaker?code=200",
|
||||
|
||||
"GET /api_breaker?code=500", "GET /api_breaker?code=500"
|
||||
]
|
||||
--- error_code eval
|
||||
[
|
||||
500, 500,
|
||||
200, 200, 200,
|
||||
500, 500
|
||||
]
|
||||
|
||||
|
||||
|
||||
=== TEST 12: trigger del healthy numeration
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local t = require("lib.test_admin").test
|
||||
local json = require("toolkit.json")
|
||||
|
||||
-- trigger to unhealth
|
||||
for i = 1, 4 do
|
||||
local code = t('/api_breaker?code=500', ngx.HTTP_GET)
|
||||
ngx.say("code: ", code)
|
||||
end
|
||||
|
||||
-- break for 3 seconds
|
||||
ngx.sleep(3)
|
||||
|
||||
-- make a try
|
||||
for i = 1, 4 do
|
||||
local code = t('/api_breaker?code=200', ngx.HTTP_GET)
|
||||
ngx.say("code: ", code)
|
||||
end
|
||||
|
||||
for i = 1, 4 do
|
||||
local code = t('/api_breaker?code=500', ngx.HTTP_GET)
|
||||
ngx.say("code: ", code)
|
||||
end
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
code: 500
|
||||
code: 500
|
||||
code: 500
|
||||
code: 599
|
||||
code: 200
|
||||
code: 200
|
||||
code: 200
|
||||
code: 200
|
||||
code: 500
|
||||
code: 500
|
||||
code: 500
|
||||
code: 599
|
||||
--- no_error_log
|
||||
[error]
|
||||
breaker_time: 4
|
||||
--- error_log
|
||||
breaker_time: 2
|
||||
|
||||
|
||||
|
||||
=== TEST 13: add plugin with default config value
|
||||
--- 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": {
|
||||
"api-breaker": {
|
||||
"break_response_code": 502,
|
||||
"break_response_body": "{\"message\":\"breaker opened.\"}",
|
||||
"break_response_headers": [{"key":"Content-Type","value":"application/json"},{"key":"Content-Type","value":"application/json+v1"}],
|
||||
"unhealthy": {
|
||||
"failures": 3
|
||||
},
|
||||
"healthy": {
|
||||
"successes": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1980": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/api_breaker"
|
||||
}]]
|
||||
)
|
||||
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
|
||||
|
||||
|
||||
=== TEST 14: default value
|
||||
--- request
|
||||
GET /api_breaker?code=500
|
||||
--- error_code: 500
|
||||
|
||||
|
||||
|
||||
=== TEST 15: trigger default value of unhealthy.http_statuses breaker
|
||||
--- request eval
|
||||
[
|
||||
"GET /api_breaker?code=200", "GET /api_breaker?code=500",
|
||||
"GET /api_breaker?code=503", "GET /api_breaker?code=500",
|
||||
"GET /api_breaker?code=500", "GET /api_breaker?code=500"
|
||||
]
|
||||
--- error_code eval
|
||||
[200, 500, 503, 500, 500, 502]
|
||||
--- response_headers eval
|
||||
["Content-Type: text/plain", "Content-Type: text/html", "Content-Type: text/html", "Content-Type: text/html", "Content-Type: text/html", "Content-Type: application/json+v1"]
|
||||
--- response_body_like eval
|
||||
[".*", ".*", ".*", ".*", ".*", "{\"message\":\"breaker opened.\"}"]
|
||||
|
||||
|
||||
|
||||
=== TEST 16: unhealthy -> timeout -> normal
|
||||
--- config
|
||||
location /mysleep {
|
||||
proxy_pass "http://127.0.0.1:1980/mysleep?seconds=1";
|
||||
}
|
||||
--- request eval
|
||||
[
|
||||
"GET /api_breaker?code=500",
|
||||
"GET /api_breaker?code=500",
|
||||
"GET /api_breaker?code=500",
|
||||
"GET /api_breaker?code=200",
|
||||
|
||||
"GET /mysleep",
|
||||
"GET /mysleep",
|
||||
"GET /mysleep",
|
||||
|
||||
"GET /api_breaker?code=200",
|
||||
"GET /api_breaker?code=200",
|
||||
"GET /api_breaker?code=200",
|
||||
"GET /api_breaker?code=200",
|
||||
"GET /api_breaker?code=200"]
|
||||
--- error_code eval
|
||||
[
|
||||
500, 500, 500, 502,
|
||||
200, 200, 200,
|
||||
200, 200, 200, 200,200
|
||||
]
|
||||
|
||||
|
||||
|
||||
=== TEST 17: unhealthy -> timeout -> unhealthy
|
||||
--- config
|
||||
location /mysleep {
|
||||
proxy_pass "http://127.0.0.1:1980/mysleep?seconds=1";
|
||||
}
|
||||
--- request eval
|
||||
[
|
||||
"GET /api_breaker?code=500", "GET /api_breaker?code=500",
|
||||
"GET /api_breaker?code=500", "GET /api_breaker?code=200",
|
||||
|
||||
"GET /mysleep", "GET /mysleep", "GET /mysleep",
|
||||
|
||||
"GET /api_breaker?code=500","GET /api_breaker?code=500",
|
||||
"GET /api_breaker?code=500","GET /api_breaker?code=500"
|
||||
]
|
||||
--- error_code eval
|
||||
[
|
||||
500, 500, 500, 502,
|
||||
200, 200, 200,
|
||||
500,502,502,502
|
||||
]
|
||||
|
||||
|
||||
|
||||
=== TEST 18: enable plugin, unhealthy.failures=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,
|
||||
[[{
|
||||
"plugins": {
|
||||
"api-breaker": {
|
||||
"break_response_code": 502,
|
||||
"max_breaker_sec": 10,
|
||||
"unhealthy": {
|
||||
"http_statuses": [500, 503],
|
||||
"failures": 1
|
||||
},
|
||||
"healthy": {
|
||||
"successes": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1980": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/api_breaker"
|
||||
}]]
|
||||
)
|
||||
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
|
||||
|
||||
|
||||
=== TEST 19: hit route 20 times, confirm the breaker time
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local t = require("lib.test_admin").test
|
||||
local json = require("toolkit.json")
|
||||
|
||||
local status_count = {}
|
||||
for i = 1, 20 do
|
||||
local code = t('/api_breaker?code=500', ngx.HTTP_GET)
|
||||
code = tostring(code)
|
||||
status_count[code] = (status_count[code] or 0) + 1
|
||||
ngx.sleep(1)
|
||||
end
|
||||
|
||||
ngx.say(json.encode(status_count))
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- no_error_log
|
||||
[error]
|
||||
phase_func(): breaker_time: 16
|
||||
--- error_log
|
||||
phase_func(): breaker_time: 2
|
||||
phase_func(): breaker_time: 4
|
||||
phase_func(): breaker_time: 8
|
||||
phase_func(): breaker_time: 10
|
||||
--- response_body
|
||||
{"500":4,"502":16}
|
||||
--- timeout: 25
|
||||
|
||||
|
||||
|
||||
=== TEST 20: reject invalid schema
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local t = require("lib.test_admin").test
|
||||
for _, case in ipairs({
|
||||
{input = {
|
||||
break_response_code = 200,
|
||||
break_response_headers = {{["content-type"] = "application/json"}}
|
||||
}},
|
||||
}) do
|
||||
local code, body = t('/apisix/admin/global_rules/1',
|
||||
ngx.HTTP_PUT,
|
||||
{
|
||||
id = "1",
|
||||
plugins = {
|
||||
["api-breaker"] = case.input
|
||||
}
|
||||
}
|
||||
)
|
||||
ngx.print(require("toolkit.json").decode(body).error_msg)
|
||||
end
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body eval
|
||||
qr/failed to check the configuration of plugin api-breaker err: property \"break_response_headers\" validation failed: failed to validate item 1: property \"(key|value)\" is required/
|
Reference in New Issue
Block a user