139 lines
3.0 KiB
Go
139 lines
3.0 KiB
Go
package collector
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type CaddySite struct {
|
|
Domain string `json:"domain"`
|
|
Upstream string `json:"upstream"`
|
|
TLS bool `json:"tls"`
|
|
}
|
|
|
|
type CaddyUpstream struct {
|
|
Address string `json:"address"`
|
|
NumRequests int `json:"num_requests"`
|
|
Fails int `json:"fails"`
|
|
Healthy bool `json:"healthy"`
|
|
}
|
|
|
|
type CaddyData struct {
|
|
Version string `json:"version"`
|
|
Sites []CaddySite `json:"sites"`
|
|
Upstreams []CaddyUpstream `json:"upstreams,omitempty"`
|
|
}
|
|
|
|
const caddyFile = "/usr/local/etc/caddy/Caddyfile"
|
|
const caddyAdminAPI = "http://127.0.0.1:2019"
|
|
|
|
func CollectCaddy() *CaddyData {
|
|
if _, err := os.Stat(caddyFile); err != nil {
|
|
return nil
|
|
}
|
|
|
|
data := &CaddyData{}
|
|
|
|
// Version
|
|
if out, err := exec.Command("/usr/local/bin/caddy", "version").Output(); err == nil {
|
|
fields := strings.Fields(strings.TrimSpace(string(out)))
|
|
if len(fields) > 0 {
|
|
data.Version = fields[0]
|
|
}
|
|
}
|
|
|
|
// Parse Caddyfile fuer Sites
|
|
content, err := os.ReadFile(caddyFile)
|
|
if err != nil {
|
|
return data
|
|
}
|
|
|
|
lines := strings.Split(string(content), "\n")
|
|
var currentDomain string
|
|
var currentTLS bool
|
|
depth := 0
|
|
|
|
for _, line := range lines {
|
|
trimmed := strings.TrimSpace(line)
|
|
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
|
|
continue
|
|
}
|
|
|
|
if depth == 0 && strings.Contains(trimmed, "{") {
|
|
parts := strings.Fields(trimmed)
|
|
if len(parts) >= 2 && parts[len(parts)-1] == "{" {
|
|
currentDomain = parts[0]
|
|
currentTLS = !strings.HasPrefix(currentDomain, "http://")
|
|
depth = 1
|
|
continue
|
|
}
|
|
}
|
|
|
|
if depth > 0 {
|
|
depth += strings.Count(trimmed, "{") - strings.Count(trimmed, "}")
|
|
|
|
if strings.HasPrefix(trimmed, "reverse_proxy") {
|
|
parts := strings.Fields(trimmed)
|
|
if len(parts) >= 2 {
|
|
upstream := parts[1]
|
|
data.Sites = append(data.Sites, CaddySite{
|
|
Domain: currentDomain,
|
|
Upstream: upstream,
|
|
TLS: currentTLS,
|
|
})
|
|
}
|
|
}
|
|
|
|
if depth <= 0 {
|
|
currentDomain = ""
|
|
depth = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
// Admin-API: Upstream-Statistiken
|
|
data.Upstreams = collectCaddyUpstreams()
|
|
|
|
return data
|
|
}
|
|
|
|
func collectCaddyUpstreams() []CaddyUpstream {
|
|
client := &http.Client{Timeout: 3 * time.Second}
|
|
resp, err := client.Get(caddyAdminAPI + "/api/reverse_proxy/upstreams")
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(io.LimitReader(resp.Body, 64*1024))
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
// Admin-API liefert: [{"address":"host:port","num_requests":N,"fails":N}]
|
|
var raw []struct {
|
|
Address string `json:"address"`
|
|
NumRequests int `json:"num_requests"`
|
|
Fails int `json:"fails"`
|
|
}
|
|
if err := json.Unmarshal(body, &raw); err != nil {
|
|
return nil
|
|
}
|
|
|
|
upstreams := make([]CaddyUpstream, 0, len(raw))
|
|
for _, u := range raw {
|
|
upstreams = append(upstreams, CaddyUpstream{
|
|
Address: u.Address,
|
|
NumRequests: u.NumRequests,
|
|
Fails: u.Fails,
|
|
Healthy: u.Fails == 0,
|
|
})
|
|
}
|
|
return upstreams
|
|
}
|