Skip to content
Open
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,4 @@ docs/redirect
site
_scratch
Pipfile
*coverage.out
27 changes: 27 additions & 0 deletions docs/tutorials/cloudflare.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,33 @@ Requires [Cloudflare for SaaS](https://developers.cloudflare.com/cloudflare-for-

Due to a limitation within the cloudflare-go v0 API, the custom hostname page size is fixed at 50.

## Error Handling and Reliability

The Cloudflare provider includes some error handling improvements to help with common operational scenarios:

### Update Fallback Behavior

When ExternalDNS attempts to update a DNS record that no longer exists (e.g., manually deleted), the provider may attempt to fallback to creating the record instead of failing. This behavior depends on both the DNS record and its corresponding
TXT registry record state - if only the DNS record is deleted but the TXT record remains, ExternalDNS will recreate the record. Note that this behavior may change in future versions.

```text
WARN: failed to find previous record for update, attempting to create instead: example.com
```

### Delete Operation Behavior

Delete operations generally don't fail when attempting to delete a record that doesn't exist. This can help reduce false-positive failures when records are deleted outside of ExternalDNS, though the exact behavior may vary.

```text
WARN: failed to find record for deletion, record may already be deleted: example.com
```

### Considerations

- **Error handling behavior may change**: The specific error handling described above is current implementation behavior and may be modified in future versions
- **Best practices recommended**: While ExternalDNS handles some edge cases, following DNS management best practices and avoiding manual DNS changes is still recommended
- **Monitor logs for warnings**: Pay attention to warning messages which may indicate configuration or operational issues

## Using CRD source to manage DNS records in Cloudflare

Please refer to the [CRD source documentation](../sources/crd.md#example) for more information.
47 changes: 28 additions & 19 deletions provider/cloudflare/cloudflare.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,37 +597,46 @@ func (p *CloudFlareProvider) submitChanges(ctx context.Context, changes []*cloud
if chErr != nil {
return fmt.Errorf("could not fetch custom hostnames from zone, %w", chErr)
}
if change.Action == cloudFlareUpdate {
switch change.Action {
case cloudFlareUpdate:
if !p.submitCustomHostnameChanges(ctx, zoneID, change, chs, logFields) {
failedChange = true
}
recordID := p.getRecordID(records, change.ResourceRecord)
if recordID == "" {
log.WithFields(logFields).Errorf("failed to find previous record: %v", change.ResourceRecord)
continue
}
recordParam := updateDNSRecordParam(*change)
recordParam.ID = recordID
err := p.Client.UpdateDNSRecord(ctx, resourceContainer, recordParam)
if err != nil {
failedChange = true
log.WithFields(logFields).Errorf("failed to update record: %v", err)
log.WithFields(logFields).Warnf("failed to find previous record for update, attempting to create instead: %v", change.ResourceRecord)
// Convert UPDATE to CREATE when record is not found
recordParam := getCreateDNSRecordParam(*change)
_, err := p.Client.CreateDNSRecord(ctx, resourceContainer, recordParam)
if err != nil {
failedChange = true
log.WithFields(logFields).Errorf("failed to create record after update failure: %v", err)
}
} else {
recordParam := updateDNSRecordParam(*change)
recordParam.ID = recordID
err := p.Client.UpdateDNSRecord(ctx, resourceContainer, recordParam)
if err != nil {
failedChange = true
log.WithFields(logFields).Errorf("failed to update record: %v", err)
}
}
} else if change.Action == cloudFlareDelete {
case cloudFlareDelete:
recordID := p.getRecordID(records, change.ResourceRecord)
if recordID == "" {
log.WithFields(logFields).Errorf("failed to find previous record: %v", change.ResourceRecord)
continue
}
err := p.Client.DeleteDNSRecord(ctx, resourceContainer, recordID)
if err != nil {
failedChange = true
log.WithFields(logFields).Errorf("failed to delete record: %v", err)
log.WithFields(logFields).Warnf("failed to find record for deletion, record may already be deleted: %v", change.ResourceRecord)
// Don't treat this as a failure - the record might already be deleted
} else {
err := p.Client.DeleteDNSRecord(ctx, resourceContainer, recordID)
if err != nil {
failedChange = true
log.WithFields(logFields).Errorf("failed to delete record: %v", err)
}
}
if !p.submitCustomHostnameChanges(ctx, zoneID, change, chs, logFields) {
failedChange = true
}
} else if change.Action == cloudFlareCreate {
case cloudFlareCreate:
recordParam := getCreateDNSRecordParam(*change)
_, err := p.Client.CreateDNSRecord(ctx, resourceContainer, recordParam)
if err != nil {
Expand Down
Loading
Loading