Загрузка данных
dmitriev-aal@VDI-Dmitriev-A:~$ kubectl --context devstbl -n isys-sinv-int-dev \
> get cm sinv-int-dev-envoy-filters -o yaml
apiVersion: v1
data:
lua__appfarm__platform__filters__envoy-platform-lua-filter__envoy-platform-lua-filter-0.3.7.lua: |
local forwarder_host = os.getenv("HTTP_LOG_FORWARDER_HOST")
local forwarder_port = os.getenv("HTTP_LOG_FORWARDER_PORT")
local pod_name = os.getenv("POD_NAME")
local namespace_name = os.getenv("POD_NAMESPACE")
local target_service_name = os.getenv("PSVC_NAME")
local target_system_name = os.getenv("ISYS_NAME")
local SOURCE_SYSTEM_HEADER_NAME = "x-appfarm-source-system"
local SOURCE_SERVICE_HEADER_NAME = "x-appfarm-source-service"
local request_id = ""
local method = ""
local path = ""
local content_types_for_log = {
"application/json",
"application/xml",
"application/x-www-form-urlencoded",
"message/http",
"text/cmd",
"text/plain",
"text/xml",
"text/markdown"
}
-- Инициализация FFI один раз при загрузке
local ok_ffi, ffi = pcall(require, "ffi")
local CLOCK_REALTIME = 0
local ts = nil
-- Если FFI недоступен, логируем предупреждение и используем запасной вариант формирования таймстампа без миллисекунд
if not ok_ffi then
local ns = namespace_name or "unknown-namespace"
local pod = pod_name or "unknown-pod"
print("[appfarm][lua-filter][" .. ns .. "/" .. pod .. "] Модуль LuaJIT FFI недоступен (Таймстамп будет формироваться без миллисекунд.")
else
ffi.cdef[[
typedef long time_t;
struct timespec { time_t tv_sec; long tv_nsec; };
int clock_gettime(int clk_id, struct timespec *tp);
]]
ts = ffi.new("struct timespec")
end
function envoy_on_request(request_handle)
handle_interaction(request_handle, "request")
end
function envoy_on_response(response_handle)
handle_interaction(response_handle, "response")
end
function handle_interaction(handle, direction)
-- Превращаем кастомную структуру, полученную из функции в мапу, в которой все заголовки окажутся уже в нижнем регистре сами по себе
-- Это необходимо, т.к. кастомная структура из функции превращается в nil, если её заюзать внутри цикла "for chunk in handle:bodyChunks() do"
-- Причём, это особенность именно Энвоя, а не Луа
local headers = headers_to_map(handle:headers())
local is_GRPC = is_GRPC(headers)
-- Сохраняем метаданные из запроса, чтобы использовать их при логгировании ответа
save_metadata(headers)
if not (need_log_body(headers) or is_GRPC) then
send_log(handle, direction, headers, "", false)
return
end
if is_GRPC then
handle_grpc(handle, direction, headers)
return
end
local request_body = ""
for chunk in handle:bodyChunks() do
if (chunk:length() > 0) then
request_body = request_body .. chunk:getBytes(0, chunk:length())
end
end
if (request_body:len() > 0) then
send_log(handle, direction, headers, request_body, false)
end
end
function handle_grpc(handle, direction, headers)
local request_body = ""
local message_remainder = ""
for chunk in handle:bodyChunks() do
if (chunk:length() > 0) then
request_body = request_body .. chunk:getBytes(0, chunk:length())
-- Если в одном дата-фрейме HTTP/2 будет находиться конец одного сообщения и начало следующего,
-- оставляем кусочек нового сообщения для следующей итерации
if message_remainder:len() > 0 then
request_body = message_remainder .. request_body
message_remainder = ""
end
-- В gRPC длина сообщения записана в 2-5 байтах
-- chunk:getBytes по какой-то причине возвращает строку, поэтому надо сконвертить её в массив байт и превратить в число
local messageSize = string_bytes_to_number(request_body:sub(2, 5)) + 5
if request_body:len() >= messageSize then
-- Если мы уже прочитали больше длины сообщения, логаем его и записываем лишние данные в остаток,
-- если они есть
send_log(handle, direction, headers, request_body:sub(1, messageSize), true)
message_remainder = request_body:sub(messageSize + 1, request_body:len())
request_body = ""
end
end
end
if (request_body:len() > 0) then
send_log(handle, direction, headers, request_body, true)
end
end
function save_metadata(headers)
for key, value in pairs(headers) do
local sanitized_key = string.gsub(key, ":", "")
-- Save data from request
if sanitized_key == "x-request-id" then
request_id = value
end
if sanitized_key == "method" then
method = value
end
if sanitized_key == "path" then
path = value
end
end
end
function prepare_request(direction, headers)
local prepared_headers = {}
local url = "outbound|" .. forwarder_port .. "||" .. forwarder_host
for key, value in pairs(headers) do
local sanitized_key = string.lower(key)
sanitized_key = string.gsub(sanitized_key, ":", "")
prepared_headers["x-original-" .. sanitized_key] = value
if direction == "response" then
put_if_not_empty(prepared_headers, "x-original-x-request-id", request_id)
put_if_not_empty(prepared_headers, "x-original-method", method)
put_if_not_empty(prepared_headers, "x-original-path", path)
end
end
put_if_not_empty(prepared_headers, "x-appfarm-target-service", target_service_name)
put_if_not_empty(prepared_headers, "x-appfarm-target-system", target_system_name)
put_if_not_empty(prepared_headers, SOURCE_SERVICE_HEADER_NAME, headers[SOURCE_SERVICE_HEADER_NAME])
put_if_not_empty(prepared_headers, SOURCE_SYSTEM_HEADER_NAME, headers[SOURCE_SYSTEM_HEADER_NAME])
prepared_headers[":method"] = "POST"
prepared_headers["x-original-timestamp"] = timestamp_rfc3339_ms()
prepared_headers[":path"] = "/"
prepared_headers["accept"] = "*/*"
prepared_headers["x-namespace-name"] = namespace_name
prepared_headers["x-pod-name"] = pod_name
prepared_headers["x-original-direction"] = direction
prepared_headers["content-type"] = "application/json"
prepared_headers[":authority"] = forwarder_host
return prepared_headers, url
end
function put_if_not_empty(map, key, value)
if value == nil or value:len() == 0 then
return
end
map[key] = value
end
function send_log(handle, direction, headers, body, do_hex_encode)
local prepared_headers, url = prepare_request(direction, headers)
local prepared_body = body
if do_hex_encode then
prepared_body = encode_to_hex(prepared_body)
end
local _, _ = handle:httpCall(
url,
prepared_headers,
prepared_body,
5000,
true
)
end
function need_log_body(headers)
if is_empty_source(headers) or is_inter_system_communication(headers) then
return false
end
local content_type = headers["content-type"]
for _, content_type_for_log in pairs(content_types_for_log) do
if content_type == content_type_for_log then
return true
end
end
return false
end
function is_empty_source(headers)
local source_system_name = headers[SOURCE_SYSTEM_HEADER_NAME]
if source_system_name == nil or source_system_name == "" then
return true
end
local source_service_name = headers[SOURCE_SERVICE_HEADER_NAME]
if source_service_name == nil or source_service_name == "" then
return true
end
end
function is_inter_system_communication(headers)
local source_system_name = headers[SOURCE_SYSTEM_HEADER_NAME]
return target_system_name == source_system_name
end
-- application/grpc+json пока не поддерживаем, т.к. это редкость
-- если понадобится поддержать, надо будет тело логать не в хекс виде, а текстовом
function is_GRPC(headers)
local content_type = headers["content-type"]
return content_type == "application/grpc" or content_type == "application/grpc+proto"
end
function headers_to_map(headers)
local headers_map = {}
for k, v in pairs(headers) do
headers_map[k] = v
end
return headers_map
end
function encode_to_hex(str)
return (str:gsub(".", function(char)
return string.format("%02x", char:byte())
end))
end
function string_bytes_to_number(s)
local number = 0
local length = s:len()
for i = 1, length do
local c = s:byte(i)
-- bit - либа для битовых операций в LuaJIT, который юзает Энвой
-- В самом Lua сейчас уже есть битовые операции, но в LuaJIT только через вызов либы
number = bit.lshift(number, 8) + c
end
return number
end
function timestamp_rfc3339_ms()
-- Основной путь: FFI clock_gettime(CLOCK_REALTIME)
if ok_ffi and ts ~= nil and ffi.C.clock_gettime(CLOCK_REALTIME, ts) == 0 then
local s = tonumber(ts.tv_sec)
local ms = math.floor(tonumber(ts.tv_nsec) / 1e6 + 0.5)
if ms >= 1000 then s = s + 1; ms = 0 end
-- Формируем UTC-метку времени (RFC3339) с миллисекундами
return os.date("!%Y-%m-%dT%H:%M:%S", s) .. string.format(".%03dZ", ms)
end
-- Запасной вариант: без миллисекунд (на случай, если FFI вдруг недоступен)
return os.date("!%Y-%m-%dT%H:%M:%SZ")
end
lua__rshbintech__integrations__ckpr__platform__filters__envoy-platform-lua-filter-out__envoy-platform-lua-filter-out-0.0.2.lua: |-
local service_name = os.getenv("PSVC_NAME")
local system_name = os.getenv("ISYS_NAME")
function envoy_on_request(request_handle)
add_system_info(request_handle)
end
function add_system_info(request_handle)
add_header_if_not_empty(request_handle, "x-appfarm-source-service", service_name)
add_header_if_not_empty(request_handle, "x-appfarm-source-system", system_name)
end
function add_header_if_not_empty(request_handle, key, value)
if value == nil or value:len() == 0 then
return
end
request_handle:headers():add(key, value)
end
kind: ConfigMap
metadata:
creationTimestamp: "2024-07-18T09:44:04Z"
name: sinv-int-dev-envoy-filters
namespace: isys-sinv-int-dev
resourceVersion: "23357390018"
uid: 8a1666e2-efbb-4b68-b32b-34a3aeca91d1
dmitriev-aal@VDI-Dmitriev-A:~$ kubectl --context devstbl -n isys-sinv-int-dev \
> exec deploy/quotes -c istio-proxy -- \
> ls -lah /var/local/lib/wasm-filters
total 20K
drwxrwsrwx 3 root 1001 4.0K Aug 25 07:27 .
drwxr-sr-x 3 root staff 4.0K Aug 25 12:59 ..
drwxr-sr-x 2 root 1001 4.0K Aug 25 07:27 ..2026_08_25_07_27_17.78035742
lrwxrwxrwx 1 root 1001 30 Aug 25 07:27 ..data -> ..2026_08_25_07_27_17.78035742
lrwxrwxrwx 1 root 1001 102 Aug 25 07:27 lua__appfarm__platform__filters__envoy-platform-lua-filter__envoy-platform-lua-filter-0.3.7.lua -> ..data/lua__appfarm__platform__filters__envoy-platform-lua-filter__envoy-platform-lua-filter-0.3.7.lua
lrwxrwxrwx 1 root 1001 133 Aug 25 07:27 lua__rshbintech__integrations__ckpr__platform__filters__envoy-platform-lua-filter-out__envoy-platform-lua-filter-out-0.0.2.lua -> ..data/lua__rshbintech__integrations__ckpr__platform__filters__envoy-platform-lua-filter-out__envoy-platform-lua-filter-out-0.0.2.lua
dmitriev-aal@VDI-Dmitriev-A:~$ kubectl --context devstbl -n isys-sinv-int-dev \
> exec deploy/quotes -c app -- \
> find /secrets -maxdepth 2 -type f -printf '%p\n'
find: unrecognized: -printf
BusyBox v1.37.0 (2025-12-16 14:19:28 UTC) multi-call binary.
Usage: find [-HL] [PATH]... [OPTIONS] [ACTIONS]
Search for files and perform actions on them.
First failed action stops processing of current file.
Defaults: PATH is current directory, action is '-print'
-L,-follow Follow symlinks
-H ...on command line only
-xdev Don't descend directories on other filesystems
-maxdepth N Descend at most N levels. -maxdepth 0 applies
actions to command line arguments only
-mindepth N Don't act on first N levels
-depth Act on directory *after* traversing it
Actions:
( ACTIONS ) Group actions for -o / -a
! ACT Invert ACT's success/failure
ACT1 [-a] ACT2 If ACT1 fails, stop, else do ACT2
ACT1 -o ACT2 If ACT1 succeeds, stop, else do ACT2
Note: -a has higher priority than -o
-name PATTERN Match file name (w/o directory name) to PATTERN
-iname PATTERN Case insensitive -name
-path PATTERN Match path to PATTERN
-ipath PATTERN Case insensitive -path
-regex PATTERN Match path to regex PATTERN
-type X File type is X (one of: f,d,l,b,c,s,p)
-executable File is executable
-perm MASK At least one mask bit (+MASK), all bits (-MASK),
or exactly MASK bits are set in file's mode
-mtime DAYS mtime is greater than (+N), less than (-N),
or exactly N days in the past
-atime DAYS atime +N/-N/N days in the past
-ctime DAYS ctime +N/-N/N days in the past
-mmin MINS mtime is greater than (+N), less than (-N),
or exactly N minutes in the past
-newer FILE mtime is more recent than FILE's
-inum N File has inode number N
-user NAME/ID File is owned by given user
-group NAME/ID File is owned by given group
-size N[bck] File size is N (c:bytes,k:kbytes,b:512 bytes(def.))
+/-N: file size is bigger/smaller than N
-links N Number of links is greater than (+N), less than (-N),
or exactly N
-empty Match empty file/directory
-prune If current file is directory, don't descend into it
If none of the following actions is specified, -print is assumed
-print Print file name
-print0 Print file name, NUL terminated
-exec CMD ARG ; Run CMD with all instances of {} replaced by
file name. Fails if CMD exits with nonzero
-exec CMD ARG + Run CMD with {} replaced by list of file names
-ok CMD ARG ; Prompt and run CMD with {} replaced
-delete Delete current file/directory. Turns on -depth option
-quit Exit
command terminated with exit code 1
dmitriev-aal@VDI-Dmitriev-A:~$