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,153 @@
--
-- 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.
--
local require = require
local radixtree = require("resty.radixtree")
local router = require("apisix.utils.router")
local service_fetch = require("apisix.http.service").get
local core = require("apisix.core")
local expr = require("resty.expr.v1")
local plugin_checker = require("apisix.plugin").plugin_checker
local event = require("apisix.core.event")
local ipairs = ipairs
local type = type
local error = error
local loadstring = loadstring
local _M = {}
function _M.create_radixtree_uri_router(routes, uri_routes, with_parameter)
routes = routes or {}
core.table.clear(uri_routes)
for _, route in ipairs(routes) do
if type(route) == "table" then
local status = core.table.try_read_attr(route, "value", "status")
-- check the status
if status and status == 0 then
goto CONTINUE
end
local filter_fun, err
if route.value.filter_func then
filter_fun, err = loadstring(
"return " .. route.value.filter_func,
"router#" .. route.value.id)
if not filter_fun then
core.log.error("failed to load filter function: ", err,
" route id: ", route.value.id)
goto CONTINUE
end
filter_fun = filter_fun()
end
local hosts = route.value.hosts or route.value.host
if not hosts and route.value.service_id then
local service = service_fetch(route.value.service_id)
if not service then
core.log.error("failed to fetch service configuration by ",
"id: ", route.value.service_id)
-- we keep the behavior that missing service won't affect the route matching
else
hosts = service.value.hosts
end
end
core.log.info("insert uri route: ",
core.json.delay_encode(route.value, true))
core.table.insert(uri_routes, {
paths = route.value.uris or route.value.uri,
methods = route.value.methods,
priority = route.value.priority,
hosts = hosts,
remote_addrs = route.value.remote_addrs
or route.value.remote_addr,
vars = route.value.vars,
filter_fun = filter_fun,
handler = function (api_ctx, match_opts)
api_ctx.matched_params = nil
api_ctx.matched_route = route
api_ctx.curr_req_matched = match_opts.matched
end
})
::CONTINUE::
end
end
event.push(event.CONST.BUILD_ROUTER, routes)
core.log.info("route items: ", core.json.delay_encode(uri_routes, true))
if with_parameter then
return radixtree.new(uri_routes)
else
return router.new(uri_routes)
end
end
function _M.match_uri(uri_router, api_ctx)
local match_opts = core.tablepool.fetch("route_match_opts", 0, 4)
match_opts.method = api_ctx.var.request_method
match_opts.host = api_ctx.var.host
match_opts.remote_addr = api_ctx.var.remote_addr
match_opts.vars = api_ctx.var
match_opts.matched = core.tablepool.fetch("matched_route_record", 0, 4)
local ok = uri_router:dispatch(api_ctx.var.uri, match_opts, api_ctx, match_opts)
core.tablepool.release("route_match_opts", match_opts)
return ok
end
-- additional check for synced route configuration, run after schema check
local function check_route(route)
local ok, err = plugin_checker(route)
if not ok then
return nil, err
end
if route.vars then
ok, err = expr.new(route.vars)
if not ok then
return nil, "failed to validate the 'vars' expression: " .. err
end
end
return true
end
function _M.init_worker(filter)
local user_routes, err = core.config.new("/routes", {
automatic = true,
item_schema = core.schema.route,
checker = check_route,
filter = filter,
})
if not user_routes then
error("failed to create etcd instance for fetching /routes : " .. err)
end
return user_routes
end
return _M

View File

@@ -0,0 +1,193 @@
--
-- 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.
--
local require = require
local router = require("apisix.utils.router")
local core = require("apisix.core")
local event = require("apisix.core.event")
local get_services = require("apisix.http.service").services
local service_fetch = require("apisix.http.service").get
local ipairs = ipairs
local type = type
local tab_insert = table.insert
local loadstring = loadstring
local pairs = pairs
local cached_router_version
local cached_service_version
local host_router
local only_uri_router
local _M = {version = 0.1}
local function push_host_router(route, host_routes, only_uri_routes)
if type(route) ~= "table" then
return
end
local filter_fun, err
if route.value.filter_func then
filter_fun, err = loadstring(
"return " .. route.value.filter_func,
"router#" .. route.value.id)
if not filter_fun then
core.log.error("failed to load filter function: ", err,
" route id: ", route.value.id)
return
end
filter_fun = filter_fun()
end
local hosts = route.value.hosts
if not hosts then
if route.value.host then
hosts = {route.value.host}
elseif route.value.service_id then
local service = service_fetch(route.value.service_id)
if not service then
core.log.error("failed to fetch service configuration by ",
"id: ", route.value.service_id)
-- we keep the behavior that missing service won't affect the route matching
else
hosts = service.value.hosts
end
end
end
local radixtree_route = {
paths = route.value.uris or route.value.uri,
methods = route.value.methods,
priority = route.value.priority,
remote_addrs = route.value.remote_addrs
or route.value.remote_addr,
vars = route.value.vars,
filter_fun = filter_fun,
handler = function (api_ctx, match_opts)
api_ctx.matched_params = nil
api_ctx.matched_route = route
api_ctx.curr_req_matched = match_opts.matched
api_ctx.real_curr_req_matched_path = match_opts.matched._path
end
}
if hosts == nil then
core.table.insert(only_uri_routes, radixtree_route)
return
end
for i, host in ipairs(hosts) do
local host_rev = host:reverse()
if not host_routes[host_rev] then
host_routes[host_rev] = {radixtree_route}
else
tab_insert(host_routes[host_rev], radixtree_route)
end
end
end
local function create_radixtree_router(routes)
local host_routes = {}
local only_uri_routes = {}
host_router = nil
routes = routes or {}
for _, route in ipairs(routes) do
local status = core.table.try_read_attr(route, "value", "status")
-- check the status
if not status or status == 1 then
push_host_router(route, host_routes, only_uri_routes)
end
end
-- create router: host_router
local host_router_routes = {}
for host_rev, routes in pairs(host_routes) do
local sub_router = router.new(routes)
core.table.insert(host_router_routes, {
paths = host_rev,
filter_fun = function(vars, opts, ...)
return sub_router:dispatch(vars.uri, opts, ...)
end,
handler = function (api_ctx, match_opts)
api_ctx.real_curr_req_matched_host = match_opts.matched._path
end
})
end
event.push(event.CONST.BUILD_ROUTER, routes)
if #host_router_routes > 0 then
host_router = router.new(host_router_routes)
end
-- create router: only_uri_router
only_uri_router = router.new(only_uri_routes)
return true
end
function _M.match(api_ctx)
local user_routes = _M.user_routes
local _, service_version = get_services()
if not cached_router_version or cached_router_version ~= user_routes.conf_version
or not cached_service_version or cached_service_version ~= service_version
then
create_radixtree_router(user_routes.values)
cached_router_version = user_routes.conf_version
cached_service_version = service_version
end
return _M.matching(api_ctx)
end
function _M.matching(api_ctx)
core.log.info("route match mode: radixtree_host_uri")
local match_opts = core.tablepool.fetch("route_match_opts", 0, 16)
match_opts.method = api_ctx.var.request_method
match_opts.remote_addr = api_ctx.var.remote_addr
match_opts.vars = api_ctx.var
match_opts.host = api_ctx.var.host
match_opts.matched = core.tablepool.fetch("matched_route_record", 0, 4)
if host_router then
local host_uri = api_ctx.var.host
local ok = host_router:dispatch(host_uri:reverse(), match_opts, api_ctx, match_opts)
if ok then
if api_ctx.real_curr_req_matched_path then
api_ctx.curr_req_matched._path = api_ctx.real_curr_req_matched_path
api_ctx.real_curr_req_matched_path = nil
end
if api_ctx.real_curr_req_matched_host then
api_ctx.curr_req_matched._host = api_ctx.real_curr_req_matched_host:reverse()
api_ctx.real_curr_req_matched_host = nil
end
core.tablepool.release("route_match_opts", match_opts)
return true
end
end
local ok = only_uri_router:dispatch(api_ctx.var.uri, match_opts, api_ctx, match_opts)
core.tablepool.release("route_match_opts", match_opts)
return ok
end
return _M

View File

@@ -0,0 +1,57 @@
--
-- 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.
--
local require = require
local core = require("apisix.core")
local base_router = require("apisix.http.route")
local get_services = require("apisix.http.service").services
local cached_router_version
local cached_service_version
local _M = {version = 0.2}
local uri_routes = {}
local uri_router
function _M.match(api_ctx)
local user_routes = _M.user_routes
local _, service_version = get_services()
if not cached_router_version or cached_router_version ~= user_routes.conf_version
or not cached_service_version or cached_service_version ~= service_version
then
uri_router = base_router.create_radixtree_uri_router(user_routes.values,
uri_routes, false)
cached_router_version = user_routes.conf_version
cached_service_version = service_version
end
if not uri_router then
core.log.error("failed to fetch valid `uri` router: ")
return true
end
return _M.matching(api_ctx)
end
function _M.matching(api_ctx)
core.log.info("route match mode: radixtree_uri")
return base_router.match_uri(uri_router, api_ctx)
end
return _M

View File

@@ -0,0 +1,57 @@
--
-- 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.
--
local require = require
local core = require("apisix.core")
local base_router = require("apisix.http.route")
local get_services = require("apisix.http.service").services
local cached_router_version
local cached_service_version
local _M = {}
local uri_routes = {}
local uri_router
function _M.match(api_ctx)
local user_routes = _M.user_routes
local _, service_version = get_services()
if not cached_router_version or cached_router_version ~= user_routes.conf_version
or not cached_service_version or cached_service_version ~= service_version
then
uri_router = base_router.create_radixtree_uri_router(user_routes.values,
uri_routes, true)
cached_router_version = user_routes.conf_version
cached_service_version = service_version
end
if not uri_router then
core.log.error("failed to fetch valid `uri_with_parameter` router: ")
return true
end
return _M.matching(api_ctx)
end
function _M.matching(api_ctx)
core.log.info("route match mode: radixtree_uri_with_parameter")
return base_router.match_uri(uri_router, api_ctx)
end
return _M

View File

@@ -0,0 +1,70 @@
--
-- 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.
--
local core = require("apisix.core")
local apisix_upstream = require("apisix.upstream")
local plugin_checker = require("apisix.plugin").plugin_checker
local services
local error = error
local _M = {
version = 0.2,
}
function _M.get(service_id)
return services:get(service_id)
end
function _M.services()
if not services then
return nil, nil
end
return services.values, services.conf_version
end
local function filter(service)
service.has_domain = false
if not service.value then
return
end
apisix_upstream.filter_upstream(service.value.upstream, service)
core.log.info("filter service: ", core.json.delay_encode(service, true))
end
function _M.init_worker()
local err
services, err = core.config.new("/services", {
automatic = true,
item_schema = core.schema.service,
checker = plugin_checker,
filter = filter,
})
if not services then
error("failed to create etcd instance for fetching /services: " .. err)
return
end
end
return _M