Archived
Handle unknown dns_record content during config validation
This commit is contained in:
@@ -110,12 +110,12 @@ func (r *dnsRecordResource) ValidateConfig(ctx context.Context, req resource.Val
|
|||||||
if skip {
|
if skip {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
content := stringPointerFromOptionalContent(config.Content)
|
content, contentKnown := stringPointerFromOptionalContentForValidation(config.Content)
|
||||||
dynamicIntent, ok := resolveDynamicIntent(recordType, config.Content, config.Dynamic, &resp.Diagnostics)
|
dynamicIntent, ok := resolveDynamicIntent(recordType, config.Content, config.Dynamic, &resp.Diagnostics)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
validateDNSRecordContentForType(recordType, content, dynamicIntent, &resp.Diagnostics)
|
validateDNSRecordContentForTypeWithKnowledge(recordType, content, contentKnown, dynamicIntent, &resp.Diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *dnsRecordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
func (r *dnsRecordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||||
@@ -393,11 +393,22 @@ func stringPointerFromOptionalContent(value types.String) *string {
|
|||||||
return &trimmed
|
return &trimmed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func stringPointerFromOptionalContentForValidation(value types.String) (*string, bool) {
|
||||||
|
if value.IsUnknown() {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return stringPointerFromOptionalContent(value), true
|
||||||
|
}
|
||||||
|
|
||||||
func stringPointer(value string) *string {
|
func stringPointer(value string) *string {
|
||||||
return &value
|
return &value
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateDNSRecordContentForType(recordType string, content *string, dynamicIntent bool, diagnostics *diag.Diagnostics) bool {
|
func validateDNSRecordContentForType(recordType string, content *string, dynamicIntent bool, diagnostics *diag.Diagnostics) bool {
|
||||||
|
return validateDNSRecordContentForTypeWithKnowledge(recordType, content, true, dynamicIntent, diagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateDNSRecordContentForTypeWithKnowledge(recordType string, content *string, contentKnown bool, dynamicIntent bool, diagnostics *diag.Diagnostics) bool {
|
||||||
normalizedType := strings.ToUpper(strings.TrimSpace(recordType))
|
normalizedType := strings.ToUpper(strings.TrimSpace(recordType))
|
||||||
trimmedContent := ""
|
trimmedContent := ""
|
||||||
if content != nil {
|
if content != nil {
|
||||||
@@ -440,6 +451,10 @@ func validateDNSRecordContentForType(recordType string, content *string, dynamic
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !contentKnown {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
if trimmedContent == "" {
|
if trimmedContent == "" {
|
||||||
diagnostics.AddError(
|
diagnostics.AddError(
|
||||||
"Missing required content for DNS record type",
|
"Missing required content for DNS record type",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package provider
|
package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||||
@@ -79,6 +80,59 @@ func TestResolveDynamicIntent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateDNSRecordContentForTypeWithKnowledge(t *testing.T) {
|
||||||
|
nonEmpty := "example.com"
|
||||||
|
blank := ""
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
recordType string
|
||||||
|
content *string
|
||||||
|
contentKnown bool
|
||||||
|
dynamic bool
|
||||||
|
wantValid bool
|
||||||
|
wantMessage string
|
||||||
|
}{
|
||||||
|
{name: "CNAME unknown content is allowed during validate", recordType: "CNAME", content: nil, contentKnown: false, wantValid: true},
|
||||||
|
{name: "CNAME null content errors", recordType: "CNAME", content: nil, contentKnown: true, wantValid: false, wantMessage: "Missing required content"},
|
||||||
|
{name: "CNAME empty content errors", recordType: "CNAME", content: &blank, contentKnown: true, wantValid: false, wantMessage: "Missing required content"},
|
||||||
|
{name: "CNAME known content passes", recordType: "CNAME", content: &nonEmpty, contentKnown: true, wantValid: true},
|
||||||
|
{name: "CNAME dynamic true errors", recordType: "CNAME", content: nil, contentKnown: false, dynamic: true, wantValid: false, wantMessage: "Dynamic mode is only supported"},
|
||||||
|
{name: "A dynamic omitted content passes", recordType: "A", content: nil, contentKnown: true, dynamic: true, wantValid: true},
|
||||||
|
{name: "AAAA dynamic omitted content passes", recordType: "AAAA", content: nil, contentKnown: true, dynamic: true, wantValid: true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
diags := diag.Diagnostics{}
|
||||||
|
got := validateDNSRecordContentForTypeWithKnowledge(tc.recordType, tc.content, tc.contentKnown, tc.dynamic, &diags)
|
||||||
|
if got != tc.wantValid {
|
||||||
|
t.Fatalf("validateDNSRecordContentForTypeWithKnowledge()=%v, want %v", got, tc.wantValid)
|
||||||
|
}
|
||||||
|
if tc.wantValid && diags.HasError() {
|
||||||
|
t.Fatalf("expected no diagnostics, got %#v", diags)
|
||||||
|
}
|
||||||
|
if !tc.wantValid {
|
||||||
|
if !diags.HasError() {
|
||||||
|
t.Fatal("expected diagnostics but got none")
|
||||||
|
}
|
||||||
|
if tc.wantMessage != "" && diags[0].Summary() != "" && !strings.Contains(diags[0].Summary(), tc.wantMessage) {
|
||||||
|
t.Fatalf("expected first diagnostic summary to contain %q, got %q", tc.wantMessage, diags[0].Summary())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringPointerFromOptionalContentForValidation(t *testing.T) {
|
||||||
|
if content, known := stringPointerFromOptionalContentForValidation(types.StringUnknown()); known || content != nil {
|
||||||
|
t.Fatalf("expected unknown content to be unknown with nil pointer, got known=%v content=%v", known, content)
|
||||||
|
}
|
||||||
|
if content, known := stringPointerFromOptionalContentForValidation(types.StringNull()); !known || content != nil {
|
||||||
|
t.Fatalf("expected null content to be known with nil pointer, got known=%v content=%v", known, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNormalizeRecordContentForState(t *testing.T) {
|
func TestNormalizeRecordContentForState(t *testing.T) {
|
||||||
if got := normalizeRecordContentForState("AAAA", "2001:0db8:0000:0000:0000:0000:0000:0123", false); got.ValueString() != "2001:db8::123" {
|
if got := normalizeRecordContentForState("AAAA", "2001:0db8:0000:0000:0000:0000:0000:0123", false); got.ValueString() != "2001:db8::123" {
|
||||||
t.Fatalf("expected canonical IPv6, got %q", got.ValueString())
|
t.Fatalf("expected canonical IPv6, got %q", got.ValueString())
|
||||||
|
|||||||
Reference in New Issue
Block a user