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:
315
CloudronPackages/APISIX/apisix-source/apisix/debug.lua
Normal file
315
CloudronPackages/APISIX/apisix-source/apisix/debug.lua
Normal file
@@ -0,0 +1,315 @@
|
||||
--
|
||||
-- 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 yaml = require("lyaml")
|
||||
local log = require("apisix.core.log")
|
||||
local profile = require("apisix.core.profile")
|
||||
local lfs = require("lfs")
|
||||
local inspect = require("inspect")
|
||||
local jsonschema = require("jsonschema")
|
||||
local io = io
|
||||
local ngx = ngx
|
||||
local re_find = ngx.re.find
|
||||
local get_headers = ngx.req.get_headers
|
||||
local type = type
|
||||
local pairs = pairs
|
||||
local setmetatable = setmetatable
|
||||
local pcall = pcall
|
||||
local ipairs = ipairs
|
||||
local unpack = unpack
|
||||
local debug_yaml_path = profile:yaml_path("debug")
|
||||
local debug_yaml
|
||||
local debug_yaml_ctime
|
||||
|
||||
|
||||
local _M = {version = 0.1}
|
||||
|
||||
|
||||
local config_schema = {
|
||||
type = "object",
|
||||
properties = {
|
||||
basic = {
|
||||
properties = {
|
||||
enable = {
|
||||
type = "boolean",
|
||||
},
|
||||
}
|
||||
},
|
||||
http_filter = {
|
||||
properties = {
|
||||
enable = {
|
||||
type = "boolean",
|
||||
},
|
||||
enable_header_name = {
|
||||
type = "string",
|
||||
},
|
||||
}
|
||||
},
|
||||
hook_conf = {
|
||||
properties = {
|
||||
enable = {
|
||||
type = "boolean",
|
||||
},
|
||||
name = {
|
||||
type = "string",
|
||||
},
|
||||
log_level = {
|
||||
enum = {"debug", "info", "notice", "warn", "error",
|
||||
"crit", "alert","emerg"},
|
||||
},
|
||||
is_print_input_args = {
|
||||
type = "boolean",
|
||||
},
|
||||
is_print_return_value = {
|
||||
type = "boolean",
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
required = {"basic", "http_filter", "hook_conf"},
|
||||
}
|
||||
|
||||
|
||||
local function read_debug_yaml()
|
||||
local attributes, err = lfs.attributes(debug_yaml_path)
|
||||
if not attributes then
|
||||
log.notice("failed to fetch ", debug_yaml_path, " attributes: ", err)
|
||||
return
|
||||
end
|
||||
|
||||
-- log.info("change: ", json.encode(attributes))
|
||||
local last_change_time = attributes.change
|
||||
if debug_yaml_ctime == last_change_time then
|
||||
return
|
||||
end
|
||||
|
||||
local f, err = io.open(debug_yaml_path, "r")
|
||||
if not f then
|
||||
log.error("failed to open file ", debug_yaml_path, " : ", err)
|
||||
return
|
||||
end
|
||||
|
||||
local found_end_flag
|
||||
for i = 1, 10 do
|
||||
f:seek('end', -i)
|
||||
|
||||
local end_flag = f:read("*a")
|
||||
-- log.info(i, " flag: ", end_flag)
|
||||
if re_find(end_flag, [[#END\s*]], "jo") then
|
||||
found_end_flag = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not found_end_flag then
|
||||
f:seek("set")
|
||||
local size = f:seek("end")
|
||||
f:close()
|
||||
|
||||
if size > 8 then
|
||||
log.warn("missing valid end flag in file ", debug_yaml_path)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
f:seek('set')
|
||||
local yaml_config = f:read("*a")
|
||||
f:close()
|
||||
|
||||
local debug_yaml_new = yaml.load(yaml_config)
|
||||
if not debug_yaml_new then
|
||||
log.error("failed to parse the content of file " .. debug_yaml_path)
|
||||
return
|
||||
end
|
||||
|
||||
debug_yaml_new.hooks = debug_yaml_new.hooks or {}
|
||||
debug_yaml = debug_yaml_new
|
||||
debug_yaml_ctime = last_change_time
|
||||
|
||||
-- validate the debug yaml config
|
||||
local validator = jsonschema.generate_validator(config_schema)
|
||||
local ok, err = validator(debug_yaml)
|
||||
if not ok then
|
||||
log.error("failed to validate debug config " .. err)
|
||||
return
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
local sync_debug_hooks
|
||||
do
|
||||
local pre_mtime
|
||||
local enabled_hooks = {}
|
||||
|
||||
local function apply_new_fun(module, fun_name, file_path, hook_conf)
|
||||
local log_level = hook_conf.log_level or "warn"
|
||||
|
||||
if not module or type(module[fun_name]) ~= "function" then
|
||||
log.error("failed to find function [", fun_name,
|
||||
"] in module:", file_path)
|
||||
return
|
||||
end
|
||||
|
||||
local fun = module[fun_name]
|
||||
local fun_org
|
||||
if enabled_hooks[fun] then
|
||||
fun_org = enabled_hooks[fun].org
|
||||
enabled_hooks[fun] = nil
|
||||
else
|
||||
fun_org = fun
|
||||
end
|
||||
|
||||
local t = {fun_org = fun_org}
|
||||
local mt = {}
|
||||
|
||||
function mt.__call(self, ...)
|
||||
local arg = {...}
|
||||
local http_filter = debug_yaml.http_filter
|
||||
local api_ctx = ngx.ctx.api_ctx
|
||||
local enable_by_hook = not (http_filter and http_filter.enable)
|
||||
local enable_by_header_filter = (http_filter and http_filter.enable)
|
||||
and (api_ctx and api_ctx.enable_dynamic_debug)
|
||||
if hook_conf.is_print_input_args then
|
||||
if enable_by_hook or enable_by_header_filter then
|
||||
log[log_level]("call require(\"", file_path, "\").", fun_name,
|
||||
"() args:", inspect(arg))
|
||||
end
|
||||
end
|
||||
|
||||
local ret = {self.fun_org(...)}
|
||||
if hook_conf.is_print_return_value then
|
||||
if enable_by_hook or enable_by_header_filter then
|
||||
log[log_level]("call require(\"", file_path, "\").", fun_name,
|
||||
"() return:", inspect(ret))
|
||||
end
|
||||
end
|
||||
return unpack(ret)
|
||||
end
|
||||
|
||||
setmetatable(t, mt)
|
||||
enabled_hooks[t] = {
|
||||
org = fun_org, new = t, mod = module,
|
||||
fun_name = fun_name
|
||||
}
|
||||
module[fun_name] = t
|
||||
end
|
||||
|
||||
|
||||
function sync_debug_hooks()
|
||||
if not debug_yaml_ctime or debug_yaml_ctime == pre_mtime then
|
||||
return
|
||||
end
|
||||
|
||||
for _, hook in pairs(enabled_hooks) do
|
||||
local m = hook.mod
|
||||
local name = hook.fun_name
|
||||
m[name] = hook.org
|
||||
end
|
||||
|
||||
enabled_hooks = {}
|
||||
|
||||
local hook_conf = debug_yaml.hook_conf
|
||||
if not hook_conf.enable then
|
||||
pre_mtime = debug_yaml_ctime
|
||||
return
|
||||
end
|
||||
|
||||
local hook_name = hook_conf.name or ""
|
||||
local hooks = debug_yaml[hook_name]
|
||||
if not hooks then
|
||||
pre_mtime = debug_yaml_ctime
|
||||
return
|
||||
end
|
||||
|
||||
for file_path, fun_names in pairs(hooks) do
|
||||
local ok, module = pcall(require, file_path)
|
||||
if not ok then
|
||||
log.error("failed to load module [", file_path, "]: ", module)
|
||||
|
||||
else
|
||||
for _, fun_name in ipairs(fun_names) do
|
||||
apply_new_fun(module, fun_name, file_path, hook_conf)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pre_mtime = debug_yaml_ctime
|
||||
end
|
||||
|
||||
end --do
|
||||
|
||||
|
||||
local function sync_debug_status(premature)
|
||||
if premature then
|
||||
return
|
||||
end
|
||||
|
||||
if not read_debug_yaml() then
|
||||
return
|
||||
end
|
||||
|
||||
sync_debug_hooks()
|
||||
end
|
||||
|
||||
|
||||
local function check()
|
||||
if not debug_yaml or not debug_yaml.http_filter then
|
||||
return false
|
||||
end
|
||||
|
||||
local http_filter = debug_yaml.http_filter
|
||||
if not http_filter or not http_filter.enable_header_name or not http_filter.enable then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function _M.dynamic_debug(api_ctx)
|
||||
if not check() then
|
||||
return
|
||||
end
|
||||
|
||||
if get_headers()[debug_yaml.http_filter.enable_header_name] then
|
||||
api_ctx.enable_dynamic_debug = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function _M.enable_debug()
|
||||
if not debug_yaml or not debug_yaml.basic then
|
||||
return false
|
||||
end
|
||||
|
||||
return debug_yaml.basic.enable
|
||||
end
|
||||
|
||||
|
||||
function _M.init_worker()
|
||||
local process = require("ngx.process")
|
||||
if process.type() ~= "worker" then
|
||||
return
|
||||
end
|
||||
|
||||
sync_debug_status()
|
||||
ngx.timer.every(1, sync_debug_status)
|
||||
end
|
||||
|
||||
|
||||
return _M
|
Reference in New Issue
Block a user