diff --git a/commands/getZones.go b/commands/getZones.go index 90ec04383..acf8d0465 100644 --- a/commands/getZones.go +++ b/commands/getZones.go @@ -151,11 +151,11 @@ func GetZone(args GetZoneArgs) error { for i, recs := range zoneRecs { zoneName := zones[i] - z := prettyzone.PrettySort(recs, zoneName, 0) + z := prettyzone.PrettySort(recs, zoneName, 0, nil) switch args.OutputFormat { case "pretty": fmt.Fprintf(w, "$ORIGIN %s.\n", zoneName) - prettyzone.WriteZoneFileRC(w, z.Records, zoneName) + prettyzone.WriteZoneFileRC(w, z.Records, zoneName, nil) fmt.Fprintln(w) case "dsl": diff --git a/docs/_includes/matrix.html b/docs/_includes/matrix.html index 608ae46fd..2a63a1788 100644 --- a/docs/_includes/matrix.html +++ b/docs/_includes/matrix.html @@ -313,7 +313,9 @@ AUTODNSSEC - + + + @@ -981,8 +983,8 @@ - - + + diff --git a/pkg/prettyzone/prettyzone.go b/pkg/prettyzone/prettyzone.go index ba62b8093..f34efc8f6 100644 --- a/pkg/prettyzone/prettyzone.go +++ b/pkg/prettyzone/prettyzone.go @@ -45,11 +45,11 @@ func mostCommonTTL(records models.Records) uint32 { // WriteZoneFileRR is a helper for when you have []dns.RR instead of models.Records func WriteZoneFileRR(w io.Writer, records []dns.RR, origin string, serial uint32) error { - return WriteZoneFileRC(w, models.RRstoRCs(records, origin, serial), origin) + return WriteZoneFileRC(w, models.RRstoRCs(records, origin, serial), origin, nil) } // WriteZoneFileRC writes a beautifully formatted zone file. -func WriteZoneFileRC(w io.Writer, records models.Records, origin string) error { +func WriteZoneFileRC(w io.Writer, records models.Records, origin string, comments []string) error { // This function prioritizes beauty over output size. // * The zone records are sorted by label, grouped by subzones to // be easy to read and pleasant to the eye. @@ -61,18 +61,19 @@ func WriteZoneFileRC(w io.Writer, records models.Records, origin string) error { // * $TTL is used to eliminate clutter. The most common TTL value is used. // * "@" is used instead of the apex domain name. - z := PrettySort(records, origin, mostCommonTTL(records)) + z := PrettySort(records, origin, mostCommonTTL(records), comments) return z.generateZoneFileHelper(w) } -func PrettySort(records models.Records, origin string, defaultTTL uint32) *zoneGenData { +func PrettySort(records models.Records, origin string, defaultTTL uint32, comments []string) *zoneGenData { if defaultTTL == 0 { defaultTTL = mostCommonTTL(records) } z := &zoneGenData{ Origin: origin + ".", DefaultTTL: defaultTTL, + Comments: comments, } z.Records = nil for _, r := range records { @@ -88,6 +89,13 @@ func (z *zoneGenData) generateZoneFileHelper(w io.Writer) error { sort.Sort(z) fmt.Fprintln(w, "$TTL", z.DefaultTTL) + for _, comment := range z.Comments { + for _, line := range strings.Split(comment, "\n") { + if line != "" { + fmt.Fprintln(w, ";", line) + } + } + } for i, rr := range z.Records { // Fake types are commented out. diff --git a/pkg/prettyzone/prettyzone_test.go b/pkg/prettyzone/prettyzone_test.go index 1e576d685..9c2afff63 100644 --- a/pkg/prettyzone/prettyzone_test.go +++ b/pkg/prettyzone/prettyzone_test.go @@ -295,8 +295,12 @@ func TestWriteZoneFileSynth(t *testing.T) { recs = append(recs, rsynz) buf := &bytes.Buffer{} - WriteZoneFileRC(buf, recs, "bosun.org") + WriteZoneFileRC(buf, recs, "bosun.org", []string{"c1", "c2", "c3\nc4"}) expected := `$TTL 300 +; c1 +; c2 +; c3 +; c4 @ IN A 192.30.252.153 IN A 192.30.252.154 ;myalias IN R53_ALIAS type= zone_id= diff --git a/pkg/prettyzone/sorting.go b/pkg/prettyzone/sorting.go index 03a78e420..ca7c88fb9 100644 --- a/pkg/prettyzone/sorting.go +++ b/pkg/prettyzone/sorting.go @@ -16,6 +16,7 @@ type zoneGenData struct { Origin string DefaultTTL uint32 Records models.Records + Comments []string } func (z *zoneGenData) Len() int { return len(z.Records) } diff --git a/providers/bind/bindProvider.go b/providers/bind/bindProvider.go index ebf322aab..80ac71dbb 100644 --- a/providers/bind/bindProvider.go +++ b/providers/bind/bindProvider.go @@ -3,7 +3,7 @@ package bind /* bind - - Generate zonefiles suitiable for BIND. + Generate zonefiles suitable for BIND. The zonefiles are read and written to the directory -bind_dir @@ -21,6 +21,7 @@ import ( "os" "path/filepath" "strings" + "time" "github.com/miekg/dns" @@ -38,6 +39,7 @@ var features = providers.DocumentationNotes{ providers.CanUseSSHFP: providers.Can(), providers.CanUseTLSA: providers.Can(), providers.CanUseTXTMulti: providers.Can(), + providers.CanAutoDNSSEC: providers.Can("Just writes out a comment indicating DNSSEC was requested"), providers.CantUseNOPURGE: providers.Cannot(), providers.DocCreateDomains: providers.Can("Driver just maintains list of zone files. It should automatically add missing ones."), providers.DocDualHost: providers.Can(), @@ -214,10 +216,19 @@ func (c *Bind) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correcti // foundDiffRecords < foundRecords // diff.Inc...(foundDiffRecords, expectedDiffRecords ) + comments := make([]string, 0, 5) + + comments = append(comments, + fmt.Sprintf("generated with dnscontrol %s", time.Now().Format(time.RFC3339)), + ) + foundRecords, err := c.GetZoneRecords(dc.Name) if err != nil { return nil, err } + if dc.AutoDNSSEC { + comments = append(comments, "Automatic DNSSEC signing requested") + } // Normalize models.PostProcessRecords(foundRecords) @@ -262,7 +273,10 @@ func (c *Bind) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correcti if err != nil { log.Fatalf("Could not create zonefile: %v", err) } - err = prettyzone.WriteZoneFileRC(zf, dc.Records, dc.Name) + // Beware that if there are any fake types, then they will + // be commented out on write, but we don't reverse that when + // reading, so there will be a diff on every invocation. + err = prettyzone.WriteZoneFileRC(zf, dc.Records, dc.Name, comments) if err != nil { log.Fatalf("WriteZoneFile error: %v\n", err)