From 8c31f2c646cfb11e526ac8f39db2dc9e49ddb3da Mon Sep 17 00:00:00 2001 From: beatz174-bit Date: Thu, 23 Apr 2026 12:58:45 +1000 Subject: [PATCH] Fix datasource hostname decode against full config objects --- internal/provider/config_hostname.go | 37 +++--------- internal/provider/config_hostname_test.go | 60 ++++++++++++++++++++ internal/provider/data_source_dns_records.go | 2 +- internal/provider/data_source_domain.go | 2 +- 4 files changed, 70 insertions(+), 31 deletions(-) create mode 100644 internal/provider/config_hostname_test.go diff --git a/internal/provider/config_hostname.go b/internal/provider/config_hostname.go index 08e9909..aa3e7b0 100644 --- a/internal/provider/config_hostname.go +++ b/internal/provider/config_hostname.go @@ -1,41 +1,20 @@ package provider import ( + "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) -func hostnameFromConfig(config tfsdk.Config) (types.String, error) { - if !config.Raw.IsKnown() { - return types.StringUnknown(), nil - } - if config.Raw.IsNull() { - return types.StringNull(), nil +func hostnameFromConfig(ctx context.Context, config tfsdk.Config) (types.String, error) { + var hostname types.String + diags := config.GetAttribute(ctx, path.Root("hostname"), &hostname) + if diags.HasError() { + return types.StringNull(), fmt.Errorf("decode hostname from data source config: %s", diags.Errors()[0].Summary()) } - attributes := map[string]tftypes.Value{} - if err := config.Raw.As(&attributes); err != nil { - return types.StringNull(), fmt.Errorf("decode data source config object: %w", err) - } - - hostnameValue, ok := attributes["hostname"] - if !ok { - return types.StringNull(), fmt.Errorf("hostname is missing from data source config") - } - if !hostnameValue.IsKnown() { - return types.StringUnknown(), nil - } - if hostnameValue.IsNull() { - return types.StringNull(), nil - } - - var hostname string - if err := hostnameValue.As(&hostname); err != nil { - return types.StringNull(), fmt.Errorf("decode hostname: %w", err) - } - - return types.StringValue(hostname), nil + return hostname, nil } diff --git a/internal/provider/config_hostname_test.go b/internal/provider/config_hostname_test.go new file mode 100644 index 0000000..3cd9ae2 --- /dev/null +++ b/internal/provider/config_hostname_test.go @@ -0,0 +1,60 @@ +package provider + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func TestHostnameFromConfigIgnoresComputedAttributesDomain(t *testing.T) { + ds := NewDomainDataSource().(*domainDataSource) + var schemaResp datasource.SchemaResponse + ds.Schema(context.Background(), datasource.SchemaRequest{}, &schemaResp) + + configType := tftypes.Object{AttributeTypes: map[string]tftypes.Type{ + "hostname": tftypes.String, + "domain": tftypes.DynamicPseudoType, + }} + configValue := tftypes.NewValue(configType, map[string]tftypes.Value{ + "hostname": tftypes.NewValue(tftypes.String, "www.example.com"), + "domain": tftypes.NewValue(tftypes.DynamicPseudoType, tftypes.UnknownValue), + }) + + hostname, err := hostnameFromConfig(context.Background(), tfsdk.Config{Schema: schemaResp.Schema, Raw: configValue}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if hostname.ValueString() != "www.example.com" { + t.Fatalf("unexpected hostname: %q", hostname.ValueString()) + } +} + +func TestHostnameFromConfigIgnoresComputedAttributesDNSRecords(t *testing.T) { + ds := NewDNSRecordsDataSource().(*dnsRecordsDataSource) + var schemaResp datasource.SchemaResponse + ds.Schema(context.Background(), datasource.SchemaRequest{}, &schemaResp) + + configType := tftypes.Object{AttributeTypes: map[string]tftypes.Type{ + "hostname": tftypes.String, + "domain_id": tftypes.Number, + "domain_name": tftypes.String, + "records": tftypes.DynamicPseudoType, + }} + configValue := tftypes.NewValue(configType, map[string]tftypes.Value{ + "hostname": tftypes.NewValue(tftypes.String, "www.example.com"), + "domain_id": tftypes.NewValue(tftypes.Number, tftypes.UnknownValue), + "domain_name": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "records": tftypes.NewValue(tftypes.DynamicPseudoType, tftypes.UnknownValue), + }) + + hostname, err := hostnameFromConfig(context.Background(), tfsdk.Config{Schema: schemaResp.Schema, Raw: configValue}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if hostname.ValueString() != "www.example.com" { + t.Fatalf("unexpected hostname: %q", hostname.ValueString()) + } +} diff --git a/internal/provider/data_source_dns_records.go b/internal/provider/data_source_dns_records.go index cf43121..2482d30 100644 --- a/internal/provider/data_source_dns_records.go +++ b/internal/provider/data_source_dns_records.go @@ -100,7 +100,7 @@ func (d *dnsRecordsDataSource) Configure(_ context.Context, req datasource.Confi } func (d *dnsRecordsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - hostname, err := hostnameFromConfig(req.Config) + hostname, err := hostnameFromConfig(ctx, req.Config) if err != nil { resp.Diagnostics.AddError("Unable to parse data source configuration", err.Error()) return diff --git a/internal/provider/data_source_domain.go b/internal/provider/data_source_domain.go index 5e225bb..8bcb503 100644 --- a/internal/provider/data_source_domain.go +++ b/internal/provider/data_source_domain.go @@ -73,7 +73,7 @@ func (d *domainDataSource) Configure(_ context.Context, req datasource.Configure } func (d *domainDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - hostname, err := hostnameFromConfig(req.Config) + hostname, err := hostnameFromConfig(ctx, req.Config) if err != nil { resp.Diagnostics.AddError("Unable to parse data source configuration", err.Error()) return