Added checker

This commit is contained in:
Magnus Root 2026-03-23 15:39:07 +03:00
parent aaf105cdfe
commit d62ca6b33f
2 changed files with 145 additions and 0 deletions

View file

@ -47,6 +47,7 @@ func main() {
} }
log.Printf("Loaded template version: %s", template.Version) log.Printf("Loaded template version: %s", template.Version)
validateAndFixTemplate(template)
// Load alert rules template // Load alert rules template
alertTmpl, err := loadAlertTemplate(config.TemplatesPath) alertTmpl, err := loadAlertTemplate(config.TemplatesPath)

144
template_validator.go Normal file
View file

@ -0,0 +1,144 @@
package main
import "log"
// validateAndFixTemplate проверяет критичные настройки шаблона и исправляет их если нужно.
// Вызывается после loadTemplate чтобы гарантировать корректность шаблона независимо от его источника.
func validateAndFixTemplate(t *DashboardTemplate) {
fixed := 0
// --- vl_interval для панелей с агрегацией ---
vlIntervalPanels := []string{"rps", "rps_multi", "status_codes", "response_time", "traffic_combined", "traffic_combined_total"}
for _, key := range vlIntervalPanels {
p, ok := t.Panels[key]
if !ok {
continue
}
pm, ok := p.(map[string]interface{})
if !ok {
continue
}
if v, _ := pm["vl_interval"].(string); v == "" {
pm["vl_interval"] = "1m"
log.Printf("[template-validator] Fixed: panels.%s.vl_interval = \"1m\"", key)
fixed++
}
}
// --- spanNulls для status_codes ---
if p, ok := t.Panels["status_codes"]; ok {
if pm, ok := p.(map[string]interface{}); ok {
if fc, ok := pm["fieldConfig"].(map[string]interface{}); ok {
if defaults, ok := fc["defaults"].(map[string]interface{}); ok {
if custom, ok := defaults["custom"].(map[string]interface{}); ok {
if v, _ := custom["spanNulls"].(bool); !v {
custom["spanNulls"] = true
log.Printf("[template-validator] Fixed: panels.status_codes.fieldConfig.defaults.custom.spanNulls = true")
fixed++
}
}
}
}
}
}
// --- vl_base_query для overview_tenant_per_nodes ---
checkOverviewVLQuery(t, "overview_tenant_per_nodes",
"log_type:access TENANT:in(${tenant:csv})",
"log_type:access AND TENANT:${tenant:lucene}",
&fixed,
)
// --- vl_base_query для overview_tenant_per_node ---
checkOverviewVLQuery(t, "overview_tenant_per_node",
"log_type:access node_name:in(${node_name:csv})",
"log_type:access AND node_name:${node_name:lucene}",
&fixed,
)
// --- layout: три столбца ---
checkLayout(t, &fixed)
if fixed > 0 {
log.Printf("[template-validator] Total fixes applied: %d", fixed)
} else {
log.Printf("[template-validator] Template OK, no fixes needed")
}
}
func checkOverviewVLQuery(t *DashboardTemplate, panelKey, expectedVL, expectedOS string, fixed *int) {
p, ok := t.Panels[panelKey]
if !ok {
return
}
pm, ok := p.(map[string]interface{})
if !ok {
return
}
qc, ok := pm["query_config"].(map[string]interface{})
if !ok {
return
}
if v, _ := qc["vl_base_query"].(string); v == "" {
qc["vl_base_query"] = expectedVL
log.Printf("[template-validator] Fixed: panels.%s.query_config.vl_base_query", panelKey)
(*fixed)++
}
if v, _ := qc["base_query"].(string); v == "log_type:access" {
qc["base_query"] = expectedOS
log.Printf("[template-validator] Fixed: panels.%s.query_config.base_query", panelKey)
(*fixed)++
}
}
func checkLayout(t *DashboardTemplate, fixed *int) {
// Проверяем что tenant_panels имеет три столбца по 8
tenantPanels := t.Layout.TenantPanels
hasTraffic := false
wrongWidth := false
for _, p := range tenantPanels {
if p.PanelKey == "traffic_combined_total" {
hasTraffic = true
}
if p.Width == 12 {
wrongWidth = true
}
}
if !hasTraffic || wrongWidth {
log.Printf("[template-validator] Fixed: layout.tenant_panels restored to 3-column (8+8+8)")
t.Layout.TenantPanels = []PanelLayout{
{PanelKey: "status_codes", TitleFormat: "Status Codes {client}", Width: 8, XOffset: 0},
{PanelKey: "rps", TitleFormat: "RPS {client}", Width: 8, XOffset: 8,
Condition: &PanelCondition{Type: "max_domains", Value: 1}},
{PanelKey: "rps_multi", TitleFormat: "RPS {client}", Width: 8, XOffset: 8,
Condition: &PanelCondition{Type: "min_domains", Value: 2}},
{PanelKey: "traffic_combined_total", TitleFormat: "Total Traffic {client}", Width: 8, XOffset: 16,
Condition: &PanelCondition{Type: "min_domains", Value: 1}},
}
(*fixed)++
}
// Проверяем domain_panels
domainPanels := t.Layout.DomainPanels
hasDomainTraffic := false
domainWrongWidth := false
for _, p := range domainPanels {
if p.PanelKey == "traffic_combined" {
hasDomainTraffic = true
}
if p.Width == 12 {
domainWrongWidth = true
}
}
if !hasDomainTraffic || domainWrongWidth {
log.Printf("[template-validator] Fixed: layout.domain_panels restored to 3-column (8+8+8)")
t.Layout.DomainPanels = []PanelLayout{
{PanelKey: "status_codes", TitleSuffix: "{sid} Status Codes", Width: 8, XOffset: 0},
{PanelKey: "response_time", TitleSuffix: "{sid} Response Time", Width: 8, XOffset: 8},
{PanelKey: "traffic_combined", TitleFormat: "{domain} {sid} Traffic", Width: 8, XOffset: 16},
}
(*fixed)++
}
}