# Usage This module is designed to be used as a dxaws **primitive**: you provide a `DnsRecordDesired` and the `DnsManager` converges Route 53 to match. The most common call pattern is: 1. Construct a `DnsManager` 2. Provide a `DnsRecordDesired` 3. Call `mgr.execute(desired)` --- ## Basic Example: Create / Update / Delete ```python from dxaws_dns.manager import DnsManager from dxaws_dns.models import DnsRecordDesired # AWS Route53 is the default provider mgr = DnsManager() # Create or update a TXT record present = DnsRecordDesired( zone_name="dev.dxaws.com", record_name="_demo", record_type="TXT", ttl=60, values=["hello from dxaws"], state="present", ) result = mgr.execute(present) print(result.outcome) # "applied" or "noop" # Delete the record absent = DnsRecordDesired( zone_name="dev.dxaws.com", record_name="_demo", record_type="TXT", state="absent", ) result2 = mgr.execute(absent) print(result2.outcome) # "applied" or "noop" ``` Notes: - `zone_name` is the human-facing identifier and is always required. - `zone_id` is optional; the manager can resolve it via the provider. - TXT values may be provided unquoted. The provider/manager normalize quoting to match Route 53 behavior. --- ## Overriding the Provider Most callers should use `DnsManager()` directly and treat providers as an internal implementation detail. The manager defaults to the AWS Route53 provider. Providers exist primarily for dependency injection (testing, alternate backends, recording providers, etc.). If you need to override the provider explicitly you can: ```python from dxaws_core import AwsSession from dxaws_dns.manager import DnsManager from dxaws_dns.providers.aws import Route53Provider aws = AwsSession(region_name="ca-central-1") provider = Route53Provider(aws=aws) mgr = DnsManager(provider=provider) ``` Application and composition code should normally interact only with the manager surface. If functionality from a provider is needed, it should be exposed through the manager interface rather than accessed directly. ## Idempotency Declarative convergence means you can run the same desired state repeatedly: ```python result1 = mgr.execute(present) result2 = mgr.execute(present) assert result2.outcome == "noop" ``` If AWS drifts (or you change inputs like TTL), the next run will return `"applied"` and re-stabilize. --- ## Record Types The acceptance harness currently validates these record types: - `TXT` - `A` - `CNAME` Example A record: ```python present_a = DnsRecordDesired( zone_name="dev.dxaws.com", record_name="_demo-a", record_type="A", ttl=60, values=["203.0.113.10"], state="present", ) mgr.execute(present_a) ``` Example CNAME record: ```python present_cname = DnsRecordDesired( zone_name="dev.dxaws.com", record_name="_demo-cname", record_type="CNAME", ttl=60, values=["example.com."], state="present", ) mgr.execute(present_cname) ``` --- ## Running Acceptance Tests Safely Acceptance tests mutate real AWS resources and are treated as **contract tests**. They are guarded by: - Opt-in: `DXAWS_AWS_TESTS=1` - Explicit confirmation: `DXAWS_ACCEPTANCE_CONFIRM` must equal the zone name - AWS identity printed before mutation ### Make target ```bash make accept-dns ZONE=dev.dxaws.com ``` This target sets: - `DXAWS_AWS_TESTS=1` - `DXAWS_ACCEPTANCE_ZONE_NAME=$(ZONE)` - `DXAWS_ACCEPTANCE_CONFIRM=$(ZONE)` The test creates and deletes temporary TXT/A/CNAME records and verifies: - Create/read/delete lifecycle - Idempotency (`noop` on second apply) - TTL drift detection + correction --- ## Troubleshooting ### "Value should be enclosed in quotation marks" Route 53 expects TXT values to be quoted. This module normalizes TXT values, so you should be able to pass unquoted strings in `values=[...]`. If you see this error, verify you are using `Route53Provider` from this module and that your running code includes the TXT normalization changes. ### "Refusing to run acceptance test..." Set: - `DXAWS_ACCEPTANCE_ZONE_NAME=` - `DXAWS_ACCEPTANCE_CONFIRM=` …and run again.