Rename dns record state field to enabled

This commit is contained in:
beatz174-bit
2026-04-29 15:43:29 +10:00
parent b6aae2c19d
commit ef99983118
10 changed files with 156 additions and 37 deletions
+72 -5
View File
@@ -4,15 +4,18 @@ import (
"context"
"errors"
"fmt"
"net"
"net/netip"
"strconv"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
@@ -37,9 +40,10 @@ type dnsRecordResourceModel struct {
Content types.String `tfsdk:"content"`
Dynamic types.Bool `tfsdk:"dynamic"`
TTL types.Int64 `tfsdk:"ttl"`
State types.Bool `tfsdk:"state"`
Enabled types.Bool `tfsdk:"enabled"`
Group types.String `tfsdk:"group"`
Host types.String `tfsdk:"host"`
Location types.String `tfsdk:"location"`
NodeName types.String `tfsdk:"node_name"`
DomainID types.Int64 `tfsdk:"domain_id"`
DomainName types.String `tfsdk:"domain_name"`
@@ -76,9 +80,10 @@ func (r *dnsRecordResource) Schema(_ context.Context, _ resource.SchemaRequest,
Description: "DNS TTL in seconds.",
Validators: []validator.Int64{int64validator.AtLeast(0)},
},
"state": schema.BoolAttribute{Optional: true, Computed: true, Description: "Whether this DNS record is active."},
"enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), Description: "Whether this DNS record is enabled/active."},
"group": schema.StringAttribute{Optional: true, Computed: true, Description: "Dynu group value for this record."},
"host": schema.StringAttribute{Optional: true, Computed: true, Description: "Host field for supported Dynu record types."},
"location": schema.StringAttribute{Optional: true, Computed: true, Description: "Dynu location hint for A/AAAA records only."},
"node_name": schema.StringAttribute{Optional: true, Computed: true, Description: "Node/label portion of the record."},
"domain_id": schema.Int64Attribute{Computed: true, Description: "Dynu domain ID resolved from hostname."},
"domain_name": schema.StringAttribute{Computed: true, Description: "Dynu root domain name resolved from hostname."},
@@ -116,6 +121,8 @@ func (r *dnsRecordResource) ValidateConfig(ctx context.Context, req resource.Val
return
}
validateDNSRecordContentForTypeWithKnowledge(recordType, content, contentKnown, dynamicIntent, &resp.Diagnostics)
validateDNSRecordTTL(config.TTL, &resp.Diagnostics)
validateDNSRecordLocation(recordType, config.Location, &resp.Diagnostics)
}
func (r *dnsRecordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
@@ -142,14 +149,21 @@ func (r *dnsRecordResource) Create(ctx context.Context, req resource.CreateReque
RecordType: recordType,
Content: stringPointerFromOptionalContent(plan.Content),
TTL: int64FromOptional(plan.TTL),
State: boolPointerFromOptional(plan.State),
State: boolPointerFromOptional(plan.Enabled),
Group: stringFromOptional(plan.Group),
Host: stringFromOptional(plan.Host),
Location: stringFromOptional(plan.Location),
}
createReq = normalizeDNSRecordCreateRequestForType(createReq)
if !validateDNSRecordContentForType(createReq.RecordType, createReq.Content, dynamicIntent, &resp.Diagnostics) {
return
}
if !validateRecordTTLSeconds(createReq.TTL, &resp.Diagnostics) {
return
}
if !validateLocationForType(createReq.RecordType, createReq.Location, &resp.Diagnostics) {
return
}
record, err := r.clientProvider.client.CreateDNSRecord(ctx, domainID, createReq)
if err != nil && dynamicIntent && isUnsupportedEmptyContentError(err) {
@@ -239,14 +253,21 @@ func (r *dnsRecordResource) Update(ctx context.Context, req resource.UpdateReque
RecordType: recordType,
Content: stringPointerFromOptionalContent(plan.Content),
TTL: int64FromOptional(preferKnownInt64(plan.TTL, state.TTL)),
State: boolPointerFromOptional(preferKnownBool(plan.State, state.State)),
State: boolPointerFromOptional(preferKnownBool(plan.Enabled, state.Enabled)),
Group: stringFromOptional(preferKnownString(plan.Group, state.Group)),
Host: stringFromOptional(preferKnownString(plan.Host, state.Host)),
Location: stringFromOptional(preferKnownString(plan.Location, state.Location)),
}
updateReq = normalizeDNSRecordUpdateRequestForType(updateReq)
if !validateDNSRecordContentForType(updateReq.RecordType, updateReq.Content, dynamicIntent, &resp.Diagnostics) {
return
}
if !validateRecordTTLSeconds(updateReq.TTL, &resp.Diagnostics) {
return
}
if !validateLocationForType(updateReq.RecordType, updateReq.Location, &resp.Diagnostics) {
return
}
if _, err := r.clientProvider.client.UpdateDNSRecord(ctx, domainID, recordID, updateReq); err != nil {
if dynamicIntent && isUnsupportedEmptyContentError(err) {
@@ -258,6 +279,7 @@ func (r *dnsRecordResource) Update(ctx context.Context, req resource.UpdateReque
State: updateReq.State,
Group: updateReq.Group,
Host: updateReq.Host,
Location: updateReq.Location,
}, &resp.Diagnostics); retryOK {
updateReq.Content = retryReq.Content
updateReq.Group = retryReq.Group
@@ -324,9 +346,10 @@ func mapDNSRecordToState(record dynuclient.DNSRecord, dynamicIntent bool) dnsRec
Content: content,
Dynamic: types.BoolValue(dynamicIntent),
TTL: types.Int64Value(record.TTL),
State: types.BoolValue(record.State),
Enabled: types.BoolValue(record.State),
Group: mapString(record.Group),
Host: mapString(record.Host),
Location: mapString(record.Location),
NodeName: mapString(record.NodeName),
DomainID: types.Int64Value(record.DomainID),
DomainName: mapString(record.DomainName),
@@ -511,6 +534,16 @@ func validateDNSRecordContentForTypeWithKnowledge(recordType string, content *st
}
return true
}
if normalizedType == "CNAME" {
if net.ParseIP(trimmedContent) != nil {
diagnostics.AddError("Invalid DNS record content", fmt.Sprintf("Record type %q requires a hostname target, got IP %q.", normalizedType, trimmedContent))
return false
}
if strings.HasPrefix(strings.ToLower(trimmedContent), "http://") || strings.HasPrefix(strings.ToLower(trimmedContent), "https://") {
diagnostics.AddError("Invalid DNS record content", fmt.Sprintf("Record type %q target must not include a URL scheme: %q.", normalizedType, trimmedContent))
return false
}
}
if dynamicIntent {
diagnostics.AddError(
@@ -535,6 +568,40 @@ func validateDNSRecordContentForTypeWithKnowledge(recordType string, content *st
return true
}
func validateDNSRecordTTL(ttl types.Int64, diagnostics *diag.Diagnostics) {
if ttl.IsNull() || ttl.IsUnknown() {
return
}
validateRecordTTLSeconds(ttl.ValueInt64(), diagnostics)
}
func validateRecordTTLSeconds(ttl int64, diagnostics *diag.Diagnostics) bool {
if ttl > 0 && ttl < int64((90*time.Second).Seconds()) {
diagnostics.AddError("Invalid TTL", fmt.Sprintf("invalid TTL %ds: Dynu requires TTL >= 90s", ttl))
return false
}
return true
}
func validateDNSRecordLocation(recordType string, location types.String, diagnostics *diag.Diagnostics) {
if location.IsNull() || location.IsUnknown() {
return
}
validateLocationForType(recordType, location.ValueString(), diagnostics)
}
func validateLocationForType(recordType string, location string, diagnostics *diag.Diagnostics) bool {
if strings.TrimSpace(location) == "" {
return true
}
normalizedType := strings.ToUpper(strings.TrimSpace(recordType))
if normalizedType != "A" && normalizedType != "AAAA" {
diagnostics.AddError("Invalid location", fmt.Sprintf("location is only supported for A and AAAA records, got %q.", normalizedType))
return false
}
return true
}
func resolveDynamicIntent(recordType string, content types.String, dynamic types.Bool, diagnostics *diag.Diagnostics) (bool, bool) {
normalizedType := strings.ToUpper(strings.TrimSpace(recordType))
contentPtr := stringPointerFromOptionalContent(content)