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,334 @@
--
-- 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 config_local = require("apisix.core.config_local")
local secret = require("apisix.secret")
local plugin = require("apisix.plugin")
local plugin_checker = require("apisix.plugin").plugin_checker
local check_schema = require("apisix.core.schema").check
local error = error
local ipairs = ipairs
local pairs = pairs
local type = type
local string_sub = string.sub
local consumers
local _M = {
version = 0.3,
}
local lrucache = core.lrucache.new({
ttl = 300, count = 512
})
-- Please calculate and set the value of the "consumers_count_for_lrucache"
-- variable based on the number of consumers in the current environment,
-- taking into account the appropriate adjustment coefficient.
local consumers_count_for_lrucache = 4096
local function remove_etcd_prefix(key)
local prefix = ""
local local_conf = config_local.local_conf()
local role = core.table.try_read_attr(local_conf, "deployment", "role")
local provider = core.table.try_read_attr(local_conf, "deployment", "role_" ..
role, "config_provider")
if provider == "etcd" and local_conf.etcd and local_conf.etcd.prefix then
prefix = local_conf.etcd.prefix
end
return string_sub(key, #prefix + 1)
end
-- /{etcd.prefix}/consumers/{consumer_name}/credentials/{credential_id} --> {consumer_name}
local function get_consumer_name_from_credential_etcd_key(key)
local uri_segs = core.utils.split_uri(remove_etcd_prefix(key))
return uri_segs[3]
end
local function is_credential_etcd_key(key)
if not key then
return false
end
local uri_segs = core.utils.split_uri(remove_etcd_prefix(key))
return uri_segs[2] == "consumers" and uri_segs[4] == "credentials"
end
local function get_credential_id_from_etcd_key(key)
local uri_segs = core.utils.split_uri(remove_etcd_prefix(key))
return uri_segs[5]
end
local function filter_consumers_list(data_list)
if #data_list == 0 then
return data_list
end
local list = {}
for _, item in ipairs(data_list) do
if not (type(item) == "table" and is_credential_etcd_key(item.key)) then
core.table.insert(list, item)
end
end
return list
end
local plugin_consumer
do
local consumers_id_lrucache = core.lrucache.new({
count = consumers_count_for_lrucache
})
local function construct_consumer_data(val, plugin_config)
-- if the val is a Consumer, clone it to the local consumer;
-- if the val is a Credential, to get the Consumer by consumer_name and then clone
-- it to the local consumer.
local consumer
if is_credential_etcd_key(val.key) then
local consumer_name = get_consumer_name_from_credential_etcd_key(val.key)
local the_consumer = consumers:get(consumer_name)
if the_consumer and the_consumer.value then
consumer = core.table.clone(the_consumer.value)
consumer.modifiedIndex = the_consumer.modifiedIndex
consumer.credential_id = get_credential_id_from_etcd_key(val.key)
else
-- Normally wouldn't get here:
-- it should belong to a consumer for any credential.
core.log.error("failed to get the consumer for the credential,",
" a wild credential has appeared!",
" credential key: ", val.key, ", consumer name: ", consumer_name)
return nil, "failed to get the consumer for the credential"
end
else
consumer = core.table.clone(val.value)
consumer.modifiedIndex = val.modifiedIndex
end
-- if the consumer has labels, set the field custom_id to it.
-- the custom_id is used to set in the request headers to the upstream.
if consumer.labels then
consumer.custom_id = consumer.labels["custom_id"]
end
-- Note: the id here is the key of consumer data, which
-- is 'username' field in admin
consumer.consumer_name = consumer.id
consumer.auth_conf = plugin_config
return consumer
end
function plugin_consumer()
local plugins = {}
if consumers.values == nil then
return plugins
end
-- consumers.values is the list that got from etcd by prefix key {etcd_prefix}/consumers.
-- So it contains consumers and credentials.
-- The val in the for-loop may be a Consumer or a Credential.
for _, val in ipairs(consumers.values) do
if type(val) ~= "table" then
goto CONTINUE
end
for name, config in pairs(val.value.plugins or {}) do
local plugin_obj = plugin.get(name)
if plugin_obj and plugin_obj.type == "auth" then
if not plugins[name] then
plugins[name] = {
nodes = {},
len = 0,
conf_version = consumers.conf_version
}
end
local consumer = consumers_id_lrucache(val.value.id .. name,
val.modifiedIndex, construct_consumer_data, val, config)
if consumer == nil then
goto CONTINUE
end
plugins[name].len = plugins[name].len + 1
core.table.insert(plugins[name].nodes, plugins[name].len,
consumer)
core.log.info("consumer:", core.json.delay_encode(consumer))
end
end
::CONTINUE::
end
return plugins
end
end
_M.filter_consumers_list = filter_consumers_list
function _M.get_consumer_key_from_credential_key(key)
local uri_segs = core.utils.split_uri(key)
return "/consumers/" .. uri_segs[3]
end
function _M.plugin(plugin_name)
local plugin_conf = core.lrucache.global("/consumers",
consumers.conf_version, plugin_consumer)
return plugin_conf[plugin_name]
end
function _M.consumers_conf(plugin_name)
return _M.plugin(plugin_name)
end
-- attach chosen consumer to the ctx, used in auth plugin
function _M.attach_consumer(ctx, consumer, conf)
ctx.consumer = consumer
ctx.consumer_name = consumer.consumer_name
ctx.consumer_group_id = consumer.group_id
ctx.consumer_ver = conf.conf_version
core.request.set_header(ctx, "X-Consumer-Username", consumer.username)
core.request.set_header(ctx, "X-Credential-Identifier", consumer.credential_id)
core.request.set_header(ctx, "X-Consumer-Custom-ID", consumer.custom_id)
end
function _M.consumers()
if not consumers then
return nil, nil
end
return filter_consumers_list(consumers.values), consumers.conf_version
end
local create_consume_cache
do
local consumer_lrucache = core.lrucache.new({
count = consumers_count_for_lrucache
})
local function fill_consumer_secret(consumer)
local new_consumer = core.table.clone(consumer)
new_consumer.auth_conf = secret.fetch_secrets(new_consumer.auth_conf, false)
return new_consumer
end
function create_consume_cache(consumers_conf, key_attr)
local consumer_names = {}
for _, consumer in ipairs(consumers_conf.nodes) do
core.log.info("consumer node: ", core.json.delay_encode(consumer))
local new_consumer = consumer_lrucache(consumer, nil,
fill_consumer_secret, consumer)
consumer_names[new_consumer.auth_conf[key_attr]] = new_consumer
end
return consumer_names
end
end
function _M.consumers_kv(plugin_name, consumer_conf, key_attr)
local consumers = lrucache("consumers_key#" .. plugin_name, consumer_conf.conf_version,
create_consume_cache, consumer_conf, key_attr)
return consumers
end
function _M.find_consumer(plugin_name, key, key_value)
local consumer
local consumer_conf
consumer_conf = _M.plugin(plugin_name)
if not consumer_conf then
return nil, nil, "Missing related consumer"
end
local consumers = _M.consumers_kv(plugin_name, consumer_conf, key)
consumer = consumers[key_value]
return consumer, consumer_conf
end
local function check_consumer(consumer, key)
local data_valid
local err
if is_credential_etcd_key(key) then
data_valid, err = check_schema(core.schema.credential, consumer)
else
data_valid, err = check_schema(core.schema.consumer, consumer)
end
if not data_valid then
return data_valid, err
end
return plugin_checker(consumer, core.schema.TYPE_CONSUMER)
end
function _M.init_worker()
local err
local cfg = {
automatic = true,
checker = check_consumer,
}
consumers, err = core.config.new("/consumers", cfg)
if not consumers then
error("failed to create etcd instance for fetching consumers: " .. err)
return
end
end
local function get_anonymous_consumer_from_local_cache(name)
local anon_consumer_raw = consumers:get(name)
if not anon_consumer_raw or not anon_consumer_raw.value or
not anon_consumer_raw.value.id or not anon_consumer_raw.modifiedIndex then
return nil, nil, "failed to get anonymous consumer " .. name
end
-- make structure of anon_consumer similar to that of consumer_mod.consumers_kv's response
local anon_consumer = anon_consumer_raw.value
anon_consumer.consumer_name = anon_consumer_raw.value.id
anon_consumer.modifiedIndex = anon_consumer_raw.modifiedIndex
local anon_consumer_conf = {
conf_version = anon_consumer_raw.modifiedIndex
}
return anon_consumer, anon_consumer_conf
end
function _M.get_anonymous_consumer(name)
local anon_consumer, anon_consumer_conf, err
anon_consumer, anon_consumer_conf, err = get_anonymous_consumer_from_local_cache(name)
return anon_consumer, anon_consumer_conf, err
end
return _M