Refactor observability configuration and support files

- Introduced `filterCertificateSupportFiles` function to filter support files for certificates in `agent.go`.
- Updated placeholder constants in `config_version.go` to reflect changes from support directory to certificate directory.
- Modified tests in `https_phase1_test.go` to align with new directory structure and removed obsolete checks for observability Lua scripts.
- Removed observability assets from `openresty_observability_assets.go` and created a new file `observability_assets.go` in `atsf_agent/internal/nginx` to manage observability Lua scripts.
- Updated documentation to reflect changes in configuration paths for certificates and Lua scripts.
- Ensured that the agent writes to the new `cert_dir` and `lua_dir` instead of the old `support_dir`.
This commit is contained in:
ryan
2026-03-15 12:32:24 +08:00
parent e1efbf3868
commit f4d36be2e6
13 changed files with 530 additions and 369 deletions
+16
View File
@@ -191,6 +191,7 @@ func GetActiveConfigForAgent() (*AgentConfigResponse, error) {
return nil, err
}
}
supportFiles = filterCertificateSupportFiles(supportFiles)
slog.Debug("agent fetched active config", "version", version.Version, "checksum", version.Checksum)
return &AgentConfigResponse{
Version: version.Version,
@@ -203,6 +204,21 @@ func GetActiveConfigForAgent() (*AgentConfigResponse, error) {
}, nil
}
func filterCertificateSupportFiles(files []SupportFile) []SupportFile {
if len(files) == 0 {
return nil
}
filtered := make([]SupportFile, 0, len(files))
for _, file := range files {
path := strings.ToLower(strings.TrimSpace(file.Path))
switch {
case strings.HasSuffix(path, ".crt"), strings.HasSuffix(path, ".key"), strings.HasSuffix(path, ".pem"):
filtered = append(filtered, file)
}
}
return filtered
}
func ReportApplyLog(payload ApplyLogPayload) (*model.ApplyLog, error) {
now := time.Now()
payload.NodeID = strings.TrimSpace(payload.NodeID)
+3 -4
View File
@@ -115,7 +115,7 @@ type configBundle struct {
}
const (
nginxSupportDirPlaceholder = "__ATSF_SUPPORT_DIR__"
nginxCertDirPlaceholder = "__ATSF_CERT_DIR__"
nginxRouteConfigPlaceholder = "__ATSF_ROUTE_CONFIG__"
nginxAccessLogPlaceholder = "__ATSF_ACCESS_LOG__"
nginxLuaDirPlaceholder = "__ATSF_LUA_DIR__"
@@ -354,7 +354,6 @@ func buildCurrentConfigBundle(requireRoutes bool) (*configBundle, error) {
if err != nil {
return nil, err
}
supportFiles = append(supportFiles, buildOpenRestyObservabilitySupportFiles()...)
mainConfig := renderMainConfig(openRestyConfig)
return &configBundle{
Routes: routes,
@@ -752,8 +751,8 @@ func renderHTTPRedirectServer(domain string) string {
}
func renderHTTPSServer(domain string, originURL string, certificateID uint, customHeaders []ProxyRouteCustomHeaderInput) string {
certPath := fmt.Sprintf("%s/%s", nginxSupportDirPlaceholder, certificateCertFileName(certificateID))
keyPath := fmt.Sprintf("%s/%s", nginxSupportDirPlaceholder, certificateKeyFileName(certificateID))
certPath := fmt.Sprintf("%s/%s", nginxCertDirPlaceholder, certificateCertFileName(certificateID))
keyPath := fmt.Sprintf("%s/%s", nginxCertDirPlaceholder, certificateKeyFileName(certificateID))
return fmt.Sprintf("server {\n listen 443 ssl;\n server_name %s;\n ssl_certificate %s;\n ssl_certificate_key %s;\n\n location / {\n%s proxy_pass %s;\n }\n}\n\n", domain, certPath, keyPath, renderProxyHeaderBlock(customHeaders), originURL)
}
+4 -13
View File
@@ -57,7 +57,7 @@ func TestCreateTLSCertificateAndRenderHTTPSConfig(t *testing.T) {
if !strings.Contains(result.Version.MainConfig, "access_log __ATSF_ACCESS_LOG__ atsflare_json;") {
t.Fatal("expected main config to include managed access log placeholder")
}
if !strings.Contains(result.Version.MainConfig, "log_by_lua_file __ATSF_LUA_DIR__/observability/log.lua;") {
if !strings.Contains(result.Version.MainConfig, "log_by_lua_file __ATSF_LUA_DIR__/log.lua;") {
t.Fatal("expected main config to include managed openresty lua log hook")
}
if !strings.Contains(result.Version.MainConfig, "listen __ATSF_OBSERVABILITY_LISTEN__;") {
@@ -72,21 +72,12 @@ func TestCreateTLSCertificateAndRenderHTTPSConfig(t *testing.T) {
if !strings.Contains(result.Version.RenderedConfig, "return 301 https://$host$request_uri;") {
t.Fatal("expected rendered config to include http redirect")
}
if !strings.Contains(result.Version.RenderedConfig, "__ATSF_SUPPORT_DIR__/") {
t.Fatal("expected rendered config to keep support dir placeholder for certificates")
if !strings.Contains(result.Version.RenderedConfig, "__ATSF_CERT_DIR__/") {
t.Fatal("expected rendered config to keep cert dir placeholder for certificates")
}
if !strings.Contains(result.Version.SupportFilesJSON, ".crt") || !strings.Contains(result.Version.SupportFilesJSON, ".key") {
t.Fatal("expected support files to contain certificate and key")
}
if !strings.Contains(result.Version.SupportFilesJSON, "observability/log.lua") || !strings.Contains(result.Version.SupportFilesJSON, "observability/read.lua") {
t.Fatal("expected support files to contain managed openresty observability lua scripts")
}
if !strings.Contains(result.Version.SupportFilesJSON, "/atsflare/observability") || !strings.Contains(result.Version.SupportFilesJSON, "/atsflare/stub_status") {
t.Fatal("expected observability log lua to skip self-observability requests")
}
if !strings.Contains(result.Version.SupportFilesJSON, "window_start = now - (now % window_size)") || !strings.Contains(result.Version.SupportFilesJSON, "local window_size = 60") {
t.Fatal("expected support files to use fixed 60-second observability windows")
}
}
func TestCreateProxyRouteRejectsHTTPSWithoutCertificate(t *testing.T) {
@@ -205,7 +196,7 @@ func TestPreviewAndDiffConfigVersion(t *testing.T) {
if !strings.Contains(preview.MainConfig, "include __ATSF_ROUTE_CONFIG__;") {
t.Fatal("expected preview main config to include managed route config placeholder")
}
if !strings.Contains(preview.MainConfig, "log_by_lua_file __ATSF_LUA_DIR__/observability/log.lua;") {
if !strings.Contains(preview.MainConfig, "log_by_lua_file __ATSF_LUA_DIR__/log.lua;") {
t.Fatal("expected preview main config to include managed openresty lua log hook")
}
if !strings.Contains(preview.RenderedConfig, `proxy_set_header X-Release "candidate";`) {
@@ -3,161 +3,11 @@ package service
import "fmt"
const (
openRestyObservabilitySupportDir = "observability"
openRestyObservabilityInitLuaPath = openRestyObservabilitySupportDir + "/init.lua"
openRestyObservabilityLogLuaPath = openRestyObservabilitySupportDir + "/log.lua"
openRestyObservabilityReadLuaPath = openRestyObservabilitySupportDir + "/read.lua"
openRestyObservabilityWindowTTL = 7200
openRestyObservabilityWindowSize = 60
openRestyObservabilityInitLuaPath = "init.lua"
openRestyObservabilityLogLuaPath = "log.lua"
openRestyObservabilityReadLuaPath = "read.lua"
)
const openRestyObservabilityInitLua = `local dict = ngx.shared.atsflare_observability
if not dict then
return
end
return
`
const openRestyObservabilityLogLua = `local dict = ngx.shared.atsflare_observability
if not dict then
return
end
local request_uri = tostring(ngx.var.uri or "")
if request_uri == "/atsflare/observability" or request_uri == "/atsflare/stub_status" then
return
end
local ttl = ` + "7200" + `
local now = ngx.time()
local window_size = ` + "60" + `
local window_start = now - (now % window_size)
local function ensure_counter(key)
dict:add(key, 0, ttl)
end
local function incr(key, delta)
ensure_counter(key)
local value, err = dict:incr(key, delta)
if not value and err == "not found" then
dict:set(key, delta, ttl)
end
end
local function remember_value(list_key, marker_key, value)
if value == "" then
return
end
if not dict:add(marker_key, 1, ttl) then
return
end
local existing = dict:get(list_key)
if not existing or existing == "" then
dict:set(list_key, value, ttl)
return
end
dict:set(list_key, existing .. "\n" .. value, ttl)
end
local window_prefix = tostring(window_start)
incr("request_count:" .. window_prefix, 1)
local status = tostring(ngx.status or 0)
if status ~= "0" then
incr("status:" .. window_prefix .. ":" .. status, 1)
remember_value(
"status_keys:" .. window_prefix,
"status_marker:" .. window_prefix .. ":" .. status,
status
)
if tonumber(status) and tonumber(status) >= 500 then
incr("error_count:" .. window_prefix, 1)
end
end
local host = tostring(ngx.var.host or "")
if host ~= "" then
incr("domain:" .. window_prefix .. ":" .. host, 1)
remember_value(
"domain_keys:" .. window_prefix,
"domain_marker:" .. window_prefix .. ":" .. host,
host
)
end
local remote_addr = tostring(ngx.var.binary_remote_addr or ngx.var.remote_addr or "")
if remote_addr ~= "" and dict:add("visitor:" .. window_prefix .. ":" .. remote_addr, 1, ttl) then
incr("unique_visitor_count:" .. window_prefix, 1)
end
local request_length = tonumber(ngx.var.request_length) or 0
if request_length > 0 then
incr("openresty_rx_bytes:" .. window_prefix, request_length)
end
local bytes_sent = tonumber(ngx.var.bytes_sent) or tonumber(ngx.var.body_bytes_sent) or 0
if bytes_sent > 0 then
incr("openresty_tx_bytes:" .. window_prefix, bytes_sent)
end
`
const openRestyObservabilityReadLua = `local cjson = require "cjson.safe"
local dict = ngx.shared.atsflare_observability
if not dict then
ngx.status = ngx.HTTP_SERVICE_UNAVAILABLE
ngx.say(cjson.encode({ message = "shared dict unavailable" }))
return
end
local now = ngx.time()
local window_size = ` + "60" + `
local window_start = now - (now % window_size)
local current_window = tostring(window_start)
local function read_counter(key)
return tonumber(dict:get(key) or 0) or 0
end
local function read_map(window_id, prefix, list_key)
local result = {}
local raw = dict:get(list_key .. ":" .. window_id)
if not raw or raw == "" then
return result
end
for value in string.gmatch(raw, "[^\n]+") do
result[value] = read_counter(prefix .. ":" .. window_id .. ":" .. value)
end
return result
end
local payload = {
window_started_at_unix = window_start,
window_ended_at_unix = now,
request_count = read_counter("request_count:" .. current_window),
error_count = read_counter("error_count:" .. current_window),
unique_visitor_count = read_counter("unique_visitor_count:" .. current_window),
status_codes = read_map(current_window, "status", "status_keys"),
top_domains = read_map(current_window, "domain", "domain_keys"),
source_countries = {},
openresty_rx_bytes = read_counter("openresty_rx_bytes:" .. current_window),
openresty_tx_bytes = read_counter("openresty_tx_bytes:" .. current_window)
}
ngx.header.content_type = "application/json"
ngx.say(cjson.encode(payload))
`
func buildOpenRestyObservabilitySupportFiles() []SupportFile {
return []SupportFile{
{Path: openRestyObservabilityInitLuaPath, Content: openRestyObservabilityInitLua},
{Path: openRestyObservabilityLogLuaPath, Content: openRestyObservabilityLogLua},
{Path: openRestyObservabilityReadLuaPath, Content: openRestyObservabilityReadLua},
}
}
func renderOpenRestyObservabilityTemplateBlock() string {
return stringsJoinLines(
" lua_shared_dict atsflare_observability 10m;",