From 5eed12a59493fc187c546aba9804ad938ea8bb4f Mon Sep 17 00:00:00 2001 From: beatz174-bit Date: Wed, 29 Apr 2026 16:10:50 +1000 Subject: [PATCH] Remove unsupported location field from DNS record resource --- README.md | 4 +- examples/live_safe_dns_record/README.md | 7 ++-- examples/live_safe_dns_record/main.tf | 2 - .../terraform.tfvars.example | 1 - examples/live_safe_dns_record/variables.tf | 6 --- .../resources/dynu_dns_record/resource.tf | 3 +- internal/dynuclient/client.go | 13 ++---- internal/provider/resource_dns_record.go | 40 ------------------- internal/provider/resource_dns_record_test.go | 29 +++++--------- 9 files changed, 18 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index 9a307bb..5e18bd5 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,6 @@ Arguments: - `enabled` (Bool, optional, defaults to `true`) - `group` (String, optional) - `host` (String, optional) -- `location` (String, optional; A/AAAA only) - `node_name` (String, optional) Attributes: @@ -185,7 +184,6 @@ resource "dynu_dns_record" "txt" { content = "198.51.100.10" ttl = 90 enabled = false - location = "us" } resource "dynu_dns_record" "dynamic_a" { @@ -246,7 +244,7 @@ Attributes: - `domain_name` (String) - `records` (List(Object)) with: - `id`, `domain_id`, `domain_name`, `node_name`, `hostname`, `record_type` - - `ttl`, `state`, `content`, `updated_on`, `group`, `host`, `location` + - `ttl`, `state`, `content`, `updated_on`, `group`, `host` Example: diff --git a/examples/live_safe_dns_record/README.md b/examples/live_safe_dns_record/README.md index d89d3ee..3f7840e 100644 --- a/examples/live_safe_dns_record/README.md +++ b/examples/live_safe_dns_record/README.md @@ -6,8 +6,8 @@ This example is an **opt-in live test** for writable Dynu provider functionality Using a single suffix (`test_suffix`), this example creates five DNS record scenarios under `dynu_root_domain`: -1. `A` record with IPv4 content, `location`, and minimum TTL=90 (`codex-a-.`) -2. `AAAA` record with IPv6 content and `location` (`codex-aaaa-.`) +1. `A` record with IPv4 content, minimum TTL=90 (`codex-a-.`) +2. `AAAA` record with IPv6 content (`codex-aaaa-.`) 3. `CNAME` record created disabled (`enabled = false`) (`codex-cname-.`) 4. **Dynamic `A` record** with omitted content (`codex-dynamic-a-.`) 5. **Dynamic `AAAA` record** with omitted content (`codex-dynamic-aaaa-.`) @@ -32,10 +32,9 @@ Edit `terraform.tfvars` and set at least: - `dynu_root_domain` - `test_suffix` (use a unique value per run) -Optional overrides include `test_ipv4`, `test_ipv6`, `test_cname_target`, `test_ttl`, and `test_location`. +Optional overrides include `test_ipv4`, `test_ipv6`, `test_cname_target`, `test_ttl`. - `test_ttl` must be `0` (provider/API default) or `>= 90`. -- `test_location` applies only to A/AAAA records. ## Run diff --git a/examples/live_safe_dns_record/main.tf b/examples/live_safe_dns_record/main.tf index 2487ce2..e66ea83 100644 --- a/examples/live_safe_dns_record/main.tf +++ b/examples/live_safe_dns_record/main.tf @@ -19,7 +19,6 @@ resource "dynu_dns_record" "a_ipv4" { content = var.test_ipv4 ttl = 90 enabled = true - location = var.test_location } resource "dynu_dns_record" "aaaa_ipv6" { @@ -28,7 +27,6 @@ resource "dynu_dns_record" "aaaa_ipv6" { content = var.test_ipv6 ttl = var.test_ttl enabled = true - location = var.test_location } resource "dynu_dns_record" "cname" { diff --git a/examples/live_safe_dns_record/terraform.tfvars.example b/examples/live_safe_dns_record/terraform.tfvars.example index 6dc6ee0..2c75fac 100644 --- a/examples/live_safe_dns_record/terraform.tfvars.example +++ b/examples/live_safe_dns_record/terraform.tfvars.example @@ -14,4 +14,3 @@ test_ipv6 = "2001:db8::123" test_cname_target = "example.com" test_ttl = 300 -test_location = "us" diff --git a/examples/live_safe_dns_record/variables.tf b/examples/live_safe_dns_record/variables.tf index 19fff2e..ce2f302 100644 --- a/examples/live_safe_dns_record/variables.tf +++ b/examples/live_safe_dns_record/variables.tf @@ -64,9 +64,3 @@ variable "test_ttl" { type = number default = 300 } - -variable "test_location" { - description = "Optional Dynu location hint for A/AAAA records." - type = string - default = "us" -} diff --git a/examples/resources/dynu_dns_record/resource.tf b/examples/resources/dynu_dns_record/resource.tf index 94b4809..a4c65d4 100644 --- a/examples/resources/dynu_dns_record/resource.tf +++ b/examples/resources/dynu_dns_record/resource.tf @@ -1,8 +1,7 @@ -resource "dynu_dns_record" "a_location_min_ttl" { +resource "dynu_dns_record" "a_enabled_min_ttl" { hostname = "api.example.com" record_type = "A" content = "198.51.100.10" ttl = 90 enabled = false - location = "us" } diff --git a/internal/dynuclient/client.go b/internal/dynuclient/client.go index 28bbc0a..5a67c61 100644 --- a/internal/dynuclient/client.go +++ b/internal/dynuclient/client.go @@ -106,7 +106,6 @@ type DNSRecord struct { UpdatedOn string `json:"updatedOn"` Group string `json:"group"` Host string `json:"host"` - Location string `json:"location"` } type CreateDNSRecordRequest struct { @@ -117,7 +116,6 @@ type CreateDNSRecordRequest struct { State *bool `json:"state,omitempty"` Group string `json:"group,omitempty"` Host string `json:"host,omitempty"` - Location string `json:"location,omitempty"` } type UpdateDNSRecordRequest struct { @@ -128,7 +126,6 @@ type UpdateDNSRecordRequest struct { State *bool `json:"state,omitempty"` Group string `json:"group,omitempty"` Host string `json:"host,omitempty"` - Location string `json:"location,omitempty"` } type listDomainsResponse struct { @@ -210,7 +207,7 @@ func (c *Client) GetDNSRecord(ctx context.Context, domainID int64, recordID int6 func (c *Client) CreateDNSRecord(ctx context.Context, domainID int64, req CreateDNSRecordRequest) (*DNSRecord, error) { var resp getDNSRecordResponse - if err := c.doRequest(ctx, http.MethodPost, fmt.Sprintf("/dns/%d/record", domainID), buildDNSRecordUpsertPayload(req.RecordType, req.NodeName, req.Content, req.TTL, req.State, req.Group, req.Host, req.Location), &resp); err != nil { + if err := c.doRequest(ctx, http.MethodPost, fmt.Sprintf("/dns/%d/record", domainID), buildDNSRecordUpsertPayload(req.RecordType, req.NodeName, req.Content, req.TTL, req.State, req.Group, req.Host), &resp); err != nil { return nil, err } normalizeDNSRecord(&resp.DNSRecord) @@ -219,7 +216,7 @@ func (c *Client) CreateDNSRecord(ctx context.Context, domainID int64, req Create func (c *Client) UpdateDNSRecord(ctx context.Context, domainID int64, recordID int64, req UpdateDNSRecordRequest) (*DNSRecord, error) { var resp getDNSRecordResponse - if err := c.doRequest(ctx, http.MethodPost, fmt.Sprintf("/dns/%d/record/%d", domainID, recordID), buildDNSRecordUpsertPayload(req.RecordType, req.NodeName, req.Content, req.TTL, req.State, req.Group, req.Host, req.Location), &resp); err != nil { + if err := c.doRequest(ctx, http.MethodPost, fmt.Sprintf("/dns/%d/record/%d", domainID, recordID), buildDNSRecordUpsertPayload(req.RecordType, req.NodeName, req.Content, req.TTL, req.State, req.Group, req.Host), &resp); err != nil { return nil, err } normalizeDNSRecord(&resp.DNSRecord) @@ -322,10 +319,9 @@ type dnsRecordUpsertPayload struct { State *bool `json:"state,omitempty"` Group string `json:"group,omitempty"` Host string `json:"host,omitempty"` - Location string `json:"location,omitempty"` } -func buildDNSRecordUpsertPayload(recordType string, nodeName string, content *string, ttl int64, state *bool, group string, host string, location string) dnsRecordUpsertPayload { +func buildDNSRecordUpsertPayload(recordType string, nodeName string, content *string, ttl int64, state *bool, group string, host string) dnsRecordUpsertPayload { normalizedType := strings.ToUpper(strings.TrimSpace(recordType)) normalizedContent := normalizeOptionalContent(content) payload := dnsRecordUpsertPayload{ @@ -335,7 +331,6 @@ func buildDNSRecordUpsertPayload(recordType string, nodeName string, content *st State: state, Group: group, Host: host, - Location: location, } switch normalizedType { @@ -351,10 +346,8 @@ func buildDNSRecordUpsertPayload(recordType string, nodeName string, content *st if normalizedContent != nil { payload.Host = *normalizedContent } - payload.Location = "" default: payload.Content = normalizedContent - payload.Location = "" } return payload diff --git a/internal/provider/resource_dns_record.go b/internal/provider/resource_dns_record.go index f872a23..e6d2427 100644 --- a/internal/provider/resource_dns_record.go +++ b/internal/provider/resource_dns_record.go @@ -43,7 +43,6 @@ type dnsRecordResourceModel struct { 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"` @@ -83,7 +82,6 @@ func (r *dnsRecordResource) Schema(_ context.Context, _ resource.SchemaRequest, "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."}, @@ -122,7 +120,6 @@ func (r *dnsRecordResource) ValidateConfig(ctx context.Context, req resource.Val } 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) { @@ -152,7 +149,6 @@ func (r *dnsRecordResource) Create(ctx context.Context, req resource.CreateReque 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) { @@ -161,9 +157,6 @@ func (r *dnsRecordResource) Create(ctx context.Context, req resource.CreateReque 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) { @@ -256,7 +249,6 @@ func (r *dnsRecordResource) Update(ctx context.Context, req resource.UpdateReque State: boolPointerFromOptional(preferKnownBool(plan.Enabled, state.Enabled)), Group: stringFromOptional(preferKnownString(plan.Group, state.Group)), Host: stringFromOptional(preferKnownString(plan.Host, state.Host)), - Location: locationForUpdate(recordType, plan.Location, state.Location), } updateReq = normalizeDNSRecordUpdateRequestForType(updateReq) if !validateDNSRecordContentForType(updateReq.RecordType, updateReq.Content, dynamicIntent, &resp.Diagnostics) { @@ -265,9 +257,6 @@ func (r *dnsRecordResource) Update(ctx context.Context, req resource.UpdateReque 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) { @@ -279,7 +268,6 @@ 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 @@ -349,7 +337,6 @@ func mapDNSRecordToState(record dynuclient.DNSRecord, dynamicIntent bool) dnsRec 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), @@ -583,33 +570,6 @@ func validateRecordTTLSeconds(ttl int64, diagnostics *diag.Diagnostics) bool { 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 locationForUpdate(recordType string, planLocation types.String, stateLocation types.String) string { - normalizedType := strings.ToUpper(strings.TrimSpace(recordType)) - if normalizedType != "A" && normalizedType != "AAAA" { - return stringFromOptional(planLocation) - } - return stringFromOptional(preferKnownString(planLocation, stateLocation)) -} - func resolveDynamicIntent(recordType string, content types.String, dynamic types.Bool, diagnostics *diag.Diagnostics) (bool, bool) { normalizedType := strings.ToUpper(strings.TrimSpace(recordType)) contentPtr := stringPointerFromOptionalContent(content) diff --git a/internal/provider/resource_dns_record_test.go b/internal/provider/resource_dns_record_test.go index c6c35f4..ad05cb3 100644 --- a/internal/provider/resource_dns_record_test.go +++ b/internal/provider/resource_dns_record_test.go @@ -1,11 +1,13 @@ package provider import ( + "context" "strings" "testing" "github.com/dynu/terraform-provider-dynu/internal/dynuclient" "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -192,26 +194,15 @@ func TestValidateRecordTTLSeconds(t *testing.T) { } } -func TestValidateLocationForType(t *testing.T) { - diags := diag.Diagnostics{} - if !validateLocationForType("A", "us", &diags) || diags.HasError() { - t.Fatal("expected location for A to be valid") +func TestDNSRecordSchemaHasEnabledAndNoLocation(t *testing.T) { + r := NewDNSRecordResource() + resp := &resource.SchemaResponse{} + r.Schema(context.Background(), resource.SchemaRequest{}, resp) + if _, ok := resp.Schema.Attributes["enabled"]; !ok { + t.Fatal("expected enabled attribute in schema") } - diags = diag.Diagnostics{} - if validateLocationForType("CNAME", "us", &diags) || !diags.HasError() { - t.Fatal("expected location for CNAME to fail") - } -} - -func TestLocationForUpdate(t *testing.T) { - if got := locationForUpdate("A", types.StringNull(), types.StringValue("us")); got != "us" { - t.Fatalf("expected A record update to preserve state location, got %q", got) - } - if got := locationForUpdate("CNAME", types.StringNull(), types.StringValue("us")); got != "" { - t.Fatalf("expected non-A/AAAA type to ignore prior state location when omitted, got %q", got) - } - if got := locationForUpdate("TXT", types.StringValue("eu"), types.StringValue("us")); got != "eu" { - t.Fatalf("expected explicit plan location to be returned as-is, got %q", got) + if _, ok := resp.Schema.Attributes["location"]; ok { + t.Fatal("expected location attribute to be removed from schema") } }