Files
ReachableCEO 54cc5f7308 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>
2025-09-04 09:42:47 -05:00

784 lines
22 KiB
Perl

#
# 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;
my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';
my $version = eval { `$nginx_binary -V 2>&1` };
if ($version !~ m/\/apisix-nginx-module/) {
plan(skip_all => "apisix-nginx-module not installed");
} else {
plan('no_plan');
}
$ENV{TEST_NGINX_REDIS_PORT} ||= 1985;
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->extra_yaml_config) {
my $extra_yaml_config = <<_EOC_;
xrpc:
protocols:
- name: redis
_EOC_
$block->set_value("extra_yaml_config", $extra_yaml_config);
}
if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
$block->set_value("no_error_log", "[error]\nRPC is not finished");
}
if (!defined $block->request) {
$block->set_value("request", "GET /t");
}
$block;
});
worker_connections(1024);
run_tests;
__DATA__
=== TEST 1: init
--- 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,
{
protocol = {
name = "redis"
},
upstream = {
nodes = {
["127.0.0.1:6379"] = 1
},
type = "roundrobin"
}
}
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 2: sanity
--- config
location /t {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
local ok, err = red:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
if not ok then
ngx.say("failed to connect: ", err)
return
end
local res, err = red:hmset("animals", "dog", "bark", "cat", "meow")
if not res then
ngx.say("failed to set animals: ", err)
return
end
ngx.say("hmset animals: ", res)
local res, err = red:hmget("animals", "dog", "cat")
if not res then
ngx.say("failed to get animals: ", err)
return
end
ngx.say("hmget animals: ", res)
local res, err = red:hget("animals", "dog")
if not res then
ngx.say("failed to get animals: ", err)
return
end
ngx.say("hget animals: ", res)
local res, err = red:hget("animals", "not_found")
if not res then
ngx.say("failed to get animals: ", err)
return
end
ngx.say("hget animals: ", res)
}
}
--- response_body
hmset animals: OK
hmget animals: barkmeow
hget animals: bark
hget animals: null
--- stream_conf_enable
=== TEST 3: error
--- config
location /t {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
local ok, err = red:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
if not ok then
ngx.say("failed to connect: ", err)
return
end
local res, err = red:get("animals")
if not res then
ngx.say("failed to set animals: ", err)
end
local res, err = red:hget("animals", "dog")
if not res then
ngx.say("failed to get animals: ", err)
return
end
ngx.say("hget animals: ", res)
}
}
--- response_body
failed to set animals: WRONGTYPE Operation against a key holding the wrong kind of value
hget animals: bark
--- stream_conf_enable
=== TEST 4: big value
--- config
location /t {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
local ok, err = red:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
if not ok then
ngx.say("failed to connect: ", err)
return
end
local res, err = red:set("big-key", ("\r\n"):rep(1024 * 1024 * 16))
if not res then
ngx.say("failed to set: ", err)
return
end
local res, err = red:get("big-key")
if not res then
ngx.say("failed to get: ", err)
return
end
ngx.print(res)
}
}
--- response_body eval
"\r\n" x 16777216
--- stream_conf_enable
=== TEST 5: pipeline
--- config
location /t {
content_by_lua_block {
local cjson = require("cjson")
local redis = require "resty.redis"
local t = {}
for i = 1, 180 do
local th = assert(ngx.thread.spawn(function(i)
local red = redis:new()
local ok, err = red:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
if not ok then
ngx.say("failed to connect: ", err)
return
end
red:init_pipeline()
red:set("mark_" .. i, i)
red:get("mark_" .. i)
red:get("counter")
for j = 1, 4 do
red:incr("counter")
end
local results, err = red:commit_pipeline()
if not results then
ngx.say("failed to commit: ", err)
return
end
local begin = tonumber(results[3])
for j = 1, 4 do
local incred = results[3 + j]
if incred ~= results[2 + j] + 1 then
ngx.log(ngx.ERR, cjson.encode(results))
end
end
end, i))
table.insert(t, th)
end
for i, th in ipairs(t) do
ngx.thread.wait(th)
end
}
}
--- response_body
--- stream_conf_enable
=== TEST 6: delay
--- 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,
{
protocol = {
name = "redis",
conf = {
faults = {
{delay = 0.01, key = "ignored", commands = {"Ping", "time"}}
}
}
},
upstream = {
nodes = {
["127.0.0.1:6379"] = 1
},
type = "roundrobin"
}
}
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 7: hit
--- config
location /t {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
local ok, err = red:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
if not ok then
ngx.say("failed to connect: ", err)
return
end
local start = ngx.now()
local res, err = red:ping()
if not res then
ngx.say(err)
return
end
local now = ngx.now()
-- use integer to bypass float point number precision problem
if math.ceil((now - start) * 1000) < 10 then
ngx.say(now, " ", start)
return
end
start = now
local res, err = red:time()
if not res then
ngx.say(err)
return
end
local now = ngx.now()
if math.ceil((now - start) * 1000) < 10 then
ngx.say(now, " ", start)
return
end
start = now
red:init_pipeline()
red:time()
red:time()
red:get("A")
local results, err = red:commit_pipeline()
if not results then
ngx.say("failed to commit: ", err)
return
end
local now = ngx.now()
if math.ceil((now - start) * 1000) < 20 or math.ceil((now - start) * 1000) > 30 then
ngx.say(now, " ", start)
return
end
ngx.say("ok")
}
}
--- response_body
ok
--- stream_conf_enable
=== TEST 8: DFS match
--- 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,
{
protocol = {
name = "redis",
conf = {
faults = {
{delay = 0.02, key = "a", commands = {"get"}},
{delay = 0.01, commands = {"get", "set"}},
}
}
},
upstream = {
nodes = {
["127.0.0.1:6379"] = 1
},
type = "roundrobin"
}
}
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 9: hit
--- config
location /t {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
local ok, err = red:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
if not ok then
ngx.say("failed to connect: ", err)
return
end
local start = ngx.now()
local res, err = red:get("a")
if not res then
ngx.say(err)
return
end
local now = ngx.now()
if math.ceil((now - start) * 1000) < 20 then
ngx.say(now, " ", start)
return
end
start = now
local res, err = red:set("a", "a")
if not res then
ngx.say(err)
return
end
local now = ngx.now()
if math.ceil((now - start) * 1000) < 10 then
ngx.say(now, " ", start)
return
end
start = now
red:init_pipeline()
red:get("b")
red:set("A", "a")
local results, err = red:commit_pipeline()
if not results then
ngx.say("failed to commit: ", err)
return
end
local now = ngx.now()
if math.ceil((now - start) * 1000) < 20 or math.ceil((now - start) * 1000) > 30 then
ngx.say(now, " ", start)
return
end
ngx.say("ok")
}
}
--- response_body
ok
--- stream_conf_enable
=== TEST 10: multi keys
--- 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,
{
protocol = {
name = "redis",
conf = {
faults = {
{delay = 0.06, key = "b", commands = {"del"}},
{delay = 0.04, key = "a", commands = {"mset"}},
{delay = 0.02, key = "b", commands = {"mset"}},
}
}
},
upstream = {
nodes = {
["127.0.0.1:6379"] = 1
},
type = "roundrobin"
}
}
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 11: hit
--- config
location /t {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
local ok, err = red:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
if not ok then
ngx.say("failed to connect: ", err)
return
end
local start = ngx.now()
local res, err = red:mset("c", 1, "a", 2, "b", 3)
if not res then
ngx.say(err)
return
end
local now = ngx.now()
if math.ceil((now - start) * 1000) < 40 then
ngx.say("mset a ", now, " ", start)
return
end
start = now
local res, err = red:mset("b", 2, "a", 3)
if not res then
ngx.say(err)
return
end
local now = ngx.now()
if math.ceil((now - start) * 1000) < 20 or math.ceil((now - start) * 1000) > 35 then
ngx.say("mset b ", now, " ", start)
return
end
start = now
local res, err = red:mset("c", "a")
if not res then
ngx.say(err)
return
end
local now = ngx.now()
if math.ceil((now - start) * 1000) > 20 then
ngx.say("mset mismatch ", now, " ", start)
return
end
start = now
local res, err = red:del("a", "b")
if not res then
ngx.say(err)
return
end
local now = ngx.now()
if math.ceil((now - start) * 1000) < 60 then
ngx.say("del b ", now, " ", start)
return
end
start = now
ngx.say("ok")
}
}
--- response_body
ok
--- stream_conf_enable
=== TEST 12: publish & subscribe
--- stream_extra_init_by_lua
local cjson = require "cjson"
local redis_proto = require("apisix.stream.xrpc.protocols.redis")
redis_proto.log = function(sess, ctx)
ngx.log(ngx.WARN, "log redis request ", cjson.encode(ctx.cmd_line))
end
--- config
location /t {
content_by_lua_block {
local cjson = require "cjson"
local redis = require "resty.redis"
local red = redis:new()
local red2 = redis:new()
red:set_timeout(1000) -- 1 sec
red2:set_timeout(1000) -- 1 sec
local ok, err = red:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
if not ok then
ngx.say("1: failed to connect: ", err)
return
end
ok, err = red2:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
if not ok then
ngx.say("2: failed to connect: ", err)
return
end
local res, err = red:subscribe("dog")
if not res then
ngx.say("1: failed to subscribe: ", err)
return
end
ngx.say("1: subscribe dog: ", cjson.encode(res))
res, err = red:subscribe("cat")
if not res then
ngx.say("1: failed to subscribe: ", err)
return
end
ngx.say("1: subscribe cat: ", cjson.encode(res))
res, err = red2:publish("dog", "Hello")
if not res then
ngx.say("2: failed to publish: ", err)
return
end
ngx.say("2: publish: ", cjson.encode(res))
res, err = red:read_reply()
if not res then
ngx.say("1: failed to read reply: ", err)
else
ngx.say("1: receive: ", cjson.encode(res))
end
red:set_timeout(10) -- 10ms
res, err = red:read_reply()
if not res then
ngx.say("1: failed to read reply: ", err)
else
ngx.say("1: receive: ", cjson.encode(res))
end
red:set_timeout(1000) -- 1s
res, err = red:unsubscribe()
if not res then
ngx.say("1: failed to unscribe: ", err)
else
ngx.say("1: unsubscribe: ", cjson.encode(res))
end
res, err = red:read_reply()
if not res then
ngx.say("1: failed to read reply: ", err)
else
ngx.say("1: receive: ", cjson.encode(res))
end
red:set_timeout(10) -- 10ms
res, err = red:read_reply()
if not res then
ngx.say("1: failed to read reply: ", err)
else
ngx.say("1: receive: ", cjson.encode(res))
end
red:set_timeout(1000) -- 1s
res, err = red:set("dog", 1)
if not res then
ngx.say("1: failed to set: ", err)
else
ngx.say("1: receive: ", cjson.encode(res))
end
red:close()
red2:close()
}
}
--- response_body_like chop
^1: subscribe dog: \["subscribe","dog",1\]
1: subscribe cat: \["subscribe","cat",2\]
2: publish: 1
1: receive: \["message","dog","Hello"\]
1: failed to read reply: timeout
1: unsubscribe: \[\["unsubscribe","(?:dog|cat)",1\],\["unsubscribe","(?:dog|cat)",0\]\]
1: failed to read reply: not subscribed
1: failed to read reply: not subscribed
1: receive: "OK"
$
--- stream_conf_enable
--- grep_error_log eval
qr/log redis request \[[^]]+\]/
--- grep_error_log_out
log redis request ["subscribe","dog"]
log redis request ["subscribe","cat"]
log redis request ["publish","dog","Hello"]
log redis request ["unsubscribe"]
log redis request ["set","dog","1"]
=== TEST 13: psubscribe & punsubscribe
--- stream_extra_init_by_lua
local cjson = require "cjson"
local redis_proto = require("apisix.stream.xrpc.protocols.redis")
redis_proto.log = function(sess, ctx)
ngx.log(ngx.WARN, "log redis request ", cjson.encode(ctx.cmd_line))
end
--- config
location /t {
content_by_lua_block {
local cjson = require "cjson"
local redis = require "resty.redis"
local red = redis:new()
local red2 = redis:new()
red:set_timeout(1000) -- 1 sec
red2:set_timeout(1000) -- 1 sec
local ok, err = red:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
if not ok then
ngx.say("1: failed to connect: ", err)
return
end
ok, err = red2:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT)
if not ok then
ngx.say("2: failed to connect: ", err)
return
end
local res, err = red:psubscribe("dog*", "cat*")
if not res then
ngx.say("1: failed to subscribe: ", err)
return
end
ngx.say("1: psubscribe: ", cjson.encode(res))
res, err = red2:publish("dog1", "Hello")
if not res then
ngx.say("2: failed to publish: ", err)
return
end
ngx.say("2: publish: ", cjson.encode(res))
res, err = red:read_reply()
if not res then
ngx.say("1: failed to read reply: ", err)
else
ngx.say("1: receive: ", cjson.encode(res))
end
res, err = red:punsubscribe("cat*", "dog*")
if not res then
ngx.say("1: failed to unscribe: ", err)
else
ngx.say("1: punsubscribe: ", cjson.encode(res))
end
res, err = red:set("dog", 1)
if not res then
ngx.say("1: failed to set: ", err)
else
ngx.say("1: receive: ", cjson.encode(res))
end
red:close()
red2:close()
}
}
--- response_body_like chop
^1: psubscribe: \[\["psubscribe","dog\*",1\],\["psubscribe","cat\*",2\]\]
2: publish: 1
1: receive: \["pmessage","dog\*","dog1","Hello"\]
1: punsubscribe: \[\["punsubscribe","cat\*",1\],\["punsubscribe","dog\*",0\]\]
1: receive: "OK"
$
--- stream_conf_enable
--- grep_error_log eval
qr/log redis request \[[^]]+\]/
--- grep_error_log_out
log redis request ["psubscribe","dog*","cat*"]
log redis request ["publish","dog1","Hello"]
log redis request ["punsubscribe","cat*","dog*"]
log redis request ["set","dog","1"]