fix(scheduler): Cert-Renewal-Alerts pro Domain statt aggregierter Zählung (v1.1.143)
- certrenewer.Result.FailedDomains []string — jeder fehlgeschlagene Domain-Name wird erfasst (Issue/Parse/Write-Fehler) - runRenewer: pro Domain eigener Alert + eigener Dedupe-Key statt einem shared "cert.renew_failed"-Key; Alert-Message nennt jetzt den Domain-Namen und gibt Hinweis auf ACME/DNS-Debugging Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -60,7 +60,7 @@ import (
|
|||||||
usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users"
|
usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.1.142"
|
var version = "1.1.143"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
addr := os.Getenv("EDGEGUARD_API_ADDR")
|
addr := os.Getenv("EDGEGUARD_API_ADDR")
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/setup"
|
"git.netcell-it.de/projekte/edgeguard-native/internal/services/setup"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.1.142"
|
var version = "1.1.143"
|
||||||
|
|
||||||
const usage = `edgeguard-ctl — EdgeGuard CLI
|
const usage = `edgeguard-ctl — EdgeGuard CLI
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ import (
|
|||||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts"
|
"git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.1.142"
|
var version = "1.1.143"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// renewTickInterval — how often we re-evaluate expiring certs.
|
// renewTickInterval — how often we re-evaluate expiring certs.
|
||||||
@@ -1062,11 +1062,18 @@ func runRenewer(ctx context.Context, r *certrenewer.Service, a *alerts.Service,
|
|||||||
slog.Info("scheduler: renewer pass complete",
|
slog.Info("scheduler: renewer pass complete",
|
||||||
"checked", res.Checked, "renewed", res.Renewed,
|
"checked", res.Checked, "renewed", res.Renewed,
|
||||||
"failed", res.Failed, "skipped", res.Skipped)
|
"failed", res.Failed, "skipped", res.Skipped)
|
||||||
if a != nil && res.Failed > 0 && d != nil && d.shouldFire("cert.renew_failed") {
|
if a != nil && d != nil {
|
||||||
_, _ = a.Fire(ctx, "cert.renew_failed", alerts.SeverityError,
|
for _, domain := range res.FailedDomains {
|
||||||
"Cert-Renewal teilweise fehlgeschlagen",
|
key := "cert.renew_failed:" + domain
|
||||||
fmt.Sprintf("Renewer-Cycle: %d checked, %d renewed, %d failed, %d skipped",
|
if !d.shouldFire(key) {
|
||||||
res.Checked, res.Renewed, res.Failed, res.Skipped))
|
continue
|
||||||
|
}
|
||||||
|
_, _ = a.Fire(ctx, "cert.renew_failed", alerts.SeverityError,
|
||||||
|
"Cert-Renewal fehlgeschlagen: "+domain,
|
||||||
|
"Let's Encrypt Erneuerung für "+domain+" ist fehlgeschlagen. "+
|
||||||
|
"Prüfe ACME-Konfiguration und DNS-Erreichbarkeit. "+
|
||||||
|
"Nächster Versuch beim nächsten Renewer-Tick (alle 6h).")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,12 +62,14 @@ func (s *Service) log() *slog.Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Result counts what one Run pass did so the scheduler can log /
|
// Result counts what one Run pass did so the scheduler can log /
|
||||||
// surface it.
|
// surface it. FailedDomains holds the domain names that failed so
|
||||||
|
// the scheduler can fire per-domain alerts.
|
||||||
type Result struct {
|
type Result struct {
|
||||||
Checked int
|
Checked int
|
||||||
Renewed int
|
Renewed int
|
||||||
Failed int
|
Failed int
|
||||||
Skipped int
|
Skipped int
|
||||||
|
FailedDomains []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run scans tls_certs for LE-issued rows that expire within the
|
// Run scans tls_certs for LE-issued rows that expire within the
|
||||||
@@ -95,6 +97,7 @@ func (s *Service) Run(ctx context.Context) (Result, error) {
|
|||||||
certPEM, chainPEM, keyPEM, err := s.Issuer.Issue(row.Domain)
|
certPEM, chainPEM, keyPEM, err := s.Issuer.Issue(row.Domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Failed++
|
res.Failed++
|
||||||
|
res.FailedDomains = append(res.FailedDomains, row.Domain)
|
||||||
s.log().Error("certrenewer: issue failed", "domain", row.Domain, "error", err)
|
s.log().Error("certrenewer: issue failed", "domain", row.Domain, "error", err)
|
||||||
_ = s.Repo.MarkError(ctx, row.Domain, err.Error())
|
_ = s.Repo.MarkError(ctx, row.Domain, err.Error())
|
||||||
continue
|
continue
|
||||||
@@ -102,12 +105,14 @@ func (s *Service) Run(ctx context.Context) (Result, error) {
|
|||||||
info, err := certstore.Parse(certPEM)
|
info, err := certstore.Parse(certPEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Failed++
|
res.Failed++
|
||||||
|
res.FailedDomains = append(res.FailedDomains, row.Domain)
|
||||||
_ = s.Repo.MarkError(ctx, row.Domain, "parse: "+err.Error())
|
_ = s.Repo.MarkError(ctx, row.Domain, "parse: "+err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
path, err := certstore.WriteCombined(s.CertDir, row.Domain, certPEM, chainPEM, keyPEM)
|
path, err := certstore.WriteCombined(s.CertDir, row.Domain, certPEM, chainPEM, keyPEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Failed++
|
res.Failed++
|
||||||
|
res.FailedDomains = append(res.FailedDomains, row.Domain)
|
||||||
_ = s.Repo.MarkError(ctx, row.Domain, "write: "+err.Error())
|
_ = s.Repo.MarkError(ctx, row.Domain, "write: "+err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user