From a993d1b7add2a631dbea1a413e8b7bb8f3f1e592 Mon Sep 17 00:00:00 2001 From: Jon Langevin Date: Tue, 16 Jun 2026 19:22:05 -0400 Subject: [PATCH] fix: plan stale DNS marker cleanup --- internal/drivers/dns.go | 14 ++++++++ internal/drivers/dns_test.go | 64 ++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/internal/drivers/dns.go b/internal/drivers/dns.go index c03902c..efc0c5e 100644 --- a/internal/drivers/dns.go +++ b/internal/drivers/dns.go @@ -579,9 +579,13 @@ func diffRecords(current, desired []Record, manageUnlisted bool, domain string) var changes []interfaces.FieldChange currentByKey := recordsByKey(current, domain) desiredKeys := map[string]struct{}{} + hasDesiredWorkflowMarker := false for _, record := range desired { key := recordKey(record, domain) desiredKeys[key] = struct{}{} + if isWorkflowManagedMarker(record, domain) { + hasDesiredWorkflowMarker = true + } candidates := currentByKey[key] if len(candidates) == 0 { changes = append(changes, interfaces.FieldChange{Path: "records", Old: nil, New: recordOutput(record)}) @@ -600,6 +604,16 @@ func diffRecords(current, desired []Record, manageUnlisted bool, domain string) } } } + if !manageUnlisted && hasDesiredWorkflowMarker { + for _, record := range current { + if !isWorkflowManagedMarker(record, domain) { + continue + } + if _, ok := desiredKeys[recordKey(record, domain)]; !ok { + changes = append(changes, interfaces.FieldChange{Path: "records", Old: recordOutput(record), New: nil}) + } + } + } return changes } diff --git a/internal/drivers/dns_test.go b/internal/drivers/dns_test.go index bff7bdd..9bedcd6 100644 --- a/internal/drivers/dns_test.go +++ b/internal/drivers/dns_test.go @@ -586,6 +586,70 @@ func TestDNSDriver_DiffNormalizesTXTQuotePresentation(t *testing.T) { } } +func TestDNSDriver_DiffDetectsStaleWorkflowManagedMarkersWhenUnlistedRecordsArePreserved(t *testing.T) { + driver := NewDNSDriverWithClient(&fakeCFClient{}) + current := &interfaces.ResourceOutput{ + Name: "gigbagg.rocks", + Type: "infra.dns", + ProviderID: "zone", + Outputs: map[string]any{ + "domain": "gigbagg.rocks", + "records": []map[string]any{ + { + "type": "TXT", + "name": "_workflow-dns-managed.gigbagg.rocks", + "data": `"heritage=wfinfra-v1 managed_by=wfctl state_dir=.state/cloudflare-staging/ resource=cf-gigbagg-rocks"`, + "ttl": 300, + "value": `"heritage=wfinfra-v1 managed_by=wfctl state_dir=.state/cloudflare-staging/ resource=cf-gigbagg-rocks"`, + }, + { + "type": "TXT", + "name": "_workflow-dns-managed.gigbagg.rocks", + "data": `"heritage=wfinfra-v1 managed_by=wfctl state_dir=.state/domain-reconcile/ resource=cf-gigbagg-rocks"`, + "ttl": 300, + "value": `"heritage=wfinfra-v1 managed_by=wfctl state_dir=.state/domain-reconcile/ resource=cf-gigbagg-rocks"`, + }, + {"type": "TXT", "name": "gigbagg.rocks", "data": "external", "ttl": 300, "value": "external"}, + }, + }, + } + diff, err := driver.Diff(context.Background(), interfaces.ResourceSpec{ + Name: "gigbagg.rocks", + Type: "infra.dns", + Config: map[string]any{ + "domain": "gigbagg.rocks", + "manage_unlisted": false, + "records": []any{ + map[string]any{ + "type": "TXT", + "name": "_workflow-dns-managed", + "data": `"heritage=wfinfra-v1 managed_by=wfctl state_dir=.state/cloudflare-staging/ resource=cf-gigbagg-rocks"`, + "ttl": 300, + }, + }, + }, + }, current) + if err != nil { + t.Fatalf("Diff: %v", err) + } + if !diff.NeedsUpdate { + t.Fatalf("diff.NeedsUpdate = false, want true for stale workflow marker") + } + if len(diff.Changes) != 1 { + t.Fatalf("changes len = %d, want 1: %#v", len(diff.Changes), diff.Changes) + } + old, ok := diff.Changes[0].Old.(map[string]any) + if !ok { + t.Fatalf("change old = %#v, want record output", diff.Changes[0].Old) + } + if got, want := old["data"], `"heritage=wfinfra-v1 managed_by=wfctl state_dir=.state/domain-reconcile/ resource=cf-gigbagg-rocks"`; got != want { + t.Fatalf("deleted marker data = %q, want %q", got, want) + } + if diff.Changes[0].New != nil { + t.Fatalf("change new = %#v, want nil delete", diff.Changes[0].New) + } +} + func TestDNSDriver_UpdatePreservesUnlistedRecordsByDefault(t *testing.T) { fake := &fakeCFClient{ zone: &Zone{ID: "zone", Name: "example.com"},