Files
arcadia-cloud/lib/arcadia_cloud/provisioning
Giuliano Silvestro e3bcd3fc77 Phase 2: DNS record write primitives
DigitalOcean.Client DNS record CRUD:
- list_domain_records/2, create_domain_record/3, update_domain_record/4,
  delete_domain_record/3 on /v2/domains/:domain/records.

Steps.UpsertDnsRecord — idempotent ensure-record:
- (type, name) absent      -> create
- present, data matches    -> no-op
- present, data differs    -> update, stashing prior state
Match comparison normalizes trailing dots (DO appends them to CNAMEs)
so a re-run doesn't false-positive into an update. compensate: deletes
records it created, restores prior data/ttl for records it updated.

Steps.DeleteDnsRecord — deletes a record by (type, name); idempotent
(already-absent succeeds); compensate recreates from stashed prior state.

Both read dns_domain / dns_record_type / dns_record_name / dns_record_data
/ dns_record_ttl from saga inputs.

Live smoke verified against sky-ai.com with TXT records (non-disruptive):
- [UpsertDnsRecord, Fail]: record created then compensation deleted it
  (saga rolled_back, record absent after).
- [UpsertDnsRecord] twice: first outcome "created", second "noop"
  (idempotency holds).
- [DeleteDnsRecord]: record deleted, absent after.
Both test records cleaned up — zero leftover.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 11:10:33 +10:00
..
2026-05-20 11:08:27 +10:00