NEW FEATURE: Added "push --report" flag which generates JSON report of changes made (#2543)

Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com>
This commit is contained in:
Florian Ritterhoff 2023-09-14 19:15:54 +02:00 committed by GitHub
parent f1fd723c80
commit 1d825be67a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 77 additions and 4 deletions

View file

@ -1,6 +1,7 @@
package commands package commands
import ( import (
"encoding/json"
"fmt" "fmt"
"os" "os"
"strings" "strings"
@ -45,6 +46,13 @@ type PreviewArgs struct {
Full bool 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 { func (args *PreviewArgs) flags() []cli.Flag {
flags := args.GetDNSConfigArgs.flags() flags := args.GetDNSConfigArgs.flags()
flags = append(flags, args.GetCredentialsArgs.flags()...) flags = append(flags, args.GetCredentialsArgs.flags()...)
@ -93,6 +101,7 @@ var _ = cmd(catMain, func() *cli.Command {
type PushArgs struct { type PushArgs struct {
PreviewArgs PreviewArgs
Interactive bool Interactive bool
Report string
} }
func (args *PushArgs) flags() []cli.Flag { func (args *PushArgs) flags() []cli.Flag {
@ -102,21 +111,26 @@ func (args *PushArgs) flags() []cli.Flag {
Destination: &args.Interactive, Destination: &args.Interactive,
Usage: "Interactive. Confirm or Exclude each correction before they run", 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 return flags
} }
// Preview implements the preview subcommand. // Preview implements the preview subcommand.
func Preview(args PreviewArgs) error { 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. // Push implements the push subcommand.
func Push(args PushArgs) error { 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 // 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 // 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. // 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 // create a WaitGroup with the length of domains for the anonymous functions (later goroutines) to wait for
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(len(cfg.Domains)) wg.Add(len(cfg.Domains))
var reportItems []ReportItem
// For each domain in dnsconfig.js... // For each domain in dnsconfig.js...
for _, domain := range cfg.Domains { for _, domain := range cfg.Domains {
// Run preview or push operations per domain as anonymous function, in preparation for the later use of goroutines. // 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) totalCorrections += len(corrections)
// When diff1 goes away, the call to printReports() should be moved to HERE. // When diff1 goes away, the call to printReports() should be moved to HERE.
//printReports(domain.Name, provider.Name, reports, out, push, notifier) //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 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 return
} }
totalCorrections += len(corrections) 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 anyErrors = printOrRunCorrections(domain.Name, domain.RegistrarName, corrections, out, push, interactive, notifier) || anyErrors
}(domain) }(domain)
} }
@ -269,6 +293,20 @@ func run(args PreviewArgs, push bool, interactive bool, out printer.CLI) error {
if totalCorrections != 0 && args.WarnChanges { if totalCorrections != 0 && args.WarnChanges {
return fmt.Errorf("there are pending changes") 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 return nil
} }

View file

@ -154,6 +154,7 @@
* [Nameservers and Delegations](nameservers.md) * [Nameservers and Delegations](nameservers.md)
* [Notifications](notifications.md) * [Notifications](notifications.md)
* [Useful code tricks](code-tricks.md) * [Useful code tricks](code-tricks.md)
* [JSON Reports](json-reports.md)
## Developer info ## Developer info

View file

@ -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 <filename>` 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 %}