diff --git a/docs/data-sources/a_record_set.md b/docs/data-sources/a_record_set.md index b1e49641..22ea654c 100644 --- a/docs/data-sources/a_record_set.md +++ b/docs/data-sources/a_record_set.md @@ -29,6 +29,10 @@ output "google_addrs" { - `host` (String) Host to look up. +### Optional + +- `use_update_server` (Boolean) Whether to use the configured update DNS server + ### Read-Only - `addrs` (List of String) A list of IP addresses. IP addresses are always sorted to avoid constant changing plans. diff --git a/internal/provider/data_dns_a_record_set.go b/internal/provider/data_dns_a_record_set.go index c0463ded..cecb0ba7 100644 --- a/internal/provider/data_dns_a_record_set.go +++ b/internal/provider/data_dns_a_record_set.go @@ -12,17 +12,21 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/miekg/dns" ) var ( - _ datasource.DataSource = (*dnsARecordSetDataSource)(nil) + _ datasource.DataSource = (*dnsARecordSetDataSource)(nil) + _ datasource.DataSourceWithConfigure = (*dnsARecordSetDataSource)(nil) ) func NewDnsARecordSetDataSource() datasource.DataSource { return &dnsARecordSetDataSource{} } -type dnsARecordSetDataSource struct{} +type dnsARecordSetDataSource struct { + client *DNSClient +} func (d *dnsARecordSetDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_a_record_set" @@ -36,6 +40,10 @@ func (d *dnsARecordSetDataSource) Schema(ctx context.Context, req datasource.Sch Required: true, Description: "Host to look up.", }, + "use_update_server": schema.BoolAttribute{ + Optional: true, + Description: "Whether to use the configured update DNS server", + }, "addrs": schema.ListAttribute{ ElementType: types.StringType, Computed: true, @@ -49,6 +57,24 @@ func (d *dnsARecordSetDataSource) Schema(ctx context.Context, req datasource.Sch } } +func (d *dnsARecordSetDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*DNSClient) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *DNSClient, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + d.client = client +} + func (d *dnsARecordSetDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { var config aRecordSetConfig @@ -57,16 +83,42 @@ func (d *dnsARecordSetDataSource) Read(ctx context.Context, req datasource.ReadR return } - host := config.Host.ValueString() - a, _, err := lookupIP(host) - if err != nil { - resp.Diagnostics.AddError(fmt.Sprintf("error looking up A records for %q: ", host), err.Error()) + if config.UseUpdateServer.ValueBool() && d.client == nil { + resp.Diagnostics.AddError("use_update_server enabled, but no update server configured", "If you set use_update_server to true, an update server needs to be configured for the provider") return } - sort.Strings(a) + + host := config.Host.ValueString() + + answers := []string{} + if !config.UseUpdateServer.ValueBool() || d.client == nil { + var err error + answers, _, err = lookupIP(host) + if err != nil { + resp.Diagnostics.AddError(fmt.Sprintf("error looking up A records for %q: ", host), err.Error()) + return + } + } else { + records, diags := resourceDnsRead_framework_flags(dnsConfig{Name: host}, d.client, dns.TypeA, dns.MsgHdr{RecursionDesired: true}) + resp.Diagnostics.Append(diags...) + if diags.HasError() { + return + } + + for _, record := range records { + addr, _, err := getAVal(record) + if err != nil { + resp.Diagnostics.AddError("Error querying DNS record:", err.Error()) + return + } + + answers = append(answers, addr) + } + } + sort.Strings(answers) var convertDiags diag.Diagnostics - config.Addrs, convertDiags = types.ListValueFrom(ctx, config.Addrs.ElementType(ctx), a) + config.Addrs, convertDiags = types.ListValueFrom(ctx, config.Addrs.ElementType(ctx), answers) resp.Diagnostics.Append(convertDiags...) if resp.Diagnostics.HasError() { return @@ -77,7 +129,8 @@ func (d *dnsARecordSetDataSource) Read(ctx context.Context, req datasource.ReadR } type aRecordSetConfig struct { - ID types.String `tfsdk:"id"` - Host types.String `tfsdk:"host"` - Addrs types.List `tfsdk:"addrs"` + ID types.String `tfsdk:"id"` + Host types.String `tfsdk:"host"` + UseUpdateServer types.Bool `tfsdk:"use_update_server"` + Addrs types.List `tfsdk:"addrs"` } diff --git a/internal/provider/data_dns_a_record_set_test.go b/internal/provider/data_dns_a_record_set_test.go index 93d1f956..58d03e19 100644 --- a/internal/provider/data_dns_a_record_set_test.go +++ b/internal/provider/data_dns_a_record_set_test.go @@ -30,3 +30,27 @@ data "dns_a_record_set" "test" { }, }) } + +func TestAccDataDnsARecordSet_BasicUpdateProvider(t *testing.T) { + recordName := "data.dns_a_record_set.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: ` +data "dns_a_record_set" "test" { + host = "terraform-provider-dns-a.hashicorptest.com" + use_update_server = true +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(recordName, "addrs.#", "1"), + resource.TestCheckTypeSetElemAttr(recordName, "addrs.*", "127.0.0.1"), + resource.TestCheckResourceAttr(recordName, "id", "terraform-provider-dns-a.hashicorptest.com"), + ), + }, + }, + }) +} diff --git a/internal/provider/data_dns_aaaa_record_set.go b/internal/provider/data_dns_aaaa_record_set.go index 5ed2ebb5..7d9c7559 100644 --- a/internal/provider/data_dns_aaaa_record_set.go +++ b/internal/provider/data_dns_aaaa_record_set.go @@ -12,17 +12,21 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/miekg/dns" ) var ( - _ datasource.DataSource = (*dnsAAAARecordSetDataSource)(nil) + _ datasource.DataSource = (*dnsAAAARecordSetDataSource)(nil) + _ datasource.DataSourceWithConfigure = (*dnsAAAARecordSetDataSource)(nil) ) func NewDnsAAAARecordSetDataSource() datasource.DataSource { return &dnsAAAARecordSetDataSource{} } -type dnsAAAARecordSetDataSource struct{} +type dnsAAAARecordSetDataSource struct { + client *DNSClient +} func (d *dnsAAAARecordSetDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_aaaa_record_set" @@ -36,6 +40,10 @@ func (d *dnsAAAARecordSetDataSource) Schema(ctx context.Context, req datasource. Required: true, Description: "Host to look up.", }, + "use_update_server": schema.BoolAttribute{ + Optional: true, + Description: "Whether to use the configured update DNS server", + }, "addrs": schema.ListAttribute{ ElementType: types.StringType, Computed: true, @@ -49,6 +57,24 @@ func (d *dnsAAAARecordSetDataSource) Schema(ctx context.Context, req datasource. } } +func (d *dnsAAAARecordSetDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*DNSClient) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *DNSClient, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + d.client = client +} + func (d *dnsAAAARecordSetDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { var config aRecordSetConfig @@ -57,16 +83,42 @@ func (d *dnsAAAARecordSetDataSource) Read(ctx context.Context, req datasource.Re return } - host := config.Host.ValueString() - _, aaaa, err := lookupIP(host) - if err != nil { - resp.Diagnostics.AddError(fmt.Sprintf("error looking up AAAA records for %q: ", host), err.Error()) + if config.UseUpdateServer.ValueBool() && d.client == nil { + resp.Diagnostics.AddError("use_update_server enabled, but no update server configured", "If you set use_update_server to true, an update server needs to be configured for the provider") return } - sort.Strings(aaaa) + + host := config.Host.ValueString() + + answers := []string{} + if !config.UseUpdateServer.ValueBool() || d.client == nil { + var err error + _, answers, err = lookupIP(host) + if err != nil { + resp.Diagnostics.AddError(fmt.Sprintf("error looking up AAAA records for %q: ", host), err.Error()) + return + } + } else { + records, diags := resourceDnsRead_framework_flags(dnsConfig{Name: host}, d.client, dns.TypeAAAA, dns.MsgHdr{RecursionDesired: true}) + resp.Diagnostics.Append(diags...) + if diags.HasError() { + return + } + + for _, record := range records { + addr, _, err := getAAAAVal(record) + if err != nil { + resp.Diagnostics.AddError("Error querying DNS record:", err.Error()) + return + } + + answers = append(answers, addr) + } + } + sort.Strings(answers) var convertDiags diag.Diagnostics - config.Addrs, convertDiags = types.ListValueFrom(ctx, config.Addrs.ElementType(ctx), aaaa) + config.Addrs, convertDiags = types.ListValueFrom(ctx, config.Addrs.ElementType(ctx), answers) resp.Diagnostics.Append(convertDiags...) if resp.Diagnostics.HasError() { return diff --git a/internal/provider/data_dns_aaaa_record_set_test.go b/internal/provider/data_dns_aaaa_record_set_test.go index cb3abedc..3fe4390b 100644 --- a/internal/provider/data_dns_aaaa_record_set_test.go +++ b/internal/provider/data_dns_aaaa_record_set_test.go @@ -30,3 +30,27 @@ data "dns_aaaa_record_set" "test" { }, }) } + +func TestAccDataDnsAAAARecordSet_BasicUpdateServer(t *testing.T) { + recordName := "data.dns_aaaa_record_set.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: ` +data "dns_aaaa_record_set" "test" { + host = "terraform-provider-dns-aaaa.hashicorptest.com" + use_update_server = true +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(recordName, "addrs.#", "1"), + resource.TestCheckTypeSetElemAttr(recordName, "addrs.*", "::1"), + resource.TestCheckResourceAttr(recordName, "id", "terraform-provider-dns-aaaa.hashicorptest.com"), + ), + }, + }, + }) +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 3b7e47e2..8f7fc4da 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -489,8 +489,6 @@ func exchange(msg *dns.Msg, tsig bool, client *DNSClient) (*dns.Msg, error) { keyname = k } - msg.RecursionDesired = false - if tsig && keyname != "" { msg.SetTsig(keyname, keyalgo, 300, time.Now().Unix()) } diff --git a/internal/provider/provider_framework.go b/internal/provider/provider_framework.go index ac814754..cdbdbca1 100644 --- a/internal/provider/provider_framework.go +++ b/internal/provider/provider_framework.go @@ -325,10 +325,13 @@ func (p *dnsProvider) Configure(ctx context.Context, req provider.ConfigureReque keytab: keytab, } - resp.ResourceData, configErr = config.Client(ctx) + client, configErr := config.Client(ctx) if configErr != nil { resp.Diagnostics.AddError("Error initializing DNS Client:", configErr.Error()) } + + resp.ResourceData = client + resp.DataSourceData = client } func (p *dnsProvider) Resources(ctx context.Context) []func() resource.Resource { @@ -496,11 +499,16 @@ func resourceFQDN_framework(config dnsConfig) string { } func resourceDnsRead_framework(config dnsConfig, client *DNSClient, rrType uint16) ([]dns.RR, diag.Diagnostics) { + return resourceDnsRead_framework_flags(config, client, rrType, dns.MsgHdr{RecursionDesired: false}) +} + +func resourceDnsRead_framework_flags(config dnsConfig, client *DNSClient, rrType uint16, flags dns.MsgHdr) ([]dns.RR, diag.Diagnostics) { var diags diag.Diagnostics fqdn := resourceFQDN_framework(config) msg := new(dns.Msg) msg.SetQuestion(fqdn, rrType) + msg.RecursionDesired = flags.RecursionDesired r, err := exchange(msg, true, client) if err != nil { diff --git a/internal/provider/provider_framework_test.go b/internal/provider/provider_framework_test.go index fd7dbd30..9fcb87fa 100644 --- a/internal/provider/provider_framework_test.go +++ b/internal/provider/provider_framework_test.go @@ -32,7 +32,6 @@ func testProviderSchemaConfig(t *testing.T, ctx context.Context, schema schema.S objectValue := types.ObjectValueMust(objectType.AttributeTypes(), values) tfTypesValue, err := objectValue.ToTerraformValue(ctx) - if err != nil { t.Fatalf("unexpected error converting to tftypes: %s", err) } @@ -92,6 +91,14 @@ func TestDnsProviderConfigure(t *testing.T) { srv_addr: ":53", transport: "udp", }, + DataSourceData: &DNSClient{ + c: &dns.Client{ + Net: "udp", + }, + retries: 3, + srv_addr: ":53", + transport: "udp", + }, }, }, "update-port-config": { @@ -127,6 +134,14 @@ func TestDnsProviderConfigure(t *testing.T) { srv_addr: ":1053", transport: "udp", }, + DataSourceData: &DNSClient{ + c: &dns.Client{ + Net: "udp", + }, + retries: 3, + srv_addr: ":1053", + transport: "udp", + }, }, }, "update-port-config-and-env": { @@ -165,6 +180,14 @@ func TestDnsProviderConfigure(t *testing.T) { srv_addr: ":1053", transport: "udp", }, + DataSourceData: &DNSClient{ + c: &dns.Client{ + Net: "udp", + }, + retries: 3, + srv_addr: ":1053", + transport: "udp", + }, }, }, "update-port-env": { @@ -185,6 +208,14 @@ func TestDnsProviderConfigure(t *testing.T) { srv_addr: ":1053", transport: "udp", }, + DataSourceData: &DNSClient{ + c: &dns.Client{ + Net: "udp", + }, + retries: 3, + srv_addr: ":1053", + transport: "udp", + }, }, }, "update-port-env-invalid": { @@ -203,7 +234,8 @@ func TestDnsProviderConfigure(t *testing.T) { "strconv.Atoi: parsing \"not-an-int\": invalid syntax", ), }, - ResourceData: nil, + ResourceData: nil, + DataSourceData: nil, }, }, "update-server-config": { @@ -239,6 +271,14 @@ func TestDnsProviderConfigure(t *testing.T) { srv_addr: "example.com:53", transport: "udp", }, + DataSourceData: &DNSClient{ + c: &dns.Client{ + Net: "udp", + }, + retries: 3, + srv_addr: "example.com:53", + transport: "udp", + }, }, }, "update-server-config-and-env": { @@ -277,6 +317,14 @@ func TestDnsProviderConfigure(t *testing.T) { srv_addr: "example.com:53", transport: "udp", }, + DataSourceData: &DNSClient{ + c: &dns.Client{ + Net: "udp", + }, + retries: 3, + srv_addr: "example.com:53", + transport: "udp", + }, }, }, "update-server-env": { @@ -297,6 +345,14 @@ func TestDnsProviderConfigure(t *testing.T) { srv_addr: "example.com:53", transport: "udp", }, + DataSourceData: &DNSClient{ + c: &dns.Client{ + Net: "udp", + }, + retries: 3, + srv_addr: "example.com:53", + transport: "udp", + }, }, }, "update-timeout-config-duration": { @@ -333,6 +389,15 @@ func TestDnsProviderConfigure(t *testing.T) { srv_addr: ":53", transport: "udp", }, + DataSourceData: &DNSClient{ + c: &dns.Client{ + Net: "udp", + Timeout: 5 * time.Second, + }, + retries: 3, + srv_addr: ":53", + transport: "udp", + }, }, }, "update-timeout-config-number": { @@ -369,6 +434,15 @@ func TestDnsProviderConfigure(t *testing.T) { srv_addr: ":53", transport: "udp", }, + DataSourceData: &DNSClient{ + c: &dns.Client{ + Net: "udp", + Timeout: 5 * time.Second, + }, + retries: 3, + srv_addr: ":53", + transport: "udp", + }, }, }, "update-timeout-config-and-env": { @@ -408,6 +482,15 @@ func TestDnsProviderConfigure(t *testing.T) { srv_addr: ":53", transport: "udp", }, + DataSourceData: &DNSClient{ + c: &dns.Client{ + Net: "udp", + Timeout: 5 * time.Second, + }, + retries: 3, + srv_addr: ":53", + transport: "udp", + }, }, }, "update-timeout-env-duration": { @@ -429,6 +512,15 @@ func TestDnsProviderConfigure(t *testing.T) { srv_addr: ":53", transport: "udp", }, + DataSourceData: &DNSClient{ + c: &dns.Client{ + Net: "udp", + Timeout: 5 * time.Second, + }, + retries: 3, + srv_addr: ":53", + transport: "udp", + }, }, }, "update-timeout-env-number": { @@ -450,6 +542,15 @@ func TestDnsProviderConfigure(t *testing.T) { srv_addr: ":53", transport: "udp", }, + DataSourceData: &DNSClient{ + c: &dns.Client{ + Net: "udp", + Timeout: 5 * time.Second, + }, + retries: 3, + srv_addr: ":53", + transport: "udp", + }, }, }, "update-timeout-env-invalid": { @@ -468,7 +569,8 @@ func TestDnsProviderConfigure(t *testing.T) { "Timeout cannot be parsed as an integer: strconv.Atoi: parsing \"not-an-int\": invalid syntax", ), }, - ResourceData: nil, + ResourceData: nil, + DataSourceData: nil, }, }, "update-transport-config": { @@ -504,6 +606,14 @@ func TestDnsProviderConfigure(t *testing.T) { srv_addr: ":53", transport: "tcp", }, + DataSourceData: &DNSClient{ + c: &dns.Client{ + Net: "tcp", + }, + retries: 3, + srv_addr: ":53", + transport: "tcp", + }, }, }, "update-transport-config-and-env": { @@ -542,6 +652,14 @@ func TestDnsProviderConfigure(t *testing.T) { srv_addr: ":53", transport: "tcp", }, + DataSourceData: &DNSClient{ + c: &dns.Client{ + Net: "tcp", + }, + retries: 3, + srv_addr: ":53", + transport: "tcp", + }, }, }, "update-transport-env": { @@ -562,6 +680,14 @@ func TestDnsProviderConfigure(t *testing.T) { srv_addr: ":53", transport: "tcp", }, + DataSourceData: &DNSClient{ + c: &dns.Client{ + Net: "tcp", + }, + retries: 3, + srv_addr: ":53", + transport: "tcp", + }, }, }, }