bump to v0.2.0: complete provider implementation with live-tested DNS record types
ci / test (push) Failing after 5m55s

- DYNU_API_KEY env var fallback in provider config
- dynu_domain resource: add 9 computed attributes (unicode_name, ipv4/ipv6 flags,
  wildcard aliases, allow_zone_transfer, dnssec, created_on, updated_on)
- dynu_dns_records data source: add 6 type-specific fields (priority, weight,
  port, flags, tag, value)
- Fix normalizeRecordContentForState for MX/SRV/NS/PTR (read host field) and CAA
  (read value field) to avoid plan/state inconsistency
- Allow underscore-prefixed DNS labels in hostname validator (RFC 2782 SRV)
- Preserve plan/state hostname across create/update/read to prevent inconsistent
  result errors when API returns full FQDN for node_name-style records
- Update all version references from 0.1.0 to 0.2.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LFZyrmtKsezBwFWGTWsb2y
This commit is contained in:
beatzaplentyandClaude Sonnet 4.6 committed 2026-08-03 23:40:27 +10:00
1 parent 12b8f2460d
commit 70bb75ad46
17 files changed
+159 -35

No files matched your search

+15 -3
View File
@@ -189,6 +189,8 @@ func (r *dnsRecordResource) Create(ctx context.Context, req resource.CreateReque
state := mapDNSRecordToState(*record, dynamicIntent)
state.ID = types.StringValue(formatDNSRecordID(record.DomainID, record.ID))
// Preserve plan hostname so node_name overrides don't produce a plan/state inconsistency.
state.Hostname = plan.Hostname
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}
@@ -219,6 +221,8 @@ func (r *dnsRecordResource) Read(ctx context.Context, req resource.ReadRequest,
dynamicIntent := inferDynamicIntentFromState(state.RecordType, state.Content, state.Dynamic)
nextState := mapDNSRecordToState(*record, dynamicIntent)
nextState.ID = state.ID
// Preserve the stored hostname so node_name overrides remain stable across refreshes.
nextState.Hostname = state.Hostname
resp.Diagnostics.Append(resp.State.Set(ctx, &nextState)...)
}
@@ -318,6 +322,8 @@ func (r *dnsRecordResource) Update(ctx context.Context, req resource.UpdateReque
nextState := mapDNSRecordToState(*record, dynamicIntent)
nextState.ID = state.ID
// Preserve plan hostname so node_name overrides remain stable after updates.
nextState.Hostname = plan.Hostname
resp.Diagnostics.Append(resp.State.Set(ctx, &nextState)...)
}
@@ -357,7 +363,7 @@ func (r *dnsRecordResource) ImportState(ctx context.Context, req resource.Import
}
func mapDNSRecordToState(record dynuclient.DNSRecord, dynamicIntent bool) dnsRecordResourceModel {
content := normalizeRecordContentForState(record.RecordType, record.Content, dynamicIntent, record.Host)
content := normalizeRecordContentForState(record.RecordType, record.Content, dynamicIntent, record.Host, record.Value)
return dnsRecordResourceModel{
Hostname: mapString(record.Hostname),
RecordType: mapString(record.RecordType),
@@ -676,7 +682,7 @@ func inferDynamicIntentFromState(recordType types.String, content types.String,
return content.IsNull() || (content.IsUnknown())
}
func normalizeRecordContentForState(recordType string, content string, dynamicIntent bool, host string) types.String {
func normalizeRecordContentForState(recordType string, content string, dynamicIntent bool, host string, value string) types.String {
if dynamicIntent {
return types.StringNull()
}
@@ -696,11 +702,17 @@ func normalizeRecordContentForState(recordType string, content string, dynamicIn
if addr, err := netip.ParseAddr(trimmed); err == nil && addr.Is4() {
return types.StringValue(addr.String())
}
case "CNAME":
case "CNAME", "MX", "SRV", "NS", "PTR":
// API returns the target in host field; content contains zone-style prefix (e.g. "10 mail.example.com.")
if trimmedHost := strings.TrimSpace(host); trimmedHost != "" {
return types.StringValue(strings.TrimSuffix(trimmedHost, "."))
}
return types.StringValue(strings.TrimSuffix(trimmed, "."))
case "CAA":
// API returns the certificate authority in value field; content contains "flags tag value"
if trimmedValue := strings.TrimSpace(value); trimmedValue != "" {
return types.StringValue(strings.TrimSuffix(trimmedValue, "."))
}
}
return types.StringValue(trimmed)