From 1d825be67a26e5a83405469a1d901daf181f144f Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff <32478819+fritterhoff@users.noreply.github.com> Date: Thu, 14 Sep 2023 19:15:54 +0200 Subject: [PATCH] NEW FEATURE: Added "push --report" flag which generates JSON report of changes made (#2543) Co-authored-by: Tom Limoncelli --- commands/previewPush.go | 46 ++++++++++++++++++++++++++++++++--- documentation/SUMMARY.md | 1 + documentation/json-reports.md | 34 ++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 documentation/json-reports.md diff --git a/commands/previewPush.go b/commands/previewPush.go index c1915deac..70b1d4370 100644 --- a/commands/previewPush.go +++ b/commands/previewPush.go @@ -1,6 +1,7 @@ package commands import ( + "encoding/json" "fmt" "os" "strings" @@ -45,6 +46,13 @@ type PreviewArgs struct { Full bool } +type ReportItem struct { + Domain string `json:"domain"` + Corrections int `json:"corrections"` + Provider string `json:"provider,omitempty"` + Registrar string `json:"registrar,omitempty"` +} + func (args *PreviewArgs) flags() []cli.Flag { flags := args.GetDNSConfigArgs.flags() flags = append(flags, args.GetCredentialsArgs.flags()...) @@ -93,6 +101,7 @@ var _ = cmd(catMain, func() *cli.Command { type PushArgs struct { PreviewArgs Interactive bool + Report string } func (args *PushArgs) flags() []cli.Flag { @@ -102,21 +111,26 @@ func (args *PushArgs) flags() []cli.Flag { Destination: &args.Interactive, Usage: "Interactive. Confirm or Exclude each correction before they run", }) + flags = append(flags, &cli.StringFlag{ + Name: "report", + Destination: &args.Report, + Usage: `Generate a machine-parseable report of performed corrections.`, + }) return flags } // Preview implements the preview subcommand. func Preview(args PreviewArgs) error { - return run(args, false, false, printer.DefaultPrinter) + return run(args, false, false, printer.DefaultPrinter, nil) } // Push implements the push subcommand. func Push(args PushArgs) error { - return run(args.PreviewArgs, true, args.Interactive, printer.DefaultPrinter) + return run(args.PreviewArgs, true, args.Interactive, printer.DefaultPrinter, &args.Report) } // run is the main routine common to preview/push -func run(args PreviewArgs, push bool, interactive bool, out printer.CLI) error { +func run(args PreviewArgs, push bool, interactive bool, out printer.CLI, report *string) error { // TODO: make truly CLI independent. Perhaps return results on a channel as they occur // This is a hack until we have the new printer replacement. @@ -151,7 +165,7 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI) error { // create a WaitGroup with the length of domains for the anonymous functions (later goroutines) to wait for var wg sync.WaitGroup wg.Add(len(cfg.Domains)) - + var reportItems []ReportItem // For each domain in dnsconfig.js... for _, domain := range cfg.Domains { // Run preview or push operations per domain as anonymous function, in preparation for the later use of goroutines. @@ -232,6 +246,11 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI) error { totalCorrections += len(corrections) // When diff1 goes away, the call to printReports() should be moved to HERE. //printReports(domain.Name, provider.Name, reports, out, push, notifier) + reportItems = append(reportItems, ReportItem{ + Domain: domain.Name, + Corrections: len(corrections), + Provider: provider.Name, + }) anyErrors = printOrRunCorrections(domain.Name, provider.Name, corrections, out, push, interactive, notifier) || anyErrors } @@ -253,6 +272,11 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI) error { return } totalCorrections += len(corrections) + reportItems = append(reportItems, ReportItem{ + Domain: domain.Name, + Corrections: len(corrections), + Registrar: domain.RegistrarName, + }) anyErrors = printOrRunCorrections(domain.Name, domain.RegistrarName, corrections, out, push, interactive, notifier) || anyErrors }(domain) } @@ -269,6 +293,20 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI) error { if totalCorrections != 0 && args.WarnChanges { return fmt.Errorf("there are pending changes") } + if report != nil { + f, err := os.OpenFile(*report, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) + if err != nil { + return err + } + defer f.Close() + b, err := json.MarshalIndent(reportItems, "", " ") + if err != nil { + return err + } + if _, err := f.Write(b); err != nil { + return err + } + } return nil } diff --git a/documentation/SUMMARY.md b/documentation/SUMMARY.md index 050ec85c3..3ff6172a7 100644 --- a/documentation/SUMMARY.md +++ b/documentation/SUMMARY.md @@ -154,6 +154,7 @@ * [Nameservers and Delegations](nameservers.md) * [Notifications](notifications.md) * [Useful code tricks](code-tricks.md) +* [JSON Reports](json-reports.md) ## Developer info diff --git a/documentation/json-reports.md b/documentation/json-reports.md new file mode 100644 index 000000000..e6efa7928 --- /dev/null +++ b/documentation/json-reports.md @@ -0,0 +1,34 @@ +# JSON Reports + +DNSControl has build in functionality to generate a machine-parseable report after pushing changes. This report is JSON formated and contains the zonename, the provider or registrar name and the amount of performed changes. + +## Usage + +To enable the report option you must use the `push` operation in combination with the `--report ` option. This generates the json file. + +{% code title="report.json" %} +```json +[ + { + "domain": "private.example.com", + "corrections": 10, + "provider": "bind" + }, + { + "domain": "private.example.com", + "corrections": 0, + "registrar": "none" + }, + { + "domain": "admin.example.com", + "corrections": 5, + "provider": "bind" + }, + { + "domain": "admin.example.com", + "corrections": 0, + "registrar": "none" + } +] +``` +{% endcode %}