diff --git a/providers/rwth/api.go b/providers/rwth/api.go index ccbaa5c0ad..8662b3e549 100644 --- a/providers/rwth/api.go +++ b/providers/rwth/api.go @@ -34,20 +34,50 @@ type RecordReply struct { Editable bool `json:"editable"` } +type dnssecKey struct { + CreatedAt time.Time `json:"created_at"` +} + +// dnssecStatus supports both API variants: +// - dnssec: false +// - dnssec: { ...keys... }. +type dnssecStatus struct { + Enabled bool `json:"-"` + ZoneSigningKey *dnssecKey `json:"zone_signing_key,omitempty"` + KeySigningKey *dnssecKey `json:"key_signing_key,omitempty"` +} + +func (d *dnssecStatus) UnmarshalJSON(data []byte) error { + // RWTH may return either a boolean or an object for the same field. + var enabled bool + if err := json.Unmarshal(data, &enabled); err == nil { + *d = dnssecStatus{Enabled: enabled} + return nil + } + + var payload struct { + ZoneSigningKey *dnssecKey `json:"zone_signing_key"` + KeySigningKey *dnssecKey `json:"key_signing_key"` + } + if err := json.Unmarshal(data, &payload); err != nil { + return fmt.Errorf("invalid dnssec value: %w", err) + } + + *d = dnssecStatus{ + Enabled: true, + ZoneSigningKey: payload.ZoneSigningKey, + KeySigningKey: payload.KeySigningKey, + } + return nil +} + type zone struct { - ID int `json:"id"` - ZoneName string `json:"zone_name"` - Status string `json:"status"` - UpdatedAt time.Time `json:"updated_at"` - LastDeploy time.Time `json:"last_deploy"` - Dnssec struct { - ZoneSigningKey struct { - CreatedAt time.Time `json:"created_at"` - } `json:"zone_signing_key"` - KeySigningKey struct { - CreatedAt time.Time `json:"created_at"` - } `json:"key_signing_key"` - } `json:"dnssec"` + ID int `json:"id"` + ZoneName string `json:"zone_name"` + Status string `json:"status"` + UpdatedAt time.Time `json:"updated_at"` + LastDeploy time.Time `json:"last_deploy"` + Dnssec dnssecStatus `json:"dnssec"` } func checkIsLockedSystemAPIRecord(record RecordReply) error { diff --git a/providers/rwth/dns.go b/providers/rwth/dns.go index 6db245c5b6..7526d709b4 100644 --- a/providers/rwth/dns.go +++ b/providers/rwth/dns.go @@ -2,7 +2,7 @@ package rwth import ( "github.com/StackExchange/dnscontrol/v4/models" - "github.com/StackExchange/dnscontrol/v4/pkg/diff" + "github.com/StackExchange/dnscontrol/v4/pkg/diff2" ) // RWTHDefaultNs is the default DNS NS for this provider. @@ -32,38 +32,42 @@ func (api *rwthProvider) GetNameservers(domain string) ([]*models.Nameserver, er func (api *rwthProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, int, error) { domain := dc.Name - toReport, create, del, modify, actualChangeCount, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) + instructions, actualChangeCount, err := diff2.ByRecord(existingRecords, dc, nil) if err != nil { return nil, 0, err } - // Start corrections with the reports - corrections := diff.GenerateMessageCorrections(toReport) + var corrections []*models.Correction - for _, d := range create { - des := d.Desired - corrections = append(corrections, &models.Correction{ - Msg: d.String(), - F: func() error { return api.createRecord(des) }, - }) - } - for _, d := range del { - existingRecord := d.Existing.Original.(RecordReply) - corrections = append(corrections, &models.Correction{ - Msg: d.String(), - F: func() error { return api.destroyRecord(existingRecord) }, - }) - } - for _, d := range modify { - rec := d.Desired - existingID := d.Existing.Original.(RecordReply).ID - corrections = append(corrections, &models.Correction{ - Msg: d.String(), - F: func() error { return api.updateRecord(existingID, *rec) }, - }) + for _, inst := range instructions { + switch inst.Type { + case diff2.REPORT: + corrections = append(corrections, &models.Correction{Msg: inst.MsgsJoined}) + case diff2.CREATE: + rec := inst.New[0] + corrections = append(corrections, &models.Correction{ + Msg: inst.MsgsJoined, + F: func() error { return api.createRecord(rec) }, + }) + case diff2.DELETE: + existingRecord := inst.Old[0].Original.(RecordReply) + corrections = append(corrections, &models.Correction{ + Msg: inst.MsgsJoined, + F: func() error { return api.destroyRecord(existingRecord) }, + }) + case diff2.CHANGE: + rec := inst.New[0] + existingID := inst.Old[0].Original.(RecordReply).ID + corrections = append(corrections, &models.Correction{ + Msg: inst.MsgsJoined, + F: func() error { return api.updateRecord(existingID, *rec) }, + }) + default: + panic("unhandled instruction type") + } } // And deploy if any corrections were applied - if len(corrections) > 0 { + if actualChangeCount > 0 { corrections = append(corrections, &models.Correction{ Msg: "Deploy zone " + domain, F: func() error { return api.deployZone(domain) }, diff --git a/providers/rwth/rwthProvider.go b/providers/rwth/rwthProvider.go index 77757f2d8e..4086ad8779 100644 --- a/providers/rwth/rwthProvider.go +++ b/providers/rwth/rwthProvider.go @@ -19,7 +19,6 @@ var features = providers.DocumentationNotes{ providers.CanAutoDNSSEC: providers.Unimplemented("Supported by RWTH but not implemented yet."), providers.CanConcur: providers.Unimplemented(), providers.CanGetZones: providers.Can(), - providers.CanOnlyDiff1Features: providers.Can(), providers.CanUseAlias: providers.Cannot(), providers.CanUseCAA: providers.Can(), providers.CanUseDS: providers.Unimplemented("DS records are only supported at the apex and require a different API call that hasn't been implemented yet."),