diff --git a/provider.go b/provider.go index 86bdb0d..7655f21 100644 --- a/provider.go +++ b/provider.go @@ -2,6 +2,7 @@ package inwx import ( "context" + "errors" "fmt" "strings" "sync" @@ -44,18 +45,20 @@ func (p *Provider) GetRecords(ctx context.Context, zone string) ([]libdns.Record } results := make([]libdns.Record, 0, len(inwxRecords)) + var errs []error for _, inwxRecord := range inwxRecords { result, err := libdnsRecord(inwxRecord, zone) if err != nil { - return nil, fmt.Errorf("parsing INWX DNS record %+v: %v", inwxRecord, err) + errs = append(errs, fmt.Errorf("parsing INWX DNS record %+v: %v", inwxRecord, err)) + continue } results = append(results, result) } - return results, nil + return results, errors.Join(errs...) } // AppendRecords adds records to the zone. It returns the records that were added. @@ -68,18 +71,27 @@ func (p *Provider) AppendRecords(ctx context.Context, zone string, records []lib } var results []libdns.Record + var errs []error for _, record := range records { - var _, err = client.createRecord(ctx, inwxRecord(record), getDomain(zone)) + inwxRecord, err := inwxRecord(record) if err != nil { - return nil, err + errs = append(errs, err) + continue + } + + _, err = client.createRecord(ctx, *inwxRecord, getDomain(zone)) + + if err != nil { + errs = append(errs, err) + continue } results = append(results, record) } - return results, nil + return results, errors.Join(errs...) } // SetRecords sets the records in the zone, either by updating existing records or creating new ones. @@ -92,45 +104,74 @@ func (p *Provider) SetRecords(ctx context.Context, zone string, records []libdns return nil, err } - var results []libdns.Record - + groupedRecords := map[string](map[string][]libdns.Record){} for _, record := range records { - matches, err := p.client.findRecords(ctx, inwxRecord(record), getDomain(zone), false) - - if err != nil { - return nil, err + rr := record.RR() + if _, ok := groupedRecords[rr.Type]; !ok { + groupedRecords[rr.Type] = map[string][]libdns.Record{} } + groupedRecords[rr.Type][rr.Name] = append(groupedRecords[rr.Type][rr.Name], record) + } - if len(matches) == 0 { - _, err := client.createRecord(ctx, inwxRecord(record), getDomain(zone)) + var results []libdns.Record + var errs []error + for recordType, typeGroup := range groupedRecords { + for recordName, nameGroup := range typeGroup { + matches, err := p.client.findRecords(ctx, nameserverRecord{Type: recordType, Name: recordName}, getDomain(zone), false) if err != nil { - return nil, err + errs = append(errs, err) + continue } + for i, record := range nameGroup { - results = append(results, record) + inwxRecord, err := inwxRecord(record) - continue - } + if err != nil { + errs = append(errs, err) + continue + } - if len(matches) > 1 { - return nil, fmt.Errorf("unexpectedly found more than 1 record for %v", record) - } + if i > len(matches)-1 { - inwxRecord := inwxRecord(record) - inwxRecord.ID = matches[0].ID + _, err := client.createRecord(ctx, *inwxRecord, getDomain(zone)) - err = client.updateRecord(ctx, inwxRecord) + if err != nil { + errs = append(errs, err) + continue + } - if err != nil { - return nil, err - } + } else { - results = append(results, record) + inwxRecord.ID = matches[i].ID + + err = client.updateRecord(ctx, *inwxRecord) + + if err != nil { + errs = append(errs, err) + continue + } + + } + + results = append(results, record) + } + if len(matches) > len(nameGroup) { + for _, record := range matches[len(nameGroup):] { + err := client.deleteRecord(ctx, record) + + if err != nil { + errs = append(errs, err) + continue + } + } + + } + } } - return results, nil + return results, errors.Join(errs...) } // DeleteRecords deletes the records from the zone. It returns the records that were deleted. @@ -143,26 +184,36 @@ func (p *Provider) DeleteRecords(ctx context.Context, zone string, records []lib } var results []libdns.Record + var errs []error for _, record := range records { - exactMatches, err := p.client.findRecords(ctx, inwxRecord(record), getDomain(zone), true) + inwxRecord, err := inwxRecord(record) if err != nil { - return nil, err + errs = append(errs, err) + continue + } + + exactMatches, err := p.client.findRecords(ctx, *inwxRecord, getDomain(zone), true) + + if err != nil { + results = append(results, record) + continue } for _, inwxRecord := range exactMatches { err := client.deleteRecord(ctx, inwxRecord) if err != nil { - return nil, err + errs = append(errs, err) + continue } results = append(results, record) } } - return results, nil + return results, errors.Join(errs...) } func (p *Provider) getClient(ctx context.Context) (*client, error) { @@ -229,7 +280,7 @@ func libdnsRecord(record nameserverRecord, zone string) (libdns.Record, error) { }.Parse() } -func inwxRecord(record libdns.Record) nameserverRecord { +func inwxRecord(record libdns.Record) (*nameserverRecord, error) { rr := record.RR() inwxRecord := nameserverRecord{ @@ -239,7 +290,12 @@ func inwxRecord(record libdns.Record) nameserverRecord { TTL: int(rr.TTL.Seconds()), } - switch rec := record.(type) { + parsed, err := rr.Parse() + if err != nil { + return nil, fmt.Errorf("failed to parse record %s %s: %w", rr.Name, rr.Type, err) + } + + switch rec := parsed.(type) { case libdns.MX: inwxRecord.Content = rec.Target inwxRecord.Priority = uint(rec.Preference) @@ -248,7 +304,7 @@ func inwxRecord(record libdns.Record) nameserverRecord { inwxRecord.Priority = uint(rec.Priority) } - return inwxRecord + return &inwxRecord, nil } // Interface guards