feat(report): add correction details to json report

- fix correction count 
- fix provider and registrar name to report 
- disable HTMLEscape when writing the report
- update docs
This commit is contained in:
Kevin Neufeld 2025-11-06 10:31:33 -08:00
parent 71c2cc24ca
commit 6ea71d50f7
3 changed files with 70 additions and 20 deletions

View file

@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"regexp"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -300,7 +301,7 @@ func prun(args PPreviewArgs, push bool, interactive bool, out printer.CLI, repor
if !skip { if !skip {
totalCorrections += len(corrections) totalCorrections += len(corrections)
out.EndProvider2(provider.Name, len(corrections)) out.EndProvider2(provider.Name, len(corrections))
reportItems = append(reportItems, genReportItem(zone.Name, corrections, provider.Name)) reportItems = append(reportItems, genReportItem(zone.Name, corrections, provider.Name, ""))
anyErrors = cmp.Or(anyErrors, pprintOrRunCorrections(zone.Name, provider.Name, corrections, out, push || args.PopulateOnPreview, interactive, notifier, report)) anyErrors = cmp.Or(anyErrors, pprintOrRunCorrections(zone.Name, provider.Name, corrections, out, push || args.PopulateOnPreview, interactive, notifier, report))
} }
} }
@ -367,7 +368,9 @@ func prun(args PPreviewArgs, push bool, interactive bool, out printer.CLI, repor
numActions := zone.GetChangeCount(provider.Name) numActions := zone.GetChangeCount(provider.Name)
totalCorrections += numActions totalCorrections += numActions
out.EndProvider2(provider.Name, numActions) out.EndProvider2(provider.Name, numActions)
reportItems = append(reportItems, genReportItem(zone.Name, corrections, provider.Name)) numCorrections := len(corrections)
out.Printf("numActions: %d \nnumCorrections: %d\n", totalCorrections, numCorrections)
reportItems = append(reportItems, genReportItem(zone.Name, corrections, provider.Name, ""))
anyErrors = cmp.Or(anyErrors, pprintOrRunCorrections(zone.Name, provider.Name, corrections, out, push, interactive, notifier, report)) anyErrors = cmp.Or(anyErrors, pprintOrRunCorrections(zone.Name, provider.Name, corrections, out, push, interactive, notifier, report))
} }
} }
@ -380,7 +383,7 @@ func prun(args PPreviewArgs, push bool, interactive bool, out printer.CLI, repor
numActions := zone.GetChangeCount(zone.RegistrarInstance.Name) numActions := zone.GetChangeCount(zone.RegistrarInstance.Name)
out.EndProvider2(zone.RegistrarName, numActions) out.EndProvider2(zone.RegistrarName, numActions)
totalCorrections += numActions totalCorrections += numActions
reportItems = append(reportItems, genReportItem(zone.Name, corrections, zone.RegistrarName)) reportItems = append(reportItems, genReportItem(zone.Name, corrections, "", zone.RegistrarName))
anyErrors = cmp.Or(anyErrors, pprintOrRunCorrections(zone.Name, zone.RegistrarInstance.Name, corrections, out, push, interactive, notifier, report)) anyErrors = cmp.Or(anyErrors, pprintOrRunCorrections(zone.Name, zone.RegistrarInstance.Name, corrections, out, push, interactive, notifier, report))
} }
} }
@ -564,19 +567,44 @@ func skipProvider(name string, providers []*models.DNSProviderInstance) bool {
}) })
} }
func genReportItem(zname string, corrections []*models.Correction, pname string) *ReportItem { func parseCorrectionMsg(s string) []string {
ansiRe := regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`)
s = ansiRe.ReplaceAllString(s, "")
corrections := strings.Split(s, "\n")
// Clean up the slice, precaution remove any empty entries.
clean := make([]string, 0, len(corrections))
for _, l := range corrections {
l = strings.TrimSpace(l)
if l != "" {
clean = append(clean, l)
}
}
return clean
}
func genReportItem(zoneName string, corrections []*models.Correction, providerName string, registrarName string) *ReportItem {
// Only count the actions, not the messages. // Only count the actions, not the messages.
// From my testing corrections is always just contains 1 connection, and Msg is concat of all the changes
cnt := 0 cnt := 0
correctionDetails := make([]string, 0)
for _, cor := range corrections { for _, cor := range corrections {
if cor.F != nil { if cor.F != nil {
cnt++ cnt++ // This seems to always be 1, so we will get the number of corrections from len(correctionDetails)
correctionDetails = append(correctionDetails, parseCorrectionMsg(cor.Msg)...)
} }
} }
r := ReportItem{ r := ReportItem{
Domain: zname, Domain: zoneName,
Corrections: cnt, Corrections: len(correctionDetails),
Provider: pname, CorrectionDetails: correctionDetails,
}
if providerName != "" {
r.Provider = providerName
}
if registrarName != "" {
r.Registrar = registrarName
} }
return &r return &r
} }
@ -635,11 +663,13 @@ func writeReport(report string, reportItems []*ReportItem) error {
return err return err
} }
defer f.Close() defer f.Close()
b, err := json.MarshalIndent(reportItems, "", " ")
if err != nil { // Disabling HTML encoding
return err enc := json.NewEncoder(f)
} enc.SetIndent("", " ")
if _, err := f.Write(b); err != nil { enc.SetEscapeHTML(false)
if err := enc.Encode(reportItems); err != nil {
return err return err
} }
return nil return nil

View file

@ -14,6 +14,7 @@ import (
type ReportItem struct { type ReportItem struct {
Domain string `json:"domain"` Domain string `json:"domain"`
Corrections int `json:"corrections"` Corrections int `json:"corrections"`
CorrectionDetails []string `json:"correction_details,omitempty"`
Provider string `json:"provider,omitempty"` Provider string `json:"provider,omitempty"`
Registrar string `json:"registrar,omitempty"` Registrar string `json:"registrar,omitempty"`
} }

View file

@ -2,14 +2,14 @@
DNSControl can generate a machine-parseable report of changes. DNSControl can generate a machine-parseable report of changes.
The report is JSON formatted and contains the zonename, the provider or The report is JSON-formatted and contains the zonename, the provider or
registrar name, and the number of changes. registrar name, the number of changes (corrections), and the correction details.
All values are in text, values may contain `<`,`>` and `&` escape as needed.
To generate the report, add the `--report <filename>` option to a preview or To generate the report, add the `--report <filename>` option to a preview or
push command (this includes `preview`, `ppreview`, `push`, push command (this includes `preview`, `ppreview`, `push`,
`ppush`). `ppush`).
The report lists the changes that would be (preview) or are (push) attempted, The report lists the changes that would be (preview) or are (push) attempted,
whether they are successful or not. whether they are successful or not.
@ -23,6 +23,18 @@ If a fatal error happens during the run, no report is generated.
{ {
"domain": "private.example.com", "domain": "private.example.com",
"corrections": 10, "corrections": 10,
"correction_details": [
"± MODIFY private.example.com A (1.1.1.1 ttl=60) -> (1.1.1.6 ttl=300)",
"+ CREATE private.example.com A 1.1.1.7 ttl=300",
"± MODIFY-TTL private.example.com TXT \"v=spf1 include:spf.protection.outlook.com -all\" ttl=(60->300)",
"+ CREATE private.example.com TXT \"v=DKIM1; k=rsa; p=xxxx....xxx\" ttl=300",
"+ CREATE private.example.com MX 0 private-example-com.mail.protection.outlook.com. ttl=300",
"+ CREATE *.private.example.com A 1.1.1.6 ttl=300",
"+ CREATE *.private.example.com A 1.1.1.7 ttl=300",
"+ CREATE ns101.private.example.com A 1.1.1.1 ttl=300",
"+ CREATE ns102.private.example.com A 1.0.0.2 ttl=300",
"- DELETE out-of-band.private.example.com TXT \"This out-of-band TXT record should be removed.\" ttl=300"
],
"provider": "bind" "provider": "bind"
}, },
{ {
@ -33,6 +45,13 @@ If a fatal error happens during the run, no report is generated.
{ {
"domain": "admin.example.com", "domain": "admin.example.com",
"corrections": 5, "corrections": 5,
"correction_details": [
"± MODIFY admin.example.com A (1.1.1.1 ttl=60) -> (1.1.1.6 ttl=300)",
"+ CREATE admin.example.com A 1.1.1.7 ttl=300",
"± MODIFY-TTL admin.example.com TXT \"v=spf1 include:spf.protection.outlook.com -all\" ttl=(60->300)",
"+ CREATE admin.example.com TXT \"v=DKIM1; k=rsa; p=xxxx....xxx\" ttl=300",
"- DELETE out-of-band.admin.example.com TXT \"This out-of-band TXT record should be removed.\" ttl=300"
],
"provider": "bind" "provider": "bind"
}, },
{ {